adbcBridge is a plain-C11 ADBC driver that talks to any ODBC data source. ADBC
(Arrow Database Connectivity) is a database API whose result sets are Apache
Arrow record batches; ODBC (Open Database Connectivity) is the older, row-based
C API that almost every database ships a driver for. The driver is one shared
library — libadbc_driver_odbc.so on Linux, .dylib on macOS,
libadbc_driver_odbc.dll on Windows.
The AdbcBridge NuGet package is a thin .NET wrapper around that library. It
does two things: it finds the shared library on the machine (or unpacks the
copy shipped inside the package), and it hands it to Apache.Arrow.Adbc,
the Apache Arrow ADBC binding for .NET. Everything after that — connections,
statements, Arrow record batches — is the Apache.Arrow.Adbc API.
This page assumes you are comfortable in C# but new to ODBC and ADBC.
- What the package contains
- Requirements
- Install
- The three entry points
- Without the wrapper:
CAdbcDriverImporterdirectly - How the library is located
- Connection strings
- Driver options
- Running a query
- Bulk ingest
- Parameters
- Metadata
- Errors
- Target frameworks and ADO.NET
- Known limitations
- Complete worked example
| Item | Value |
|---|---|
| Package id | AdbcBridge |
| Version | 0.1.4 |
| Target frameworks | netstandard2.0, net8.0 |
| Dependency | Apache.Arrow.Adbc 0.24.0 |
| Public type | AdbcBridge.Driver (static), AdbcBridge.DriverNotFoundException |
| License | Apache-2.0 |
The package always contains the managed wrapper. It may also carry the native driver as a runtime asset, depending on how it was packed:
- The package attached to a GitHub Release is packed with the native library
present, laid out as
runtimes/<rid>/native/libadbc_driver_odbc.{so,dylib,dll}for each RID (runtime identifier — .NET'sos-archplatform tag). The RIDs built for releases arelinux-x64,linux-arm64,osx-arm64andwin-x64. - A package packed without native assets is managed-only; it relies on finding the library elsewhere on the machine (see How the library is located).
Tip: runtimes/<rid>/native/ is the standard NuGet layout for native assets. A
framework-dependent net8.0 build copies the whole runtimes/ tree next to
your application; a RID-specific publish (dotnet publish -r <rid>) flattens the
one matching library beside the application. Either way the wrapper finds it
automatically.
- An ODBC driver manager, which the native library links against:
- Linux: unixODBC (provides
libodbc.so.2); on Debian/Ubuntu,unixodbc. - macOS: unixODBC or iODBC.
- Windows: built into the operating system.
- Linux: unixODBC (provides
- The ODBC driver for your database (for example the SQLite ODBC driver
libsqlite3odbc.so, or psqlodbc for PostgreSQL). adbcBridge bridges to whichever ODBC drivers are installed; it does not contain them. - The .NET SDK/runtime for one of the target frameworks.
dotnet add package AdbcBridge --version 0.1.4The package carries the native library for linux-x64, linux-arm64, osx-arm64
and win-x64, so nothing else needs installing beyond an ODBC driver for your database.
The same .nupkg is attached to each release. On a machine without registry access,
download it, register the folder that holds it as a package source, then add the package:
# 1. Download AdbcBridge.0.1.2.nupkg from the Release into ./localnuget/
mkdir -p localnuget
# (place AdbcBridge.0.1.2.nupkg in ./localnuget/)
# 2. Register the folder as a NuGet source.
dotnet nuget add source "$PWD/localnuget" --name adbcbridge-local
# 3. Add the package to your project.
dotnet add package AdbcBridge --version 0.1.4 --source adbcbridge-localThe release .nupkg is available on the project's GitHub Releases page at
https://github.com/singhpratech/adbcbridge/releases. The package's own
readme, csharp/README.md, is the short form of this
page and ships inside the .nupkg.
The wrapper is a convenience, not a requirement. The driver is a plain C shared library, so the Apache binding alone can load it:
dotnet add package Apache.Arrow.Adbc --version 0.24.0You then locate the library yourself and hand its path to
CAdbcDriverImporter.Load — see
Without the wrapper.
From a source checkout (csharp/):
cd csharp
dotnet build
dotnet test # needs SQLITE_ODBC_DRIVER, skips otherwise
dotnet pack AdbcBridge -c Release # managed-only package
dotnet pack AdbcBridge -c Release -p:NativeRoot=/abs/path/to/native # with native assetsdotnet test runs AdbcBridge.Tests, which holds two tests. The first is the
real thing: find the driver, load it, connect to SQLite through the ODBC driver
SQLITE_ODBC_DRIVER names (Driver=<value>;Database=:memory:;), run
SELECT 1 and read the Arrow batch. It is skipped with a message when
SQLITE_ODBC_DRIVER is unset. The second points ADBCBRIDGE_LIBRARY at a path
that does not exist and checks that DriverNotFoundException names the
variable, the path, and every place searched. The
driver itself is found through Driver.Path(), so either set
ADBC_ODBC_DRIVER, or build the repo (cmake --build build) and let the
build-tree lookup find it.
NativeRoot must be a directory laid out as <rid>/libadbc_driver_odbc.{so,dylib,dll}:
native/
├── linux-x64/libadbc_driver_odbc.so
├── linux-arm64/libadbc_driver_odbc.so
├── osx-arm64/libadbc_driver_odbc.dylib
└── win-x64/libadbc_driver_odbc.dll
Every library found there is packed under runtimes/<rid>/native/. Missing RIDs
are simply skipped; a NativeRoot that is not a directory, or that holds no
library at all, fails the pack.
Everything the wrapper adds lives on the static class AdbcBridge.Driver:
using AdbcBridge;
using Apache.Arrow.Adbc;
// 1. Where is the native library? Returns an absolute path, or throws
// DriverNotFoundException listing every place it looked.
string path = Driver.Path();
// 2. Load it: returns the Apache.Arrow.Adbc.AdbcDriver produced by
// CAdbcDriverImporter.Load(path). Dispose it when done.
using AdbcDriver driver = Driver.Load();
// 3. Or skip straight to an open connection from an ODBC connection string.
using AdbcConnection connection = Driver.Connect("Driver=SQLite3;Database=my.db;");| Method | Returns | Notes |
|---|---|---|
Driver.Path() |
string |
Absolute path of the native library. Throws DriverNotFoundException if not found. |
Driver.Load() |
AdbcDriver |
Calls CAdbcDriverImporter.Load(Driver.Path()). Caller disposes. |
Driver.Connect(string connectionString, IReadOnlyDictionary<string,string>? options = null) |
AdbcConnection |
Loads the driver, opens an AdbcDatabase with uri set to connectionString, and connects. |
Driver.RuntimeIdentifier() |
string |
The os-arch RID (linux-x64, …) the native asset is filed under for this process. |
Driver.Connect keeps the AdbcDatabase and AdbcDriver it opened alive for
as long as the returned AdbcConnection is; dispose the connection when done and
the rest is released with it. A uri entry in options is overridden by
connectionString.
The constants used along the way are public:
| Constant | Value |
|---|---|
Driver.LibraryVariable |
ADBCBRIDGE_LIBRARY |
Driver.DriverVariable |
ADBC_ODBC_DRIVER |
Driver.ManifestPathVariable |
ADBC_DRIVER_PATH |
Driver.ManifestName |
odbc.toml |
Driver.Load() / Driver.Connect(connectionString) are thin: they find the
library and call Apache.Arrow.Adbc.C.CAdbcDriverImporter.Load(path). With only
the upstream package (see Install) you make
that call yourself, with the path of libadbc_driver_odbc.so (.dylib,
.dll), and open the database and connection by hand:
using Apache.Arrow;
using Apache.Arrow.Adbc;
using Apache.Arrow.Adbc.C;
using Apache.Arrow.Ipc;
using AdbcDriver driver = CAdbcDriverImporter.Load("/path/to/libadbc_driver_odbc.so");
string uri = "Driver=/usr/lib/x86_64-linux-gnu/odbc/libsqlite3odbc.so;Database=my.db;";
using AdbcDatabase database = driver.Open(new Dictionary<string, string> { ["uri"] = uri });
using AdbcConnection connection = database.Connect(null);
using AdbcStatement statement = connection.CreateStatement();
statement.SqlQuery = "SELECT * FROM my_table";
QueryResult result = statement.ExecuteQuery();
IArrowArrayStream stream = result.Stream!;
while (await stream.ReadNextRecordBatchAsync() is RecordBatch batch)
{
Console.WriteLine($"{batch.Length} rows");
}CAdbcDriverImporter.Load dlopens the shared library and calls its
AdbcDriverInit export, so no ADBC driver manager or manifest is involved (the
ODBC driver manager from Requirements is still needed at run
time). Pass a second argument to use a different entry point: the library also
exports AdbcDriverOdbcInit, and
CAdbcDriverImporter.Load(path, "AdbcDriverOdbcInit") works the same.
Everything from Running a query onwards applies unchanged —
the AdbcConnection is the same type whichever way you obtained it. The one
thing you give up is How the library is located;
DriverNotFoundException is the wrapper's, so a wrong path here surfaces as
the importer's own load error instead.
The smoke test in tests/csharp/ is a runnable example
of exactly this path — it depends only on Apache.Arrow.Adbc and xunit, not on
the AdbcBridge package — and it also compiles the snippets above so they
cannot rot. With the .NET 8 SDK and an ODBC driver manager installed:
cd tests/csharp
SQLITE_ODBC_DRIVER=/path/to/libsqlite3odbc.so dotnet testADBC_ODBC_DRIVER names the driver under test and defaults to
build/libadbc_driver_odbc.so at the repository root; set it to test an
installed copy. tests/csharp/README.md has the
containerised variant and what each test asserts.
Driver.Path() returns the first hit, in this order. The environment variables
are the two ways to point it at a specific build.
| # | Where it looks | Detail |
|---|---|---|
| 1 | ADBCBRIDGE_LIBRARY |
The library's full path. Set but not a file is an error, not a fall-through. |
| 2 | ADBC_ODBC_DRIVER |
The same variable the Python package and the test suite use. Same error rule. |
| 3 | Native asset in this package | runtimes/<rid>/native/libadbc_driver_odbc.* next to the AdbcBridge assembly, or flattened beside it by a RID-specific publish. |
| 4 | ADBC driver manifest odbc.toml |
In the directories the ADBC driver manager searches: ADBC_DRIVER_PATH first, then ~/.config/adbc/drivers ($XDG_CONFIG_HOME/adbc/drivers), /etc/adbc/drivers, /usr/local/etc/adbc/drivers, /usr/share/adbc/drivers, /usr/local/share/adbc/drivers. On macOS, ~/Library/Application Support/ADBC/Drivers and /Library/Application Support/ADBC/Drivers; on Windows, %LOCALAPPDATA%\ADBC\Drivers and %PROGRAMDATA%\ADBC\Drivers. This is what install.sh and cmake --install write. |
| 5 | Common install directories | ~/.local/lib, /usr/local/lib, /usr/lib (and their lib64 and <arch>-linux-gnu variants), /opt/adbcbridge/lib, /opt/homebrew/lib. On Windows, %LOCALAPPDATA%\adbcbridge\bin and %ProgramFiles%\adbcbridge\bin. |
| 6 | A CMake build/ tree |
build/, build/Release, build/Debug, build/RelWithDebInfo, build/MinSizeRel in any directory above the application, the assembly, or the current directory — so a source checkout works right after cmake --build build. |
When nothing matches, Driver.Path() throws AdbcBridge.DriverNotFoundException.
Its Message and its Searched (IReadOnlyList<string>) property list every
location that was examined, in order, and its LibraryName is the file name it
looked for.
Troubleshooting: if Driver.Path() throws, read the Searched list — it names
every directory tried. The quickest fix is to set ADBC_ODBC_DRIVER to the
absolute path of your libadbc_driver_odbc.*, or install the driver with
./install.sh / cmake --install so the odbc.toml manifest is written.
The first argument to Driver.Connect is a plain ODBC connection string. It
becomes the ADBC database's uri option. Two forms work:
-
DSN-less (names the ODBC driver directly):
Driver=/usr/lib/x86_64-linux-gnu/odbc/libsqlite3odbc.so;Database=my.db;Driver=takes either a driver name registered inodbcinst.ini(for exampleDriver=SQLite3;) or the path of the ODBC driver library. -
DSN (names a data source from
odbc.ini):DSN=warehouse;UID=me;PWD=secret;
Real connection strings from the project's compatibility matrix:
| Database | Connection string |
|---|---|
| SQLite | Driver=<libsqlite3odbc.so>;Database=/path/to/m.db; |
| PostgreSQL | Driver=<psqlodbcw.so>;Server=127.0.0.1;Port=15432;Database=adbc;Uid=adbc;Pwd=adbc; |
| MySQL | Driver=<libmyodbc.so>;Server=127.0.0.1;Port=13307;Database=adbc;User=adbc;Password=adbc; |
| SQL Server | Driver=<msodbcsql>;Server=127.0.0.1,14331;Database=master;Uid=sa;Pwd=…;TrustServerCertificate=yes; |
| Oracle | Driver=<liboraodbc.so>;DBQ=127.0.0.1:11521/FREEPDB1;UID=adbc;PWD=adbc; |
Substitute the driver name or path for the value in angle brackets.
Pass driver options as the second argument to Driver.Connect (an
IReadOnlyDictionary<string,string>), or set them directly on an AdbcDatabase
if you drove Driver.Load() yourself. Keys and string values:
using AdbcConnection connection = Driver.Connect(
"Driver=SQLite3;Database=my.db;",
new Dictionary<string, string> { ["adbc.odbc.batch_size"] = "4096" });The database also understands the generic ADBC options uri (the full ODBC
connection string — this is what connectionString becomes), dsn (a DSN name,
appended as DSN=…), and username / password (appended as UID= / PWD=).
The adbc.odbc.* options below come from the driver:
| Option | Meaning |
|---|---|
adbc.odbc.batch_size |
Rows per Arrow batch (default 1024). |
adbc.odbc.max_bind_bytes |
Widest value bound at the width the driver declares for it, in bytes (default 32768). Wider values are bound at long_bind_bytes, or read with SQLGetData where that is not possible. |
adbc.odbc.long_bind_bytes |
Width, in bytes, at which to bind a column whose declared width is not a real bound — TEXT/NVARCHAR(MAX)/LONGTEXT/bytea and similar (default 2048). Values longer than this are re-read in full, so this trades only speed. |
adbc.odbc.rowset_bytes |
Ceiling on a reader's bound rowset buffers, in bytes (default 8388608). Holds batch_size rows unless that would cost more than this. |
adbc.odbc.decimal_as_string |
true to return DECIMAL/NUMERIC as strings. |
adbc.odbc.partitions |
How many partitions AdbcStatementExecutePartitions splits a query into — 0 (default) chooses from the table's size, 1 never splits. Set on the statement. |
adbc.odbc.prefetch |
Rowsets kept in flight on a background fetch thread — 0 (default) is off, 1 is double buffering, up to 8. Settable on the database, connection, or statement. |
adbc.odbc.delegate |
auto (default) / never / always — see Native delegation. |
adbc.odbc.delegate.driver |
Force a specific native driver: a bare name (postgresql) or manifest name; a path only with allow_paths. |
adbc.odbc.delegate.search_path |
Extra directories to search for native drivers (:-separated); needs allow_paths. |
adbc.odbc.delegate.allow_paths |
true to let the two options above name filesystem paths (default false). |
adbc.odbc.delegate.last_error |
Read-only: why delegation did not happen. |
adbc.odbc.delegated_to |
Read-only: the native driver serving this database/connection, or odbc. |
adbc.odbc.tune |
true (default) / false — may the driver add ODBC connection keywords of its own where it recognises the target driver? false sends your connection string through untouched. |
adbc.odbc.sqllen_32bit |
true/false to force the 32-bit-SQLLEN driver quirk on or off. Autodetected (on for IBM Db2), so you normally never set it. Also settable on the connection and statement. |
Some databases have a first-class native ADBC driver (PostgreSQL, SQLite,
DuckDB, Snowflake, BigQuery, Flight SQL). When adbc.odbc.delegate is auto
(the default) and adbcBridge recognises such a target, it hands the whole
database over to that native driver for native speed. Set
adbc.odbc.delegate=never to force the ODBC path (this is what the benchmark
harness does so its numbers describe the ODBC driver), or always to make a
missing native driver an error instead of a fall-through. Delegation is not
implemented on Windows — there auto always takes the ODBC path.
Driver.Connect gives you an Apache.Arrow.Adbc.AdbcConnection. Create a
statement, set its SQL, execute, and drain the resulting Arrow stream:
using Apache.Arrow;
using Apache.Arrow.Adbc;
using Apache.Arrow.Ipc; // IArrowArrayStream
using AdbcConnection connection = Driver.Connect("Driver=SQLite3;Database=my.db;");
using AdbcStatement statement = connection.CreateStatement();
statement.SqlQuery = "SELECT id, name FROM customers";
QueryResult result = statement.ExecuteQuery();
IArrowArrayStream stream = (IArrowArrayStream)result.Stream;
while (await stream.ReadNextRecordBatchAsync() is RecordBatch batch)
{
Console.WriteLine($"batch of {batch.Length} rows");
// batch.Column(0), batch.Schema.GetFieldByIndex(0).Name, ...
}statement.ExecuteQuery()returns aQueryResult; itsStreamis anIArrowArrayStream(Apache.Arrow.Ipc).stream.Schemais the ArrowSchema;stream.ReadNextRecordBatchAsync()yieldsRecordBatchvalues until it returnsnull.- For DML that returns no rows, use
statement.ExecuteUpdate()instead ofExecuteQuery().
Tip: dispose (or read to completion on) the IArrowArrayStream on the same
thread that created it, before disposing the statement. If the stream's release
callback runs later on the .NET finalizer thread, an ODBC driver whose client
library is thread-affine can crash inside SQLCloseCursor. A using on the
stream is the simplest guarantee.
Transactions: connection.AutoCommit defaults to on. Set it to false and call
connection.Commit() / connection.Rollback() to control transactions
yourself.
Bulk ingest writes an Arrow RecordBatch into a table. The option keys come from
Apache.Arrow.Adbc.AdbcOptions.Ingest:
using Apache.Arrow;
using Apache.Arrow.Adbc;
RecordBatch batch = /* build your batch */;
using AdbcStatement statement = connection.CreateStatement();
statement.SetOption(AdbcOptions.Ingest.TargetTable, "my_table");
statement.SetOption(AdbcOptions.Ingest.Mode, AdbcOptions.IngestMode.Create);
statement.Bind(batch, batch.Schema);
statement.ExecuteUpdate();
// With AutoCommit off, commit when the ingest is done:
connection.Commit();AdbcOptions.Ingest.ModeacceptsAdbcOptions.IngestMode.Create(create the table and insert), among the other ADBC ingest modes.- The driver generates the
CREATE TABLEDDL from the Arrow schema, then sends one multi-rowINSERTper batch of rows inside a single transaction. It works on every ODBC driver that can bind a parameter.adbc.odbc.rows_per_insertoverrides the rows-per-INSERTthe driver chooses.
The driver supports parameter binding (Bind / BindStream). Parameters are
bound as an Arrow RecordBatch, one column per ? and one row per execution;
Bind takes the batch and its schema. To run a parameterised statement, bind a
single-row (or multi-row) RecordBatch whose columns are the parameter values,
then execute:
using AdbcStatement statement = connection.CreateStatement();
statement.SqlQuery = "SELECT * FROM customers WHERE id = ?";
statement.Bind(parameterBatch, parameterBatch.Schema); // one column per '?'
QueryResult result = statement.ExecuteQuery();Building the batch by hand, for a two-row INSERT with a NULL in the second
row:
using Apache.Arrow.Types;
Schema parameters = new Schema.Builder()
.Field(new Field("id", Int64Type.Default, nullable: true))
.Field(new Field("name", StringType.Default, nullable: true))
.Build();
RecordBatch batch = new RecordBatch(
parameters,
new IArrowArray[]
{
new Int64Array.Builder().Append(1).Append(2).Build(),
new StringArray.Builder().Append("ada").AppendNull().Build(),
},
length: 2);
using AdbcStatement insert = connection.CreateStatement();
insert.SqlQuery = "INSERT INTO my_table (id, name) VALUES (?, ?)";
insert.Prepare();
insert.Bind(batch, parameters);
Console.WriteLine($"{insert.ExecuteUpdate().AffectedRows} rows inserted");Binding a multi-row batch applies the statement once per row — as a column-wise ODBC
parameter array where the driver handles them (adbc.odbc.array_binding, on by default),
row by row otherwise. Bulk ingest takes its own route, a multi-row INSERT rewrite by
default — see bulk ingest. The placeholder syntax (?, $1,
:name, …) is whatever the underlying ODBC driver and database accept.
The ADBC connection exposes the standard metadata calls, backed by the ODBC
driver's catalog functions: GetInfo, GetObjects (catalogs, schemas, tables,
columns), GetTableTypes, and GetTableSchema. Each returns Arrow data through
the same Apache.Arrow.Adbc.AdbcConnection you already have. Consult the
Apache.Arrow.Adbc API for the exact method signatures on your version
(0.24.0).
Operations throw Apache.Arrow.Adbc.AdbcException. The driver maps each ODBC
diagnostic into it as a structured error carrying the SQLSTATE (the
five-character ODBC status code) and the driver's native error code, so you
can branch on the database's own error identity rather than parsing message
text. The precise property names belong to the Apache.Arrow.Adbc API; catch
AdbcException and inspect it:
try
{
using AdbcConnection connection = Driver.Connect("Driver=SQLite3;Database=my.db;");
using AdbcStatement statement = connection.CreateStatement();
statement.SqlQuery = "SELECT * FROM does_not_exist";
statement.ExecuteQuery();
}
catch (AdbcException ex)
{
Console.Error.WriteLine(ex.Message); // includes SQLSTATE + native code
}AdbcBridge.DriverNotFoundException is separate: it is thrown only when the
native library itself cannot be located (see
How the library is located), before any database
work begins.
The package targets netstandard2.0 and net8.0.
- On
net8.0, native assets underruntimes/<rid>/native/are resolved automatically by the build. - On
netstandard2.0consumers running on .NET Framework,runtimes/assets are not copied by the build; install the driver on the machine, or setADBCBRIDGE_LIBRARY.
The AdbcBridge package does not provide an ADO.NET (System.Data)
provider — it is a locator and loader for the ADBC driver, and its surface is the
Apache.Arrow.Adbc types (AdbcConnection, AdbcStatement, and Arrow record
batches), not IDbConnection / IDbCommand.
- The ODBC stack is not bundled. You still need an ODBC driver manager
(unixODBC/iODBC on Unix, built in on Windows) and the ODBC driver for your
database. The native library links against the driver manager
(
libodbc.so.2on Linux). - Native assets are present only if the package was packed with them. The
release
.nupkgcarrieslinux-x64,linux-arm64,osx-arm64andwin-x64. Any other platform, or a managed-only package, must find the library through the environment, a manifest, or an install directory. netstandard2.0on .NET Framework does not getruntimes/native assets copied automatically; provide the library yourself.- Native delegation is not available on Windows —
autoalways takes the ODBC path there andalwaysfails with a message. - Thread affinity. Release the Arrow result stream on the thread that created
it (a
using), not on the finalizer thread — some ODBC client libraries are thread-affine. Apache.Arrow.Adbcversion. The package is built against0.24.0; use a compatible version in your project.
A minimal console application that finds the driver, connects to an in-memory SQLite database through the SQLite ODBC driver, runs a query, and reads the Arrow result.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<LangVersion>latest</LangVersion>
<!-- Brings in System, System.Linq, System.Collections.Generic, etc.,
which the Program.cs below uses (.Select, Dictionary, top-level await). -->
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<!-- From nuget.org; for a release .nupkg instead, add a local source:
dotnet nuget add source "$PWD/localnuget" --name adbcbridge-local -->
<PackageReference Include="AdbcBridge" Version="0.1.4" />
</ItemGroup>
</Project>// SPDX-License-Identifier: Apache-2.0
using AdbcBridge;
using Apache.Arrow;
using Apache.Arrow.Adbc;
using Apache.Arrow.Ipc;
// The SQLite ODBC driver: a name from odbcinst.ini (e.g. "SQLite3") or the path
// of libsqlite3odbc.so. Point SQLITE_ODBC_DRIVER at it, or edit the fallback.
string sqlite = Environment.GetEnvironmentVariable("SQLITE_ODBC_DRIVER") ?? "SQLite3";
try
{
// 1. Find + load + connect, in one call. The string is an ODBC connection string.
using AdbcConnection connection = Driver.Connect(
$"Driver={sqlite};Database=:memory:;",
new Dictionary<string, string> { ["adbc.odbc.batch_size"] = "1024" });
// 2. Run a query.
using AdbcStatement statement = connection.CreateStatement();
statement.SqlQuery = "SELECT 42 AS answer, 'hi' AS greeting";
// 3. Drain the Arrow stream on this thread.
QueryResult result = statement.ExecuteQuery();
using IArrowArrayStream stream = (IArrowArrayStream)result.Stream!;
Console.WriteLine("columns: " +
string.Join(", ", stream.Schema.FieldsList.Select(f => f.Name)));
while (await stream.ReadNextRecordBatchAsync() is RecordBatch batch)
{
var answers = (Int32Array)batch.Column(0);
for (int i = 0; i < batch.Length; i++)
{
Console.WriteLine($"row {i}: answer = {answers.GetValue(i)}");
}
}
}
catch (DriverNotFoundException ex)
{
Console.Error.WriteLine("adbcBridge library not found:");
Console.Error.WriteLine(ex.Message); // lists every place searched
Environment.Exit(1);
}
catch (AdbcException ex)
{
Console.Error.WriteLine("database error: " + ex.Message);
Environment.Exit(1);
}# The SQLite ODBC driver must be installed; name or path in SQLITE_ODBC_DRIVER.
export SQLITE_ODBC_DRIVER=/usr/lib/x86_64-linux-gnu/odbc/libsqlite3odbc.so
dotnet runExpected output:
columns: answer, greeting
row 0: answer = 42