PG::TextDecoder::

Record class

This class decodes one record of values received from a composite type column in text format. See PostgreSQL Composite Types for a description of the format and how it can be used.

PostgreSQL allows composite types to be used in many of the same ways that simple types can be used. For example, a column of a table can be declared to be of a composite type.

The columns are returned from the decoder as array of values. The single values are decoded as defined in the assigned type_map. If no type_map was assigned, all values are converted to strings by PG::TextDecoder::String.

Decode a record in Composite Type format from String to Array<String> (uses default type map TypeMapAllStrings):

PG::TextDecoder::Record.new.decode("(1,2)")  # => ["1", "2"]

Decode a record from String to Array<Float> :

# Build a type map for two Floats
tm = PG::TypeMapByColumn.new([PG::TextDecoder::Float.new]*2)
# Use this type map to decode the record:
PG::TextDecoder::Record.new(type_map: tm).decode("(1,2)")
# => [1.0, 2.0]

Records can also be encoded and decoded directly to and from the database. This avoids intermediate String allocations and is very fast. Take the following type and table definitions:

conn.exec("CREATE TYPE complex AS (r float, i float) ")
conn.exec("CREATE TABLE my_table (v1 complex, v2 complex) ")
conn.exec("INSERT INTO my_table VALUES((2,3), (4,5)), ((6,7), (8,9)) ")

The record can be decoded by applying a type map to the PG::Result object:

# Build a type map for two floats "r" and "i"
tm = PG::TypeMapByColumn.new([PG::TextDecoder::Float.new]*2)
# Build a record decoder to decode this two-value type:
deco = PG::TextDecoder::Record.new(type_map: tm)
# Fetch table data and use the decoder to cast the two complex values "v1" and "v2":
conn.exec("SELECT * FROM my_table").map_types!(PG::TypeMapByColumn.new([deco]*2)).to_a
# => [{"v1"=>[2.0, 3.0], "v2"=>[4.0, 5.0]}, {"v1"=>[6.0, 7.0], "v2"=>[8.0, 9.0]}]

It’s more very convenient to use the PG::BasicTypeRegistry, which is based on database OIDs.

# Fetch a NULL record of our type to retrieve the OIDs of the two fields "r" and "i"
oids = conn.exec( "SELECT (NULL::complex).*" )
# Build a type map (PG::TypeMapByColumn) for decoding the "complex" type
dtm = PG::BasicTypeMapForResults.new(conn).build_column_map( oids )
# Build a type map and populate with basic types
btr = PG::BasicTypeRegistry.new.register_default_types
# Register a new record decoder for decoding our type "complex"
btr.register_coder(PG::TextDecoder::Record.new(type_map: dtm, name: "complex"))
# Apply our basic type registry to all results retrieved from the server
conn.type_map_for_results = PG::BasicTypeMapForResults.new(conn, registry: btr)
# Now queries decode the "complex" type (and many basic types) automatically
conn.exec("SELECT * FROM my_table").to_a
# => [{"v1"=>[2.0, 3.0], "v2"=>[4.0, 5.0]}, {"v1"=>[6.0, 7.0], "v2"=>[8.0, 9.0]}]

Records can also be nested or further wrapped into other decoders like PG::TextDecoder::CopyRow.

See also PG::TextEncoder::Record for the encoding direction (data sent to the server).