adbc-driver-db2
A pure-Go Apache Arrow ADBC driver for
IBM Db2 that speaks Db2's native wire protocol,
DRDA, directly. No IBM CLI / ODBC
driver, no db2jcc, no cgo dependency on IBM libraries — one
statically-linked shared library that plugs into every ADBC language
binding (Python, Go, R, C/C++, C#, Rust, JavaScript) and returns
Arrow record batches.
pip install adbc-driver-db2
import adbc_driver_db2.dbapi as db2
with db2.connect(
uri="db2://db2host:50000/SAMPLE",
username="db2inst1",
password="********",
) as conn, conn.cursor() as cur:
cur.execute("SELECT * FROM SALES.ORDERS WHERE ORDER_DATE >= ?", parameters=("2024-01-01",))
table = cur.fetch_arrow_table() # or cur.fetch_record_batch() to stream
Status: alpha. Tested against Db2 LUW 12.1 (Community Edition). The DRDA implementation parses the server's type definition, so Db2 for z/OS and Db2 for i should work in principle but have not been exercised yet — reports welcome.
Why
- Arrow-native, streaming. Result sets are pulled one DRDA query
block at a time (1 MiB by default) and surfaced as Arrow record
batches, so a 100 M-row
SELECTneeds about one batch of client memory. Perfect forDb2 → Arrow → somewhere elsepipelines. - Zero-install. IBM's clients are large, click-wrap licensed
downloads. This is a
pip install/go get. - The full ADBC feature set: queries, parameter binding, bulk
ingest (
adbc_ingestwith create/append/replace/create_append and declared temporary tables), transactions and isolation levels,GetObjects/GetTableSchema/GetInfocatalog metadata for tools like DBeaver, connection profiles, and the ADBC driver manifest. The driver passes theapache/arrow-adbcGo conformance suite.
Connection URI and options
db2://[user[:password]@]host[:port]/DATABASE[?param=value&...]
| URI parameter / ADBC option | Meaning |
|---|---|
tls=true / adbc.db2.tls |
TLS (Db2's SSL port is conventionally 50001; required for Db2 on Cloud) |
tls_ca_cert=/path.pem / adbc.db2.tls.ca_cert |
CA bundle for self-signed servers |
tls_skip_verify=true / adbc.db2.tls.skip_verify |
Skip certificate verification |
secmec=9 / adbc.db2.security_mechanism |
DRDA security mechanism: 9 encrypted user id + password (default when the server allows it), 3 cleartext password (inside TLS this is fine), 4 user id only |
schema=NAME / adbc.db2.current_schema |
SET CURRENT SCHEMA after connecting |
query_block_size=N / adbc.db2.query_block_size |
DRDA QRYBLKSZ in bytes (default 1 MiB) |
batch_size=N / adbc.db2.batch_size |
Max rows per Arrow record batch (default 65536) |
connect_timeout=30 / adbc.db2.connect_timeout |
Seconds or Go duration |
application_name=X / adbc.db2.application_name |
Reported to the server |
package=COLL.PKG / adbc.db2.package |
Dynamic-SQL package (default NULLID.SYSSH200); bound automatically if missing (adbc.db2.no_auto_bind=true disables) |
| `trace=true | hex/adbc.db2.trace` |
trace_file=/path / adbc.db2.trace_file |
Write the trace to a file instead of stderr (use from notebooks) |
Standard ADBC options also apply: username, password,
adbc.connection.autocommit, adbc.connection.transaction.isolation_level
(mapped to SET CURRENT ISOLATION UR/CS/RS/RR), adbc.connection.catalog,
adbc.connection.db_schema, and the adbc.ingest.* statement options.
Python
Streaming large result sets
with db2.connect(uri=uri, username=user, password=pw) as conn, conn.cursor() as cur:
cur.execute("SELECT * FROM BIG.TABLE")
reader = cur.fetch_record_batch() # pyarrow.RecordBatchReader
for batch in reader: # one query block at a time
process(batch)
Db2 → GizmoSQL (or any ADBC target), ADBC to ADBC
The reader above can be handed straight to another driver's bulk ingest, so a table moves from Db2 into GizmoSQL without ever being materialised on the client:
import adbc_driver_db2.dbapi as db2
import adbc_driver_gizmosql.dbapi as gizmosql
with db2.connect(uri=db2_uri, username=db2_user, password=db2_pw) as src, \
gizmosql.connect(gizmosql_uri, username="token", password=token) as dst:
with src.cursor() as s, dst.cursor() as d:
s.execute("SELECT * FROM PFWF6076.CGIBASE")
rows = d.adbc_ingest(table_name="cgibase", data=s.fetch_record_batch(), mode="replace")
dst.commit()
print(f"Loaded {rows:,} rows")
Bulk ingest (Arrow → Db2)
import pyarrow as pa
table = pa.table({"id": [1, 2, 3], "name": ["alpha", "beta", None]})
with db2.connect(uri=uri, username=user, password=pw, autocommit=True) as conn, conn.cursor() as cur:
cur.adbc_ingest("new_table", table, mode="create") # create | append | replace | create_append
Rows are pipelined many per DRDA round trip (1000 by default;
adbc.db2.ingest.batch_rows). VARCHAR/VARBINARY columns of a created
table are sized from the first batch (adbc.db2.ingest.varchar_length
overrides) because Db2's row-size limit depends on the tablespace page
size. Values over 32 KiB are sent as out-of-line BLOB/CLOB data.
pandas and Polars
with db2.connect(uri=uri, username=user, password=pw) as conn, conn.cursor() as cur:
cur.execute("SELECT * FROM SYSCAT.TABLES")
df = cur.fetch_df() # pandas
# or, zero-copy into Polars:
import polars as pl
cur.execute("SELECT * FROM SYSCAT.COLUMNS")
pl_df = pl.from_arrow(cur.fetch_arrow_table())
Parameters and executemany
import datetime
with db2.connect(uri=uri, username=user, password=pw, autocommit=True) as conn, conn.cursor() as cur:
cur.execute("CREATE TABLE EVENTS (ID INTEGER NOT NULL, NAME VARCHAR(40), AT TIMESTAMP)")
cur.executemany(
"INSERT INTO EVENTS VALUES (?, ?, ?)",
[(1, "start", datetime.datetime(2024, 1, 1, 9, 0)), (2, "stop", None)],
) # rows are pipelined many-per-round-trip, not sent one at a time
cur.execute("SELECT NAME FROM EVENTS WHERE ID = ?", parameters=(2,))
print(cur.fetchone())
Query Db2 live from DuckDB or GizmoSQL (adbc_scanner)
The c-shared driver plugs straight into DuckDB's
adbc_scanner community
extension (see the GizmoSQL guide) — and therefore into GizmoSQL,
which embeds DuckDB. Store the credentials in a DuckDB secret once, then
ATTACH Db2 like any other database and query it with plain SQL
(projection and filter pushdown included):
INSTALL adbc_scanner FROM community;
LOAD adbc_scanner;
CREATE SECRET db2_secret (
TYPE adbc,
SCOPE 'db2://db2host:50000/SAMPLE',
driver 'db2', -- by name after `python -m adbc_driver_db2 install-manifest`,
-- or a path: '/path/to/libadbc_driver_db2.so'
uri 'db2://db2host:50000/SAMPLE',
username 'db2inst1',
password '********'
);
ATTACH 'db2://db2host:50000/SAMPLE' AS db2 (TYPE adbc);
SELECT * FROM db2.SALES.ORDERS WHERE ORDER_DATE >= DATE '2024-01-01';
-- join Db2 with local data without copying it first
SELECT o.ORDER_ID, c.name
FROM db2.SALES.ORDERS o
JOIN customers c ON c.id = o.CUST_ID;
-- materialise a copy
CREATE TABLE orders AS SELECT * FROM db2.SALES.ORDERS;
For arbitrary Db2 SQL (or to push data the other way) the secret also drives the function API:
SET VARIABLE db2 = adbc_connect({'secret': 'db2_secret'});
SELECT * FROM adbc_scan(getvariable('db2')::BIGINT, 'SELECT * FROM SYSCAT.TABLES FETCH FIRST 10 ROWS ONLY');
Query Db2 from DuckDB via connection profiles (Columnar's adbc extension)
Columnar's adbc
community extension (see the GizmoSQL guide)
resolves databases through ADBC
connection profiles,
and additionally supports writing (INSERT, CREATE TABLE AS) into the
attached database through ADBC bulk ingest. Install this driver's manifest
once, write a profile, and Db2 is a catalog:
python -m adbc_driver_db2 install-manifest # registers driver "db2"
cat > ~/.config/adbc/profiles/warehouse.toml <<EOF # macOS: ~/Library/Application Support/ADBC/Profiles/
profile_version = 1
driver = "db2"
[Options]
uri = "db2://db2host:50000/SAMPLE"
username = "db2inst1"
password = "********"
EOF
INSTALL adbc FROM community;
LOAD adbc;
SELECT * FROM read_adbc('profile://warehouse', 'SELECT * FROM SALES.ORDERS FETCH FIRST 10 ROWS ONLY');
ATTACH 'profile://warehouse' AS db2 (TYPE adbc);
USE db2.SALES;
SELECT COUNT(*) FROM ORDERS;
CREATE TABLE ORDERS_2024 AS SELECT * FROM memory.staged_orders; -- bulk ingest into Db2
INSERT INTO ORDERS_2024 SELECT * FROM memory.late_orders;
Both DuckDB extensions are exercised in this repo's test suite
(python/tests/test_adbc_scanner.py, python/tests/test_duckdb_adbc_client.py).
Alternative: drive adbc_driver_manager directly
from adbc_driver_manager import dbapi
import adbc_driver_db2
conn = dbapi.connect(
driver=adbc_driver_db2._driver_path(),
entrypoint="Db2DriverInit",
db_kwargs={"uri": "db2://host:50000/SAMPLE", "username": "u", "password": "p"},
)
Connection profiles and the driver manifest
python -m adbc_driver_db2 install-manifest
writes a db2.toml ADBC driver manifest so the driver resolves by name
from any ADBC consumer — adbc_driver_manager.dbapi.connect(uri="db2://..."),
DuckDB's adbc_connect({'driver': 'db2', ...}), DBeaver's ADBC
connection type — and from connection profiles:
# ~/.config/adbc/profiles/warehouse.toml
driver = "db2"
uri = "db2://db2host:50000/SAMPLE?schema=SALES"
username = "reporting"
password = "********"
from adbc_driver_manager import dbapi
conn = dbapi.connect(profile="warehouse")
Go
import (
"github.com/apache/arrow-adbc/go/adbc"
"github.com/apache/arrow-go/v18/arrow/memory"
"github.com/gizmodata/adbc-driver-db2/driver/db2"
)
drv := db2.NewDriver(memory.DefaultAllocator)
database, _ := drv.NewDatabase(map[string]string{
adbc.OptionKeyURI: "db2://host:50000/SAMPLE",
adbc.OptionKeyUsername: "db2inst1",
adbc.OptionKeyPassword: "********",
})
conn, _ := database.Open(ctx)
stmt, _ := conn.NewStatement()
stmt.SetSqlQuery("SELECT * FROM SYSCAT.TABLES")
reader, _, _ := stmt.ExecuteQuery(ctx)
for reader.Next() { rec := reader.RecordBatch(); ... }
Bulk ingest from Go:
stmt, _ := conn.NewStatement()
stmt.SetOption(adbc.OptionKeyIngestTargetTable, "ORDERS_COPY")
stmt.SetOption(adbc.OptionKeyIngestMode, adbc.OptionValueIngestModeCreateAppend)
stmt.BindStream(ctx, reader) // any array.RecordReader — e.g. from Parquet, Flight, or another ADBC driver
rows, _ := stmt.ExecuteUpdate(ctx)
The internal/drda package is a self-contained DRDA client (connect,
describe, execute, streaming cursors, parameter binding, LOBs) that the
ADBC layer sits on.
Type mapping
| Db2 | Arrow |
|---|---|
| SMALLINT / INTEGER / BIGINT | int16 / int32 / int64 |
| DECIMAL(p,s), NUMERIC | decimal128(p,s) |
| DECFLOAT(16/34) | utf8 (exact text; no fixed scale) |
| REAL / DOUBLE | float32 / float64 |
| BOOLEAN | bool |
| CHAR, VARCHAR, LONG VARCHAR, (VAR)GRAPHIC, CLOB, DBCLOB, XML | utf8 |
| BINARY, VARBINARY, BLOB, ROWID | binary |
| DATE / TIME | date32 / time32[s] |
| TIMESTAMP(p) | timestamp[s/ms/us/ns] by precision |
Every field carries db2:type, db2:length, db2:precision,
db2:scale metadata; a schema produced by this driver round-trips
through bulk ingest with the original Db2 types.
Development
go test ./... # unit tests (no server needed)
DB2_HOST=localhost go test ./... # integration + ADBC conformance suite
go build -buildmode=c-shared -tags driverlib -o pkg/db2/libadbc_driver_db2.dylib ./pkg/db2
ADBC_DB2_LIBRARY=$PWD/pkg/db2/libadbc_driver_db2.dylib pip install -e ".[test]"
DB2_HOST=localhost pytest
A Db2 for testing: docker run -d -p 50000:50000 --privileged -e LICENSE=accept -e DB2INST1_PASSWORD=password -e DBNAME=testdb icr.io/db2_community/db2
(first start takes ~10 minutes). go run ./cmd/drda-sniff is a
transparent proxy that decodes DRDA traffic — handy when comparing this
driver's messages with IBM's own clients.
License
MIT — see LICENSE. DRDA is an open standard published by The Open Group; this implementation was written from the specification and the open-source Apache Derby and pydrda clients.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file adbc_driver_db2-0.1.8-py3-none-win_amd64.whl.
File metadata
- Download URL: adbc_driver_db2-0.1.8-py3-none-win_amd64.whl
- Upload date:
- Size: 8.1 MB
- Tags: Python 3, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bd9ba41e2dc261b4d351308163229edc18d157a32cd65f9b5a6a95f3174d95dc
|
|
| MD5 |
be2a403ea6a920510552ac60debfdadf
|
|
| BLAKE2b-256 |
6a778b0e6ecb937a5fe1c5bbbdf551f0750dff0e0d17990f2e109b61c9a18bb6
|
Provenance
The following attestation bundles were made for adbc_driver_db2-0.1.8-py3-none-win_amd64.whl:
Publisher:
ci.yml on gizmodata/adbc-driver-db2
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
adbc_driver_db2-0.1.8-py3-none-win_amd64.whl -
Subject digest:
bd9ba41e2dc261b4d351308163229edc18d157a32cd65f9b5a6a95f3174d95dc - Sigstore transparency entry: 2617981012
- Sigstore integration time:
-
Permalink:
gizmodata/adbc-driver-db2@3f3378d59f19bacdfccc950651690666ef17220e -
Branch / Tag:
refs/tags/v0.1.8 - Owner: https://github.com/gizmodata
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@3f3378d59f19bacdfccc950651690666ef17220e -
Trigger Event:
push
-
Statement type:
File details
Details for the file adbc_driver_db2-0.1.8-py3-none-manylinux2014_x86_64.whl.
File metadata
- Download URL: adbc_driver_db2-0.1.8-py3-none-manylinux2014_x86_64.whl
- Upload date:
- Size: 8.3 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2d141f9a991ad0b8d8cfb3cb4a79a09a3cef167be79904f690025a742e9e87c2
|
|
| MD5 |
8bb62cdb5d3aa19e4f47a7ba753bd3a3
|
|
| BLAKE2b-256 |
4c3eb69f3050dc5381ff6f88051cc8f920b53336e66426e6aff41783d861ba05
|
Provenance
The following attestation bundles were made for adbc_driver_db2-0.1.8-py3-none-manylinux2014_x86_64.whl:
Publisher:
ci.yml on gizmodata/adbc-driver-db2
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
adbc_driver_db2-0.1.8-py3-none-manylinux2014_x86_64.whl -
Subject digest:
2d141f9a991ad0b8d8cfb3cb4a79a09a3cef167be79904f690025a742e9e87c2 - Sigstore transparency entry: 2617981022
- Sigstore integration time:
-
Permalink:
gizmodata/adbc-driver-db2@3f3378d59f19bacdfccc950651690666ef17220e -
Branch / Tag:
refs/tags/v0.1.8 - Owner: https://github.com/gizmodata
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@3f3378d59f19bacdfccc950651690666ef17220e -
Trigger Event:
push
-
Statement type:
File details
Details for the file adbc_driver_db2-0.1.8-py3-none-manylinux2014_aarch64.whl.
File metadata
- Download URL: adbc_driver_db2-0.1.8-py3-none-manylinux2014_aarch64.whl
- Upload date:
- Size: 7.5 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
83f9c3aa5e461944e765459f5912a7bd38d759a588aaf7cfabc0a46058c87e04
|
|
| MD5 |
dd80b008f6ad0bc3d07a8969d4dd336e
|
|
| BLAKE2b-256 |
900dd2f7969ea4a6edbb423c74e07468bd4bb74c77391a9bd8e8d7cd5747d09e
|
Provenance
The following attestation bundles were made for adbc_driver_db2-0.1.8-py3-none-manylinux2014_aarch64.whl:
Publisher:
ci.yml on gizmodata/adbc-driver-db2
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
adbc_driver_db2-0.1.8-py3-none-manylinux2014_aarch64.whl -
Subject digest:
83f9c3aa5e461944e765459f5912a7bd38d759a588aaf7cfabc0a46058c87e04 - Sigstore transparency entry: 2617980999
- Sigstore integration time:
-
Permalink:
gizmodata/adbc-driver-db2@3f3378d59f19bacdfccc950651690666ef17220e -
Branch / Tag:
refs/tags/v0.1.8 - Owner: https://github.com/gizmodata
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@3f3378d59f19bacdfccc950651690666ef17220e -
Trigger Event:
push
-
Statement type:
File details
Details for the file adbc_driver_db2-0.1.8-py3-none-macosx_12_0_universal2.whl.
File metadata
- Download URL: adbc_driver_db2-0.1.8-py3-none-macosx_12_0_universal2.whl
- Upload date:
- Size: 4.0 MB
- Tags: Python 3, macOS 12.0+ universal2 (ARM64, x86-64)
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7c0dc5edde8fd62a905c559d0da73443ee9730d68954af110dc4105d955fdd70
|
|
| MD5 |
0a706cbd885bc2bcc7de8354e0794910
|
|
| BLAKE2b-256 |
4d5448fc9460b52a21f4b8259115a55a87624f3ede0ff9e8d3d272efb7f84078
|
Provenance
The following attestation bundles were made for adbc_driver_db2-0.1.8-py3-none-macosx_12_0_universal2.whl:
Publisher:
ci.yml on gizmodata/adbc-driver-db2
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
adbc_driver_db2-0.1.8-py3-none-macosx_12_0_universal2.whl -
Subject digest:
7c0dc5edde8fd62a905c559d0da73443ee9730d68954af110dc4105d955fdd70 - Sigstore transparency entry: 2617981031
- Sigstore integration time:
-
Permalink:
gizmodata/adbc-driver-db2@3f3378d59f19bacdfccc950651690666ef17220e -
Branch / Tag:
refs/tags/v0.1.8 - Owner: https://github.com/gizmodata
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@3f3378d59f19bacdfccc950651690666ef17220e -
Trigger Event:
push
-
Statement type: