PostgreSQL ODBC Visual C++ BLOB Getting? - c++

I am trying to read a large object from a PostgreSQL database using ODBC from Visual Studio C++. I can't get it to work.
The driver is PostgresSQL ANSI(x64) 9.50.04.00.
The data type of the column is lo, created with:
CREATE DOMAIN lo AS oid;
The column contains the object ids of the large objects.
My understanding is that when the driver sees a lo type, it will handle it as SQL_LONGVARBINARY.
However, the query fails. The diagnostics message is:
HY003 [Microsoft][OBDC Driver Manager] Program type out of range
Here is the code with the noise removed:
wchar_t* connect0 = L"DSN=picdb;";
SQLHENV env;
SQLHDBC dbc;
SQLHSTMT stmt;
SQLLEN sqllen = -1;
PBYTE image = new BYTE[0];
SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env);
SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (void *)SQL_OV_ODBC3, 0);
SQLAllocHandle(SQL_HANDLE_DBC, env, &dbc);
SQLDriverConnect(dbc, NULL, connect0, SQL_NTS, NULL, 0, NULL, SQL_DRIVER_COMPLETE);
SQLAllocHandle(SQL_HANDLE_STMT, dbc, &stmt);
SQLExecDirect(stmt, L"select lo from image_list where id=964945", SQL_NTS);
SQLFetch(stmt);
SQLGetData(stmt, 1, SQL_LONGVARBINARY, image, 0, &sqllen); //<----- It fails here
image = new BYTE[sqllen];
SQLGetData(stmt, 1, SQL_LONGVARBINARY, image, sqllen, &sqllen);
SQLDisconnect(dbc);
SQLFreeHandle(SQL_HANDLE_DBC, dbc);
SQLFreeHandle(SQL_HANDLE_ENV, env);
I can read regular data from the database just fine. And I can read the BLOB data from the database with Java/JDBC, but I need it to work in C++.
Why do I get the error, and what can I do to fix it?

This seems to work:
wchar_t* connect0 = L"DSN=picdb;";
SQLHENV env;
SQLHDBC dbc;
SQLHSTMT stmt;
SQLLEN sqllen = -1;
PBYTE image = new BYTE[0];
SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env);
SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (void *)SQL_OV_ODBC3, 0);
SQLAllocHandle(SQL_HANDLE_DBC, env, &dbc);
SQLDriverConnect(dbc, NULL, connect0, SQL_NTS, NULL, 0, NULL, SQL_DRIVER_COMPLETE);
SQLAllocHandle(SQL_HANDLE_STMT, dbc, &stmt);
SQLExecDirect(stmt, L"select lo from image_list where id=964945", SQL_NTS);
SQLFetch(stmt);
SQLGetData(stmt, 1, SQL_C_BINARY, image, 0, &sqllen);
image = new BYTE[sqllen];
SQLBindParameter(stmt, 1, SQL_PARAM_OUTPUT, SQL_C_BINARY, SQL_LONGVARBINARY, 0, 0, image, sqllen, NULL );
SQLGetData(stmt, 1, SQL_C_BINARY, image, sqllen, &sqllen);
SQLDisconnect(dbc);
SQLFreeHandle(SQL_HANDLE_DBC, dbc);
SQLFreeHandle(SQL_HANDLE_ENV, env);

Related

PostgreSQL "cursor is not positioned on a row" with ODBC driver

I'm trying to use on PostgreSQL 12.2 an updatable cursor inside transaction using the ODBC driver (ANSI 12.1) and C++.
If I declare the cursor with SQLSetCursorName and SQLPrepare + SQLExecute for the query in one statement, and the update in other statement it gives the SQLSTATE 24000 and message cursor "cur0" is not positioned on a row.
If the update is made in the same statement, it gives the SQLSTATE 24000 and message Invalid cursor state.
On the DSN I've checked Use Declare/Fetch, set the Level of rollback on errors option to Statement, and unchecked Updatable Cursors, following what is in https://www.microfocus.com/documentation/enterprise-developer/ed40pu15/ED-VS2015/GUID-1F1C4505-B771-4F8E-B274-952CAF3E8265.html.
The only way possible was with SQLSetPos, but using the same statement for the SELECT and the UPDATE, as described in http://micronetinternational.com/index.pl/en/00/https/www.postgresql-archive.org/ERROR-with-quot-Update-where-Current-of-quot-td4499184.html.
Using DBeaver, which uses a JDBC driver, it works normally.
Is it possible to make the update with a different update statement on PostgreSQL using the ODBC driver?
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);

C++ ODBC SQL - Insert into table not working

I have been trying to insert a row into my SQL table, i get no syntax error but unfortunately when i check my table in the SQL Server Management Studio, no new entry is added. When debugging, the retCode becomes less than zero starting from the SQLConnect() function.
int main()
{
SQLHANDLE SQLEnvHandle = NULL;
SQLHANDLE SQLConnectionHandle = NULL;
SQLHANDLE SQLStatementHandle = NULL;
SQLRETURN retCode = 0;
// Insert Query
char SQLQuery[] = "insert into crm.dbo.company_name values (22,'01 electronics','#01electronics.net');";
// SQL Server Identifier
char SQLServer[] = "DRIVER={SQL Server}; SERVER=localhost, 8000; DATABASE=xxxx; UID=xxxx_xxxx; PWD=xxxx;";
do
{
// Allocate environment
retCode = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &SQLEnvHandle);
// Set ODBC Version
retCode = SQLSetEnvAttr(SQLEnvHandle, SQL_ATTR_ODBC_VERSION,(SQLPOINTER*)SQL_OV_ODBC3, 0);
// Allocate Connection
retCode = SQLAllocHandle(SQL_HANDLE_DBC, SQLEnvHandle, &SQLConnectionHandle);
// Set Login Timeout
retCode = SQLSetConnectAttr(SQLConnectionHandle, SQL_LOGIN_TIMEOUT, (SQLPOINTER)5, 0);
// Set Auto Commit
retCode = SQLSetConnectAttr(SQLConnectionHandle, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)TRUE, 0);
// Connect to DSN
retCode = SQLConnect(SQLConnectionHandle, (SQLCHAR*) SQLServer, SQL_NTS, (SQLCHAR*)NULL, 0, NULL, 0);
// Allocate Statement Handle
retCode = SQLAllocHandle(SQL_HANDLE_STMT, SQLConnectionHandle, &SQLStatementHandle);
// Prepare Statement
retCode = SQLPrepare(SQLStatementHandle, (SQLCHAR*)SQLQuery, SQL_NTS);
// Execute Statement
if (SQLExecute(SQLStatementHandle) == SQL_SUCCESS || SQLExecute(SQLStatementHandle) == SQL_SUCCESS_WITH_INFO)
cout << "SUCCESS";
else
cout << "FAILURE";
} while (FALSE);
// Frees the resources and disconnects
SQLFreeHandle(SQL_HANDLE_STMT, SQLStatementHandle);
SQLDisconnect(SQLConnectionHandle);
SQLFreeHandle(SQL_HANDLE_DBC, SQLConnectionHandle);
SQLFreeHandle(SQL_HANDLE_ENV, SQLEnvHandle);
getchar();
}
When debugging, the retCode becomes less than zero starting from the SQLConnect() function.
From the docs:
When SQLConnect returns SQL_ERROR or SQL_SUCCESS_WITH_INFO, an
associated SQLSTATE value can be obtained by calling SQLGetDiagRec
with a HandleType of SQL_HANDLE_DBC and a Handle of ConnectionHandle

Read Custom String From File in Connection

How to make this to read from custom string the information
I would like to read Host,User,Password,Database from the custom string, how can i do that?
Also another problem here in code is that it show that is connected with ODBC , but it doesnt read anything from database
GetPrivateProfileStringA("SQL","Host","127.0.0.1",szServer2,sizeof(szServer2),SQL_PATH);
GetPrivateProfileStringA("SQL","User","sa",szUser,sizeof(szUser),SQL_PATH);
GetPrivateProfileStringA("SQL","Password","12345",szPassword,sizeof(szPassword),SQL_PATH);
GetPrivateProfileStringA("SQL","Database","DbName",szDatabase,sizeof(szDatabase),SQL_PATH);
BOOL SQLCONNECT::Connect()
{
SQLHENV env;
SQLHDBC dbc;
SQLHSTMT stmt;
SQLRETURN ret;
SQLSMALLINT columns;
int row = 0;
/* Allocate an environment handle */
SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env);
/* We want ODBC 3 support */
SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (void *)SQL_OV_ODBC3, 0);
/* Allocate a connection handle */
SQLAllocHandle(SQL_HANDLE_DBC, env, &dbc);
/* Connect to the DSN */
SQLDriverConnectW(dbc, NULL, L"DRIVER={SQL Server};SERVER=(local);DATABASE=DbName;UID=sa;PWD=password;", SQL_NTS, NULL, 0, NULL, SQL_DRIVER_COMPLETE);
/* Check for success */
if (SQL_SUCCESS != SQLAllocHandle(SQL_HANDLE_STMT, dbc, &stmt))
{
MessageBoxA(0, "Failed to connect to Database!", "Error", MB_OK);
}
std::cin.get();
return FALSE;
}

C++: ODBC Driver will not prompt

I am trying to figure out why ODBC connection to the database is not working, I wrote code (the top part) and commented out the source code of another program. That is working completely fine and the driver window pops up asking me to select the database / etc. I've been looking at this for over 20 mins now and my code (in my eyes) is COMPLETELY the same, but doesn't work. So I would like to have a fresh set of eyes to look at this and let me know if something is different than the commented out part.
Thanks <3
case IDB_LOGIN:{
SQLHENV SQLEnviroment = NULL;
SQLHDBC SQLConnection = NULL;
if (SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &SQLEnviroment) != SQL_SUCCESS) {
MessageBox(NULL, NULL, NULL, NULL);
}
if (SQLSetEnvAttr(SQLEnviroment, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0) != SQL_SUCCESS)
{
MessageBox(NULL, NULL, NULL, NULL);
}
if (SQLAllocHandle(SQL_HANDLE_DBC, SQLEnviroment, &SQLConnection) != SQL_SUCCESS) {
MessageBox(NULL, NULL, NULL, NULL);
}
if (SQLDriverConnect(SQLConnection, GetDesktopWindow(), L"DRIVER={SQL DRIVER}; Server=LIVINGTREE; Database=Application Database;Uid=AdminNick;Pwd=spawn1234;", SQL_NTS, NULL, 0, NULL, SQL_DRIVER_PROMPT) == SQL_SUCCESS_WITH_INFO) {
MessageBox(NULL, L"Connected!", NULL, MB_OK);
}
//SQLHENV hEnv = NULL;
//SQLHDBC hDbc = NULL;
//SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &hEnv);
//
//SQLSetEnvAttr(hEnv,SQL_ATTR_ODBC_VERSION,(SQLPOINTER)SQL_OV_ODBC3,0);
//SQLAllocHandle(SQL_HANDLE_DBC, hEnv, &hDbc);
//SQLDriverConnect(hDbc,
// GetDesktopWindow(),
// L"Driver={SQL Server};Server=.\\LIVINGTREE;Database=Application Database;Uid=AdminNick; Pwd=spawn123;",
// SQL_NTS,
// NULL,
// 0,
// NULL,
// SQL_DRIVER_COMPLETE);
break;
}

Changing odbc timeout

I am trying to run following stored procedure using ODBC:
CREATE PROCEDURE [dbo].[Add]
--WITH ENCRYPTION
AS
DECLARE #LoopVar BIGINT = 0
, #MaxVar BIGINT = 0
, #rows BIGINT = 0
SET #LoopVar = 1
set #rows = 125000
insert into debug values(987654321)
insert into debug values(#LoopVar)
insert into debug values(#rows)
WHILE(#LoopVar <= #rows)
BEGIN
SET #LoopVar = #LoopVar + 1
WAITFOR DELAY '00:00:01'
insert into debug values(#LoopVar)
END
insert into debug values(123456789)
GO
The C++ code for running the stored procedure is:
RETCODE rc = SQL_SUCCESS;
HENV henv = SQL_NULL_HENV;
HDBC hdbc = SQL_NULL_HDBC;
SQLHSTMT hstmt = SQL_NULL_HSTMT;
SQLTCHAR * pszConnection = _T("DRIVER={SQL Server Native Client 10.0};Server=myserver;Trusted_Connection=Yes;Initial Catalog=testdb;");
SQLTCHAR * pszInsertStmt = _T("{call [testdb].[dbo].Add}");
SQLLEN cbParamLength;
SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HENV, &henv);
SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0);
SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
SQLSetConnectAttr( hdbc, SQL_ATTR_LOGIN_TIMEOUT, reinterpret_cast<SQLPOINTER>(600), SQL_IS_UINTEGER);
SQLDriverConnect( hdbc, NULL, pszConnection, SQL_NTS, NULL, 0, NULL, SQL_DRIVER_NOPROMPT);
SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
SQLSetStmtAttr(hstmt, SQL_QUERY_TIMEOUT, (SQLPOINTER)12000, SQL_IS_UINTEGER);
SQLSetStmtAttr(hstmt, SQL_ATTR_QUERY_TIMEOUT, (SQLPOINTER)12000, SQL_IS_UINTEGER);
SQLINTEGER attr;
rc = SQLGetStmtAttr( hstmt, SQL_ATTR_QUERY_TIMEOUT, &attr, 0, NULL ) ;
rc = SQLGetStmtAttr( hstmt, SQL_QUERY_TIMEOUT, &attr, 0, NULL ) ;
rc = SQLGetConnectAttr(hdbc, SQL_ATTR_CONNECTION_TIMEOUT, &attr, 0, NULL);
rc = SQLExecDirect(hstmt, pszInsertStmt, SQL_NTS);
if (!SUCCESS(rc)) {
if (hstmt)
PrintError(SQL_HANDLE_STMT, hstmt);
if (hdbc)
PrintError(SQL_HANDLE_DBC, hdbc);
if(henv)
PrintError(SQL_HANDLE_ENV, henv);
}
if (hstmt)
SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
if (hdbc) {
SQLDisconnect(hdbc);
SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
}
if (henv)
SQLFreeHandle(SQL_HANDLE_ENV, henv);
I have set query time in the code above. The connection time out is 0 (which I believe means no timeout). But no matter what I do, the stored procedure times out in 78 seconds. Does any one have any idea as to what I should do so that stored procedure can run indefinitely?
Please note if I run the stored procedure from SQL Server Management Studio directly, it works just fine..
Thanks in advance,
-Neel.
If anyone interested, the solution was to put "SET NOCOUNT ON" as the first line in stored procedure.