更新时间:2025-09-04 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");
}
回退方法
通过SQLEndTran接口对事务内的异常操作进行回滚,具体回滚事务方法如下:
SQLEndTran(SQL_HANDLE_DBC, hdbc, SQL_ROLLBACK);
父主题: 操作步骤