更新时间:2025-08-19 GMT+08:00
分享

完整示例

/**********************************************************************
 * 请在数据源中打开UseBatchProtocol,同时指定数据库中参数support_batch_bind为on。
 * CHECK_ERROR和CHECK_ERROR_VOID的作用是检查并打印错误信息。
 * 此示例将与用户交互式获取DSN、批量绑定的数据量,并将最终数据插入到test_odbc_batch_insert中。
 ***********************************************************************/
#ifdef WIN32
#include <windows.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <sql.h>
#include <sqlext.h>
#include <string.h>
#define CHECK_ERROR(e, s, t, h)                               \
    ({                                                        \
        if (e != SQL_SUCCESS && e != SQL_SUCCESS_WITH_INFO) { \
            fprintf(stderr, "FAILED:\t");                     \
            print_diag(s, h, t);                              \
            goto exit;                                        \
        }                                                     \
    })
#define CHECK_ERROR_VOID(e, s, t, h)                          \
    ({                                                        \
        if (e != SQL_SUCCESS && e != SQL_SUCCESS_WITH_INFO) { \
            fprintf(stderr, "FAILED:\t");                     \
            print_diag(s, h, t);                              \
        }                                                     \
    })
#define BATCH_SIZE 100  // 批量绑定的数据量。
// 打印报错信息。
void print_diag(char *msg, SQLSMALLINT htype, SQLHANDLE handle);
// 执行SQL语句。
void Exec(SQLHDBC hdbc, SQLCHAR *sql)
{
    SQLRETURN retcode;                // 返回错误码。
    SQLHSTMT hstmt = SQL_NULL_HSTMT;  // 语句句柄。
    SQLCHAR loginfo[2048];
    // 分配语句句柄。
    retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
    if (!SQL_SUCCEEDED(retcode)) {
        printf("SQLAllocHandle(SQL_HANDLE_STMT) failed");
        return;
    }
    // 准备语句。
    retcode = SQLPrepare(hstmt, (SQLCHAR *)sql, SQL_NTS);
    sprintf((char *)loginfo, "SQLPrepare log: %s", (char *)sql);
    if (!SQL_SUCCEEDED(retcode)) {
        printf("SQLPrepare(hstmt, (SQLCHAR*) sql, SQL_NTS) failed");
        return;
    }
    // 执行语句。
    retcode = SQLExecute(hstmt);
    sprintf((char *)loginfo, "SQLExecute stmt log: %s", (char *)sql);
    if (!SQL_SUCCEEDED(retcode)) {
        printf("SQLExecute(hstmt) failed");
        return;
    }
    // 释放句柄。
    retcode = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
    sprintf((char *)loginfo, "SQLFreeHandle stmt log: %s", (char *)sql);
    if (!SQL_SUCCEEDED(retcode)) {
        printf("SQLFreeHandle(SQL_HANDLE_STMT, hstmt) failed");
        return;
    }
}
int main()
{
    SQLHENV henv = SQL_NULL_HENV;
    SQLHDBC hdbc = SQL_NULL_HDBC;
    SQLLEN rowsCount = 0;
    int i = 0;
    SQLRETURN retcode;
    SQLCHAR dsn[1024] = {'\0'};
    SQLCHAR loginfo[2048];
    // 分配环境句柄。
    retcode = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
    if (!SQL_SUCCEEDED(retcode)) {
        printf("SQLAllocHandle failed");
        goto exit;
    }
    CHECK_ERROR(retcode, "SQLAllocHandle henv", henv, SQL_HANDLE_ENV);
    // 设置ODBC版本。
    retcode = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER *)SQL_OV_ODBC3, 0);
    CHECK_ERROR(retcode, "SQLSetEnvAttr", henv, SQL_HANDLE_ENV);
    // 分配连接。
    retcode = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
    CHECK_ERROR(retcode, "SQLAllocHandle hdbc", hdbc, SQL_HANDLE_DBC);
    // 设置登录超时。
    retcode = SQLSetConnectAttr(hdbc, SQL_LOGIN_TIMEOUT, (SQLPOINTER)5, 0);
    CHECK_ERROR(retcode, "SQLSetConnectAttr SQL_LOGIN_TIMEOUT", hdbc, SQL_HANDLE_DBC);
    // 关闭自动提交,使用事务提交。
    retcode = SQLSetConnectAttr(hdbc, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF, 0);
    CHECK_ERROR(retcode, "SQLSetConnectAttr SQL_ATTR_AUTOCOMMIT", hdbc, SQL_HANDLE_DBC);
    // 连接到数据库。
    retcode = SQLConnect(hdbc, (SQLCHAR *)"gaussdb", SQL_NTS, (SQLCHAR *)"", 0, (SQLCHAR *)"", 0);
    CHECK_ERROR(retcode, "SQLSetConnectAttr SQL_ATTR_AUTOCOMMIT", hdbc, SQL_HANDLE_DBC);
    printf("SQLConnect success\n");
    // 初始化表信息。
    Exec(hdbc, "DROP TABLE IF EXISTS test_odbc_batch_insert");
    Exec(hdbc, "CREATE TABLE test_odbc_batch_insert (id INT PRIMARY KEY, col VARCHAR2(50))");
    // 对于其他SQL操作可以分段进行事务提交。
    retcode = SQLEndTran(SQL_HANDLE_DBC, hdbc, SQL_COMMIT);
    CHECK_ERROR(retcode, "SQLEndTran", hdbc, SQL_HANDLE_DBC);
    // 根据用户输入的数据量,构造出需要批量插入的数据。
    {
        SQLRETURN retcode;
        SQLHSTMT hstmt = SQL_NULL_HSTMT;
        SQLCHAR *sql = NULL;
        SQLINTEGER *ids = NULL;
        SQLCHAR *cols = NULL;
        SQLLEN *bufLenIds = NULL;
        SQLLEN *bufLenCols = NULL;
        SQLUSMALLINT *operptr = NULL;
        SQLUSMALLINT *statusptr = NULL;
        SQLULEN process = 0;
        // 按列构造每个字段。
        ids = (SQLINTEGER *)malloc(sizeof(ids[0]) * BATCH_SIZE);
        cols = (SQLCHAR *)malloc(sizeof(cols[0]) * BATCH_SIZE * 50);
        // 每个字段中,每一行数据的内存长度。
        bufLenIds = (SQLLEN *)malloc(sizeof(bufLenIds[0]) * BATCH_SIZE);
        bufLenCols = (SQLLEN *)malloc(sizeof(bufLenCols[0]) * BATCH_SIZE);
        if (NULL == ids || NULL == cols || NULL == bufLenCols || NULL == bufLenIds) {
            fprintf(stderr, "FAILED:\tmalloc data memory failed\n");
            goto exit;
        }
        // 对数据进行赋值。
        for (i = 0; i < BATCH_SIZE; i++) {
            ids[i] = i;
            sprintf(cols + 50 * i, "column test value %d", i);
            bufLenIds[i] = sizeof(ids[i]);
            bufLenCols[i] = strlen(cols + 50 * i);
        }
        // 分配语句句柄。
        retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
        CHECK_ERROR(retcode, "SQLSetConnectAttr SQL_ATTR_AUTOCOMMIT", hstmt, SQL_HANDLE_STMT);
        // 预编译语句。
        sql = (SQLCHAR *)"INSERT INTO test_odbc_batch_insert VALUES(?, ?)";
        retcode = SQLPrepare(hstmt, (SQLCHAR *)sql, SQL_NTS);
        CHECK_ERROR(retcode, "SQLPrepare", hstmt, SQL_HANDLE_STMT);
        // 绑定参数。
        retcode = SQLBindParameter(
            hstmt, 1, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, sizeof(ids[0]), 0, &(ids[0]), 0, bufLenIds);
        CHECK_ERROR(retcode, "SQLBindParameter 1", hstmt, SQL_HANDLE_STMT);
        retcode = SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 50, 50, cols, 50, bufLenCols);
        CHECK_ERROR(retcode, "SQLBindParameter 2", hstmt, SQL_HANDLE_STMT);
        // 设置参数数组的总行数。
        retcode = SQLSetStmtAttr(hstmt, SQL_ATTR_PARAMSET_SIZE, (SQLPOINTER)BATCH_SIZE, sizeof(BATCH_SIZE));
        CHECK_ERROR(retcode, "SQLSetStmtAttr", hstmt, SQL_HANDLE_STMT);
        // 设置已处理的行数。
        retcode = SQLSetStmtAttr(hstmt, SQL_ATTR_PARAMS_PROCESSED_PTR, (SQLPOINTER)&process, sizeof(process));
        CHECK_ERROR(retcode, "SQLSetStmtAttr SQL_ATTR_PARAMS_PROCESSED_PTR", hstmt, SQL_HANDLE_STMT);
        // 执行批量插入。
        retcode = SQLExecute(hstmt);
        // 手动提交事务。
        if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
            retcode = SQLEndTran(SQL_HANDLE_DBC, hdbc, SQL_COMMIT);
            CHECK_ERROR(retcode, "SQLEndTran", hdbc, SQL_HANDLE_DBC);
        }
        // 失败则回滚事务。
        else {
            CHECK_ERROR_VOID(retcode, "SQLExecute", hstmt, SQL_HANDLE_STMT);
            retcode = SQLEndTran(SQL_HANDLE_DBC, hdbc, SQL_ROLLBACK);
            printf("Transaction rollback\n");
            CHECK_ERROR(retcode, "SQLEndTran", hdbc, SQL_HANDLE_DBC);
        }
        // 获取批量处理的行数。
        SQLRowCount(hstmt, &rowsCount);
        sprintf((char *)loginfo, "SQLRowCount : %ld", rowsCount);
        puts(loginfo);
        // 检查插入行数与process处理行数是否一致。
        if (rowsCount != process) {
            sprintf(loginfo, "process(%d) != rowsCount(%d)", process, rowsCount);
            puts(loginfo);
        } else {
            sprintf(loginfo, "process(%d) == rowsCount(%d)", process, rowsCount);
        }
        retcode = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
        CHECK_ERROR(retcode, "SQLFreeHandle", hstmt, SQL_HANDLE_STMT);
    }
exit:
    (void)printf("Complete.\n");
    // 断开连接。
    if (hdbc != SQL_NULL_HDBC) {
        SQLDisconnect(hdbc);
        SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
    }
    // 释放环境句柄。
    if (henv != SQL_NULL_HENV)
        SQLFreeHandle(SQL_HANDLE_ENV, henv);
    return 0;
}
void print_diag(char *msg, SQLSMALLINT htype, SQLHANDLE handle)
{
    char sqlstate[32];
    char message[1000];
    SQLINTEGER nativeerror;
    SQLSMALLINT textlen;
    SQLRETURN ret;
    SQLSMALLINT recno = 0;
    if (msg)
        printf("%s\n", msg);
    do {
        recno++;
        // 获取诊断信息。
        ret = SQLGetDiagRec(
            htype, handle, recno, (SQLCHAR *)sqlstate, &nativeerror, (SQLCHAR *)message, sizeof(message), &textlen);
        if (ret == SQL_INVALID_HANDLE)
            printf("Invalid handle\n");
        else if (SQL_SUCCEEDED(ret))
            printf("%s=%s\n", sqlstate, message);
    } while (ret == SQL_SUCCESS);
    if (ret == SQL_NO_DATA && recno == 1)
        printf("No error information\n");
}

结果验证

在ODBC成功连接数据库后,批量插入了100条数据,完整示例预期结果如下:

SQLConnect success
SQLRowCount : 100
Complete.

回退方法

通过SQLEndTran接口对事务内的异常操作进行回滚,具体回滚事务方法如下:

SQLEndTran(SQL_HANDLE_DBC, hdbc, SQL_ROLLBACK);

相关文档