Skip to content

适用虚谷数据库版本

v12.10.13



适用虚谷数据库版本

v12.10.13


虚谷 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(SQLHDBC hdbc);
void query(SQLHDBC hdbc);

int main()
{
    SQLHENV henv;
    SQLHDBC hdbc;
    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);
        goto _end;
    }

    insert(hdbc);
    query(hdbc);

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

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);

    SQLCHAR* 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 = 0;
    char name[20] = { 0 };
    SQL_DATE_STRUCT date = { 0 };
    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✔已弃用
SQLSetPos✔Operation 仅支持 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);
}