我正在尝试使用 ODBC 驱动程序(ANSI 12.1)和 C++ 在 PostgreSQL 12.2 上使用事务内的可更新游标。
如果我在一个语句中使用 SQLSetCursorName 和 SQLPrepare + SQLExecute 为查询声明游标,而在另一条语句中更新它会给出 SQLSTATE 24000 并且消息游标“cur0”未定位在一行上。
如果在同一语句中进行更新,则会给出 SQLSTATE 24000 和消息Invalid cursor state。
在 DSN 上,我检查了Use Declare/Fetch,将Level of rollback on errors选项设置为Statement,并取消选中Updatable Cursors,遵循https://www.microfocus.com/documentation/enterprise-developer/ed40pu15/中的内容ED-VS2015/GUID-1F1C4505-B771-4F8E-B274-952CAF3E8265.html。
唯一可能的方法是使用 SQLSetPos,但对 SELECT 和 UPDATE 使用相同的语句,如http://micronetinternational.com/index.pl/en/00/https/www.postgresql-archive.org/ERROR中所述-with-quot-Update-where-Current-of-quot-td4499184.html。
使用使用JDBC驱动的DBeaver,可以正常工作。
是否可以使用 ODBC 驱动程序在 PostgreSQL 上使用不同的更新语句进行更新?
rc = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
check_rc(henv, hdbc, hstmt, rc);
rc = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER*)SQL_OV_ODBC3, 0);
check_rc(henv, hdbc, hstmt, rc);
rc = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
check_rc(henv, hdbc, hstmt, rc);
rc = lpfSqlConnect(hdbc, (SQLCHAR*)"mydsn", SQL_NTS, (SQLCHAR*)"user", SQL_NTS, (SQLCHAR*)"pass", SQL_NTS);
check_rc(henv, hdbc, hstmt, rc);
rc = SQLSetConnectAttr(hdbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_OFF, SQL_IS_INTEGER);
check_rc(henv, hdbc, hstmt, rc);
rc = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
check_rc(henv, hdbc, hstmt, rc);
rc = SQLSetStmtAttr(hstmt, SQL_ATTR_NOSCAN, (SQLPOINTER)SQL_NOSCAN_ON, SQL_IS_INTEGER);
check_rc(henv, hdbc, hstmt, rc);
rc = SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (void *)SQL_CURSOR_KEYSET_DRIVEN, 0);
check_rc(henv, hdbc, hstmt, rc);
rc = SQLSetStmtAttr(hstmt, SQL_ATTR_CONCURRENCY, (SQLPOINTER)SQL_CONCUR_ROWVER, 0);
check_rc(henv, hdbc, hstmt, rc);
rc = SQLSetCursorName(hstmt, (SQLCHAR *)"cur0", SQL_NTS);
check_rc(henv, hdbc, hstmt, rc);
rc = SQLPrepare(hstmt, (SQLCHAR *)"select field1, field2 from mytable", SQL_NTS);
check_rc(henv, hdbc, hstmt, rc);
rc = SQLExecute(hstmt);
check_rc(henv, hdbc, hstmt, rc);
SQLSMALLINT iNumCols = 0;
rc = SQLNumResultCols(hstmt, &iNumCols);
check_rc(henv, hdbc, hstmt, rc);
for(i = 0; i < iNumCols; ++i)
{
rc = SQLBindCol(hstmt, i + 1, SQL_C_CHAR, data[i], collen[i], &outlen[i]);
check_rc(henv, hdbc, hstmt, rc);
}
for (i = 0; i < 4; ++i)
{
rc = SQLFetch(hstmt);
check_rc(henv, hdbc, hstmt, rc);
}
//////////////////////////////////////////////////////////////////
//it returns SQL_ERROR, SQLSTATE=24000, message "[Microsoft][ODBC Driver Manager] Invalid cursor state"
rc = SQLExecDirect(hstmt, (SQLCHAR *)"UPDATE mytable SET field3 = 'newvalue' where current of cur0", SQL_NTS);
check_rc(henv, hdbc, hstmt, rc);
/////////////////////////////////////////////////////////////////
//or
SQLHSTMT hstmt2 = SQL_NULL_HSTMT; // Statement Handle
rc = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt2);
check_rc(henv, hdbc, hstmt2, rc);
rc = SQLSetStmtAttr(hstmt2, SQL_ATTR_RETRIEVE_DATA, (SQLPOINTER)SQL_RD_OFF, SQL_IS_INTEGER);
check_rc(henv, hdbc, hstmt2, rc);
//it returns SQL_ERROR, SQLSTATE=24000, message ERROR: cursor "cur0" is not positioned on a row;
rc = SQLExecDirect(hstmt2, (SQLCHAR *)"UPDATE mytable SET field3 = 'newvalue' where current of cur0", SQL_NTS);
check_rc(henv, hdbc, hstmt2, rc);
////////////////////////////////////////////////////////////
rc = SQLEndTran(SQL_HANDLE_DBC, hdbc, SQL_COMMIT);
check_rc(henv, hdbc, NULL, rc);
rc = SQLSetConnectAttr(hdbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_ON, SQL_IS_INTEGER);
check_rc(henv, hdbc, hstmt, rc);