GithubHelp home page GithubHelp logo

Comments (1)

larskanis avatar larskanis commented on August 13, 2024

Assuming the following tables:

create table employees (id serial primary key, name text, dept_id int);
create table departments (id serial primary key, name text);
insert into departments (name) VALUES ('Sales'), ('Marketing');
insert into employees (name, dept_id) VALUES ('Alice', 1), ('Bob', 2), ('Charlie', 1);

Then you can simply call:

c = PG.connect
c.exec("select emp.*, dept.* from employees as emp join departments as dept on emp.dept_id = dept.id").values
# => [["1", "Alice", "1", "1", "Sales"], ["2", "Bob", "2", "2", "Marketing"], ["3", "Charlie", "1", "1", "Sales"]] 

You can also receive and decode record types by using a type map with PG::TextDecorder::Record like so:

c.exec("select row(emp.*), row(dept.*) from employees as emp join departments as dept on emp.dept_id = dept.id")
 .map_types!(PG::TypeMapByColumn.new([ PG::TextDecoder::Record.new ]*2)).values
# => [[["1", "Alice", "1"], ["1", "Sales"]], [["2", "Bob", "2"], ["2", "Marketing"]], [["3", "Charlie", "1"], ["1", "Sales"]]] 

It's also possible to directly decode to non-strings like Integer, when using a universal type map as the default for result:

c.type_map_for_results = PG::BasicTypeMapForResults.new(c)
c.exec("select emp.*, dept.* from employees as emp join departments as dept on emp.dept_id = dept.id").values
# => [[1, "Alice", 1, 1, "Sales"], [2, "Bob", 2, 2, "Marketing"], [3, "Charlie", 1, 1, "Sales"]] 

But it can not decode record types properly, since the types of the record entries are not provided by the PostgreSQL server (only the generic record type OID 2249). So these OIDs must be fetched manually like so:

# Fetch a NULL record of our types to retrieve the OIDs of the record entries
eoids = c.exec( "SELECT (NULL::employees).*" )
doids = c.exec( "SELECT (NULL::departments).*" )
# Build type maps (PG::TypeMapByColumn) for decoding both record types
etm = c.type_map_for_results.build_column_map( eoids )
dtm = c.type_map_for_results.build_column_map( doids )
# Now add the type maps to the record query above
c.exec("select row(emp.*), row(dept.*) from employees as emp join departments as dept on emp.dept_id = dept.id")
 .map_types!(PG::TypeMapByColumn.new([
   PG::TextDecoder::Record.new(type_map: etm),
   PG::TextDecoder::Record.new(type_map: dtm)
 ])).values
# => [[[1, "Alice", 1], [1, "Sales"]], [[2, "Bob", 2], [2, "Marketing"]], [[3, "Charlie", 1], [1, "Sales"]]] 

And for completeness it's also possible to not retrieve the record OIDs from the database, but to provide them manually. This returns the same result:

c.exec("select row(emp.*), row(dept.*) from employees as emp join departments as dept on emp.dept_id = dept.id")
 .map_types!(PG::TypeMapByColumn.new([
    PG::TextDecoder::Record.new(type_map: PG::TypeMapByColumn.new([
      # columns of employees
      PG::TextDecoder::Integer.new,
      PG::TextDecoder::String.new,
      PG::TextDecoder::Integer.new,      
    ])),
    PG::TextDecoder::Record.new(type_map: PG::TypeMapByColumn.new([
      # columns of departments
      PG::TextDecoder::Integer.new,
      PG::TextDecoder::String.new,
    ]))
 ])).values
# => [[[1, "Alice", 1], [1, "Sales"]], [[2, "Bob", 2], [2, "Marketing"]], [[3, "Charlie", 1], [1, "Sales"]]] 

And decoding everything automatically can be archived by adding the two composite types of the two tables to your own type registry like so:

# Build a type registry and populate with basic types
btr = PG::BasicTypeRegistry.new.register_default_types
# Build an intermediate type map for generation of the record types
btmfr = PG::BasicTypeMapForResults.new(c)

%w[ employees departments ].each do |type|
  # Fetch a NULL record of our types to retrieve the OIDs of the specific record entries
  oids = c.exec( "SELECT (NULL::#{type}).*" )
  # Build a type map (PG::TypeMapByColumn) for decoding this record type
  dtm = btmfr.build_column_map( oids )
  # Register a new record decoder for decoding our composite type
  btr.register_coder(PG::TextDecoder::Record.new(type_map: dtm, name: type))
end
# Apply our basic type registry to all results retrieved from the server
c.type_map_for_results = PG::BasicTypeMapForResults.new(c, registry: btr)
# Now queries decode both types automatically, if they are returned without wrapping by row()
c.exec("select emp, dept from employees as emp join departments as dept on emp.dept_id = dept.id").values
# => [[[1, "Alice", 1], [1, "Sales"]], [[2, "Bob", 2], [2, "Marketing"]], [[3, "Charlie", 1], [1, "Sales"]]] 

from ruby-pg.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.