Skip to content

适用虚谷数据库版本

v12.9



适用虚谷数据库版本

v12.9


虚谷 ODBC 开发手册

📄字数 11.4K
👁️阅读量 加载中...

ODBC(Open Database Connectivity)是由微软制定的一套标准化数据库访问接口,允许应用程序通过统一的 API 连接和操作不同类型的数据库,无需针对特定数据库编写专用代码。虚谷 ODBC 遵循 ODBC 3.0 规范设计并开发,实现了 ODBC 应用程序对虚谷数据库的操作。

官方文档:https://learn.microsoft.com/zh-cn/sql/odbc/microsoft-open-database-connectivity-odbc

一、开发环境搭建

1.1 获取软件包

XuguDB 提供两种 XuguDB-ODBC 驱动的下载方式:产品包或单独获取。

1.1.1 产品包获取

XuguDB-ODBC 驱动压缩包在虚谷数据库的产品包内,解压产品包后,XuguDB-ODBC 驱动路径为:Driver/ODBC。不同操作系统和 CPU 架构的 XuguDB-ODBC 驱动,需要通过下载不同的产品包获取。

1.1.2 单独获取

我们提供在线下载链接,可以获取最新试用版和稳定版本的 XuguDB-ODBC 驱动。

操作系统CPU下载地址
Windowsx86, 64-bit下载
Linuxx86, 64-bit下载
LinuxARM, 64-bit下载

提示

  1. Windows 版本建议在 Windows 10 及以上系统使用。
  2. Linux 版本可以在所有对应的 CPU 架构的类 Unix 系统下运行。
  3. 如果有在其它系统上使用虚谷 XuguDB-ODBC 驱动的需求,请联系虚谷。

1.2 配置数据源

1.2.1 Windows

  1. 进入 ODBC 目录,右键 install.exe,点击以管理员身份运行;
  2. 运行 odbcad32 或点击 控制面板 > 管理工具 > ODBC数据源;
  3. 点击系统 DSN > 添加 > 选择数据源 XuguSQL > 完成;
  4. 输入连接参数,点击测试并成功连接数据库;
  5. 点击确定按钮。

1.2.2 Linux

  1. 安装 unixODBC
  2. 执行命令 odbcinst -j 查看 ODBC 数据源的配置文件位置
  3. 将如下内容追加进 odbcinst.ini
ini
[XuguSQL]
Description=Xugu ODBC driver for linux
Driver=/your/odbc/path/libxgodbc.so
UsageCount=1
  1. 将如下内容追加进 odbc.ini
ini
[xugu]
Description=ODBC data source for Xugu
Driver=XuguSQL
Server=127.0.0.1
Port=5138
Database=SYSTEM
User=SYSDBA
UseSSL=false
AutoCommit=true
StrictCommit=false
RetSchema=false
RetCursorId=false
RetRowId=false
UserServerCursor=false
LobRet=true
RecvIsAsyn=true
CacheFOCR=true
CharSet=utf8
TimeZone=GMT+08:00
IsoLevel=read committed
LockTimeout=60000
SubthreadRows=2000
  1. 执行 odbcinst -q -d 查看驱动信息
  2. 执行 odbcinst -q -s 查看数据源信息
  3. 执行 isql xugu SYSDBA SYSDBA 连接数据库

提示

虚谷 ODBC 支持与数据库进行数据加密传输,连接属性为 UseSSL,其值为 true 即为开启。在 Windows 的 ODBC 数据源管理程序中勾选“启用安全连接”即可开启数据的加密传输。

1.3 编译

1.3.1 Visual Studio

  1. 项目属性 -> 配置属性 -> 高级 -> 字符集,选择"使用多字节字符集"
  2. 在源码头部添加如下必备头文件:
C
#include <Windows.h>
#include <sql.h>
#include <sqlext.h>
  1. 生成项目

1.3.2 gcc

  1. 在源码头部添加如下必备头文件:
C
#include <sql.h>
#include <sqlext.h>
  1. 使用如下命令编译:
BASH
gcc demo.c -lodbc

二、快速入门

此章节将使用最简单的代码展示如何使用 ODBC 向虚谷数据库插入和查询数据,以及展示如何通过 ODBC 操作数值类型、字符类型和结构体类型的字段以实现数值的插入和查询。

2.1 创表

使用 xgconsole 或其他管理工具连接虚谷数据库实例,在 SYSTEM 库中创建此章节所依赖的表。建表语句如下:

SQL
CREATE TABLE tab_test (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(20),
    created DATE
);

2.2 认识句柄

在此章节中将使用四个句柄,分别是:环境句柄、连接句柄和语句句柄。它们所对应的类型常量为:

  • SQL_HANDLE_ENV
  • SQL_HANDLE_DBC
  • SQL_HANDLE_STMT

2.3 创建句柄

句柄的创建依赖于其它已成功创建的句柄,依赖关系为:

  • SQL_HANDLE_ENV 没有依赖的句柄
  • SQL_HANDLE_DBC 依赖于 SQL_HANDLE_ENV
  • SQL_HANDLE_STMT 依赖于 SQL_HANDLE_DBC

创建句柄所使用的接口原型为:

C
SQLRETURN SQLAllocHandle(SQLSMALLINT HandleType, SQLHANDLE InputHandle, SQLHANDLE *OutputHandle);

其中,第一个参数为待创建的句柄类型,第二个参数为被依赖的句柄,第三个参数为待创建的句柄。创建成功则返回 SQL_SUCCESS。因此创建句柄的代码为:

C
SQLHENV henv;
SQLHDBC hdbc;
SQLHSTMT hstmt;

//创建环境句柄
SQLAllocHandle(SQL_HANDLE_ENV, NULL, &henv);
//创建连接句柄
SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
//创建语句句柄
SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);

需要注意的是,创建连接句柄前需设置 ODBC 版本,创建语句句柄前需成功连接数据库。

2.4 连接数据库

连接虚谷数据库至少需要五个信息,分别为:数据库服务端的 IP、数据库服务端的端口、连接的库名、用户名和密码。连接数据库的接口原型为:

C
SQLRETURN SQLConnect(SQLHDBC ConnectionHandle,
           SQLCHAR *ServerName, SQLSMALLINT NameLength1,
           SQLCHAR *UserName, SQLSMALLINT NameLength2,
           SQLCHAR *Authentication, SQLSMALLINT NameLength3);

其中,第一个参数为连接句柄,第二个参数为数据源名称,第四个参数为用户名,第六个参数为密码。第三五七个参数为长度,可传入 SQL_NTS。连接成功则返回 SQL_SUCCESS,若连接失败可使用 SQLGetDiagRec 获取错误码以及错误信息。

C
SQLRETURN ret;
ret = SQLConnect(hdbc, "xugu", SQL_NTS, "SYSDBA", SQL_NTS, "SYSDBA", SQL_NTS);
if (ret != SQL_SUCCESS)
{
    SQLCHAR sqlState[20] = {0};
    SQLINTEGER nativeErr = 0;
    SQLCHAR errMsg[200] = {0};
    SQLSMALLINT len;
    SQLGetDiagRec(SQL_HANDLE_DBC, hdbc, 1, sqlState, &nativeErr, errMsg, 200, &len);
    printf("[%s][%d]%s\n", sqlState, nativeErr, errMsg);
}

2.5 执行 SQL 语句

当连接建立成功后,即可向数据库服务端发送待执行的 SQL 语句。所使用的接口原型为:

C
SQLRETURN SQLExecDirect(SQLHSTMT StatementHandle, SQLCHAR* StatementText, SQLINTEGER TextLength);

其中,第一个参数为语句句柄,第二个参数为 SQL 语句,第三个参数为 SQL 语句的长度。执行成功则返回 SQL_SUCCESS,若执行失败可使用 SQLGetDiagRec 获取错误码以及错误信息。

若执行查询,则需将变量绑定至各个列,通过 fetch 将当前行的值写入绑定的变量中。代码为:

C
int id;
char name[20];
SQL_DATE_STRUCT date;
SQLLEN len[3] = { 0 };
SQLBindCol(hstmt, 1, SQL_C_LONG, &id, 4, &len[0]);
SQLBindCol(hstmt, 2, SQL_C_CHAR, name, 20, &len[1]);
SQLBindCol(hstmt, 3, SQL_C_DATE, &date, sizeof(date), &len[2]);

ret = SQLExecDirect(hstmt, "SELECT id, name, created FROM tab_test", SQL_NTS);
while ((ret = SQLFetch(hstmt)) == SQL_SUCCESS)
    printf("%d, %s, %04d-%02d-%02d\n", id, name, date.year, date.month, date.day);

2.6 销毁句柄

每个句柄均存储相关的信息,因此句柄使用完毕后需将其销毁以释放内存,其接口原型为:

C
SQLRETURN SQLFreeHandle(SQLSMALLINT HandleType, SQLHANDLE Handle);

其中,参数为待销毁的句柄。

C
SQLFreeHandle(hstmt);
SQLFreeHandle(hdbc);
SQLFreeHandle(henv);

2.7 完整代码

C
//main.c
#ifdef _WIN32
#include <Windows.h>
#endif
#include <stdio.h>
#include <sql.h>
#include <sqlext.h>

void printError(SQLHANDLE handle, SQLSMALLINT type);
void insert(HDBC hdbc);
void query(HDBC hdbc);

int main()
{
    SQLHENV henv;
    SQLHDBC hdbc;
    SQLHSTMT hstmt;
    SQLRETURN ret;
    SQLAllocHandle(SQL_HANDLE_ENV, NULL, &henv);
    SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, SQL_IS_INTEGER);
    SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);

    ret = SQLConnect(hdbc, "xugu", SQL_NTS, "SYSDBA", SQL_NTS, "SYSDBA", SQL_NTS);
    if (ret != SQL_SUCCESS)
    {
        printError(hdbc, SQL_HANDLE_DBC);
        return;
    }

    insert(hdbc);
    query(hdbc);

    SQLDisconnect(hdbc);
    SQLFreeHandle(SQL_HANDLE_ENV, hdbc);
    SQLFreeHandle(SQL_HANDLE_ENV, henv);
    return 0;
}

void printError(SQLHANDLE handle, SQLSMALLINT type)
{
    SQLCHAR sqlState[20] = {0};
    SQLINTEGER nativeErr = 0;
    SQLCHAR errMsg[200] = {0};
    SQLSMALLINT len;

    SQLGetDiagRec(type, handle, 1, sqlState, &nativeErr, errMsg, 200, &len);
    printf("[%s][%d]%s\n", sqlState, nativeErr, errMsg);
}

void insert(SQLHDBC hdbc)
{
    SQLHSTMT hstmt;
    SQLRETURN ret;
    ret = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);

    const char* sql = "INSERT INTO tab_test values(1, 'Tom', '2000-01-01')";
    ret = SQLExecDirect(hstmt, sql, SQL_NTS);

    if (ret != SQL_SUCCESS)
        printError(hstmt, SQL_HANDLE_STMT);

    SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
}

void query(SQLHDBC hdbc)
{
    SQLHSTMT hstmt;
    SQLRETURN ret;
    SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);

    int id;
    char name[20];
    SQL_DATE_STRUCT date;
    SQLLEN len[3] = { 0 };
    SQLBindCol(hstmt, 1, SQL_C_LONG, &id, 4, &len[0]);
    SQLBindCol(hstmt, 2, SQL_C_CHAR, name, 20, &len[1]);
    SQLBindCol(hstmt, 3, SQL_C_DATE, &date, sizeof(date), &len[2]);

    ret = SQLExecDirect(hstmt, "SELECT id, name, created FROM tab_test", SQL_NTS);
    while ((ret = SQLFetch(hstmt)) == SQL_SUCCESS)
        printf("%d, %s, %04d-%02d-%02d\n", id, name, date.year, date.month, date.day);

    SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
}

三、接口参考

3.1 数据类型

ODBC 在发送数据时需将 C 语言中的值类型转换为数据库中的值类型。在参数绑定时,需要指定被绑定变量值的类型,还需指定对应列的类型,但两种类型并非需要严格对应,部分类型间可完成数值类型转换。

3.1.1 C 语言类型

下表列出了 C 数据类型的有效类型标识符。 该表还列出了对应于每个标识符的 ODBC C 数据类型以及此数据类型的定义。

C 类型标识符ODBC C typedefC 类型
SQL_C_TINYINTSQLCHARchar
SQL_C_STINYINTSQLSCHARchar
SQL_C_UTINYINTSQLCHARunsigned char
SQL_C_SHORTSQLSMALLINTshort int
SQL_C_SSHORTSQLSMALLINTshort int
SQL_C_USHORTSQLUSMALLINTunsigned short int
SQL_C_LONGSQLINTEGERlong int
SQL_C_SLONGSQLINTEGERlong int
SQL_C_ULONGSQLUINTEGERunsigned long int
SQL_C_SBIGINTSQLBIGINTint64
SQL_C_UBIGINTSQLUBIGINTunsigned int64
SQL_C_FLOATSQLREALfloat
SQL_C_DOUBLESQLDOUBLE、SQLFLOATdouble
SQL_C_CHARSQLCHAR *unsigned char *
SQL_C_BINARYSQLCHAR *unsigned char *
SQL_C_DATESQL_DATE_STRUCTSQL_DATE_STRUCT
SQL_C_TIMESQL_TIME_STRUCTSQL_TIME_STRUCT
SQL_C_TIMESTAMPSQL_TIMESTAMP_STRUCTSQL_TIMESTAMP_STRUCT

3.1.2 SQL 类型

下表为 SQL 数据类型的对应的 SQL 类型标识符。

SQL 数据SQL 类型标识符
TINYINTSQL_TINYINT
SMALLINTSQL_SMALLINT
INTEGERSQL_INTEGER
BIGINTSQL_BIGINT
REALSQL_REAL
DOUBLESQL_FLOAT
DOUBLESQL_DOUBLE
CHAR(n)SQL_CHAR
VARCHAR(n)SQL_VARCHAR
BINARY(n)SQL_BINARY
DECIMAL(p, s)SQL_DECIMAL
NUMERIC(p, s)SQL_NUMERIC
DATESQL_TYPE_DATE
TIMESQL_TYPE_TIME
DATETIMESQL_TYPE_TIMESTAMP
TIMESTAMPSQL_TYPE_TIMESTAMP

3.2 API 接口

接口用法,请参考官方文档:https://learn.microsoft.com/zh-cn/sql/odbc/reference/syntax/odbc-api-reference

接口支持备注
SQLAllocConnect已弃用
SQLAllocEnv已弃用
SQLAllocHandle
SQLAllocStmt已弃用
SQLBindCol
SQLBindParameter
SQLBrowseConnect
SQLBulkOperations
SQLCancel
SQLCancelHandle
SQLCloseCursor
SQLColAttribute
SQLColAttributes已弃用
SQLColumnPrivileges
SQLColumns
SQLCompleteAsync
SQLConnect
SQLCopyDesc
SQLDataSources
SQLDescribeCol
SQLDescribeParam
SQLDisconnect
SQLDriverConnect
SQLDrivers
SQLEndTran
SQLError已弃用
SQLExecDirect
SQLExecute
SQLExtendedFetch已弃用
SQLFetch
SQLFetchScroll
SQLForeignKeys
SQLFreeConnect已弃用
SQLFreeEnv已弃用
SQLFreeHandle
SQLFreeStmt已弃用
SQLGetConnectAttr
SQLGetConnectOption已弃用
SQLGetCursorName
SQLGetData
SQLGetDescField
SQLGetDescRec
SQLGetDiagField
SQLGetDiagRec
SQLGetEnvAttr
SQLGetFunctions
SQLGetInfo
SQLGetStmtAttr
SQLGetStmtOption已弃用
SQLGetTypeInfo
SQLMoreResults
SQLNativeSql
SQLNumParams
SQLNumResultCols
SQLParamData
SQLParamOptions已弃用
SQLPrepare
SQLPrimaryKeys
SQLProcedureColumns
SQLProcedures
SQLPutData
SQLRowCount
SQLSetConnectAttr
SQLSetConnectOption已弃用
SQLSetCursorName
SQLSetDescField
SQLSetDescRec
SQLSetEnvAttr
SQLSetParam已弃用
SQLSetPosOperation 仅支持 SQL_POSITION,LockType 未使用
SQLSetScrollOptions已弃用
SQLSetStmtAttr
SQLSetStmtOption已弃用
SQLSpecialColumns
SQLStatistics
SQLTablePrivileges
SQLTables
SQLTransact已弃用

四、示例

4.1 连接的建立与释放

C
#ifdef _WIN32
#include <Windows.h>
#endif
#include <stdio.h>
#include <sql.h>
#include <sqlext.h>

void printError(SQLHANDLE handle, SQLSMALLINT type)
{
    SQLCHAR sqlState[20] = {0};
    SQLINTEGER nativeErr = 0;
    SQLCHAR errMsg[200] = {0};
    SQLSMALLINT len;

    SQLGetDiagRec(type, handle, 1, sqlState, &nativeErr, errMsg, 200, &len);
    printf("[%s][%d]%s\n", sqlState, nativeErr, errMsg);
}

int main()
{
    SQLHENV henv;
    SQLHDBC hdbc;
    SQLHSTMT hstmt;
    SQLRETURN ret;
    SQLAllocHandle(SQL_HANDLE_ENV, NULL, &henv);
    SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0);
    SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);

    ret = SQLConnect(hdbc, "xugu", SQL_NTS, "SYSDBA", SQL_NTS, "SYSDBA", SQL_NTS);
    if (ret != SQL_SUCCESS)
    {
        printError(hdbc, SQL_HANDLE_DBC);
        goto _end;
    }

    // insert(hdbc);
    // query(hdbc);
    // procedure(hdbc);
    // function(hdbc);
    // insertLob(hdbc);
    // queryLob(hdbc);

    SQLDisconnect(hdbc);
_end:
    SQLFreeHandle(SQL_HANDLE_ENV, hdbc);
    SQLFreeHandle(SQL_HANDLE_ENV, henv);
    return 0;
}

4.2 参数绑定

C
void insert(SQLHDBC hdbc)
{
    SQLHSTMT hstmt;
    SQLRETURN ret;
    SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
    ret = SQLPrepare(hstmt, "INSERT INTO tab_test(id, name, created) VALUES(?, ?, ?)", SQL_NTS);
    if (ret != SQL_SUCCESS)
        printError(hstmt, SQL_HANDLE_STMT);

    int id = 1;
    char name[20] = "Tom";
    SQL_DATE_STRUCT date = {2000, 1, 1};
    SQLLEN len[3] = { 0 };// 0 取数据实际长度,-1 为 null
    SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, &id, 4, &len[0]);
    SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 0, 0, name, 20, &len[1]);
    SQLBindParameter(hstmt, 3, SQL_PARAM_INPUT, SQL_C_DATE, SQL_DATE, 0, 0, &date, sizeof(date), &len[2]);
    ret = SQLExecute(hstmt);

    if (ret != SQL_SUCCESS)
        printError(hstmt, SQL_HANDLE_STMT);
    SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
}

4.3 数据查询

示例 1:SQLBindCol

C
void query(SQLHDBC hdbc)
{
    SQLHSTMT hstmt;
    SQLRETURN ret;
    SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);

    int id;
    char name[20];
    SQL_DATE_STRUCT date;
    SQLLEN len[3] = { 0 };
    SQLBindCol(hstmt, 1, SQL_C_LONG, &id, 0, &len[0]);
    SQLBindCol(hstmt, 2, SQL_C_CHAR, name, 20, &len[1]);
    SQLBindCol(hstmt, 3, SQL_C_DATE, &date, 0, &len[2]);

    ret = SQLExecDirect(hstmt, "SELECT id, name, created FROM tab_test", SQL_NTS);
    if (ret != SQL_SUCCESS)
        printError(hstmt, SQL_HANDLE_STMT);

    while (SQLFetch(hstmt) == SQL_SUCCESS)
    {
        printf(len[0] != -1 ? "%d, " : "%s, ", len[0] != -1 ? id : "null");
        printf("%s, ", len[1] != -1 ? name : "null");
        if (len[2] != -1)
            printf("%04d-%02d-%02d\n", date.year, date.month, date.day);
        else
            printf("null\n");
    }

    SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
}

示例 2:SQLGetData

C
void query(SQLHDBC hdbc)
{
    SQLHSTMT hstmt;
    SQLRETURN ret;
    SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);

    int id;
    char name[20];
    SQL_DATE_STRUCT date;
    SQLLEN len[3] = { 0 };

    ret = SQLExecDirect(hstmt, "SELECT id, name, created FROM tab_test", SQL_NTS);
    if (ret != SQL_SUCCESS)
        printError(hstmt, SQL_HANDLE_STMT);

    while (SQLFetch(hstmt) == SQL_SUCCESS)
    {
        SQLGetData(hstmt, 1, SQL_C_LONG, &id, 0, &len[0]);
        SQLGetData(hstmt, 2, SQL_C_CHAR, name, 20, &len[1]);
        SQLGetData(hstmt, 3, SQL_C_DATE, &date, 0, &len[2]);

        printf(len[0] != -1 ? "%d, " : "%s, ", len[0] != -1 ? id : "null");
        printf("%s, ", len[1] != -1 ? name : "null");
        if (len[2] != -1)
            printf("%04d-%02d-%02d\n", date.year, date.month, date.day);
        else
            printf("null\n");
    }

    SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
}

4.4 存储过程调用

SQL
-- 创建存储过程
CREATE OR REPLACE PROCEDURE proc_add(a INT, b INT, c OUT INT)
AS 
BEGIN 
    c := a + b;
END;
C
void procedure(SQLHDBC hdbc)
{
    SQLHSTMT hstmt;
    SQLRETURN ret;
    SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);

    const char* sql = "proc_add(?, ?, ?)";

    int a = 1;
    int b = 2;
    int sum;
    SQLLEN len[3] = { 0 };
    SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, &a, 4, &len[0]);
    SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, &b, 4, &len[1]);
    SQLBindParameter(hstmt, 3, SQL_PARAM_OUTPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, &sum, 4, &len[2]);

    ret = SQLExecDirect(hstmt, sql, SQL_NTS);
    if (ret != SQL_SUCCESS)
        printError(hstmt, SQL_HANDLE_STMT);
    else
        printf("%d + %d = %d\n", a, b, sum);

    SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
}

4.5 存储函数调用

SQL
-- 创建存储函数
CREATE OR REPLACE FUNCTION func_add(a INT, b INT)
RETURN INT
AS 
BEGIN 
    RETURN a + b;
END;
C
void function(SQLHDBC hdbc)
{
    SQLHSTMT hstmt;
    SQLRETURN ret;
    SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);

    const char* sql = "func_add(?, ?)";

    int a = 1;
    int b = 2;
    int sum;
    SQLLEN len[3] = { 0 };
    SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, &a, 4, &len[0]);
    SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, &b, 4, &len[1]);
    SQLBindParameter(hstmt, 3, SQL_PARAM_OUTPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, &sum, 4, &len[2]);

    ret = SQLExecDirect(hstmt, sql, SQL_NTS);
    if (ret != SQL_SUCCESS)
        printError(hstmt, SQL_HANDLE_STMT);
    else
        printf("%d + %d = %d\n", a, b, sum);

    SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
}

4.6 LOB 插入

C
void insertLob(SQLHDBC hdbc)
{
    SQLHSTMT hstmt;
    SQLRETURN ret;
    SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);

    const char* sql = "INSERT INTO tab_lob (lob) VALUES(?)";
    SQLPrepare(hstmt, sql, SQL_NTS);

    SQLLEN len = SQL_LEN_DATA_AT_EXEC(0);
    SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, 0, 0, (SQLPOINTER)1, 0, &len);

    SQLPOINTER token = NULL;
    FILE* fp = fopen("blob.png", "rb");
    size_t size;
    char buf[4096];

    ret = SQLExecute(hstmt);
    while (ret == SQL_NEED_DATA)
    {
        ret = SQLParamData(hstmt, &token);
        if (ret == SQL_NEED_DATA)
        {
            do {
                size = fread(buf, 1, 4096, fp);
                if (size <= 0) break;
                SQLPutData(hstmt, buf, size);
            } while (1);
        }
    }

    if (ret != SQL_SUCCESS)
        printError(hstmt, SQL_HANDLE_STMT);
    fclose(fp);
    SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
}

4.7 LOB 查询

C
void queryLob(SQLHDBC hdbc)
{
    SQLHSTMT hstmt;
    SQLRETURN ret;
    SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);

    const char* sql = "SELECT lob FROM tab_test WHERE id = 1";
    SQLExecDirect(hstmt, sql, SQL_NTS);
    SQLFetch(hstmt);

    char buf[1024];
    SQLLEN len;
    FILE* fp = fopen("blob_out.png", "wb");

    while (TRUE)
    {
        ret = SQLGetData(hstmt, 1, SQL_CHAR, buf, 1024, &len);
        if (ret != SQL_SUCCESS)
            break;
        fwrite(buf, 1, len, fp);
    }

    fclose(fp);
    SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
}