更新时间: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);
父主题: 操作步骤