Zum Inhalt springen

ADBC-Client

Installation Um den DuckDB-ADBC-Client zu verwenden, laden Sie das libduckdb-Archiv für Ihre Plattform herunter und folgen Sie den Anweisungen unten.

Die aktuelle stabile Version des DuckDB-ADBC-Clients ist 1.5.5.

Arrow Database Connectivity (ADBC) ist ähnlich wie ODBC und JDBC eine C-ähnliche API, die Code-Portabilität zwischen verschiedenen Datenbanksystemen ermöglicht. So können Entwickler Anwendungen bauen, die mit Datenbanksystemen kommunizieren, ohne systemspezifischen Code zu verwenden. Der wesentliche Unterschied zwischen ADBC und ODBC/JDBC ist, dass ADBC Arrow verwendet, um Daten zwischen dem Datenbanksystem und der Anwendung zu übertragen. DuckDB hat einen ADBC-Treiber, der die Zero-Copy-Integration zwischen DuckDB und Arrow nutzt, um Daten effizient zu übertragen.

Bitte lesen Sie die ADBC-Dokumentationsseite für eine ausführlichere Diskussion zu ADBC und eine detaillierte API-Erklärung.

Implementierte Funktionalität

Der DuckDB-ADBC-Treiber implementiert die vollständige ADBC-Spezifikation, mit Ausnahme der Funktionen ConnectionReadPartition und StatementExecutePartitions. Beide Funktionen existieren zur Unterstützung von Systemen, die intern die Abfrageergebnisse partitionieren, was auf DuckDB nicht zutrifft. In diesem Abschnitt beschreiben wir die wichtigsten Funktionen, die in ADBC existieren, zusammen mit den Argumenten, die sie entgegennehmen, und geben Beispiele für jede Funktion.

Database

Satz von Funktionen, die auf einer Datenbank operieren.

Funktionsname Beschreibung Argumente Beispiel
DatabaseNew Allokiert eine neue (aber nicht initialisierte) Datenbank. (AdbcDatabase *database, AdbcError *error) AdbcDatabaseNew(&adbc_database, &adbc_error)
DatabaseSetOption Setzt eine char*-Option. (AdbcDatabase *database, const char *key, const char *value, AdbcError *error) AdbcDatabaseSetOption(&adbc_database, "path", "test.db", &adbc_error)
DatabaseInit Schließt das Setzen von Optionen ab und initialisiert die Datenbank. (AdbcDatabase *database, AdbcError *error) AdbcDatabaseInit(&adbc_database, &adbc_error)
DatabaseRelease Zerstört die Datenbank. (AdbcDatabase *database, AdbcError *error) AdbcDatabaseRelease(&adbc_database, &adbc_error)

Database-Optionen

Option Beschreibung
driver Pfad zur DuckDB-Shared-Library (libduckdb.so, libduckdb.dylib oder duckdb.dll).
entrypoint Name der Einstiegsfunktion. Muss duckdb_adbc_init sein.
path Pfad zu einer DuckDB-Datenbankdatei. Wenn nicht gesetzt, wird eine In-Memory-Datenbank erzeugt.
uri Alternative zu path. Akzeptiert einfache Pfade oder file:-URIs (z. B. file:test.db, file:///absolute/path.db). Hat Vorrang vor path, wenn beide gesetzt sind.

Connection

Ein Satz von Funktionen, die eine Verbindung zum Interagieren mit einer Datenbank erzeugen und zerstören.

Funktionsname Beschreibung Argumente Beispiel
ConnectionNew Allokiert eine neue (aber nicht initialisierte) Verbindung. (AdbcConnection*, AdbcError*) AdbcConnectionNew(&adbc_connection, &adbc_error)
ConnectionSetOption Optionen können vor ConnectionInit gesetzt werden. (AdbcConnection*, const char*, const char*, AdbcError*) AdbcConnectionSetOption(&adbc_connection, ADBC_CONNECTION_OPTION_AUTOCOMMIT, ADBC_OPTION_VALUE_DISABLED, &adbc_error)
ConnectionInit Schließt das Setzen von Optionen ab und initialisiert die Verbindung. (AdbcConnection*, AdbcDatabase*, AdbcError*) AdbcConnectionInit(&adbc_connection, &adbc_database, &adbc_error)
ConnectionRelease Zerstört diese Verbindung. (AdbcConnection*, AdbcError*) AdbcConnectionRelease(&adbc_connection, &adbc_error)

Ein Satz von Funktionen, die Metadaten über die Datenbank abrufen. Im Allgemeinen geben diese Funktionen Arrow-Objekte zurück, insbesondere einen ArrowArrayStream.

Funktionsname Beschreibung Argumente Beispiel
ConnectionGetInfo Holt Metadaten über Treiber und Datenbank. (AdbcConnection*, const uint32_t*, size_t, ArrowArrayStream*, AdbcError*) AdbcConnectionGetInfo(&adbc_connection, NULL, 0, &arrow_stream, &adbc_error)
ConnectionGetObjects Holt eine hierarchische Ansicht aller Kataloge, Datenbankschemas, Tabellen und Spalten. (AdbcConnection*, int, const char*, const char*, const char*, const char**, const char*, ArrowArrayStream*, AdbcError*) AdbcDatabaseInit(&adbc_database, &adbc_error)
ConnectionGetTableSchema Holt das Arrow-Schema einer Tabelle. (AdbcConnection*, const char*, const char*, const char*, ArrowSchema*, AdbcError*) AdbcDatabaseRelease(&adbc_database, &adbc_error)
ConnectionGetTableTypes Holt eine Liste der Tabellentypen in der Datenbank. (AdbcConnection*, ArrowArrayStream*, AdbcError*) AdbcDatabaseNew(&adbc_database, &adbc_error)

Die Funktion ConnectionGetInfo unterstützt die folgenden Info-Codes:

Info-Code Konstante Beschreibung
0 ADBC_INFO_VENDOR_NAME Name des Datenbankanbieters (duckdb).
1 ADBC_INFO_VENDOR_VERSION Datenbankversion.
100 ADBC_INFO_DRIVER_NAME Treibername.
101 ADBC_INFO_DRIVER_VERSION Treiberversion.
102 ADBC_INFO_DRIVER_ARROW_VERSION Version der Arrow-Bibliothek.
103 ADBC_INFO_DRIVER_ADBC_VERSION Von dem Treiber unterstützte ADBC-Spezifikationsversion (als Ganzzahl, z. B. 1001000 für 1.1.0).

Ein Satz von Funktionen mit Transaktionssemantik für die Verbindung. Standardmäßig starten alle Verbindungen mit aktiviertem Auto-Commit-Modus, dies kann jedoch über die Funktion ConnectionSetOption deaktiviert werden.

Funktionsname Beschreibung Argumente Beispiel
ConnectionCommit Commitet ausstehende Transaktionen. (AdbcConnection*, AdbcError*) AdbcConnectionCommit(&adbc_connection, &adbc_error)
ConnectionRollback Führt einen Rollback ausstehender Transaktionen durch. (AdbcConnection*, AdbcError*) AdbcConnectionRollback(&adbc_connection, &adbc_error)

Statement

Statements halten den Zustand zur Abfrageausführung. Sie repräsentieren sowohl einmalige Abfragen als auch Prepared Statements. Sie können wiederverwendet werden; dabei werden jedoch vorherige Resultsets dieses Statements ungültig.

Die Funktionen zum Erzeugen, Zerstören und Setzen von Optionen für ein Statement:

Funktionsname Beschreibung Argumente Beispiel
StatementNew Erzeugt ein neues Statement für eine gegebene Verbindung. (AdbcConnection*, AdbcStatement*, AdbcError*) AdbcStatementNew(&adbc_connection, &adbc_statement, &adbc_error)
StatementRelease Zerstört ein Statement. (AdbcStatement*, AdbcError*) AdbcStatementRelease(&adbc_statement, &adbc_error)
StatementSetOption Setzt eine String-Option an einem Statement. (AdbcStatement*, const char*, const char*, AdbcError*) StatementSetOption(&adbc_statement, ADBC_INGEST_OPTION_TARGET_TABLE, "TABLE_NAME", &adbc_error)

Funktionen zur Abfrageausführung:

Funktionsname Beschreibung Argumente Beispiel
StatementSetSqlQuery Setzt die auszuführende SQL-Abfrage. Die Abfrage kann dann mit StatementExecuteQuery ausgeführt werden. (AdbcStatement*, const char*, AdbcError*) AdbcStatementSetSqlQuery(&adbc_statement, "SELECT * FROM TABLE", &adbc_error)
StatementSetSubstraitPlan Setzt einen Substrait-Plan zur Ausführung. Die Abfrage kann dann mit StatementExecuteQuery ausgeführt werden. (AdbcStatement*, const uint8_t*, size_t, AdbcError*) AdbcStatementSetSubstraitPlan(&adbc_statement, substrait_plan, length, &adbc_error)
StatementExecuteQuery Führt ein Statement aus und holt die Ergebnisse. (AdbcStatement*, ArrowArrayStream*, int64_t*, AdbcError*) AdbcStatementExecuteQuery(&adbc_statement, &arrow_stream, &rows_affected, &adbc_error)
StatementPrepare Wandelt dieses Statement in ein Prepared Statement um, das mehrfach ausgeführt werden kann. (AdbcStatement*, AdbcError*) AdbcStatementPrepare(&adbc_statement, &adbc_error)

Funktionen zum Binden, verwendet für Bulk-Einfügungen oder in Prepared Statements.

Funktionsname Beschreibung Argumente Beispiel
StatementBindStream Bindet einen Arrow Stream. Dies kann für Bulk-Inserts oder Prepared Statements verwendet werden. (AdbcStatement*, ArrowArrayStream*, AdbcError*) StatementBindStream(&adbc_statement, &input_data, &adbc_error)

Ingestionsmodi

Beim Ingestieren von Daten über StatementBindStream kann der Ingestionsmodus mit ADBC_INGEST_OPTION_MODE gesetzt werden:

Modus Konstante Beschreibung
Create ADBC_INGEST_OPTION_MODE_CREATE Erzeugt eine neue Tabelle (Fehler, wenn die Tabelle bereits existiert). Dies ist der Standard.
Append ADBC_INGEST_OPTION_MODE_APPEND Hängt an eine vorhandene Tabelle an (Fehler, wenn die Tabelle nicht existiert).
Replace ADBC_INGEST_OPTION_MODE_REPLACE Löscht die vorhandene Tabelle und erzeugt eine neue.
Create or Append ADBC_INGEST_OPTION_MODE_CREATE_APPEND Erzeugt eine neue Tabelle, wenn sie nicht existiert, andernfalls hängt an sie an.

StatementExecuteQuery gibt die Anzahl der betroffenen Zeilen über den Ausgabeparameter rows_affected zurück.

Ingestionsoptionen

Option Beschreibung
adbc.ingest.target_table Der Zieltabellenname für die Ingestion.
adbc.ingest.target_catalog Der Zielkatalog (angehängte Datenbank) für die Ingestion. Wenn ohne adbc.ingest.target_db_schema gesetzt, ist das Schema standardmäßig main.
adbc.ingest.target_db_schema Das Zielschema für die Ingestion.
adbc.ingest.temporary Auf enabled setzen, um eine temporäre Tabelle zu erzeugen. Inkompatibel mit target_catalog und target_db_schema.

Einrichten des DuckDB-ADBC-Treibers

Bevor Sie DuckDB als ADBC-Treiber verwenden, müssen Sie die Shared Library libduckdb auf Ihrem System installieren und Ihrer Anwendung zur Verfügung stellen. Diese Bibliothek enthält die DuckDB-Kern-Engine, mit der der ADBC-Treiber kommuniziert.

libduckdb herunterladen

Laden Sie die passende libduckdb-Bibliothek für Ihre Plattform von der DuckDB-Releases-Seite herunter:

  • Linux: libduckdb-linux-amd64.zip (enthält libduckdb.so)
  • macOS: libduckdb-osx-universal.zip (enthält libduckdb.dylib)
  • Windows: libduckdb-windows-amd64.zip (enthält duckdb.dll)

Entpacken Sie das Archiv, um die Shared-Library-Datei zu erhalten.

Installieren der Bibliothek

Linux

  1. Extrahieren Sie die Datei libduckdb.so aus dem heruntergeladenen Archiv

  2. Stellen Sie sicher, dass Ihr Code die Bibliothek verwenden kann. Sie können:

    • Entweder in ein System-Bibliotheksverzeichnis kopieren (erfordert Root-Zugriff):

      Terminal window
      sudo cp libduckdb.so /usr/local/lib/
      sudo ldconfig
    • Oder in ein eigenes Verzeichnis legen und dieses Verzeichnis zu Ihrem LD_LIBRARY_PATH hinzufügen:

      Terminal window
      mkdir -p ~/lib
      cp libduckdb.so ~/lib/
      export LD_LIBRARY_PATH=~/lib:$LD_LIBRARY_PATH

macOS

  1. Extrahieren Sie die Datei libduckdb.dylib aus dem heruntergeladenen Archiv

  2. Stellen Sie sicher, dass Ihr Code die Bibliothek verwenden kann. Sie können:

    • Entweder in ein System-Bibliotheksverzeichnis kopieren:

      Terminal window
      sudo cp libduckdb.dylib /usr/local/lib/
    • Oder in ein eigenes Verzeichnis legen und dieses Verzeichnis zu Ihrem DYLD_LIBRARY_PATH hinzufügen:

      Terminal window
      mkdir -p ~/lib
      cp libduckdb.dylib ~/lib/
      export DYLD_LIBRARY_PATH=~/lib:$DYLD_LIBRARY_PATH

Windows

  1. Extrahieren Sie die Datei duckdb.dll aus dem heruntergeladenen Archiv
  2. Legen Sie sie an einem der folgenden Orte ab:
    • Im selben Verzeichnis wie Ihre Anwendungs-Executable
    • In einem Verzeichnis, das in Ihrer Umgebungsvariable PATH aufgeführt ist
    • Im Windows-Systemverzeichnis (z. B. C:\Windows\System32)

Bibliothekspfade verstehen

LD_LIBRARY_PATH (Linux) und DYLD_LIBRARY_PATH (macOS) sind Umgebungsvariablen, die dem System mitteilen, wo zur Laufzeit nach Shared Libraries gesucht werden soll. Wenn Ihre Anwendung versucht, libduckdb zu laden, durchsucht das System diese Pfade, um die Bibliotheksdatei zu finden.

Installation überprüfen

Sie können überprüfen, dass die Bibliothek korrekt installiert und zugänglich ist:

Linux/macOS:

Terminal window
ldd path/to/your/application # Linux
otool -L path/to/your/application # macOS

Beispiele

Unabhängig von der verwendeten Programmiersprache gibt es zwei Datenbankoptionen, die erforderlich sind, um ADBC mit DuckDB zu nutzen. Die erste ist driver, die einen Pfad zur DuckDB-Bibliothek entgegennimmt (siehe Einrichten des DuckDB-ADBC-Treibers oben für Installationsanweisungen). Die zweite Option ist entrypoint, eine exportierte Funktion des DuckDB-ADBC-Treibers, die alle ADBC-Funktionen initialisiert. Sobald wir diese beiden Optionen konfiguriert haben, können wir optional die Option path setzen und einen Pfad auf der Festplatte angeben, um unsere DuckDB-Datenbank zu speichern. Wenn nicht gesetzt, wird eine In-Memory-Datenbank erzeugt. Nach dem Konfigurieren aller notwendigen Optionen können wir mit der Initialisierung unserer Datenbank fortfahren. Unten sehen Sie, wie Sie das in verschiedenen Sprachumgebungen tun.

C++

Wir beginnen unser C++-Beispiel mit der Deklaration der wesentlichen Variablen zum Abfragen von Daten über ADBC. Diese Variablen umfassen Error, Database, Connection, Statement-Handling und einen Arrow Stream, um Daten zwischen DuckDB und der Anwendung zu übertragen.

AdbcError adbc_error;
AdbcDatabase adbc_database;
AdbcConnection adbc_connection;
AdbcStatement adbc_statement;
ArrowArrayStream arrow_stream;

Anschließend können wir unsere Datenbankvariable initialisieren. Vor der Initialisierung der Datenbank müssen wir die Optionen driver und entrypoint wie oben erwähnt setzen. Dann setzen wir die Option path und initialisieren die Datenbank. Die Option driver sollte auf Ihre installierte libduckdb-Bibliothek zeigen – siehe Einrichten des DuckDB-ADBC-Treibers für Installationsanweisungen.

AdbcDatabaseNew(&adbc_database, &adbc_error);
AdbcDatabaseSetOption(&adbc_database, "driver", "path/to/libduckdb.dylib", &adbc_error);
AdbcDatabaseSetOption(&adbc_database, "entrypoint", "duckdb_adbc_init", &adbc_error);
// By default, we start an in-memory database, but you can optionally define a path to store it on disk.
AdbcDatabaseSetOption(&adbc_database, "path", "test.db", &adbc_error);
AdbcDatabaseInit(&adbc_database, &adbc_error);

Nach der Initialisierung der Datenbank müssen wir eine Verbindung dazu erzeugen und initialisieren.

AdbcConnectionNew(&adbc_connection, &adbc_error);
AdbcConnectionInit(&adbc_connection, &adbc_database, &adbc_error);

Nun können wir unser Statement initialisieren und Abfragen über unsere Verbindung ausführen. Nach AdbcStatementExecuteQuery ist arrow_stream mit dem Ergebnis gefüllt.

AdbcStatementNew(&adbc_connection, &adbc_statement, &adbc_error);
AdbcStatementSetSqlQuery(&adbc_statement, "SELECT 42", &adbc_error);
int64_t rows_affected;
AdbcStatementExecuteQuery(&adbc_statement, &arrow_stream, &rows_affected, &adbc_error);
arrow_stream.release(arrow_stream)

Neben dem Ausführen von Abfragen können wir auch Daten über arrow_streams ingestieren. Dazu müssen wir eine Option mit dem Tabellennamen setzen, in den wir einfügen möchten, den Stream binden und dann die Abfrage ausführen.

StatementSetOption(&adbc_statement, ADBC_INGEST_OPTION_TARGET_TABLE, "AnswerToEverything", &adbc_error);
StatementBindStream(&adbc_statement, &arrow_stream, &adbc_error);
StatementExecuteQuery(&adbc_statement, nullptr, nullptr, &adbc_error);

Python

Als Erstes sollten Sie mit pip den ADBC Driver Manager installieren. Sie müssen außerdem pyarrow installieren, um direkt auf im Apache-Arrow-Format vorliegende Resultsets zuzugreifen (z. B. mit fetch_arrow_table).

Terminal window
pip install adbc_driver_manager pyarrow

Details zum Paket adbc_driver_manager finden Sie in der Paketdokumentation zu adbc_driver_manager.

Wie bei C++ müssen wir Initialisierungsoptionen bereitstellen, bestehend aus dem Speicherort des libduckdb-Shared-Objects und der Einstiegsfunktion. Beachten Sie, dass das Argument path für DuckDB über das Dictionary db_kwargs übergeben wird.

import adbc_driver_duckdb.dbapi
with adbc_driver_duckdb.dbapi.connect("test.db") as conn, conn.cursor() as cur:
cur.execute("SELECT 42")
# fetch a pyarrow table
tbl = cur.fetch_arrow_table()
print(tbl)

Neben fetch_arrow_table sind weitere Methoden aus DBApi am Cursor implementiert, etwa fetchone und fetchall. Daten können auch über arrow_streams ingestiert werden. Wir müssen nur Optionen am Statement setzen, um den Datenstream zu binden, und die Abfrage ausführen.

import adbc_driver_duckdb.dbapi
import pyarrow
data = pyarrow.record_batch(
[[1, 2, 3, 4], ["a", "b", "c", "d"]],
names = ["ints", "strs"],
)
with adbc_driver_duckdb.dbapi.connect("test.db") as conn, conn.cursor() as cur:
cur.adbc_ingest("AnswerToEverything", data)

Go

Stellen Sie zuerst sicher, dass Sie die Bibliothek libduckdb installiert haben – siehe Einrichten des DuckDB-ADBC-Treibers für detaillierte Installationsanweisungen.

Das folgende Beispiel verwendet eine In-Memory-DuckDB-Datenbank, um In-Memory-Arrow-RecordBatches über SQL-Abfragen zu verändern:

package main
import (
"bytes"
"context"
"fmt"
"io"
"github.com/apache/arrow-adbc/go/adbc"
"github.com/apache/arrow-adbc/go/adbc/drivermgr"
"github.com/apache/arrow-go/v18/arrow"
"github.com/apache/arrow-go/v18/arrow/array"
"github.com/apache/arrow-go/v18/arrow/ipc"
"github.com/apache/arrow-go/v18/arrow/memory"
)
func _makeSampleArrowRecord() arrow.Record {
b := array.NewFloat64Builder(memory.DefaultAllocator)
b.AppendValues([]float64{1, 2, 3}, nil)
col := b.NewArray()
defer col.Release()
defer b.Release()
schema := arrow.NewSchema([]arrow.Field{{Name: "column1", Type: arrow.PrimitiveTypes.Float64}}, nil)
return array.NewRecord(schema, []arrow.Array{col}, int64(col.Len()))
}
type DuckDBSQLRunner struct {
ctx context.Context
conn adbc.Connection
db adbc.Database
}
func NewDuckDBSQLRunner(ctx context.Context) (*DuckDBSQLRunner, error) {
var drv drivermgr.Driver
db, err := drv.NewDatabase(map[string]string{
"driver": "duckdb",
"entrypoint": "duckdb_adbc_init",
"path": ":memory:",
})
if err != nil {
return nil, fmt.Errorf("failed to create new in-memory DuckDB database: %w", err)
}
conn, err := db.Open(ctx)
if err != nil {
return nil, fmt.Errorf("failed to open connection to new in-memory DuckDB database: %w", err)
}
return &DuckDBSQLRunner{ctx: ctx, conn: conn, db: db}, nil
}
func serializeRecord(record arrow.Record) (io.Reader, error) {
buf := new(bytes.Buffer)
wr := ipc.NewWriter(buf, ipc.WithSchema(record.Schema()))
if err := wr.Write(record); err != nil {
return nil, fmt.Errorf("failed to write record: %w", err)
}
if err := wr.Close(); err != nil {
return nil, fmt.Errorf("failed to close writer: %w", err)
}
return buf, nil
}
func (r *DuckDBSQLRunner) importRecord(sr io.Reader) error {
rdr, err := ipc.NewReader(sr)
if err != nil {
return fmt.Errorf("failed to create IPC reader: %w", err)
}
defer rdr.Release()
_, err = adbc.IngestStream(r.ctx, r.conn, rdr, "temp_table", adbc.OptionValueIngestModeCreate, adbc.IngestStreamOptions{})
return err
}
func (r *DuckDBSQLRunner) runSQL(sql string) ([]arrow.Record, error) {
stmt, err := r.conn.NewStatement()
if err != nil {
return nil, fmt.Errorf("failed to create new statement: %w", err)
}
defer stmt.Close()
if err := stmt.SetSqlQuery(sql); err != nil {
return nil, fmt.Errorf("failed to set SQL query: %w", err)
}
out, n, err := stmt.ExecuteQuery(r.ctx)
if err != nil {
return nil, fmt.Errorf("failed to execute query: %w", err)
}
defer out.Release()
result := make([]arrow.Record, 0, n)
for out.Next() {
rec := out.Record()
rec.Retain() // .Next() will release the record, so we need to retain it
result = append(result, rec)
}
if out.Err() != nil {
return nil, out.Err()
}
return result, nil
}
func (r *DuckDBSQLRunner) RunSQLOnRecord(record arrow.Record, sql string) ([]arrow.Record, error) {
serializedRecord, err := serializeRecord(record)
if err != nil {
return nil, fmt.Errorf("failed to serialize record: %w", err)
}
if err := r.importRecord(serializedRecord); err != nil {
return nil, fmt.Errorf("failed to import record: %w", err)
}
result, err := r.runSQL(sql)
if err != nil {
return nil, fmt.Errorf("failed to run SQL: %w", err)
}
if _, err := r.runSQL("DROP TABLE temp_table"); err != nil {
return nil, fmt.Errorf("failed to drop temp table after running query: %w", err)
}
return result, nil
}
func (r *DuckDBSQLRunner) Close() {
r.conn.Close()
r.db.Close()
}
func main() {
rec := _makeSampleArrowRecord()
fmt.Println(rec)
runner, err := NewDuckDBSQLRunner(context.Background())
if err != nil {
panic(err)
}
defer runner.Close()
resultRecords, err := runner.RunSQLOnRecord(rec, "SELECT column1+1 FROM temp_table")
if err != nil {
panic(err)
}
for _, resultRecord := range resultRecords {
fmt.Println(resultRecord)
resultRecord.Release()
}
}

Die Ausführung erzeugt die folgende Ausgabe:

record:
schema:
fields: 1
- column1: type=float64
rows: 3
col[0][column1]: [1 2 3]
record:
schema:
fields: 1
- (column1 + 1): type=float64, nullable
rows: 3
col[0][(column1 + 1)]: [2 3 4]