ODBC SQLParamData return data - c++

I am trying to insert o blob in a database. I have succeed to enter data i two columns.
I am having problem whit SQLParamData .It returns an error when it should return SQL_NEED_DATA(i will post code)
When i run SQLGetDiagRec it returns S1010 with error text Function sequence error .
I search this error on the internet and i learned that it could be related to a parameter from SQLBindParameter .
// Bind the parameter marker.
retCode = retcode = SQLBindParameter(hstmt, // hstmt
1, // ipar
SQL_PARAM_INPUT, // fParamType
SQL_C_BINARY, // fCType
SQL_LONGVARBINARY, // FSqlType
lbytes, // cbColDef
0, // ibScale
&pParmID, // rgbValue
0, // cbValueMax
&cbTextSize); // pcbValue
SqlError(hstmt,SQL_HANDLE_STMT,_T("WriteBlob"), _T("CTLSqlConnection"), _T("SQLBindParameter"));
if(retCode != SQL_SUCCESS)
{
delete pData;
if(!EndTransaction(FALSE))
return ERR_ENDTRANSACTION_FAILED;
else
return -3;
}
//SQLExec
retcode = retCode = SQLExecDirect(hstmt,(SQLTCHAR*)szSqlStat, SQL_NTS);
SQLRETURN ret;
SQLCHAR* SQLState;
SQLINTEGER NativeError;
SQLSMALLINT errmsglen;
SQLCHAR errmsg[255];
SQLCHAR errstate[50];
retCode = SQLParamData(hstmt, &pParmID);
SQLGetDiagRec(SQL_HANDLE_STMT, hstmt, 1, (SQLCHAR*)errstate, &NativeError, (SQLCHAR*)errmsg, sizeof(errmsg), &errmsglen);
if(retCode == SQL_NEED_DATA)
{
// Put final batch.
SQLPutData(hstmt, pData, lbytes);
}
else
{
delete pData;
If this part of the code is not relevant enough i will post more.
Hope you can help me .Thanks .

Your logic looks wrong here. It is when SQLExecute (SQLExecDirect) returns SQL_NEED_DATA you call SQLParamData and it tells you will parameter you need to supply data for with SQLPutData. If you call SQLParamData when SQLExecute did not return SQL_NEED_DATA I can well imagine it is a function sequence error. I could probably dig you out an example from somewhere if you still need it.

Related

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

ODBC - Coding Prepared Statements in C++

Hi all,
Plugging away at learning how to develop ODBC SQL driver and data source stuff, but I seem to have run into a bit of a snag. I'm currently working with prepared statements on the a database with the following statement:
select * from TEST1 where NAME = ? and LOCATION__LATITUDE__S = ?
In English, find all records from TEST1 with to-be-specified name and latitudinal coordinate. I'm able to do the above with the ODBCTest app, so I know I can connect to the data source and query it with parameterized queries. Here's what I have for code for my problematic function:
void ExecPreparedStatement(const char* stmt) {
HSTMT hstmt;
SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
RETCODE rc = SQLPrepare(hstmt, (WCHAR*)stmt, SQL_NTS);
SQLSMALLINT numParams;
rc = SQLNumParams(hstmt, &numParams);
WCHAR* param1 = (WCHAR*)L"Jacob";
SQLFLOAT param2 = 40.0;
rc = SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, 80, 0, (SQLPOINTER)param1, 300, NULL);
rc = SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_FLOAT, SQL_FLOAT, 0, 0, &param2, 300, NULL);
rc = SQLExecute(hstmt); /* <fails here> */
SQLSMALLINT numCols;
SQLNumResultCols(hstmt, &numCols);
DisplayRecords(hstmt, numCols);
SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
}
This should give me the same results from the test app: 3 records of 13 attributes (3 rows, 13 columns). Instead, it fails on execution. For ease of reading, I've removed all of my RetCode handling from the code, but I do have it in there to check that the statements are completed and to handle it gracefully if they fail. There must be something I'm misunderstanding here - it also shows me that the number of parameters from the statement is 0 (with the numParams variable); I theorize that this is because it should be placed after the Execute call, but I can't test that right now because I never get to that point in execution.
Any ideas? Banging my head on a brick wall here; MSDN and other online sources are proving less than informative on this.
Clarification on the main question: Does anybody have any idea why the Execute function is failing?
ANSWER FOUND
The issue was malformed SQL through misused casting. Instead of passing in a const char* string to the function, I pass in a WCHAR*-casted string instead, and inside the function I use WCHAR*. The functional code now looks like this:
HSTMT hstmt;
SQLAllocStmt(hdbc, &hstmt);
TryODBC(hstmt, SQL_HANDLE_STMT, SQLPrepare(hstmt, stmt, SQL_NTS));
// Prepare passes test - we return 0.
WCHAR* param1 = (WCHAR*)L"Jacob";
TryODBC(hstmt, SQL_HANDLE_STMT,
SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_WCHAR,
SQL_WVARCHAR, 80, 0, (SQLPOINTER)param1, 300, NULL)
);
TryODBC(hstmt, SQL_HANDLE_STMT, SQLExecute(hstmt));
SQLSMALLINT numCols;
TryODBC(hstmt, SQL_HANDLE_STMT, SQLNumResultCols(hstmt, &numCols));
DisplayRecords(hstmt, numCols);
SQLFreeStmt(hstmt, SQL_CLOSE);
Where TryODBC() is a function as follows:
bool disconnectOnError = false;
if (rc == SQL_SUCCESS_WITH_INFO || rc == SQL_ERROR) {
if (!Success(rc)) {
disconnectOnError = true;
}
SQLWCHAR state[6], errorMsg[SQL_MAX_MESSAGE_LENGTH];
SQLINTEGER nativeError;
SQLSMALLINT i = 1, msgLen;
while ((rc = SQLGetDiagRec(handleType, handle, i, state,
&nativeError, errorMsg, sizeof(errorMsg), &msgLen)) != SQL_NO_DATA)
{
ShowMessage(nativeError, errorMsg);
i++;
}
}
if (disconnectOnError) {
Disconnect(-1);
}
Massive thanks to #erg for directing me towards the SQLGetDiagRec() function.
You are passing a wide string but specifying SQL_C_CHAR as the parameter type, this should be SQL_C_WCHAR.
300 as your BufferLength parameter makes no sense, for the string argument pass the correct number of bytes, for param2 just pass 0 (BufferLength is ignored for non-character or binary-string data).
And you really need to check the error code after every ODBC call, and if it's an error dump the diagnostics. Either that or turn on the trace connection attribute and look at the results.
Answer noted in original question.

SQLBindParam: setting Null value fails (ODBC)

I am trying to bind an NULL-Value to a parameter in a sql query but I always get an error HY009 Invalid argument value when I execute the statement. That strange behavior only appears when I build in release mode. In debug mode it's working fine. Any ideas?
My Code:
SQLRETURN nRet = SQLPrepare(m_hStmt, (SQLTCHAR *)strSQL, SQL_NTS); // returns SQL_SUCCESS
SQLINTEGER cbNumeric = SQL_NULL_DATA;
nRet = SQLBindParameter(m_hStmt,
parameterIndex,
SQL_PARAM_INPUT,
SQL_C_CHAR,
SQL_LONGVARCHAR,
0,
NULL,
NULL,
0,
&cbNumeric); // returns SQL_SUCCESS
nRet = SQLExecute(m_hStmt); // returns SQL_NEED_DATA
nRet = _SQLParamDataPutData(nRet); // returns Debug: SQL_SUCCESS, Release: SQL_ERROR
If I understand the documentation at https://msdn.microsoft.com/en-us/library/ms710963%28v=vs.85%29.aspx correctly, you are not allowed to pass a NULL-pointer as ParameterValuePtr argument - even if the value is a NULL-Value.
From the comments section in the doc, about the error HY009:
(DM) The argument ParameterValuePtr was a null pointer, the argument
StrLen_or_IndPtr was a null pointer, and the argument InputOutputType
was not SQL_PARAM_OUTPUT.
Does it work if you change your code to something like this:
SQLRETURN nRet = SQLPrepare(m_hStmt, (SQLTCHAR *)strSQL, SQL_NTS);
SQLCHAR dummy[1];
dummy[0] = '\0';
SQLINTEGER cbNumeric = SQL_NULL_DATA;
nRet = SQLBindParameter(m_hStmt,
parameterIndex,
SQL_PARAM_INPUT,
SQL_C_CHAR,
SQL_LONGVARCHAR,
0,
NULL,
dummy,
SQL_NTS,
&cbNumeric); // returns SQL_SUCCESS
nRet = SQLExecute(m_hStmt); // returns SQL_NEED_DATA
nRet = _SQLParamDataPutData(nRet);

C++ assign variable value from SQL Server SELECT

I've got a script that does a bunch of SQL inserts but I've been trying to add a section that assigns the returned value of a select query into a variable.
The code I've been using to do the inserts is:
if (SQL_SUCCESS != SQLExecDirect(sqlstatementhandle, (SQLCHAR*)"BULK INSERT mytable FROM 'C:/dir/myfile.csv' WITH (FIRSTROW = 1, FIELDTERMINATOR = ',', ROWTERMINATOR = '\n');", SQL_NTS)) {
show_error(SQL_HANDLE_STMT, sqlstatementhandle);
That all works fine, but I can't figure out how to use the output of a query. The output will be a single int value, which I'd like to assign to an int variable.
Apologies if this is something that's blindingly obvious.
EDIT
Based on the answer below, the following has now worked for me. Thanks!
SQLRETURN retcode;
SQLHSTMT hstmt; // I use my own stmnthndl(sqlstatementhandle) below, this line left in for demonstration
retcode = SQLExecDirect(sqlstatementhandle, (SQLCHAR*)"SELECT count(*) FROM mytable;",SQL_NTS);
SQLINTEGER sCustID;
SQLLEN cbCustID;
if (retcode == SQL_SUCCESS) {
while (TRUE) {
retcode = SQLFetch(sqlstatementhandle);
if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
// get the first column
SQLGetData(sqlstatementhandle, 1, SQL_C_ULONG, &sCustID, 0, &cbCustID);
//You can now print it
cout << "CustID:" << sCustID;
}
else {
break;
}
}
}
You can use SQLGetData to get the data for a single column in the resultset. Taken from this example, first you execute the SQL query with SQLExecDirect:
SQLRETURN retcode;
SQLHSTMT hstmt;
retcode = SQLExecDirect(hstmt,
(SQLCHAR*)"SELECT CUSTID, NAME, PHONE FROM CUSTOMERS ORDER BY 2, 1, 3",
SQL_NTS);
Then you can get the data, I show a reduced version of the same example:
SQLINTEGER sCustID, cbCustID;
if (retcode == SQL_SUCCESS) {
while (TRUE) {
retcode = SQLFetch(hstmt);
if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO) {
show_error();
}
if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO){
// get the first column
SQLGetData(hstmt, 1, SQL_C_ULONG, &sCustID, 0, &cbCustID);
//You can now print it
fprintf(out, "CustID: %-5d", sCustID);
} else {
break;
}
}
}
The syntax of SQLGetData is:
SQLRETURN SQLGetData(
SQLHSTMT StatementHandle,
SQLUSMALLINT Col_or_Param_Num,
SQLSMALLINT TargetType,
SQLPOINTER TargetValuePtr,
SQLLEN BufferLength,
SQLLEN * StrLen_or_IndPtr);
With:
StatementHandle: the handle to the executed SQL query.
Col_or_Param_Num: the column number, which starts at 1.
TargetType: the data type, you can probably use SQL_INTEGER, see SQL data types and C data types.
TargetValuePtr: the pointer to the output data.
BufferLength: the length, but is not used for fixed length data such as integers.
StrLen_or_IndPtr: an optional output that can return the length of the data or an error code.
Note: like you commented you might need to cast the SQL query to SQLCHAR * since SQLCHAR is an unsigned char, see the data types.

ORA-01460: unimplemented or unreasonable conversion requested blob insert

I am trying to insert an blob into a database .
For this I am using c++ and microsoft ODBC driver .
The app works fine with files that have less than 4000 bits .
When i am trying to insert a file that has more than 4000 bytes , meaning 3,9 KB it returns : ORA-01460: unimplemented or unreasonable conversion requested .
The database is oracle 11.2.0 .
I have searched the internat for a solution but find none .
How could i solve this problem ?
What can i do ?
The code for my insert function is :
try{
CString strSqlStat(szSqlStat);
if(IsConnectionDead())
{
if(!Reconnect())
return ERR_RECONNECT_FAILED;
}
CFileException exFile;
CFile sourceFile;
if(!sourceFile.Open(szFilePath, CFile::modeRead | CFile::shareDenyNone, &exFile))
return ERR_BLOB_READFILE;
int nrBytesToRead = (int)sourceFile.GetLength();
char* pData = new char[nrBytesToRead+1];
DWORD nrBytesRead;
if(!ReadFile((HANDLE)sourceFile.m_hFile,pData,nrBytesToRead, &nrBytesRead, NULL))
{
delete pData;
return ERR_BLOB_READFILE;
}
sourceFile.Close();
if(nrBytesRead == 0)//file empty
{
delete pData;
return 0;
}
//variables
SQLRETURN retCode;
SDWORD cbTextSize, lbytes;
lbytes = (SDWORD)nrBytesRead;
cbTextSize = SQL_LEN_DATA_AT_EXEC(lbytes);
PTR pParmID;
SDWORD cbBatch = nrBytesRead;
int rgbValue = 1;
// Bind the parameter marker.
retCode = retcode = SQLBindParameter(hstmt, // hstmt
1, // ipar
SQL_PARAM_INPUT, // fParamType
SQL_C_BINARY, // fCType
SQL_LONGVARBINARY, // FSqlType
lbytes, // cbColDef
0, // ibScale
&rgbValue, // rgbValue
0, // cbValueMax
&cbTextSize); // pcbValue
SqlError(hstmt,SQL_HANDLE_STMT,_T("WriteBlob"), _T("CTLSqlConnection"), _T("SQLBindParameter"));
if(retCode != SQL_SUCCESS)
{
delete pData;
if(!EndTransaction(FALSE))
return ERR_ENDTRANSACTION_FAILED;
else
return -3;
}
//SQLExec
retcode = retCode = SQLExecDirect(hstmt,(SQLTCHAR*)szSqlStat, SQL_NTS);
retcode = retCode = SQLParamData(hstmt, &pParmID);
SQLRETURN ret; //ADI fix all warnings - including this var that is unreferenced - delete it if you don't use it
SQLCHAR* SQLState; //ADI same here
SQLINTEGER NativeError;
SQLSMALLINT errmsglen;
SQLWCHAR errmsg[255];
SQLWCHAR errstate[50];
SQLGetDiagRec(SQL_HANDLE_STMT, hstmt, 1, (SQLWCHAR*)errstate, &NativeError, (SQLWCHAR*)errmsg, sizeof(errmsg), &errmsglen);
if(retCode == SQL_NEED_DATA)
{
// Put final batch.
SQLPutData(hstmt, pData, lbytes);
}
else
{
delete pData;
SqlError(hstmt,SQL_HANDLE_STMT,_T("WriteBlob"), _T("CTLSqlConnection"), _T("SQLExecDirect or SQLParamData"));
if(!EndTransaction(FALSE))
return ERR_ENDTRANSACTION_FAILED;
else
return -4;
}
delete pData;
// Make final SQLParamData call.
retcode = retCode = SQLParamData(hstmt, &pParmID);
/*SQLRETURN ret; //ADI fix all warnings - including this var that is unreferenced - delete it if you don't use it
SQLCHAR* SQLState; //ADI same here
SQLINTEGER NativeError;
SQLSMALLINT errmsglen;
SQLWCHAR errmsg[255];
SQLWCHAR errstate[50];
*/
SQLGetDiagRec(SQL_HANDLE_STMT, hstmt, 1, (SQLWCHAR*)errstate, &NativeError, (SQLWCHAR*)errmsg, sizeof(errmsg), &errmsglen);
if(SqlError(hstmt,SQL_HANDLE_STMT,_T("WriteBlob"), _T("CTLSqlConnection"), _T("The last SQLParamData")))
if(!EndTransaction(FALSE))
return ERR_ENDTRANSACTION_FAILED;
else
return -5;
retcode = SQLCloseCursor(hstmt);
if(!EndTransaction(TRUE))
return ERR_ENDTRANSACTION_FAILED;
}catch(...){
WRITEERRORINLOGFILE(_T("Error: An exception has been caught in WriteBlob."));
return -10;
}
return 0;
}
Waitin for your answers .
Thanks .