module Sequel::Postgres::PGRange::DatabaseMethods

Public Class Methods

extended(db) click to toggle source

Add the conversion procs to the database and extend the datasets to correctly literalize ruby Range values.

# File lib/sequel/extensions/pg_range.rb, line 136
def self.extended(db)
  db.instance_exec do
    @pg_range_schema_types ||= {}
    extend_datasets(DatasetMethods)
    register_range_type('int4range', :oid=>3904, :subtype_oid=>23)
    register_range_type('numrange', :oid=>3906, :subtype_oid=>1700)
    register_range_type('tsrange', :oid=>3908, :subtype_oid=>1114)
    register_range_type('tstzrange', :oid=>3910, :subtype_oid=>1184)
    register_range_type('daterange', :oid=>3912, :subtype_oid=>1082)
    register_range_type('int8range', :oid=>3926, :subtype_oid=>20)
    if respond_to?(:register_array_type)
      register_array_type('int4range', :oid=>3905, :scalar_oid=>3904, :scalar_typecast=>:int4range)
      register_array_type('numrange', :oid=>3907, :scalar_oid=>3906, :scalar_typecast=>:numrange)
      register_array_type('tsrange', :oid=>3909, :scalar_oid=>3908, :scalar_typecast=>:tsrange)
      register_array_type('tstzrange', :oid=>3911, :scalar_oid=>3910, :scalar_typecast=>:tstzrange)
      register_array_type('daterange', :oid=>3913, :scalar_oid=>3912, :scalar_typecast=>:daterange)
      register_array_type('int8range', :oid=>3927, :scalar_oid=>3926, :scalar_typecast=>:int8range)
    end
    [:int4range, :numrange, :tsrange, :tstzrange, :daterange, :int8range].each do |v|
      @schema_type_classes[v] = PGRange
    end

    procs = conversion_procs
    add_conversion_proc(3908, Parser.new("tsrange", procs[1114]))
    add_conversion_proc(3910, Parser.new("tstzrange", procs[1184]))
    if defined?(PGArray::Creator)
      add_conversion_proc(3909, PGArray::Creator.new("tsrange", procs[3908]))
      add_conversion_proc(3911, PGArray::Creator.new("tstzrange", procs[3910]))
    end
  end
end

Public Instance Methods

bound_variable_arg(arg, conn) click to toggle source

Handle Range and PGRange values in bound variables

Calls superclass method
# File lib/sequel/extensions/pg_range.rb, line 169
def bound_variable_arg(arg, conn)
  case arg
  when PGRange 
    arg.unquoted_literal(schema_utility_dataset)
  when Range
    PGRange.from_range(arg).unquoted_literal(schema_utility_dataset)
  else
    super
  end
end
freeze() click to toggle source

Freeze the pg range schema types to prevent adding new ones.

Calls superclass method
# File lib/sequel/extensions/pg_range.rb, line 181
def freeze
  @pg_range_schema_types.freeze
  super
end
register_range_type(db_type, opts=OPTS, &block) click to toggle source

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, line 198
def register_range_type(db_type, opts=OPTS, &block)
  oid = opts[:oid]
  soid = opts[:subtype_oid]

  if has_converter = opts.has_key?(:converter)
    raise Error, "can't provide both a block and :converter option to register_range_type" if block
    converter = opts[:converter]
  else
    has_converter = true if block
    converter = block
  end

  unless (soid || has_converter) && oid
    range_oid, subtype_oid = from(:pg_range).join(:pg_type, :oid=>:rngtypid).where(:typname=>db_type.to_s).get([:rngtypid, :rngsubtype])
    soid ||= subtype_oid unless has_converter
    oid ||= range_oid
  end

  db_type = db_type.to_s.dup.freeze

  if converter = opts[:converter]
    raise Error, "can't provide both a block and :converter option to register" if block
  else
    converter = block
  end

  if soid
    raise Error, "can't provide both a converter and :subtype_oid option to register" if has_converter 
    raise Error, "no conversion proc for :subtype_oid=>#{soid.inspect} in conversion_procs" unless converter = conversion_procs[soid]
  end

  parser = Parser.new(db_type, converter)
  add_conversion_proc(oid, parser)

  @pg_range_schema_types[db_type] = db_type.to_sym

  singleton_class.class_eval do
    meth = :"typecast_value_#{db_type}"
    define_method(meth){|v| typecast_value_pg_range(v, parser)}
    private meth
  end

  @schema_type_classes[:"#{opts[:type_symbol] || db_type}"] = PGRange
  nil
end

Private Instance Methods

bound_variable_array(a) click to toggle source

Handle arrays of range types in bound variables.

Calls superclass method
# File lib/sequel/extensions/pg_range.rb, line 247
def bound_variable_array(a)
  case a
  when PGRange, Range
    "\"#{bound_variable_arg(a, nil)}\""
  else
    super
  end
end
schema_column_type(db_type) click to toggle source

Recognize the registered database range types.

Calls superclass method
# File lib/sequel/extensions/pg_range.rb, line 257
def schema_column_type(db_type)
  if type = @pg_range_schema_types[db_type]
    type
  else
    super
  end
end
schema_post_process(_) click to toggle source

Set the :ruby_default value if the default value is recognized as a range.

Calls superclass method
# File lib/sequel/extensions/pg_range.rb, line 266
def schema_post_process(_)
  super.each do |a|
    h = a[1]
    db_type = h[:db_type]
    if @pg_range_schema_types[db_type] && h[:default] =~ /\A'([^']+)'::#{db_type}\z/
      default = $1
      if convertor = conversion_procs[h[:oid]]
        h[:ruby_default] = convertor.call(default)
      end
    end
  end
end
typecast_value_pg_range(value, parser) click to toggle source

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, line 286
def typecast_value_pg_range(value, parser)
  case value
  when PGRange
    if value.db_type.to_s == parser.db_type
      value
    elsif value.empty?
      PGRange.empty(parser.db_type)
    else
      PGRange.new(value.begin, value.end, :exclude_begin=>value.exclude_begin?, :exclude_end=>value.exclude_end?, :db_type=>parser.db_type)
    end
  when Range
    PGRange.from_range(value, parser.db_type)
  when String
    parser.call(value)
  else
    raise Sequel::InvalidValue, "invalid value for range type: #{value.inspect}"
  end
end