Zum Inhalt springen

ODBC-Erweiterung

Bitte beachten Sie, dass DuckDB auch einen ODBC-Client anbietet, mit dem Sie sich mit DuckDB über ODBC aus anderen Anwendungen verbinden können.

Die Erweiterung odbc_scanner ermöglicht die Verbindung zu anderen Datenbanken (über deren ODBC-Treiber) und das Ausführen von Abfragen mit den Funktionen odbc_query oder das Kopieren von Daten aus DuckDB mit odbc_copy. Die Erweiterung ist auch unter dem Alias odbc verfügbar.

Installation und Laden

Unter Linux und macOS benötigt die Erweiterung den Treiber-Manager unixODBC. Siehe unten für Installationsanweisungen.

Die Erweiterung kann automatisch installiert werden, muss aber manuell geladen werden mit:

LOAD odbc;

Verwendungsbeispiel

-- load extension
LOAD odbc;
-- open ODBC connection to a remote DB
SET VARIABLE conn = odbc_connect('Driver={Oracle driver};DBQ=//127.0.0.1:1521/XE', 'scott', 'tiger');
-- simple query
FROM odbc_query(getvariable('conn'), 'SELECT SYSTIMESTAMP FROM DUAL');
-- query with parameters
FROM odbc_query(getvariable('conn')
'SELECT CAST(? AS NVARCHAR2(2)) || CAST(? AS VARCHAR2(5)) FROM DUAL',
params = row('🦆', 'quack'));
-- copy data into remote DB
FROM odbc_copy(getvariable('conn'),
source_file = 'https://blobs.duckdb.org/nl_stations.csv',
dest_table = 'NL_TRAIN_STATIONS',
create_table = true);
-- close connection
SELECT odbc_close(getvariable('conn'));

Nightly-Version installieren

Die ODBC-Erweiterung wird mit der versionsunabhängigen DuckDB-C-API gebaut. Dasselbe Binary (für die jeweilige Plattform, zum Beispiel: windows_amd64) kann auf DuckDB-Version 1.2.0 oder jeder neueren Version installiert und geladen werden.

Binaries mit den neuesten Änderungen, die im DuckDB-Nightly-Repository veröffentlicht werden, können wie folgt installiert werden:

INSTALL 'http://nightly-extensions.duckdb.org/v1.2.0/⟨platform⟩/odbc_scanner.duckdb_extension.gz';

Die URL mit der Version 1.2.0 sollte auch dann verwendet werden, wenn Sie eine neuere Version von DuckDB ausführen.

Dabei ist ⟨platform⟩{:.language-sql .highlight} eine der folgenden:

  • linux_amd64
  • linux_arm64
  • linux_amd64_musl
  • linux_arm64_musl
  • osx_amd64
  • osx_arm64
  • windows_amd64
  • windows_arm64

Um die installierte Erweiterung auf die neueste Nightly-Version zu aktualisieren, führen Sie aus:

FORCE INSTALL 'http://nightly-extensions.duckdb.org/v1.2.0/⟨platform⟩/odbc_scanner.duckdb_extension.gz';

Die installierte Version (Commit-ID) kann mit der folgenden Abfrage geprüft werden:

FROM duckdb_extensions()
WHERE extension_name = 'odbc_scanner';

Um eine aus einem bestimmten Commit gebaute Version zu installieren, führen Sie aus:

FORCE INSTALL 'http://nightly-extensions.duckdb.org/odbc_scanner/⟨7_character_commit_id⟩/v1.2.0/⟨platform⟩/odbc_scanner.duckdb_extension.gz';

Unterstützungsstatus DBMS-spezifischer Typen

Stufe 1:

Stufe 2:

  • PostgreSQL: grundlegende Typen abgedeckt
  • MySQL/MariaDB: grundlegende Typen abgedeckt
  • Firebird: Status der Typabdeckung

Stufe 3:

  • Snowflake: Status der Typabdeckung
  • ClickHouse: grundlegende Typen abgedeckt
  • Spark: grundlegende Typen abgedeckt
  • Arrow Flight SQL: grundlegende Typen abgedeckt

unixODBC-Treiber-Manager unter Linux oder macOS installieren

Unter Linux kann unixODBC über den System-Paketmanager installiert werden. Je nach Linux-Distribution kann einer der folgenden Installationsbefehle verwendet werden.

Debian, Ubuntu:

Terminal window
sudo apt-get install unixodbc

RHEL, Alma, Rocky, Amazon, Fedora:

Terminal window
sudo dnf install unixODBC

Alpine:

Terminal window
sudo apk add unixodbc

Unter macOS kann unixODBC über den Homebrew-Paketmanager installiert werden:

Terminal window
brew install unixodbc

Um ältere x86_64-ODBC-Treiber unter dem Rosetta-Übersetzer zu verwenden, muss unixODBC mit der x86_64-Version von Homebrew installiert werden:

Terminal window
arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
/usr/local/bin/brew install unixodbc

Beispiele für Verbindungszeichenketten

Eine ODBC-Verbindung kann über einen Datenquellennamen in der Form DSN=data_source1_name oder ohne konfigurierte Datenquelle in der Form Driver={Driver name};parameter1=values1;... hergestellt werden.

Die Funktionen odbc_list_drivers und odbc_list_data_sources können verwendet werden, um verfügbare Treiber und Datenquellen zu ermitteln.

Beispiele für Verbindungszeichenketten ohne konfigurierte Datenquelle:

Oracle:

Driver={Oracle in instantclient_23_0};DBQ=//127.0.0.1:1521/XE;UID=scott;PWD=tiger;

SQL Server:

Driver={ODBC Driver 18 for SQL Server};Server=tcp:127.0.0.1,1433;UID=sa;PWD=pwd;TrustServerCertificate=Yes;Database=test_db;

DB2:

Driver={IBM DB2 ODBC DRIVER};HostName=127.0.0.1;Port=50000;Database=testdb;UID=db2inst1;PWD=pwd;

PostgreSQL:

Driver={PostgreSQL Unicode};Server=127.0.0.1;Port=5432;Username=postgres;Password=postgres;Database=test_db;

MySQL/MariaDB:

Driver={MariaDB ODBC 3.1 Driver};SERVER=127.0.0.1;PORT=3306;USER=root;PASSWORD=root;DATABASE=test_db;

Firebird:

Driver={Firebird ODBC Driver};Database=127.0.0.1/3050:C:/path/to/test.fdb;UID=SYSDBA;PWD=pwd;CHARSET=UTF8;

Snowflake:

Driver={SnowflakeDSIIDriver};Server=foobar-ab12345.snowflakecomputing.com;Database=SNOWFLAKE_SAMPLE_DATA;UID=username;PWD=pwd;

ClickHouse:

Driver={ClickHouse ODBC Driver (Unicode)};Server=127.0.0.1;Port=8123;

Spark:

Driver={Simba Spark ODBC Driver};Host=127.0.0.1;Port=10000;

Arrow Flight SQL (Dremio ODBC + GizmoSQL):

Driver={Dremio Flight SQL ODBC Driver};Host=127.0.0.1;Port=31337;UID=gizmosql_username;PWD=gizmosql_password;useEncryption=true;

Abfrageparameter

Wenn eine DuckDB-Abfrage über ein Prepared Statement ausgeführt wird, können Eingabeparameter aus dem Client-Code übergeben werden. Die Erweiterung ermöglicht es, solche Eingabeparameter über die ODBC-API an die Abfragen in Remote-Datenbanken weiterzuleiten.

Zwei Methoden zur Übergabe von Abfrageparametern werden unterstützt, über das benannte Argument params oder params_handle der Funktion odbc_query.

Das Argument params nimmt einen STRUCT-Wert als Eingabe entgegen. Struct-Feldnamen werden ignoriert, sodass die Funktion row() verwendet werden kann, um einen STRUCT-Wert inline zu erstellen:

FROM odbc_query(
getvariable('conn'),
'
SELECT CAST(? AS VARCHAR2(3)) || CAST(? AS VARCHAR2(3)) FROM DUAL
',
params = row(?, ?))

Wenn wir diese Abfrage mit duckdb_prepare() vorbereiten, foo und bar als VARCHAR-Werte mit duckdb_bind_value() binden und sie mit duckdb_execute_prepared() ausführen, werden die Eingabeparameter foo und bar an die ODBC-Abfrage in der Remote-DB weitergeleitet.

Das Problem dieses Ansatzes ist, dass DuckDB die Parametertypen (die in der äußeren Abfrage angegeben sind) nicht auflösen kann, bevor duckdb_execute_prepared() aufgerufen wird – solche Typen können sich bei nachfolgenden Aufrufen von duckdb_execute_prepared() unterscheiden, und es gibt keine Möglichkeit, diese Typen explizit anzugeben.

Das führt dazu, dass die innere Abfrage in der Remote-DB bei jedem Aufruf von duckdb_execute_prepared() erneut vorbereitet wird.

Um dieses Problem zu vermeiden, kann eine zweistufige Parameterbindung mit dem benannten Argument params_handle von odbc_query verwendet werden:

-- create parameters handle
SET VARIABLE params = odbc_create_params();
-- when 'duckdb_prepare()' is called, the inner query will be prepared in the remote DB
FROM odbc_query(
getvariable('conn'),
'
SELECT CAST(? AS VARCHAR2(3)) || CAST(? AS VARCHAR2(3)) FROM DUAL
',
params_handle = getvariable('params'));
-- now we can repeatedly bind new parameters to the handle using 'odbc_bind_params()'
-- and call 'duckdb_execute_prepared()' to run the prepared query with
-- these new parameters in remote DB
SELECT odbc_bind_params(getvariable('conn'), getvariable('params'), row(?, ?));

Das Parameter-Handle ist an das Prepared Statement gebunden und wird freigegeben, wenn das Statement zerstört wird.

Verbindungen und Nebenläufigkeit

DuckDB verwendet eine multithreaded Ausführungs-Engine, um Teile der Abfrage parallel auszuführen. ODBC-Treiber unterstützen die gleichzeitige Verwendung derselben Verbindung aus verschiedenen Threads möglicherweise nicht. Um mögliche Nebenläufigkeitsprobleme zu vermeiden, erlaubt die Erweiterung nicht, dieselbe Verbindung aus mehreren Threads zu verwenden. Zum Beispiel schlägt die folgende Abfrage:

FROM odbc_query(getvariable('conn'), 'SELECT ''foo'' col1 FROM DUAL')
UNION ALL
FROM odbc_query(getvariable('conn'), 'SELECT ''bar'' col1 FROM DUAL');

fehl mit:

Invalid Input Error:
'odbc_query' error: ODBC connection not found on global init, id: 139760181976192

Das kann durch die Verwendung mehrerer ODBC-Verbindungen vermieden werden:

FROM odbc_query(getvariable('conn1'), 'SELECT ''foo'' col1 FROM DUAL')
UNION ALL
FROM odbc_query(getvariable('conn2'), 'SELECT ''bar'' col1 FROM DUAL');

Oder indem die multithreaded Ausführung deaktiviert wird, indem die DuckDB-Option threads auf 1 gesetzt wird.

Transaktionsverwaltung

Laut ODBC-Spezifikation wird erwartet, dass Verbindungen zu Remote-DBs standardmäßig den Auto-Commit-Modus aktiviert haben.

Als allgemeine Regel sollen die Transaktionsbefehle BEGIN TRANSACTION/COMMIT/ROLLBACK nicht als SQL-Befehle über ODBC gesendet werden. Das kann vom jeweiligen Treiber unterstützt werden oder auch nicht. Stattdessen stellt ODBC die API zur Transaktionsverwaltung bereit.

Diese API ist in den folgenden Funktionen verfügbar:

Wenn odbc_begin_transaction auf der Verbindung aufgerufen wird, wird der Auto-Commit-Modus auf dieser Verbindung deaktiviert und eine implizite Transaktion gestartet. Derzeit gibt es keine Unterstützung, Auto-Commit auf einer solchen Verbindung wieder zu aktivieren.

Nach dem Start der Transaktion rufen Sie odbc_commit oder odbc_rollback auf, um diese Transaktion abzuschließen. Nach dem Abschluss wird auf dieser Verbindung automatisch eine neue implizite Transaktion gestartet.

Leistung

ODBC ist keine Hochleistungs-API, odbc_query verwendet mehrere API-Aufrufe pro Zeile und führt für jeden VARCHAR-Wert eine Konvertierung von UCS-2 nach UTF-8 durch. Darüber hinaus ist die Abfrageverarbeitung strikt single-threaded.

Wenn Sie Issues einreichen, die sich nur auf die Leistung beziehen, prüfen Sie bitte die Leistung in vergleichbaren Szenarien, zum Beispiel mit pyodbc.