Ruby

red-adbc is the Ruby binding for ADBC, built on top of ADBC GLib. It is distributed as the red-adbc gem on RubyGems.

Here we’ll briefly tour basic features of ADBC with Ruby using the SQLite driver.

Installation

red-adbc depends on the native Arrow GLib and ADBC GLib libraries. The rubygems-requirements-system plugin installs these system dependencies automatically using your system package manager, so install it first.

With gem:

gem install rubygems-requirements-system
gem install red-adbc

Or, with Bundler, add the plugin and the gem to your Gemfile:

source "https://rubygems.org"

plugin "rubygems-requirements-system"

gem "red-adbc"

Installing Drivers

You also need a driver for the database you want to connect to. See Installing Drivers for instructions. For the example below, you could install dbc and then install the SQLite driver with:

dbc install sqlite

Creating a Connection

require "adbc"

ADBC::Database.open(driver: "sqlite",
                    uri: ":memory") do |database|
  database.connect do |connection|
    # Use connection
  end
end

In application code, the database and connection must be closed after usage or memory may leak. Both Database.open and database.connect accept blocks to accomplish this automatically.

Executing a Query

We can execute a query and get the results as an Arrow table:

require "adbc"

ADBC::Database.open(driver: "sqlite",
                    uri: ":memory:") do |database|
  database.connect do |connection|
    table, = connection.query("SELECT 1, 2.0, 'Hello, world!'")
    puts table
  end
end

Output:

   1  2.0  'Hello, world!'
   (int64)  (double)  (utf8)
0  1  2.0  Hello, world!

Ingesting Bulk Data

We can insert a table of Arrow data into a new database table:

require "adbc"

ADBC::Database.open(driver: "sqlite",
                    uri: ":memory:") do |database|
  database.connect do |connection|
    # Create an Arrow table
    table = Arrow::Table.new(
      ints: Arrow::Int64Array.new([1, 2]),
      strs: Arrow::StringArray.new(["a", nil])
    )

    # Ingest into database
    connection.ingest("sample", table, mode: :create)

    # Query the data
    result, = connection.query("SELECT COUNT(DISTINCT ints) FROM sample")
    puts result
  end
end

We can also append to an existing table:

require "adbc"

ADBC::Database.open(driver: "sqlite",
                    uri: ":memory:") do |database|
  database.connect do |connection|
    # Create initial table
    table = Arrow::Table.new(
      ints: Arrow::Int64Array.new([1, 2]),
      strs: Arrow::StringArray.new(["a", nil])
    )
    connection.ingest("sample", table, mode: :create)

    # Append more data
    table2 = Arrow::Table.new(
      ints: Arrow::Int64Array.new([2, 3]),
      strs: Arrow::StringArray.new([nil, "c"])
    )
    connection.ingest("sample", table2, mode: :append)

    # Query the combined data
    result, = connection.query("SELECT COUNT(DISTINCT ints) FROM sample")
    puts result
  end
end

The mode parameter can be:

  • :create - Create a new table (default)

  • :append - Append to an existing table

  • :replace - Replace an existing table

Getting Database/Driver Metadata

We can get information about the driver and the database:

require "adbc"

ADBC::Database.open(driver: "sqlite",
                    uri: ":memory:") do |database|
  database.connect do |connection|
    info = connection.info
    puts "Vendor: #{info[:vendor_name]}"
    puts "Driver: #{info[:driver_name]}"
  end
end

We can also query for tables and columns in the database. This gives a nested structure describing all the catalogs, schemas, tables, and columns:

require "adbc"

ADBC::Database.open(driver: "sqlite",
                    uri: ":memory:") do |database|
  database.connect do |connection|
    # Create a sample table first
    connection.query("CREATE TEMPORARY TABLE sample (ints INTEGER, strs TEXT)")

    # Get database objects
    objects = connection.get_objects(depth: :all)
    catalog = objects.raw_records[1]
    schema = catalog[1][0]
    tables = schema["db_schema_tables"]

    puts "Table: #{tables[0]["table_name"]}"
    columns = tables[0]["table_columns"]
    puts "Columns: #{columns.map { |c| c["column_name"] }.join(", ")}"
  end
end

Next Steps