Spec-Zone.ru › DuckDB

ODBC 101: Путеводитель по ODBC с утятами

Что такое ODBC?

ODBC, что расшифровывается как Open Database Connectivity, — это стандарт, позволяющий различным программам взаимодействовать с разными базами данных, включая, конечно же, DuckDB 🦆. Это упрощает создание программ, работающих с многими базами данных, экономит время разработчиков, так как им не нужно писать специальный код для подключения к каждой базе. Вместо этого они могут использовать стандартный интерфейс ODBC, что сокращает время и затраты на разработку, а программы проще поддерживать. Однако ODBC может быть медленнее, чем другие методы подключения к базе данных, такие как использование родного драйвера, поскольку он добавляет дополнительный уровень абстракции между приложением и базой данных. Кроме того, поскольку DuckDB — это база данных с колончатой моделью, а ODBC — с построчной, использование ODBC с DuckDB может сопровождаться некоторыми неэффективностями.

На этой странице есть ссылки на официальную документацию Microsoft ODBC, которая является отличным ресурсом для получения дополнительной информации об ODBC.

Основные понятия

  • Дескрипторы
  • Подключение
  • Обработка ошибок и диагностика
  • Буферы и связывание

Дескрипторы

Дескриптор — это указатель на конкретный объект ODBC, используемый для взаимодействия с базой данных. Существуют несколько различных типов дескрипторов, каждый со своим назначением: дескриптор среды, дескриптор подключения, дескриптор оператора и дескриптор описателя. Дескрипторы выделяются с помощью функции SQLAllocHandle, которая в качестве входных данных принимает тип выделяемого дескриптора и указатель на дескриптор; драйвер затем создаёт новый дескриптор указанного типа и возвращает его приложению.

Драйвер ODBC для DuckDB имеет следующие типы дескрипторов.

Среда

Имя дескриптора Среда
Имя типа SQL_HANDLE_ENV
Описание Управляет настройками среды для операций ODBC и предоставляет глобальный контекст для доступа к данным.
Случаи использования Инициализация ODBC, управление поведением драйвера, выделение ресурсов
Дополнительная информация Должен быть выделен один раз на приложение при запуске и освобождён в конце.

Подключение

Имя дескриптора Подключение
Имя типа SQL_HANDLE_DBC
Описание Представляет подключение к источнику данных. Используется для установления, управления и завершения подключений. Определяет как драйвер, так и источник данных для использования в рамках драйвера.
Случаи использования Установление подключения к базе данных, управление состоянием подключения
Дополнительная информация Можно создавать несколько дескрипторов подключений по мере необходимости, позволяя одновременные подключения к нескольким источникам данных. Примечание: Выделение дескриптора подключения не устанавливает подключение, но оно должно быть выделено в первую очередь, а затем использовано после установления подключения.

Оператор

Имя дескриптора Оператор
Имя типа SQL_HANDLE_STMT
Описание Управляет выполнением SQL-запросов, а также возвращаемыми наборами результатов.
Случаи использования Выполнение SQL-запросов, получение наборов результатов, управление параметрами оператора.
Дополнительная информация Для обеспечения выполнения нескольких запросов одновременно, можно выделять несколько дескрипторов операторов на подключение.

Описание

Имя дескриптора Описание
Имя типа SQL_HANDLE_DESC
Описание Описывает атрибуты структуры данных или параметра и позволяет приложению указать структуру данных для привязки/получения.
Случаи использования Описание структур таблиц, наборов результатов, привязка столбцов к буферам приложения
Дополнительная информация Используется в ситуациях, когда структуры данных необходимо явно определить, например, во время привязки параметров или получения наборов результатов. Они автоматически выделяются при выделении оператора, но также могут быть выделены явно.

Подключение

Первым шагом является подключение к источнику данных, чтобы приложение могло выполнять операции с базой данных. Сначала приложение должно выделить дескриптор среды, а затем дескриптор подключения. Затем дескриптор подключения используется для подключения к источнику данных. Для подключения к источнику данных можно использовать две функции: SQLDriverConnect и SQLConnect. Первая используется для подключения к источнику данных с помощью строки подключения, а вторая — для подключения с использованием DSN.

Строка подключения

Строка подключения — это строка, содержащая информацию, необходимую для подключения к источнику данных. Она имеет формат списка пар ключ-значение, разделённых точкой с запятой; однако в настоящее время DuckDB использует только DSN и игнорирует остальные параметры.

DSN

DSN (Имя источника данных) — это строка, идентифицирующая базу данных. Это может быть путь к файлу, URL-адрес или имя базы данных. Например: C:\Users\me\duckdb.db и DuckDB — это допустимые DSN. Дополнительную информацию о DSN можно найти на странице “Выбор источника данных или драйвера” в документации SQL Server.

Обработка ошибок и диагностика

Все функции в ODBC возвращают код, представляющий успешность или неудачу функции. Это позволяет легко обрабатывать ошибки, так как приложение может просто проверить код возврата каждого вызова функции, чтобы определить, был ли он успешным. В случае неудачи приложение может использовать функцию SQLGetDiagRec для получения информации об ошибке. В следующей таблице определены коды возврата:

Код возврата Описание
SQL_SUCCESS Функция выполнена успешно.
SQL_SUCCESS_WITH_INFO Функция выполнена успешно, но доступна дополнительная информация, включая предупреждение
SQL_ERROR Функция завершилась неудачно.
SQL_INVALID_HANDLE Предоставленный дескриптор недействителен, указывая на ошибку программирования, например, когда дескриптор не выделен перед использованием или имеет неправильный тип
SQL_NO_DATA Функция выполнена успешно, но больше данных недоступно
SQL_NEED_DATA Требуются дополнительные данные, например, при отправке данных параметра во время выполнения или требуется дополнительная информация о подключении.
SQL_STILL_EXECUTING Функция, выполнявшаяся асинхронно, всё ещё выполняется.

Буферы и привязка

Буфер — это блок памяти, используемый для хранения данных. Буферы используются для хранения данных, полученных из базы данных, или для отправки данных в базу данных. Буферы выделяются приложением, а затем привязываются к столбцу в наборе результатов или параметру в запросе с помощью функций SQLBindCol и SQLBindParameter. При получении приложением строки из набора результатов или выполнении запроса данные хранятся в буфере. При отправке приложением запроса в базу данных данные в буфере отправляются в базу данных.

Настройка приложения

Ниже приведён пошаговый гайд по настройке приложения, использующего ODBC для подключения к базе данных, выполнения запроса и получения результатов в C++.

Чтобы установить драйвер, а также всё остальное, вам потребуется выполнить эти инструкции.

1. Включение заголовочных файлов SQL

Первый шаг — включение заголовочных файлов SQL:

#include <sql.h>
#include <sqlext.h>

Эти файлы содержат определения функций ODBC, а также типы данных, используемые ODBC. Для использования этих заголовочных файлов необходимо установить пакет unixodbc:

На macOS:

brew install unixodbc

На Ubuntu и Debian:

sudo apt-get install -y unixodbc-dev

На Fedora, CentOS и Red Hat:

sudo yum install -y unixODBC-devel

Не забудьте добавить путь к заголовочным файлам в CFLAGS.

Для MAKEFILE:

CFLAGS=-I/usr/local/include
# or
CFLAGS=-/opt/homebrew/Cellar/unixodbc/2.3.11/include

Для CMAKE:

include_directories(/usr/local/include)
# or
include_directories(/opt/homebrew/Cellar/unixodbc/2.3.11/include)

Также необходимо подключить библиотеку в CMAKE или MAKEFILE. Для CMAKE:

target_link_libraries(ODBC_application /path/to/duckdb_odbc/libduckdb_odbc.dylib)

Для MAKEFILE:

LDLIBS=-L/path/to/duckdb_odbc/libduckdb_odbc.dylib

2. Определение дескрипторов ODBC и подключение к базе данных

2.a. Подключение с помощью SQLConnect

Затем настройте дескрипторы ODBC, выделите их и подключитесь к базе данных. Сначала выделяется дескриптор среды, затем среда устанавливается на версию ODBC 3, затем выделяется дескриптор соединения, и, наконец, осуществляется подключение к базе данных. Следующий фрагмент кода демонстрирует, как это сделать:

SQLHANDLE env;
SQLHANDLE dbc;

SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env);

SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0);

SQLAllocHandle(SQL_HANDLE_DBC, env, &dbc);

std::string dsn = "DSN=duckdbmemory";
SQLConnect(dbc, (SQLCHAR*)dsn.c_str(), SQL_NTS, NULL, 0, NULL, 0);

std::cout << "Connected!" << std::endl;

2.b. Подключение с помощью SQLDriverConnect

В качестве альтернативы вы можете подключиться к драйверу ODBC с помощью SQLDriverConnect. SQLDriverConnect принимает строку подключения, в которой вы можете настроить базу данных, используя любые доступные опции конфигурации DuckDB.

SQLHANDLE env;
SQLHANDLE dbc;

SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env);

SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0);

SQLAllocHandle(SQL_HANDLE_DBC, env, &dbc);

SQLCHAR str[1024];
SQLSMALLINT strl;
std::string dsn = "DSN=DuckDB;allow_unsigned_extensions=true;access_mode=READ_ONLY"
SQLDriverConnect(dbc, nullptr, (SQLCHAR*)dsn.c_str(), SQL_NTS, str, sizeof(str), &strl, SQL_DRIVER_COMPLETE)

std::cout << "Connected!" << std::endl;

3. Добавление запроса

Теперь, когда приложение настроено, мы можем добавить в него запрос. Сначала нам нужно выделить дескриптор запроса:

SQLHANDLE stmt;
SQLAllocHandle(SQL_HANDLE_STMT, dbc, &stmt);

Затем мы можем выполнить запрос:

SQLExecDirect(stmt, (SQLCHAR*)"SELECT * FROM integers", SQL_NTS);

4. Получение результатов

Теперь, когда мы выполнили запрос, мы можем получить результаты. Сначала нам нужно связать столбцы в наборе результатов с буферами:

SQLLEN int_val;
SQLLEN null_val;
SQLBindCol(stmt, 1, SQL_C_SLONG, &int_val, 0, &null_val);

Затем мы можем получить результаты:

SQLFetch(stmt);

5. Действия с результатами

Теперь, когда у нас есть результаты, мы можем сделать с ними всё, что захотим. Например, мы можем вывести их:

std::cout << "Value: " << int_val << std::endl;

или выполнить любую другую обработку. Кроме того, можно выполнить дополнительные запросы и выполнить любые другие действия с базой данных, такие как вставка, обновление или удаление данных.

6. Освобождение дескрипторов и отключение

Наконец, нам нужно освободить дескрипторы и отключиться от базы данных. Сначала нам нужно освободить дескриптор запроса:

SQLFreeHandle(SQL_HANDLE_STMT, stmt);

Затем нам нужно отключиться от базы данных:

SQLDisconnect(dbc);

И, наконец, нам нужно освободить дескриптор соединения и дескриптор среды:

SQLFreeHandle(SQL_HANDLE_DBC, dbc);
SQLFreeHandle(SQL_HANDLE_ENV, env);

Освобождение дескрипторов соединения и среды может быть выполнено только после закрытия подключения к базе данных. Попытка их освободить до отключения от базы данных приведет к ошибке.

Пример приложения

Ниже приведен пример приложения, который включает файл cpp , подключающийся к базе данных, выполняющий запрос, получающий результаты и выводящий их. Он также отключается от базы данных, освобождает дескрипторы и включает функцию проверки возвращаемых значений функций ODBC. Он также включает файл CMakeLists.txt, который можно использовать для сборки приложения.

Пример файла .cpp

#include <iostream>
#include <sql.h>
#include <sqlext.h>

void check_ret(SQLRETURN ret, std::string msg) {
    if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
        std::cout << ret << ": " << msg << " failed" << std::endl;
        exit(1);
    }
    if (ret == SQL_SUCCESS_WITH_INFO) {
        std::cout << ret << ": " << msg << " succeeded with info" << std::endl;
    }
}

int main() {
    SQLHANDLE env;
    SQLHANDLE dbc;
    SQLRETURN ret;

    ret = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env);
    check_ret(ret, "SQLAllocHandle(env)");

    ret = SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0);
    check_ret(ret, "SQLSetEnvAttr");

    ret = SQLAllocHandle(SQL_HANDLE_DBC, env, &dbc);
    check_ret(ret, "SQLAllocHandle(dbc)");

    std::string dsn = "DSN=duckdbmemory";
    ret = SQLConnect(dbc, (SQLCHAR*)dsn.c_str(), SQL_NTS, NULL, 0, NULL, 0);
    check_ret(ret, "SQLConnect");

    std::cout << "Connected!" << std::endl;

    SQLHANDLE stmt;
    ret = SQLAllocHandle(SQL_HANDLE_STMT, dbc, &stmt);
    check_ret(ret, "SQLAllocHandle(stmt)");

    ret = SQLExecDirect(stmt, (SQLCHAR*)"SELECT * FROM integers", SQL_NTS);
    check_ret(ret, "SQLExecDirect(SELECT * FROM integers)");

    SQLLEN int_val;
    SQLLEN null_val;
    ret = SQLBindCol(stmt, 1, SQL_C_SLONG, &int_val, 0, &null_val);
    check_ret(ret, "SQLBindCol");

    ret = SQLFetch(stmt);
    check_ret(ret, "SQLFetch");

    std::cout << "Value: " << int_val << std::endl;

    ret = SQLFreeHandle(SQL_HANDLE_STMT, stmt);
    check_ret(ret, "SQLFreeHandle(stmt)");

    ret = SQLDisconnect(dbc);
    check_ret(ret, "SQLDisconnect");

    ret = SQLFreeHandle(SQL_HANDLE_DBC, dbc);
    check_ret(ret, "SQLFreeHandle(dbc)");

    ret = SQLFreeHandle(SQL_HANDLE_ENV, env);
    check_ret(ret, "SQLFreeHandle(env)");
}

Пример файла CMakelists.txt

cmake_minimum_required(VERSION 3.25)
project(ODBC_Tester_App)

set(CMAKE_CXX_STANDARD 17)
include_directories(/opt/homebrew/Cellar/unixodbc/2.3.11/include)

add_executable(ODBC_Tester_App main.cpp)
target_link_libraries(ODBC_Tester_App /duckdb_odbc/libduckdb_odbc.dylib)

© Copyright 2018–2024 Stichting DuckDB Foundation
Licensed under the MIT License.
https://duckdb.org/docs/guides/odbc/general.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API