module Sequel::Postgres::PGArray::DatabaseMethods

Constants

BLOB_RANGE

Public Class Methods

extended(db) click to toggle source

Create the local hash of database type strings to schema type symbols, used for array types local to this database.

# File lib/sequel/extensions/pg_array.rb, line 85
def self.extended(db)
  db.instance_exec do
    @pg_array_schema_types ||= {}
    register_array_type('timestamp without time zone', :oid=>1115, :scalar_oid=>1114, :type_symbol=>:datetime)
    register_array_type('timestamp with time zone', :oid=>1185, :scalar_oid=>1184, :type_symbol=>:datetime_timezone, :scalar_typecast=>:datetime)

    register_array_type('text', :oid=>1009, :scalar_oid=>25, :type_symbol=>:string)
    register_array_type('integer', :oid=>1007, :scalar_oid=>23)
    register_array_type('bigint', :oid=>1016, :scalar_oid=>20, :scalar_typecast=>:integer)
    register_array_type('numeric', :oid=>1231, :scalar_oid=>1700, :type_symbol=>:decimal)
    register_array_type('double precision', :oid=>1022, :scalar_oid=>701, :type_symbol=>:float)

    register_array_type('boolean', :oid=>1000, :scalar_oid=>16)
    register_array_type('bytea', :oid=>1001, :scalar_oid=>17, :type_symbol=>:blob)
    register_array_type('date', :oid=>1182, :scalar_oid=>1082)
    register_array_type('time without time zone', :oid=>1183, :scalar_oid=>1083, :type_symbol=>:time)
    register_array_type('time with time zone', :oid=>1270, :scalar_oid=>1266, :type_symbol=>:time_timezone, :scalar_typecast=>:time)

    register_array_type('smallint', :oid=>1005, :scalar_oid=>21, :scalar_typecast=>:integer)
    register_array_type('oid', :oid=>1028, :scalar_oid=>26, :scalar_typecast=>:integer)
    register_array_type('real', :oid=>1021, :scalar_oid=>700, :scalar_typecast=>:float)
    register_array_type('character', :oid=>1014, :converter=>nil, :array_type=>:text, :scalar_typecast=>:string)
    register_array_type('character varying', :oid=>1015, :converter=>nil, :scalar_typecast=>:string, :type_symbol=>:varchar)

    register_array_type('xml', :oid=>143, :scalar_oid=>142)
    register_array_type('money', :oid=>791, :scalar_oid=>790)
    register_array_type('bit', :oid=>1561, :scalar_oid=>1560)
    register_array_type('bit varying', :oid=>1563, :scalar_oid=>1562, :type_symbol=>:varbit)
    register_array_type('uuid', :oid=>2951, :scalar_oid=>2950)

    register_array_type('xid', :oid=>1011, :scalar_oid=>28)
    register_array_type('cid', :oid=>1012, :scalar_oid=>29)

    register_array_type('name', :oid=>1003, :scalar_oid=>19)
    register_array_type('tid', :oid=>1010, :scalar_oid=>27)
    register_array_type('int2vector', :oid=>1006, :scalar_oid=>22)
    register_array_type('oidvector', :oid=>1013, :scalar_oid=>30)

    [:string_array, :integer_array, :decimal_array, :float_array, :boolean_array, :blob_array, :date_array, :time_array, :datetime_array].each do |v|
      @schema_type_classes[v] = PGArray
    end
  end
end

Public Instance Methods

add_named_conversion_proc(name, &block) click to toggle source
Calls superclass method
# File lib/sequel/extensions/pg_array.rb, line 129
def add_named_conversion_proc(name, &block)
  ret = super
  name = name.to_s if name.is_a?(Symbol)
  from(:pg_type).where(:typname=>name).select_map([:oid, :typarray]).each do |scalar_oid, array_oid|
    register_array_type(name, :oid=>array_oid.to_i, :scalar_oid=>scalar_oid.to_i)
  end
  ret
end
bound_variable_arg(arg, conn) click to toggle source

Handle arrays in bound variables

Calls superclass method
# File lib/sequel/extensions/pg_array.rb, line 139
def bound_variable_arg(arg, conn)
  case arg
  when PGArray
    bound_variable_array(arg.to_a)
  when Array
    bound_variable_array(arg)
  else
    super
  end
end
freeze() click to toggle source

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

Calls superclass method
# File lib/sequel/extensions/pg_array.rb, line 151
def freeze
  @pg_array_schema_types.freeze
  super
end
register_array_type(db_type, opts=OPTS, &block) click to toggle source

Register a database specific array type. Options:

:array_type

The type to automatically cast the array to when literalizing the array. Usually the same as db_type.

:converter

A callable object (e.g. Proc), that is called with each element of the array (usually a string), and should return the appropriate typecasted object.

:oid

The PostgreSQL OID for the array type. This is used by the Sequel postgres adapter to set up automatic type conversion on retrieval from the database.

:scalar_oid

Should be the PostgreSQL OID for the scalar version of this array type. If given, automatically sets the :converter option by looking for scalar conversion proc.

:scalar_typecast

Should be a symbol indicating the typecast method that should be called on each element of the array, when a plain array is passed into a database typecast method. For example, for an array of integers, this could be set to :integer, so that the typecast_value_integer method is called on all of the array elements. Defaults to :type_symbol option.

:type_symbol

The base of the schema type symbol for this type. For example, if you provide :integer, Sequel will recognize this type as :integer_array during schema parsing. Defaults to the db_type argument.

If a block is given, it is treated as the :converter option.

# File lib/sequel/extensions/pg_array.rb, line 177
def register_array_type(db_type, opts=OPTS, &block)
  oid = opts[:oid]
  soid = opts[:scalar_oid]

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

  unless (soid || has_converter) && oid
    array_oid, scalar_oid = from(:pg_type).where(:typname=>db_type.to_s).get([:typarray, :oid])
    soid ||= scalar_oid unless has_converter
    oid ||= array_oid
  end

  db_type = db_type.to_s
  type = (opts[:type_symbol] || db_type).to_sym
  typecast_method_map = @pg_array_schema_types

  if soid
    raise Error, "can't provide both a converter and :scalar_oid option to register" if has_converter 
    converter = conversion_procs[soid]
  end

  array_type = (opts[:array_type] || db_type).to_s.dup.freeze
  creator = Creator.new(array_type, converter)
  add_conversion_proc(oid, creator)

  typecast_method_map[db_type] = :"#{type}_array"

  singleton_class.class_eval do
    meth = :"typecast_value_#{type}_array"
    scalar_typecast_method = :"typecast_value_#{opts.fetch(:scalar_typecast, type)}"
    define_method(meth){|v| typecast_value_pg_array(v, creator, scalar_typecast_method)}
    private meth
    alias_method(meth, meth)
  end

  @schema_type_classes[:"#{type}_array"] = PGArray
  nil
end

Private Instance Methods

bound_variable_array(a) click to toggle source

Format arrays used in bound variables.

# File lib/sequel/extensions/pg_array.rb, line 225
def bound_variable_array(a)
  case a
  when Array
    "{#{a.map{|i| bound_variable_array(i)}.join(',')}}"
  when Sequel::SQL::Blob
    bound_variable_array_string(literal(a)[BLOB_RANGE].gsub("''", "'"))
  when Sequel::LiteralString
    a
  when String
    bound_variable_array_string(a)
  when Float
    if a.infinite?
      a > 0 ? '"Infinity"' : '"-Infinity"'
    elsif a.nan?
      '"NaN"'
    else
      literal(a)
    end
  when Time, Date
    @default_dataset.literal_date_or_time(a)
  else
    if (s = bound_variable_arg(a, nil)).is_a?(String)
      bound_variable_array_string(s)
    else
      literal(a)
    end
  end
end
bound_variable_array_string(s) click to toggle source

Escape strings used as array members in bound variables. Most complex will create a regular string with #bound_variable_arg, and then use this escaping to format it as an array member.

# File lib/sequel/extensions/pg_array.rb, line 257
def bound_variable_array_string(s)
  "\"#{s.gsub(/("|\\)/, '\\\\\1')}\""
end
column_definition_default_sql(sql, column) click to toggle source

Convert ruby arrays to PostgreSQL arrays when used as default values.

Calls superclass method
# File lib/sequel/extensions/pg_array.rb, line 289
def column_definition_default_sql(sql, column)
  if (d = column[:default]) && d.is_a?(Array) && !Sequel.condition_specifier?(d)
    sql << " DEFAULT (#{literal(Sequel.pg_array(d))}::#{type_literal(column)})"
  else
    super
  end
end
pg_array_schema_type(type) click to toggle source

Look into both the current database's array schema types and the global array schema types to get the type symbol for the given database type string.

# File lib/sequel/extensions/pg_array.rb, line 264
def pg_array_schema_type(type)
  @pg_array_schema_types[type]
end
schema_array_type(db_type) click to toggle source

Make the column type detection handle registered array types.

Calls superclass method
# File lib/sequel/extensions/pg_array.rb, line 269
def schema_array_type(db_type)
  if (db_type =~ /\A([^(]+)(?:\([^(]+\))?\[\]\z/io) && (type = pg_array_schema_type($1))
    type
  else
    super
  end
end
schema_post_process(_) click to toggle source

Set the :callable_default value if the default value is recognized as an empty array.

Calls superclass method
# File lib/sequel/extensions/pg_array.rb, line 278
def schema_post_process(_)
  super.each do |a|
    h = a[1]
    if h[:default] =~ /\A(?:'\{\}'|ARRAY\[\])::([\w ]+)\[\]\z/
      type = $1.freeze
      h[:callable_default] = lambda{Sequel.pg_array([], type)}
    end
  end
end
typecast_value_pg_array(value, creator, scalar_typecast_method=nil) click to toggle source

Given a value to typecast and the type of PGArray subclass:

  • If given a PGArray with a matching array_type, use it directly.

  • If given a PGArray with a different array_type, return a PGArray with the creator's type.

  • If given an Array, create a new PGArray instance for it. This does not typecast all members of the array in ruby for performance reasons, but it will cast the array the appropriate database type when the array is literalized.

# File lib/sequel/extensions/pg_array.rb, line 305
def typecast_value_pg_array(value, creator, scalar_typecast_method=nil)
  case value
  when PGArray
    if value.array_type != creator.type
      PGArray.new(value.to_a, creator.type)
    else
      value
    end
  when Array
    if scalar_typecast_method && respond_to?(scalar_typecast_method, true)
      value = Sequel.recursive_map(value, method(scalar_typecast_method))
    end
    PGArray.new(value, creator.type)
  else
    raise Sequel::InvalidValue, "invalid value for array type: #{value.inspect}"
  end
end