module Sequel::Postgres::PGRow::DatabaseMethods

Constants

COMMA
ESCAPE_RE
ESCAPE_REPLACEMENT

Attributes

row_types[R]

A hash mapping row type keys (usually symbols), to option hashes. At the least, the values will contain the :parser option for the Parser instance that the type will use.

Public Class Methods

extended(db) click to toggle source

Do some setup for the data structures the module uses.

# File lib/sequel/extensions/pg_row.rb, line 386
def self.extended(db)
  # Return right away if row_types has already been set. This
  # makes things not break if a user extends the database with
  # this module more than once (since extended is called every
  # time).
  return if db.row_types

  db.instance_eval do
    @row_types = {}
    @row_schema_types = {}
    extend(@row_type_method_module = Module.new)
    copy_conversion_procs([2249, 2287])
  end
end

Public Instance Methods

bound_variable_arg(arg, conn) click to toggle source

Handle ArrayRow and HashRow values in bound variables.

Calls superclass method
# File lib/sequel/extensions/pg_row.rb, line 402
def bound_variable_arg(arg, conn)
  case arg
  when ArrayRow
    "(#{arg.map{|v| bound_variable_array(v) if v}.join(COMMA)})"
  when HashRow
    arg.check_columns!
    "(#{arg.values_at(*arg.columns).map{|v| bound_variable_array(v) if v}.join(COMMA)})"
  else
    super
  end
end
freeze() click to toggle source

Freeze the row types and row schema types to prevent adding new ones.

Calls superclass method
# File lib/sequel/extensions/pg_row.rb, line 415
def freeze
  @row_types.freeze
  @row_schema_types.freeze
  @row_type_method_module.freeze
  super
end
register_row_type(db_type, opts=OPTS) click to toggle source

Register a new row type for the Database instance. db_type should be the type symbol. This parses the PostgreSQL system tables to get information the composite type, and by default has the type return instances of a subclass of HashRow.

The following options are supported:

:converter

Use a custom converter for the parser.

:typecaster

Use a custom typecaster for the parser.

# File lib/sequel/extensions/pg_row.rb, line 431
def register_row_type(db_type, opts=OPTS)
  procs = @conversion_procs
  rel_oid = nil
  array_oid = nil
  parser_opts = {}

  # Try to handle schema-qualified types.
  type_schema, type_name = schema_and_table(db_type)
  schema_type_string = type_name.to_s

  # Get basic oid information for the composite type.
  ds = from(:pg_type).
    select{[pg_type[:oid], :typrelid, :typarray]}.
    where([[:typtype, 'c'], [:typname, type_name.to_s]])
  if type_schema
    ds = ds.join(:pg_namespace, [[:oid, :typnamespace], [:nspname, type_schema.to_s]])
    schema_type_symbol = :"pg_row_#{type_schema}__#{type_name}" 
  else
    schema_type_symbol = :"pg_row_#{type_name}"
  end
  unless row = ds.first
    raise Error, "row type #{db_type.inspect} not found in database"
  end
  # Manually cast to integer using to_i, because adapter may not cast oid type
  # correctly (e.g. swift)
  parser_opts[:oid], rel_oid, array_oid = row.values_at(:oid, :typrelid, :typarray).map(&:to_i)

  # Get column names and oids for each of the members of the composite type.
  res = from(:pg_attribute).
    join(:pg_type, :oid=>:atttypid).
    where(:attrelid=>rel_oid).
    where{attnum > 0}.
    exclude(:attisdropped).
    order(:attnum).
    select_map{[:attname, Sequel.case({0=>:atttypid}, pg_type[:typbasetype], pg_type[:typbasetype]).as(:atttypid)]}
  if res.empty?
    raise Error, "no columns for row type #{db_type.inspect} in database"
  end
  parser_opts[:columns] = res.map{|r| r[0].to_sym}
  parser_opts[:column_oids] = res.map{|r| r[1].to_i}

  # Using the conversion_procs, lookup converters for each member of the composite type
  parser_opts[:column_converters] = parser_opts[:column_oids].map do |oid|
    if pr = procs[oid]
      pr
    elsif !Sequel::Postgres::STRING_TYPES.include?(oid)
      # It's not a string type, and it's possible a conversion proc for this
      # oid will be added later, so do a runtime check for it.
      lambda{|s| (pr = procs[oid]) ? pr.call(s) : s}
    end
  end

  # Setup the converter and typecaster
  parser_opts[:converter] = opts.fetch(:converter){HashRow.subclass(db_type, parser_opts[:columns])}
  parser_opts[:typecaster] = opts.fetch(:typecaster, parser_opts[:converter])

  parser = Parser.new(parser_opts)
  @conversion_procs[parser.oid] = parser

  if defined?(PGArray) && PGArray.respond_to?(:register) && array_oid && array_oid > 0
    array_type_name = if type_schema
      "#{type_schema}.#{type_name}"
    else
      type_name
    end
    PGArray.register(array_type_name, :oid=>array_oid, :converter=>parser, :type_procs=>@conversion_procs, :scalar_typecast=>schema_type_symbol)
  end

  @row_types[literal(db_type)] = opts.merge(:parser=>parser, :type=>db_type)
  @row_schema_types[schema_type_string] = schema_type_symbol 
  @schema_type_classes[schema_type_symbol] = ROW_TYPE_CLASSES
  @row_type_method_module.class_eval do
    meth = :"typecast_value_#{schema_type_symbol}"
    define_method(meth) do |v|
      row_type(db_type, v)
    end
    private meth
  end

  conversion_procs_updated
  nil
end
reset_conversion_procs() click to toggle source

When reseting conversion procs, reregister all the row types so that the system tables are introspected again, picking up database changes.

Calls superclass method
# File lib/sequel/extensions/pg_row.rb, line 516
def reset_conversion_procs
  procs = super

  row_types.values.each do |opts|
    register_row_type(opts[:type], opts)
  end

  procs
end
row_type(db_type, obj) click to toggle source

Handle typecasting of the given object to the given database type. In general, the given database type should already be registered, but if obj is an array, this will handled unregistered types.

# File lib/sequel/extensions/pg_row.rb, line 529
def row_type(db_type, obj)
  (type_hash = @row_types[literal(db_type)]) &&
    (parser = type_hash[:parser])

  case obj
  when ArrayRow, HashRow
    obj
  when Array
    if parser
      parser.typecast(obj)
    else
      obj = ArrayRow.new(obj)
      obj.db_type = db_type
      obj
    end
  when Hash
    if parser 
      parser.typecast(obj)
    else
      raise InvalidValue, "Database#row_type requires the #{db_type.inspect} type have a registered parser and typecaster when called with a hash"
    end
  else
    raise InvalidValue, "cannot convert #{obj.inspect} to row type #{db_type.inspect}"
  end
end

Private Instance Methods

bound_variable_array(arg) click to toggle source

Format composite types used in bound variable arrays.

Calls superclass method
# File lib/sequel/extensions/pg_row.rb, line 558
def bound_variable_array(arg)
  case arg
  when ArrayRow
    "\"(#{arg.map{|v| bound_variable_array(v) if v}.join(COMMA).gsub(ESCAPE_RE, ESCAPE_REPLACEMENT)})\""
  when HashRow
    arg.check_columns!
    "\"(#{arg.values_at(*arg.columns).map{|v| bound_variable_array(v) if v}.join(COMMA).gsub(ESCAPE_RE, ESCAPE_REPLACEMENT)})\""
  else
    super
  end
end
schema_column_type(db_type) click to toggle source

Make the column type detection handle registered row types.

Calls superclass method
# File lib/sequel/extensions/pg_row.rb, line 571
def schema_column_type(db_type)
  if type = @row_schema_types[db_type]
    type
  else
    super
  end
end