PostgreSQL Driver

Language: C/C++ Status: Stable

The PostgreSQL driver provides access to any database that supports the PostgreSQL wire format. It wraps libpq, the client library for PostgreSQL. The project owes credit to 0x0L’s pgeon for the overall approach.

Note

This project is not affiliated with PostgreSQL in any way.

Note

Some older versions (<=1.11.0) of this driver had experimental support for Amazon Redshift, but this was removed in 1.11.0. We recommend using the Redshift-specific driver from the ADBC Driver Foundry (a third party).

Installation

Install libadbc-driver-postgresql from conda-forge:

mamba install libadbc-driver-postgresql

Install the C/C++ driver, then use the Go driver manager. Requires CGO.

go get github.com/apache/arrow-adbc/go/adbc/drivermgr

Install adbc-driver-postgresql from conda-forge:

mamba install adbc-driver-postgresql

Install adbc-driver-postgresql from PyPI:

pip install adbc-driver-postgresql

Install adbcpostgresql from CRAN:

install.packages("adbcpostgresql")

Additionally, the driver may be used from C/C++, C#, GLib, Go, R, Ruby, and Rust via the driver manager.

Usage

To connect to a database, supply the “uri” parameter when constructing the AdbcDatabase. This should be a connection URI.

#include "arrow-adbc/adbc.h"

// Ignoring error handling
struct AdbcDatabase database;
AdbcDatabaseNew(&database, nullptr);
AdbcDatabaseSetOption(&database, "uri", "postgresql://localhost:5433", nullptr);
AdbcDatabaseInit(&database, nullptr);

You must have libadbc_driver_postgresql.so on your LD_LIBRARY_PATH, or in the same directory as the executable when you run this. This requires CGO and loads the C++ ADBC postgresql driver.

import (
   "context"

   "github.com/apache/arrow-adbc/go/adbc"
   "github.com/apache/arrow-adbc/go/adbc/drivermgr"
)

func main() {
   var drv drivermgr.Driver
   db, err := drv.NewDatabase(map[string]string{
      "driver": "adbc_driver_postgresql",
      adbc.OptionKeyURI: "postgresql://user:pass@localhost:5433/postgres",
   })
   if err != nil {
      // handle error
   }
   defer db.Close()

   cnxn, err := db.Open(context.Background())
   if err != nil {
      // handle error
   }
   defer cnxn.Close()
}
import adbc_driver_postgresql.dbapi

uri = "postgresql://user:pass@localhost:5433/postgres"
with adbc_driver_postgresql.dbapi.connect(uri) as conn:
    pass

For more examples, see PostgreSQL Recipes.

library(adbcdrivermanager)

# Use the driver manager to connect to a database
uri <- Sys.getenv("ADBC_POSTGRESQL_TEST_URI")
db <- adbc_database_init(adbcpostgresql::adbcpostgresql(), uri = uri)
con <- adbc_connection_init(db)

Supported Features

The PostgreSQL driver supports features defined in the ADBC API specification 1.0.0.

COPY query execution

The PostgreSQL driver executes queries with COPY for best performance. PostgreSQL does not support this for all queries, however (such as SHOW). The optimization can be disabled by the statement option adbc.postgresql.use_copy. For an example, see Execute a statement without COPY.

Bulk Ingestion

Bulk ingestion is supported. The mapping from Arrow types to PostgreSQL types is the same as below.

Partitioned Result Sets

Partitioned result sets are not supported.

Transactions

Transactions are supported.

Type Support

PostgreSQL allows defining new types at runtime, so the driver must build a mapping of available types. This is currently done once at startup.

Type support is currently limited depending on the type and whether it is being read or written.

Arrow type to PostgreSQL type mapping

Arrow Type

As Bind Parameter

In Bulk Ingestion [1]

binary

BYTEA

BYTEA

bool

BOOLEAN

BOOLEAN

date32

DATE

DATE

date64

dictionary

(as unpacked type)

(as unpacked type, only for binary/string)

duration

INTERVAL

INTERVAL

float32

REAL

REAL

float64

DOUBLE PRECISION

DOUBLE PRECISION

int8

SMALLINT

SMALLINT

int16

SMALLINT

SMALLINT

int32

INTEGER

INTEGER

int64

BIGINT

BIGINT

large_binary

large_string

TEXT

TEXT

month_day_nano_interval

INTERVAL

INTERVAL

string

TEXT

TEXT

timestamp

TIMESTAMP [3]

TIMESTAMP/TIMESTAMP WITH TIMEZONE

PostgreSQL type to Arrow type mapping

PostgreSQL Type

In Result Set

ARRAY

list

BIGINT

int64

BINARY

binary

BOOLEAN

bool

CHAR

utf8

DATE

date32

DOUBLE PRECISION

float64

INTEGER

int32

INTERVAL

month_day_nano_interval

JSON

utf8 (extension<arrow.json>)

JSONB

utf8 (extension<arrow.json>)

NUMERIC

utf8 [2]

REAL

float32

SMALLINT

int16

TEXT

utf8

TIME

time64

TIMESTAMP WITH TIME ZONE

timestamp[unit, UTC]

TIMESTAMP WITHOUT TIME ZONE

timestamp[unit]

VARCHAR

utf8

Unknown Types

Types without direct Arrow equivalents can still be returned by the driver. In this case, the Arrow type will be binary, and the contents will be the raw bytes as provided by the PostgreSQL wire protocol.

For Arrow implementations that support the Opaque canonical extension type, the extension type metadata is also always present. This helps differentiate when the driver intentionally returned a binary column from when it returned a binary column as a fallback.

Warning

Currently, the driver also attaches a metadata key named ADBC:postgresql:typname to the schema field of the unknown column, but this has been deprecated in favor of the Opaque type and you should not rely on this key continuing to exist.

Resolving Composite and User-Defined Types

PostgreSQL allows users to define their own types. On top of that, databases may use the PostgreSQL wire protocol and binary format but not necessarily the same data types. The driver has to somehow determine which Arrow type to use for a given PostgreSQL type.

By default, the driver will query system tables upon initial connection to determine the type mapping, and to be able to understand user-defined and composite types. This can result in a significant startup cost, however, especially if the database contains many tables, columns, and/or user-defined types; some users have reported that initial connection takes several minutes! (Note that in PostgreSQL, every table automatically has a user-defined type defined for it.)

Starting in driver version 1.13 (ADBC release 25), the driver now has an alternative: by setting the database option adbc.postgresql.type_resolver_mode to builtin on initial connect, the driver will skip these queries and instead use a hardcoded table of type OIDs. In this mode, the driver will be unable to read composite and user-defined types, because it does not have the necessary information. We cannot guarantee compatibility with non-PostgreSQL database vendors that use the PostgreSQL wire protocol in this mode.

We plan to improve support here: eventually, the driver will start with the hardcoded type table and will fetch type information on-the-fly as necessary.

Software Versions & Vendor Compatibility

For Python wheels, the shipped version of the PostgreSQL client libraries is 18.4. For conda-forge packages, the version of libpq is the same as the version of libpq in your Conda environment.

The PostgreSQL driver is tested against the following DBMSes, which all use the PostgreSQL wire protocol:

  • PostgreSQL (version 14 through 18)

  • Citus

  • CockroachDB

  • CrateDB

  • Google AlloyDB Omni

  • ParadeDB

  • TimescaleDB

  • YugabyteDB

Note that for vendors besides PostgreSQL, certain features and/or data types may not be supported. In particular, COPY query execution (see above) is often not supported.

We are aware that the driver is not currently compatible with the following vendors:

  • CedarDB v2026-8-13 (unless the “builtin” type resolver is used, see above)

The driver is not and will not support the following vendors:

  • Amazon Redshift (note that a dedicated driver is available for Redshift from a third party, see Available Drivers)