Skip to main content

ArrowFEBE — Examples & recipes

Copy-paste snippets for the common tasks. All assume the driver is installed and loadable by name (driver="arrowfebe") — see the install guide. For the full option reference behind each db_kwargs, see CONNECTION.md.


Connect and query to Arrow

The same recipe in every ADBC client — the driver is loaded by name (arrowfebe) from the installed ADBC manifest, so only the language changes:

import adbc_driver_manager.dbapi as dbapi

with dbapi.connect(
driver="arrowfebe",
db_kwargs={"uri": "postgresql://alice:<password>@localhost:5432/tpch?sslmode=require"},
autocommit=True,
) as conn, conn.cursor() as cur:
cur.execute("SELECT * FROM lineitem LIMIT 10")
table = cur.fetch_arrow_table() # pyarrow.Table, COPY-binary fast path
print(table.schema)

Connect with discrete options

Discrete options keep secrets out of a single string and override any uri. Set them where the uri went — the rest of each program is identical to the recipe above:

conn = dbapi.connect(driver="arrowfebe", db_kwargs={
"adbc.arrowfebe.server": "localhost",
"adbc.arrowfebe.port": "5432",
"adbc.arrowfebe.database": "tpch",
"adbc.arrowfebe.username": "alice",
"adbc.arrowfebe.password": "<password>",
"adbc.arrowfebe.sslmode": "require",
}, autocommit=True)

Or the DB-API shortcut from the Python package:

Python
import arrowfebe_adbc
conn = arrowfebe_adbc.connect("localhost", "tpch", "alice", "<password>")

Straight to pandas / Polars / DuckDB

Because results are already Arrow, there is no row-by-row marshalling. These handoffs are Python-ecosystem specific:

Python
with conn.cursor() as cur:
cur.execute("SELECT * FROM orders WHERE o_orderdate >= '1996-01-01'")

df = cur.fetch_arrow_table().to_pandas() # pandas
# or, without materialising twice:
import polars as pl
pf = pl.from_arrow(cur.fetch_arrow_table()) # Polars
import duckdb
tbl = conn.cursor().execute("SELECT * FROM lineitem").fetch_arrow_table()
duckdb.sql("SELECT l_returnflag, count(*) FROM tbl GROUP BY 1").show()

Prepared statements & parameter binding

Bind a parameter to a $1 placeholder. In the compiled clients you bind an Arrow batch of parameters to the statement before executing:

with conn.cursor() as cur:
cur.execute("SELECT * FROM orders WHERE o_orderkey = $1", parameters=[42])
row = cur.fetch_arrow_table()

PostgreSQL uses $1, $2, … placeholders in the extended-query protocol. Array binding — bind a whole Arrow batch of parameters in one call (Python's executemany):

Python
with conn.cursor() as cur:
cur.executemany(
"INSERT INTO t(a, b) VALUES ($1, $2)",
seq_of_parameters=[(1, "x"), (2, "y"), (3, "z")],
)
conn.commit()

Bulk ingest an Arrow table

Native COPY FROM STDIN (binary) — pipe an Arrow table straight in. Use autocommit=True for the single-connection TRUNCATE+ingest pattern:

import pyarrow as pa

conn = dbapi.connect(driver="arrowfebe", db_kwargs={...}, autocommit=True)

table = pa.table({
"id": pa.array([1, 2, 3], pa.int32()),
"name": pa.array(["a", "b", "c"], pa.string()),
})

with conn.cursor() as cur:
cur.adbc_ingest("my_table", table, mode="create") # create | append | replace | create_append
note

Rust & C++ have no one-call adbc_ingest helper: set the standard statement options adbc.ingest.target_table / adbc.ingest.mode, Bind the Arrow batch, then execute_update (Rust) / AdbcStatementExecuteQuery (C++).

Ingest into a specific catalog/schema or a temp table via the statement options adbc.ingest.target_catalog, adbc.ingest.target_db_schema, adbc.ingest.temporary (see CONNECTION.md). The ingest path introspects the target column OIDs, so an Arrow utf8 column lands correctly in a jsonb / inet / uuid column — see DATA_TYPES.md.

Export a query to Parquet

Python
import pyarrow.parquet as pq

with conn.cursor() as cur:
cur.execute("SELECT * FROM lineitem")
reader = cur.fetch_record_batch() # streaming RecordBatchReader
with pq.ParquetWriter("lineitem.parquet", reader.schema) as w:
for batch in reader:
w.write_batch(batch)

Streaming keeps memory flat regardless of result size; the driver auto-flushes a batch early if a wide column would cross Arrow's 2 GiB offset limit.

TLS with certificate verification

Verify the server certificate chain and hostname, and pin a custom root CA:

Python
conn = dbapi.connect(driver="arrowfebe", db_kwargs={
"adbc.arrowfebe.server": "db.example.com",
"adbc.arrowfebe.database": "mydb",
"adbc.arrowfebe.username": "alice",
"adbc.arrowfebe.password": "<password>",
"adbc.arrowfebe.sslmode": "verify-full",
"adbc.arrowfebe.ssl_root_cert": "/etc/ssl/certs/pg-root.pem",
# bind SCRAM to the TLS channel, refusing a PLUS-stripping downgrade:
"adbc.arrowfebe.channel_binding": "require",
}, autocommit=True)

See CONNECTION.md for the sslmode matrix.

Integrated / Kerberos auth

No username/password — POSIX Kerberos or Windows SSPI:

Python
conn = dbapi.connect(driver="arrowfebe", db_kwargs={
"adbc.arrowfebe.server": "db.example.com",
"adbc.arrowfebe.database": "mydb",
"adbc.arrowfebe.auth_type": "integrated",
# optional explicit Kerberos credentials on Linux/macOS:
# "adbc.arrowfebe.krb5.ccache": "/tmp/krb5cc_1000",
# "adbc.arrowfebe.krb5.keytab": "/etc/postgresql/pg.keytab",
# encrypt without TLS (POSIX/GSSAPI builds):
# "adbc.arrowfebe.gssencmode": "require",
})

See AUTHENTICATION.md for SPN and credential details.

Loading by explicit path

Load-by-name (above) uses the ADBC manifest the installer registers. When the driver is not installed system-wide, point the driver manager at the shared library instead — e.g. in C# via CAdbcDriverImporter.Load("libarrowfebe_adbc_driver.so", "AdbcDriverInit"), or by setting ARROWFEBE/ADBC_DRIVER_PATH for the name-based loaders. Everything after loading is identical.

See also