ArpeTeradata — Examples & recipes
Copy-paste snippets for the common tasks. All assume the driver is loadable by
name (driver="arpeteradata"), which the installer sets up through
an arpeteradata.toml manifest. Otherwise, use
Loading by explicit path. For the full option
reference behind each db_kwargs, see
Connection.
- Connect and query to Arrow
- Straight to pandas / Polars
- Parameter binding
- Bulk ingest an Arrow table
- Export a query to Parquet
- TLS and logon mechanisms
- .NET through NuGet
- Loading by explicit path
Connect and query to Arrow
The same recipe in every ADBC client. The driver is loaded by name
(arpeteradata) and configured with discrete options, so only the language
changes:
- Python
- C / C++
- C#
- Go
- Java
- R
- Rust
import adbc_driver_manager.dbapi as dbapi
with dbapi.connect(
driver="arpeteradata",
db_kwargs={
"adbc.arpeteradata.server": "td.example.com",
"adbc.arpeteradata.username": "alice",
"adbc.arpeteradata.password": "<pw>",
},
autocommit=True,
) as conn, conn.cursor() as cur:
cur.execute("SELECT TOP 10 * FROM SALES.ORDERS")
table = cur.fetch_arrow_table() # pyarrow.Table
print(table.schema)
#include <cstdio>
#include <cstdlib>
#include <arrow-adbc/adbc.h>
#include <arrow-adbc/adbc_driver_manager.h>
#define CHECK(EXPR) \
if (AdbcStatusCode s = (EXPR); s != ADBC_STATUS_OK) { \
std::fprintf(stderr, "%s failed: %s\n", #EXPR, \
error.message ? error.message : "(unknown)"); \
std::exit(1); \
}
int main() {
AdbcError error = {};
AdbcDatabase database = {};
CHECK(AdbcDatabaseNew(&database, &error));
// Load the driver by name from its ADBC manifest.
CHECK(AdbcDatabaseSetOption(&database, "driver", "arpeteradata", &error));
CHECK(AdbcDatabaseSetOption(&database, "adbc.arpeteradata.server", "td.example.com", &error));
CHECK(AdbcDatabaseSetOption(&database, "adbc.arpeteradata.username", "alice", &error));
CHECK(AdbcDatabaseSetOption(&database, "adbc.arpeteradata.password", "<pw>", &error));
CHECK(AdbcDatabaseInit(&database, &error));
AdbcConnection connection = {};
CHECK(AdbcConnectionNew(&connection, &error));
CHECK(AdbcConnectionInit(&connection, &database, &error)); // logs on
AdbcStatement statement = {};
CHECK(AdbcStatementNew(&connection, &statement, &error));
CHECK(AdbcStatementSetSqlQuery(&statement, "SELECT TOP 10 * FROM SALES.ORDERS", &error));
ArrowArrayStream stream = {}; // Arrow C Data interface
int64_t rows_affected = -1;
CHECK(AdbcStatementExecuteQuery(&statement, &stream, &rows_affected, &error));
// consume `stream` with nanoarrow / Arrow C++, then release:
stream.release(&stream);
AdbcStatementRelease(&statement, &error);
AdbcConnectionRelease(&connection, &error);
AdbcDatabaseRelease(&database, &error);
return 0;
}
using Apache.Arrow.Adbc;
using Apache.Arrow.Adbc.DriverManager;
var parameters = new Dictionary<string, string>
{
["adbc.arpeteradata.server"] = "td.example.com",
["adbc.arpeteradata.username"] = "alice",
["adbc.arpeteradata.password"] = "<pw>",
};
// Load the driver by name from its ADBC manifest.
using AdbcDriver driver = AdbcDriverManager.FindLoadDriver(
"arpeteradata", loadOptions: AdbcLoadFlags.Default);
using AdbcDatabase database = driver.Open(parameters);
using AdbcConnection connection = database.Connect(null);
using AdbcStatement statement = connection.CreateStatement();
statement.SqlQuery = "SELECT TOP 10 * FROM SALES.ORDERS";
QueryResult result = statement.ExecuteQuery();
using var stream = result.Stream!; // IArrowArrayStream of RecordBatches
Console.WriteLine(stream.Schema);
package main
import (
"context"
"fmt"
"github.com/apache/arrow-adbc/go/adbc/drivermgr"
)
func main() {
ctx := context.Background()
var drv drivermgr.Driver
// Load the driver by name from its ADBC manifest.
db, err := drv.NewDatabase(map[string]string{
"driver": "arpeteradata",
"adbc.arpeteradata.server": "td.example.com",
"adbc.arpeteradata.username": "alice",
"adbc.arpeteradata.password": "<pw>",
})
if err != nil {
panic(err)
}
defer db.Close()
conn, err := db.Open(ctx)
if err != nil {
panic(err)
}
defer conn.Close()
stmt, err := conn.NewStatement()
if err != nil {
panic(err)
}
defer stmt.Close()
if err := stmt.SetSqlQuery("SELECT TOP 10 * FROM SALES.ORDERS"); err != nil {
panic(err)
}
reader, _, err := stmt.ExecuteQuery(ctx) // (array.RecordReader, int64, error)
if err != nil {
panic(err)
}
defer reader.Release()
fmt.Println(reader.Schema())
}
import java.util.HashMap;
import java.util.Map;
import org.apache.arrow.adbc.core.AdbcConnection;
import org.apache.arrow.adbc.core.AdbcDatabase;
import org.apache.arrow.adbc.core.AdbcStatement;
import org.apache.arrow.adbc.driver.jni.JniDriver;
import org.apache.arrow.memory.BufferAllocator;
import org.apache.arrow.memory.RootAllocator;
import org.apache.arrow.vector.ipc.ArrowReader;
public class ConnectAndQuery {
public static void main(String[] args) throws Exception {
Map<String, Object> parameters = new HashMap<>();
// Load the driver by name from its ADBC manifest.
JniDriver.PARAM_DRIVER.set(parameters, "arpeteradata");
parameters.put("adbc.arpeteradata.server", "td.example.com");
parameters.put("adbc.arpeteradata.username", "alice");
parameters.put("adbc.arpeteradata.password", "<pw>");
try (BufferAllocator allocator = new RootAllocator();
AdbcDatabase db = new JniDriver(allocator).open(parameters);
AdbcConnection connection = db.connect();
AdbcStatement statement = connection.createStatement()) {
statement.setSqlQuery("SELECT TOP 10 * FROM SALES.ORDERS");
try (AdbcStatement.QueryResult result = statement.executeQuery()) {
ArrowReader reader = result.getReader();
System.out.println(reader.getVectorSchemaRoot().getSchema());
}
}
}
}
library(adbcdrivermanager)
library(arrow)
# Load the driver by name from its ADBC manifest.
db <- adbc_database_init(
adbc_driver("arpeteradata"),
adbc.arpeteradata.server = "td.example.com",
adbc.arpeteradata.username = "alice",
adbc.arpeteradata.password = "<pw>"
)
con <- adbc_connection_init(db)
table <- read_adbc(con, "SELECT TOP 10 * FROM SALES.ORDERS") |>
as_arrow_table()
print(table$schema)
use adbc_core::options::{AdbcVersion, OptionDatabase, OptionValue};
use adbc_core::{Connection, Database, Driver, Statement, LOAD_FLAG_DEFAULT};
use adbc_driver_manager::ManagedDriver;
use arrow_array::RecordBatchReader;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Load the driver by name from its ADBC manifest.
let mut driver = ManagedDriver::load_from_name(
"arpeteradata", None, AdbcVersion::default(), LOAD_FLAG_DEFAULT, None,
)?;
let opt = |k: &str, v: &str| (OptionDatabase::Other(k.into()), OptionValue::String(v.into()));
let mut database = driver.new_database_with_opts([
opt("adbc.arpeteradata.server", "td.example.com"),
opt("adbc.arpeteradata.username", "alice"),
opt("adbc.arpeteradata.password", "<pw>"),
])?;
let mut connection = database.new_connection()?;
let mut statement = connection.new_statement()?;
statement.set_sql_query("SELECT TOP 10 * FROM SALES.ORDERS")?;
let reader = statement.execute()?; // impl RecordBatchReader
println!("{}", reader.schema());
Ok(())
}
dbapi.connect() turns autocommit off by default, which opens a Teradata
transaction. The recipes on this page pass autocommit=True for reads; when
you write with autocommit off, call conn.commit() before closing, or the
work is rolled back.
Straight to pandas / Polars
The result is already Arrow, so these handoffs need no row-by-row conversion. pandas and Polars read straight from the ADBC connection:
import adbc_driver_manager.dbapi as dbapi
import pandas as pd
import polars as pl
db_kwargs = {
"adbc.arpeteradata.server": "td.example.com",
"adbc.arpeteradata.username": "alice",
"adbc.arpeteradata.password": "<pw>",
}
with dbapi.connect(driver="arpeteradata", db_kwargs=db_kwargs, autocommit=True) as conn:
# pandas: Arrow-backed dtypes, no Python-object columns
df = pd.read_sql(
"SELECT * FROM SALES.ORDERS WHERE order_date >= DATE '2026-01-01'",
conn, dtype_backend="pyarrow",
)
# Polars: reads the same ADBC result directly
pf = pl.read_database(
"SELECT * FROM SALES.ORDERS WHERE order_date >= DATE '2026-01-01'", conn
)
Parameter binding
Use ? placeholders. The driver sends the values as typed parameters (see
Bound parameters):
- Python
- C / C++
- Go
with conn.cursor() as cur:
cur.execute("SELECT * FROM SALES.ORDERS WHERE order_id = ?", parameters=[42])
row = cur.fetch_arrow_table()
// One-row, one-column (int64) parameter batch, built with nanoarrow.
ArrowSchema param_schema = {};
ArrowSchemaInitFromType(¶m_schema, NANOARROW_TYPE_STRUCT);
ArrowSchemaAllocateChildren(¶m_schema, 1);
ArrowSchemaInitFromType(param_schema.children[0], NANOARROW_TYPE_INT64);
ArrowSchemaSetName(param_schema.children[0], "0");
ArrowArray param_array = {};
ArrowArrayInitFromSchema(¶m_array, ¶m_schema, nullptr);
ArrowArrayStartAppending(¶m_array);
ArrowArrayAppendInt(param_array.children[0], 42);
ArrowArrayFinishElement(¶m_array);
ArrowArrayFinishBuildingDefault(¶m_array, nullptr);
CHECK(AdbcStatementSetSqlQuery(&statement, "SELECT * FROM SALES.ORDERS WHERE order_id = ?", &error));
CHECK(AdbcStatementBind(&statement, ¶m_array, ¶m_schema, &error));
CHECK(AdbcStatementExecuteQuery(&statement, &stream, &rows_affected, &error));
import (
"github.com/apache/arrow-go/v18/arrow"
"github.com/apache/arrow-go/v18/arrow/array"
"github.com/apache/arrow-go/v18/arrow/memory"
)
schema := arrow.NewSchema([]arrow.Field{
{Name: "0", Type: arrow.PrimitiveTypes.Int64},
}, nil)
bldr := array.NewRecordBuilder(memory.DefaultAllocator, schema)
defer bldr.Release()
bldr.Field(0).(*array.Int64Builder).Append(42)
rec := bldr.NewRecord()
defer rec.Release()
_ = stmt.SetSqlQuery("SELECT * FROM SALES.ORDERS WHERE order_id = ?")
if err := stmt.Bind(ctx, rec); err != nil {
panic(err)
}
reader, _, err := stmt.ExecuteQuery(ctx)
if err != nil {
panic(err)
}
defer reader.Release()
Several parameter rows in one call (Python's executemany) are packed into as
few requests as possible:
with conn.cursor() as cur:
cur.executemany(
"INSERT INTO t(a, b) VALUES (?, ?)",
seq_of_parameters=[(1, "x"), (2, "y"), (3, "z")],
)
conn.commit() # needed when autocommit is off (the dbapi default)
For a SELECT, the bound rows must fit in one request (about 1 MB).
Bulk ingest an Arrow table
adbc_ingest loads an Arrow table into Teradata through packed, parameterised
INSERT requests (since v0.3.2). Tables the driver creates are MULTISET
and NO PRIMARY INDEX (see
Compatibility → Bulk ingest):
- Python
- Go
- R
import pyarrow as pa
table = pa.table({
"id": pa.array([1, 2, 3], pa.int32()),
"name": pa.array(["a", "b", "c"], pa.string()),
})
with dbapi.connect(driver="arpeteradata", db_kwargs=db_kwargs, autocommit=True) as conn:
with conn.cursor() as cur:
cur.adbc_ingest("my_table", table, mode="create") # create | append | replace | create_append
import (
"github.com/apache/arrow-adbc/go/adbc"
"github.com/apache/arrow-go/v18/arrow"
"github.com/apache/arrow-go/v18/arrow/array"
"github.com/apache/arrow-go/v18/arrow/memory"
)
schema := arrow.NewSchema([]arrow.Field{
{Name: "id", Type: arrow.PrimitiveTypes.Int32},
{Name: "name", Type: arrow.BinaryTypes.String},
}, nil)
bldr := array.NewRecordBuilder(memory.DefaultAllocator, schema)
defer bldr.Release()
bldr.Field(0).(*array.Int32Builder).AppendValues([]int32{1, 2, 3}, nil)
bldr.Field(1).(*array.StringBuilder).AppendValues([]string{"a", "b", "c"}, nil)
rec := bldr.NewRecord()
defer rec.Release()
rdr, _ := array.NewRecordReader(schema, []arrow.Record{rec})
defer rdr.Release()
_ = stmt.SetOption(adbc.OptionKeyIngestTargetTable, "my_table")
_ = stmt.SetOption(adbc.OptionKeyIngestMode, adbc.OptionValueIngestModeCreate)
if err := stmt.BindStream(ctx, rdr); err != nil {
panic(err)
}
if _, err := stmt.ExecuteUpdate(ctx); err != nil {
panic(err)
}
tbl <- arrow::arrow_table(
id = arrow::Array$create(1:3, type = arrow::int32()),
name = c("a", "b", "c")
)
tbl |> write_adbc(con, "my_table", mode = "create")
With autocommit on, all rows load in one transaction: all or none. Strings
become VARCHAR(4000) CHARACTER SET UNICODE columns; for longer strings, raise
the width on the statement, because a longer value is truncated silently
in Teradata session mode:
with conn.cursor() as cur:
cur.adbc_statement.set_options(**{"adbc.arpeteradata.ingest.varchar_length": "16000"})
cur.adbc_ingest("my_table", table, mode="replace")
Ingest into another database or a volatile table with the statement options
adbc.ingest.target_db_schema / adbc.ingest.temporary (Python:
adbc_ingest(..., db_schema_name="SALES") / temporary=True). Table and column
names are quoted as given.
Export a query to Parquet
import pyarrow.parquet as pq
with dbapi.connect(driver="arpeteradata", db_kwargs=db_kwargs, autocommit=True) as conn, \
conn.cursor() as cur:
cur.execute("SELECT * FROM SALES.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; batch_size sets the
rows per batch (default 32000). The ARPETERADATA:type field metadata travels
into the Parquet schema, so the Teradata type of each column is kept.
TLS and logon mechanisms
Verify the server certificate (recommended on any untrusted network), with a CA file for a gateway certificate signed by an internal CA:
conn = dbapi.connect(driver="arpeteradata", db_kwargs={
"adbc.arpeteradata.server": "td.example.com",
"adbc.arpeteradata.username": "alice",
"adbc.arpeteradata.password": "<pw>",
"adbc.arpeteradata.sslmode": "verify-full",
"adbc.arpeteradata.sslca": "/etc/ssl/teradata-ca.pem",
}, autocommit=True)
A server without TLS, through TTU on the legacy port, with an explicit TTU directory:
conn = dbapi.connect(driver="arpeteradata", db_kwargs={
"adbc.arpeteradata.server": "td.example.com",
"adbc.arpeteradata.username": "alice",
"adbc.arpeteradata.password": "<pw>",
"adbc.arpeteradata.sslmode": "disable",
"adbc.arpeteradata.ttu_path": "/opt/teradata/client/20.00/lib64",
}, autocommit=True)
Kerberos single sign-on (after kinit) and JWT, over TLS without TTU:
kerberos = {
"adbc.arpeteradata.server": "td.example.com",
"adbc.arpeteradata.sslmode": "verify-full",
"adbc.arpeteradata.logmech": "KRB5",
}
jwt = {
"adbc.arpeteradata.server": "td.example.com",
"adbc.arpeteradata.sslmode": "verify-full",
"adbc.arpeteradata.logmech": "JWT",
"adbc.arpeteradata.logdata": "token=" + token,
}
More in Authentication.
.NET through NuGet
Besides the installer, the driver is packaged as the native-only NuGet package
ArpeTeradata.AdbcDriver: the driver library for linux-x64 and win-x64
under runtimes/<rid>/native/, which NuGet copies into your application's
output. It has no managed assembly. The package is not on nuget.org: ask
sales@arpe.io for the .nupkg, then add the folder
that holds it as a package source:
<PropertyGroup>
<RestoreSources>$(RestoreSources);/path/to/nupkg-folder</RestoreSources>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ArpeTeradata.AdbcDriver" Version="0.3.5" />
</ItemGroup>
Load the native library from the output directory with the .NET ADBC driver
manager, and supply the licence as a database option (or with
ARPEIO_ADBC_LICENCE_FILE):
using Apache.Arrow.Adbc;
using Apache.Arrow.Adbc.C;
string lib = OperatingSystem.IsWindows()
? "arpeteradata_adbc_driver-win-x64.dll"
: "libarpeteradata_adbc_driver-linux-x64.so";
using AdbcDriver driver = CAdbcDriverImporter.Load(lib, "AdbcDriverInit");
using AdbcDatabase database = driver.Open(new Dictionary<string, string> {
["adbc.arpeteradata.server"] = "td.example.com",
["adbc.arpeteradata.username"] = "alice",
["adbc.arpeteradata.password"] = "<pw>",
["arpeio.adbc.license_file"] = "/etc/arpeio/arpeio_adbc.lic",
});
Check the name of the library file the package placed in your output
directory, and adjust lib if needed.
Loading by explicit path
Load-by-name uses an arpeteradata.toml ADBC driver manifest on the driver
manager's search path (point ADBC_DRIVER_PATH at its directory if needed).
Without one, pass the shared library's path as the driver:
conn = dbapi.connect(
driver="/opt/arpeio/libarpeteradata_adbc_driver-linux-x64.so", # Windows: r"C:\arpeio\arpeteradata_adbc_driver-win-x64.dll"
db_kwargs={
"adbc.arpeteradata.server": "td.example.com",
"adbc.arpeteradata.username": "alice",
"adbc.arpeteradata.password": "<pw>",
},
)
In C# use CAdbcDriverImporter.Load("libarpeteradata_adbc_driver-linux-x64.so", "AdbcDriverInit").
Everything after loading is identical. When loading by path, put the licence
file arpeio_adbc.lic next to the library, or supply the licence another way
(see Licensing).
See also
- Connection: every connection option
- Data types: the Arrow types you get back
- Troubleshooting: when a snippet does not connect