SQLExecDirect result -1 state 24000 in C - c++

hello there im studing C and ODBC
i got error when call SQLExecDirect function.
please help me
here is the code
#include <Windows.h>
#include <sql.h>
#include <sqlext.h>
int main( void )
{
//DB connect example
SQLRETURN nResult = 0;
SQLHANDLE handleEnv = 0;
nResult = SQLAllocHandle( SQL_HANDLE_ENV, SQL_NULL_HANDLE, ( SQLHANDLE* )&handleEnv );
//nResult = SQLSetEnvAttr( handleEnv, SQL_ATTR_ODBC_VERSION, ( SQLPOINTER )SQL_OV_ODBC2, SQL_IS_INTEGER );
nResult = SQLSetEnvAttr( handleEnv, SQL_ATTR_ODBC_VERSION, ( SQLPOINTER )SQL_OV_ODBC3, SQL_IS_INTEGER );
nResult = SQLSetEnvAttr( handleEnv, SQL_ATTR_CONNECTION_POOLING, (SQLPOINTER)SQL_CP_ONE_PER_HENV, SQL_IS_INTEGER);
SQLHANDLE handleDBC = 0;
nResult = SQLAllocHandle( SQL_HANDLE_DBC, handleEnv, ( SQLHANDLE* )&handleDBC );
SQLCHAR strConnect[ 256 ] = "DSN=MY TEST DB; UID=TESTsa; PWD=TESTpw";
SQLCHAR strConnectOut[ 1024 ] = {0};
SQLSMALLINT nNumOut = 0;
nResult = SQLDriverConnect( handleDBC, NULL, ( SQLCHAR* )strConnect, SQL_NTS, ( SQLCHAR* )strConnectOut, sizeof(strConnectOut),
&nNumOut, SQL_DRIVER_NOPROMPT );
SQLHSTMT handleStatement = 0;
//nResult = ::SQLAllocStmt( handleDBC, &handleStatement );
nResult = SQLAllocHandle( SQL_HANDLE_STMT, handleDBC, ( SQLHANDLE* )&handleStatement );
SQLHSTMT hstmt = handleStatement;
int sqlResultOutput = 0;
SQLINTEGER cbValue = 0;
nResult = SQLBindParameter( hstmt, 1, SQL_PARAM_OUTPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &sqlResultOutput, 0, &cbValue );
//here is the problem
nResult = SQLExecDirect( hstmt, (SQLTCHAR*) "{CALL my_TEST_SP(?)}", SQL_NTS );
//nResult Value is -1 and SQL State is 24000
return 0;
}
every call result is fine. return SQL_SUCCESS all but one
SQLExecDirect function is return -1 and SQLState is 24000
stored procedure is simple like this
CREATE PROCEDURE my_TEST_SP
#retValue int = 0 output
AS
BEGIN
SET NOCOUNT ON
SET #retValue = 0
SELECT myTestCol
FROM tTestTable
IF ##ERROR <> 0
BEGIN
SET #retValue = -1
END
END
my OS is windows7.
using visual studio 2010 sp1, sqlserver 2012
what i miss? please tell me and have a good day :)

The main difference seems to be the odbc driver version. My version is 2014.120.2000.08. I dont think it is due to the different visual studio versions - I think they all use the same odbc32.lib under the hood.
The following code works for me: I changed the stored procedure to return something that is not 0 (to verify I can actually read the data returned):
#retValue int = 0 output
AS
BEGIN
SET NOCOUNT ON
SET #retValue = 3499
SELECT myTestCol
FROM tTestTable
IF ##ERROR <> 0
BEGIN
SET #retValue = -1
END
END
And I used the following code in Visual Studio 2013:
void printErrDbc(SQLHDBC hDbc)
{
SQLSMALLINT recNr = 1;
SQLRETURN ret = SQL_SUCCESS;
while (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO)
{
SQLWCHAR errMsg[SQL_MAX_MESSAGE_LENGTH + 1];
SQLWCHAR sqlState[5 + 1];
errMsg[0] = 0;
SQLINTEGER nativeError;
SQLSMALLINT cb = 0;
ret = SQLGetDiagRec(SQL_HANDLE_DBC, hDbc, recNr, sqlState, &nativeError, errMsg, SQL_MAX_MESSAGE_LENGTH + 1, &cb);
if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO)
{
std::wcerr << L"ERROR; native: " << nativeError << L"; state: " << sqlState << L"; msg: " << errMsg << std::endl;
}
++recNr;
}
}
void printErrStmt(SQLHSTMT hStmt)
{
SQLSMALLINT recNr = 1;
SQLRETURN ret = SQL_SUCCESS;
while (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO)
{
SQLWCHAR errMsg[SQL_MAX_MESSAGE_LENGTH + 1];
SQLWCHAR sqlState[5 + 1];
errMsg[0] = 0;
SQLINTEGER nativeError;
SQLSMALLINT cb = 0;
ret = SQLGetDiagRec(SQL_HANDLE_STMT, hStmt, recNr, sqlState, &nativeError, errMsg, SQL_MAX_MESSAGE_LENGTH + 1, &cb);
if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO)
{
std::wcerr << L"ERROR; native: " << nativeError << L"; state: " << sqlState << L"; msg: " << errMsg << std::endl;
}
++recNr;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
//DB connect example
SQLRETURN nResult = 0;
SQLHANDLE handleEnv = 0;
nResult = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, (SQLHANDLE*)&handleEnv);
//nResult = SQLSetEnvAttr( handleEnv, SQL_ATTR_ODBC_VERSION, ( SQLPOINTER )SQL_OV_ODBC2, SQL_IS_INTEGER );
nResult = SQLSetEnvAttr(handleEnv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, SQL_IS_INTEGER);
nResult = SQLSetEnvAttr(handleEnv, SQL_ATTR_CONNECTION_POOLING, (SQLPOINTER)SQL_CP_ONE_PER_HENV, SQL_IS_INTEGER);
SQLHANDLE handleDBC = 0;
nResult = SQLAllocHandle(SQL_HANDLE_DBC, handleEnv, (SQLHANDLE*)&handleDBC);
SQLWCHAR strConnect[256] = L"DSN=test;UID=sa;PWD=MySecretPassword";
SQLWCHAR strConnectOut[1024] = { 0 };
SQLSMALLINT nNumOut = 0;
nResult = SQLDriverConnect(handleDBC, NULL, (SQLWCHAR*)strConnect, SQL_NTS, (SQLWCHAR*)strConnectOut, sizeof(strConnectOut),
&nNumOut, SQL_DRIVER_NOPROMPT);
if (!SQL_SUCCEEDED(nResult))
{
printErrDbc(handleDBC);
}
SQLHSTMT handleStatement = 0;
//nResult = ::SQLAllocStmt( handleDBC, &handleStatement );
nResult = SQLAllocHandle(SQL_HANDLE_STMT, handleDBC, (SQLHANDLE*)&handleStatement);
SQLHSTMT hstmt = handleStatement;
int sqlResultOutput = 0;
SQLINTEGER cbValue = 0;
nResult = SQLBindParameter(hstmt, 1, SQL_PARAM_OUTPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &sqlResultOutput, 0, &cbValue);
//here is the problem
nResult = SQLExecDirect(hstmt, (SQLWCHAR*)L"{CALL my_TEST_SP(?)}", SQL_NTS);
if (!SQL_SUCCEEDED(nResult))
{
printErrStmt(hstmt);
}
nResult = SQLMoreResults(hstmt);
if (!(SQL_SUCCEEDED(nResult) || nResult == SQL_NO_DATA))
{
printErrStmt(hstmt);
}
std::wcout << L"Value of sqlResultOutput is: " << sqlResultOutput << std::endl;
return 0;
}
In the ODBC Data Source Administrator I created a corresponding DSN named test using ODBC Driver 11 for SQL Server with version 2014.120.2000.08. At the end the DSN entry shows the following properties (in the dialog where you can hit Test Data Source...):
Microsoft ODBC Driver for SQL Server Version 12.00.2000
Data Source Name: test
Data Source Description:
Server: .\SQLSERVERINSTANCE
Use Integrated Security: Yes
Database: Test
Language: (Default)
Data Encryption: No
Trust Server Certificate: No
Multiple Active Result Sets(MARS): No
Mirror Server:
Translate Character Data: Yes
Log Long Running Queries: No
Log Driver Statistics: No
Use Regional Settings: No
Use ANSI Quoted Identifiers: Yes
Use ANSI Null, Paddings and Warnings: Yes
If I run this sample app I get the expected output:
Value of sqlResultOutput is: 3499
So I would suggest that you try with the latest ODBC Driver for SQL Server. I think it is this download here: http://www.microsoft.com/de-ch/download/details.aspx?id=36434

Related

Problem binding output parameter for SQL Server stored procedure in ODBC

I'm testing the behaviour of how SQL Server's ODBC driver returns output parameters, and I've come up with the following test program:
#include <windows.h>
#define SQL_NOUNICODEMAP
#include <sql.h>
#include <sqltypes.h>
#include <sqlext.h>
#include <algorithm>
#include <condition_variable>
#include <functional>
#include <mutex>
#include <queue>
#include <utility>
#include <cmath>
#include <iomanip>
#include <iostream>
#include <map>
#include <memory>
#include <string>
#include <thread>
#include <tuple>
#include <vector>
#include <cassert>
using namespace std;
/// #brief Global variable indicating if the application is connected to the driver/data source.
bool g_isConnected = false;
/// #brief Check if an ODBC call failed and display the SQLState, native error code,
/// and error message on stderr.
///
/// #param rc The return code.
/// #param stmt The statement handle.
/// #param conn The connection handle.
/// #param env The environment handle.
///
/// #return true If the API call was successful.
SQLRETURN CheckDiagnostics(SQLRETURN rc, SQLHANDLE stmt, SQLHANDLE conn = SQL_NULL_HANDLE, SQLHANDLE env = SQL_NULL_HANDLE)
{
if (SQL_ERROR == rc || SQL_SUCCESS_WITH_INFO == rc)
{
char stateBuffer[6];
char msgBuffer[1024];
SQLINTEGER nativeError;
SQLError(
env,
conn,
stmt,
reinterpret_cast<SQLCHAR*>(stateBuffer),
&nativeError,
reinterpret_cast<SQLCHAR*>(msgBuffer),
sizeof(msgBuffer),
NULL);
cerr << (SQL_ERROR == rc ? "ERROR" : "WARNING") << ": [" << stateBuffer << "] (" << nativeError << "): " << msgBuffer << endl;
}
return rc;
}
SQLRETURN CheckDiagnosticsFatal(SQLRETURN rc, SQLSMALLINT handleType, SQLHANDLE handle)
{
if (SQL_ERROR == rc || SQL_SUCCESS_WITH_INFO == rc)
{
char stateBuffer[6];
char msgBuffer[1024];
SQLINTEGER nativeError;
for (SQLSMALLINT rec = 1; SQL_NO_DATA != SQLGetDiagRec(handleType, handle, rec, reinterpret_cast<SQLCHAR*>(stateBuffer), &nativeError, reinterpret_cast<SQLCHAR*>(msgBuffer), sizeof(msgBuffer), NULL); ++rec)
{
cerr << (SQL_ERROR == rc ? "ERROR" : "WARNING") << ": [" << stateBuffer << "] (" << nativeError << "): " << msgBuffer << endl;
}
if (SQL_ERROR == rc)
{
exit(-1);
}
}
return rc;
}
/// #brief Clean up the handles passed in.
void CleanupHandles(SQLHANDLE stmt, SQLHANDLE dbc, SQLHANDLE env)
{
if (SQL_NULL_HANDLE != stmt)
{
SQLFreeStmt(stmt, SQL_CLOSE);
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
}
if (g_isConnected)
{
g_isConnected = false;
SQLDisconnect(dbc);
}
if (SQL_NULL_HANDLE != dbc)
{
SQLFreeHandle(SQL_HANDLE_DBC, dbc);
}
if (SQL_NULL_HANDLE != env)
{
SQLFreeHandle(SQL_HANDLE_ENV, env);
}
}
/// #brief Check if an ODBC call failed and display the SQLState, native error code,
/// and error message on stderr. Abort execution if the return code was SQL_ERROR.
///
/// #param rc The return code.
/// #param stmt The statement handle.
/// #param conn The connection handle.
/// #param env The environment handle.
SQLRETURN CheckDiagnosticsFatal(SQLRETURN rc, SQLHANDLE stmt, SQLHANDLE conn = SQL_NULL_HANDLE, SQLHANDLE env = SQL_NULL_HANDLE)
{
CheckDiagnostics(rc, stmt, conn, env);
if (SQL_ERROR == rc)
{
CleanupHandles(stmt, conn, env);
exit(-1);
}
return rc;
}
int main(int argc, char* argv[])
{
// Connect to the DSN and allocate a statement handle
SQLHANDLE env = SQL_NULL_HANDLE;
SQLHANDLE dbc = SQL_NULL_HANDLE;
SQLHANDLE stmt = SQL_NULL_HANDLE;
CheckDiagnosticsFatal(SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env), stmt, dbc, env);
CheckDiagnosticsFatal(SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0), stmt, dbc, env);
CheckDiagnosticsFatal(SQLSetEnvAttr(env, SQL_ATTR_CONNECTION_POOLING, (SQLPOINTER)SQL_CP_ONE_PER_HENV, 0), stmt, dbc, env);
SQLCHAR outConnBuffer[1024];
SQLSMALLINT outLen;
CheckDiagnosticsFatal(SQLAllocHandle(SQL_HANDLE_DBC, env, &dbc), stmt, dbc, env);
CheckDiagnosticsFatal(SQLDriverConnect(
dbc,
NULL,
(SQLCHAR*)"DSN=SqlServer",
SQL_NTS,
outConnBuffer,
sizeof(outConnBuffer),
&outLen,
SQL_DRIVER_NOPROMPT),
stmt,
dbc,
env);
CheckDiagnosticsFatal(SQLAllocHandle(SQL_HANDLE_STMT, dbc, &stmt), stmt, dbc, env);
CheckDiagnosticsFatal(SQLSetStmtAttr(stmt, SQL_ATTR_PARAMSET_SIZE, (void*)(SQLULEN)2, 0), stmt);
SQLCHAR inputParamBuffer[2][10] = { "1", "2" };
SQLLEN inputParamLenInd[2] = { SQL_NTS, SQL_NTS };
SQLINTEGER outputParamBuffer[2] = { 0, 0 };
SQLLEN outputParamLenInd[2];
CheckDiagnosticsFatal(SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 10, 0, &inputParamBuffer, sizeof(inputParamBuffer[0]), inputParamLenInd), stmt);
CheckDiagnosticsFatal(SQLBindParameter(stmt, 2, SQL_PARAM_INPUT_OUTPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &outputParamBuffer, sizeof(outputParamBuffer[0]), outputParamLenInd), stmt);
// const SQLSMALLINT unnamedValue(SQL_UNNAMED);
//
// SQLHDESC ipd;
// CheckDiagnosticsFatal(SQLGetStmtAttr(stmt, SQL_ATTR_IMP_PARAM_DESC, &ipd, 0, 0), stmt);
// CheckDiagnosticsFatal(SQLSetDescField(ipd, 1, SQL_DESC_NAME, "#inparam", SQL_NTS), SQL_HANDLE_DESC, ipd);
// CheckDiagnosticsFatal(SQLSetDescField(ipd, 1, SQL_DESC_UNNAMED, (SQLPOINTER)unnamedValue, SQL_IS_SMALLINT), SQL_HANDLE_DESC, ipd);
// CheckDiagnosticsFatal(SQLSetDescField(ipd, 2, SQL_DESC_NAME, "#outparam", SQL_NTS), SQL_HANDLE_DESC, ipd);
// CheckDiagnosticsFatal(SQLSetDescField(ipd, 2, SQL_DESC_UNNAMED, (SQLPOINTER)unnamedValue, SQL_IS_SMALLINT), SQL_HANDLE_DESC, ipd);
SQLSMALLINT paramStatus[2];
CheckDiagnosticsFatal(SQLSetStmtAttr(stmt, SQL_ATTR_PARAM_STATUS_PTR, paramStatus, 0), stmt);
// Prepare the query, and then execute/fetch in a loop.
SQLCHAR* const query((SQLCHAR*)"{CALL TestV1.dbo.TestLateOutput4(?)}");
cout << "Preparing query: " << query << endl;
if (SQL_SUCCEEDED(CheckDiagnostics(SQLPrepare(stmt, query, SQL_NTS), stmt)))
{
cout << "Executing query..." << endl;
CheckDiagnosticsFatal(SQLExecute(stmt), stmt);
//while (SQL_NO_DATA != CheckDiagnosticsFatal(SQLFetch(stmt), stmt));
CheckDiagnosticsFatal(SQLMoreResults(stmt), stmt);
//while (SQL_NO_DATA != CheckDiagnosticsFatal(SQLFetch(stmt), stmt));
CheckDiagnosticsFatal(SQLMoreResults(stmt), stmt);
CleanupHandles(stmt, dbc, env);
return EXIT_SUCCESS;
}
else
{
cout << "Failed to prepare query." << endl;
CheckDiagnosticsFatal(SQL_ERROR, stmt);
abort();
}
}
The stored procedure that it's invoking, TestV1.dbo.TestLateOutput4 has been created by executing the following:
create procedure TestLateOutput4 #inparam varchar(10), #outparam int output
as
select 1
SET #outparam = #inparam
return
The problem I'm getting is that SQLExecute is returning SQL_SUCCESS_WITH_INFO (setting all of the parameter set statuses to SQL_PARAM_ERROR), with the message [42000] (201): [Microsoft][SQL Server Native Client 11.0][SQL Server]Procedure or function 'TestLateOutput4' expects parameter '#outparam', which was not supplied.
As far as I can tell, I did supply both parameters needed (by binding parameters 1 & 2). I also tried setting the parameter names (see the commented out lines setting SQL_DESC_NAME), but that didn't change anything. I also tried explicitly setting SQL_DESC_UNNAMED to SQL_UNNAMED (even though that should be the default), but nothing.
The other thing I tried was to bind the second parameter as input/output (because I know that SQL Server treats 'output' parameters that way), originally I was just binding it as output.
What am I doing wrong?

Visual Studio C++ connect to DB2

I'm trying to write a C ++ program using a database - IBM DB2 Express-C. Also, I use Visual Studio 2015. Via IBM OLE DB Provider for DB2 my Visual Studio successfully connected to DB2. I can see tables and all OK:
Then I created new C++ program, I successfully connect several header files, some from them I found in my DB2 directory, for example sqlcli1.h
#pragma once
#include "targetver.h"
#include <stdio.h>
#include <tchar.h>
#include <stdlib.h>
#include <string.h>
#include <sqlcli1.h>
#include <windows.h>
The following is the code of the main program. In which I am trying to connect to the database to which successfully was connected the Visual Studio.
#include "stdafx.h"
int main()
{
SQLHENV henv;
SQLHDBC hdbc;
SQLHSTMT hstmt;
SQLRETURN retcode;
SQLCHAR * OutConnStr = (SQLCHAR *)malloc(255);
SQLSMALLINT * OutConnStrLen = (SQLSMALLINT *)malloc(255);
// Allocate environment handle
retcode = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
// Set the ODBC version environment attribute
if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
// Allocate connection handle
if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
retcode = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
// Connect to data source
retcode = SQLConnect(hdbc, (SQLWCHAR*) "DATABASE=SAMPLE;HOSTNAME=localhost", SQL_NTS, (SQLWCHAR*) "db2admin", SQL_NTS, (SQLWCHAR*) "password", SQL_NTS);
if (retcode != SQL_SUCCESS) {
printf(">--- Error while connecting to database:");
printf("SQLConnect: %d\n", retcode);
}
// Allocate statement handle
if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
printf("\nAllocate Connection handle successfully.");
// Process data
if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
}
SQLDisconnect(hdbc);
}
SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
}
SQLFreeHandle(SQL_HANDLE_ENV, henv);
}
system("pause");
return 0;
}
There are no errors at compile time, but can not connect to the database.When program execution comes to the block.
// Connect to data source
retcode = SQLConnect(hdbc, (SQLWCHAR*) "DATABASE=SAMPLE;HOSTNAME=localhost", SQL_NTS, (SQLWCHAR*) "db2admin", SQL_NTS, (SQLWCHAR*) "password", SQL_NTS);
if (retcode != SQL_SUCCESS) {
printf(">--- Error while connecting to database:");
printf("SQLConnect: %d\n", retcode);
}
I see >--- Error while connecting to database:SQLConnect:-2
What does it means? what I am doing is wrong? I also tried it:
retcode = SQLConnect(hdbc, (SQLWCHAR*) "SAMPLE", SQL_NTS, (SQLWCHAR*) "db2admin", SQL_NTS, (SQLWCHAR*) "password", SQL_NTS);
But it also does not work. May be the second argument should look like in some other way?
Changed
I tried use this function SQLGetDiagRec I created some variables
SQLWCHAR SqlState[6], SQLStmt[100], Msg[SQL_MAX_MESSAGE_LENGTH];
SQLINTEGER NativeError;
SQLSMALLINT i, MsgLen;
SQLRETURN rc1, rc2;
I also change my connect block
// Connect to data source
retcode = SQLConnect(hdbc, (SQLWCHAR*) "SAMPLE", SQL_NTS, (SQLWCHAR*) "db2admin", SQL_NTS, (SQLWCHAR*) "password", SQL_NTS);
if (retcode != SQL_SUCCESS) {
printf(">--- Error while connecting to database:");
printf("SQLConnect: %d\n", retcode);
if ((retcode == SQL_SUCCESS_WITH_INFO) || (retcode == SQL_ERROR)) {
// Get the status records.
i = 1;
while ((rc2 = SQLGetDiagRec(SQL_HANDLE_STMT, hdbc, i, SqlState, &NativeError,
Msg, sizeof(Msg), &MsgLen)) != SQL_NO_DATA) {
cout << SqlState;
cout << i;
cout << NativeError;
cout << Msg;
i++;
}
}
}
But nothing has changed. Does anyone have any idea?
If a call to any CLI function doesn't return SQL_SUCCESS, then your error handling should use SQLGetDiagRec() to get the details of the warning or error.
Documentation: SQLGetDiagRec

sqlbindparameter not used for all parameters - mysql & C++

I have the following table defined in MySQL
INSERT INTO routemaster_log (`EntryDateTime`,`Entry`,`Message`,`EntryType`) VALUES (?,?,?,?);
CREATE TABLE `routemaster_log` (
`EntryDateTime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`Entry` varchar(127) NOT NULL,
`Message` int(11) NOT NULL DEFAULT '0',
`EntryType` varchar(20) NOT NULL DEFAULT 'INFORMATION'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
And the following C++ code to write a record to it
void LogEntries::Insert(string_t LogText, string_t LogType, int MessageNumber)
{
SQLHANDLE hEnv;
SQLRETURN retCode;
SQLCHAR* query;
SQLINTEGER textLength = SQL_NTS;
// now set the text to be written to char *
char* logText = from_string_t(LogText);
char* logType = from_string_t(LogType);
std::string SQLQuery("INSERT INTO routemaster_log (`Entry`,`Message`,`EntryType`) VALUES (?,?,?)");
query = (SQLCHAR *)SQLQuery.c_str();
.
.
.
// log Text
retCode = SQLBindParameter(hStmnt, 1, SQL_PARAM_INPUT,
SQL_C_CHAR, SQL_CHAR, 0, 0,
logText, sizeof(logText), &textLength);
if (retCode != SQL_SUCCESS && retCode != SQL_SUCCESS_WITH_INFO) {
std::cout << "SQL Insert failed binding parameters (LogText)" << std::endl;
}
// Message Number
retCode = SQLBindParameter(hStmnt, 1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, &MessageNumber, 0, NULL);
if (retCode != SQL_SUCCESS && retCode != SQL_SUCCESS_WITH_INFO) {
std::cout << "SQL Insert failed binding parameters (Message Number)" << std::endl;
}
// Log Type
retCode = SQLBindParameter(hStmnt, 1, SQL_PARAM_INPUT,
SQL_C_CHAR, SQL_CHAR, 0, 0,
logType, sizeof(logType), &textLength);
if (retCode != SQL_SUCCESS && retCode != SQL_SUCCESS_WITH_INFO) {
std::cout << "SQL Insert failed binding parameters (LogType)" << std::endl;
}
retCode = SQLExecDirectA(hStmnt, query, SQL_NTS);
if (retCode != SQL_SUCCESS && retCode != SQL_SUCCESS_WITH_INFO)
{
SQLWCHAR Msg[255], SqlState[6];
SQLINTEGER NativeError;
SQLRETURN ret2;
SQLSMALLINT i, MsgLen;
i = 1;
while ((ret2 = SQLGetDiagRec(SQL_HANDLE_STMT, hStmnt, i, SqlState, &NativeError,
Msg, sizeof(Msg), &MsgLen)) != SQL_NO_DATA) {
std::cout << Msg << std::endl;
i++;
}
std::cout << "SQL Insert failed writing data to database." << std::endl;
}
}
everything is fine until the SQLExecDirectA this fails with the SQLBindParameter not used for all parameters error (retrieved in the following while loop).
I would expect this if the number of Parameters being bound was different to the number of '?' in the query but they are the same.
Anyone got a suggestion as to what is wrong?
There is actually two issues with the code in the question.
a) The Calls to SQLBindParameter go before the call to SQLPrepareA
b) In the Calls to SQLBindParameter the second Parameter gives the position of the parameter
The revised code - which works is as follows;
// log Text
retCode = SQLBindParameter(hStmnt, 1, SQL_PARAM_INPUT,
SQL_C_CHAR, SQL_CHAR, 0, 0,
logText, sizeof(logText), &textLength);
if (retCode != SQL_SUCCESS && retCode != SQL_SUCCESS_WITH_INFO) {
std::cout << "SQL Insert failed binding parameters (LogText)" << std::endl;
}
// Message Number
retCode = SQLBindParameter(hStmnt, 2, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, &MsgNumber, 0, NULL);
if (retCode != SQL_SUCCESS && retCode != SQL_SUCCESS_WITH_INFO) {
std::cout << "SQL Insert failed binding parameters (Message Number)" << std::endl;
}
// Log Type
retCode = SQLBindParameter(hStmnt, 3, SQL_PARAM_INPUT,
SQL_C_CHAR, SQL_CHAR, 0, 0,
logType, sizeof(logType), &textLength);
if (retCode != SQL_SUCCESS && retCode != SQL_SUCCESS_WITH_INFO) {
std::cout << "SQL Insert failed binding parameters (LogType)" << std::endl;
}
retCode = SQLPrepareA(hStmnt, query, SQL_NTS);
if (retCode != SQL_SUCCESS && retCode != SQL_SUCCESS_WITH_INFO) {
std::cout << "SQL Insert failed preparing statement" << std::endl;
}
retCode = SQLExecDirectA(hStmnt, query, SQL_NTS);

SQL Statement in C using a variable in query

I am building a C++ program that accesss an SQL database. I am able to connect and return values for the following statment:
retcode = SQLExecDirect(hstmt, (SQLWCHAR*)L"SELECT Beta FROM Equity WHERE Ticker = 'AAPL'", SQL_NTS);
However, I would like to be able to replace 'AAPL' with the string 'ticker', which can be defined using cin>>.
Is there any way to do this?
Thanks for the help, the entire code is below:
#include "stdafx.h"
#include <iostream>
#include <windows.h>
#include <sql.h>
#include <sqltypes.h>
#include <sqlext.h>
#include <string>
using namespace std;
int main(){
SQLHENV henv;
SQLHDBC hdbc;
SQLHSTMT hstmt;
SQLRETURN retcode;
SQLWCHAR OutConnStr[255];
SQLSMALLINT OutConnStrLen;
// Allocate environment handle
retcode = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
// Set the ODBC version environment attribute
if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
retcode = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0);
// Allocate connection handle
if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
retcode = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
// Set login timeout to 5 seconds
if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
SQLSetConnectAttr(hdbc, SQL_LOGIN_TIMEOUT, (SQLPOINTER)5, 0);
// Connect to data source
retcode = SQLDriverConnect(
hdbc,
0,
(SQLWCHAR*)L"DSN=Phoenix;SERVER=XXX;UID=XXX;PWD=XXX;DRIVER=MySQL Server;",
_countof(L"DSN=Phoenix;SERVER=XXX;UID=XXX;PWD=XXX;DRIVER=MySQL Server;"),
OutConnStr,
255,
&OutConnStrLen,
SQL_DRIVER_COMPLETE );
// Allocate statement handle
if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
// Process data
string ticker;
cout << " Enter Ticker: "; //Prompt Ticker
cin >> ticker; //Stores Ticker
//retcode = SQLExecDirect(hstmt, (SQLWCHAR*)L"SELECT Beta FROM Equity WHERE Ticker = 'AAPL'", SQL_NTS);
retcode = SQLExecDirect(hstmt, (SQLWCHAR*)L"SELECT Beta FROM Equity WHERE Ticker = ticker", SQL_NTS);
if (retcode == SQL_SUCCESS) {
SQLINTEGER cbTestStr, iCount = 1;
SQLFLOAT dTestFloat;
SQLCHAR beta[200];
while (TRUE) {
retcode = SQLFetch(hstmt);
if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO) {
cout<<"An error occurred";
}
if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO){
SQLGetData(hstmt, 1, SQL_C_CHAR, beta, 200, &cbTestStr);
/* Print the row of data */
cout<<"Beta for "<< ticker << " = " << beta <<endl;
double bi;
cin >> bi;
} else {
break;
}
}
}else{
cout<<"Query execution error."<<endl;
}
SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
SQLDisconnect(hdbc);
}else{
cout<<"Connection error"<<endl;
}
SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
}
}
SQLFreeHandle(SQL_HANDLE_ENV, henv);
}
system("pause");
return 0;
}
Have you tried something like after your cin >> ticker; line.
string sql;
sql = "SELECT Beta FROM Equity WHERE Ticker='";
strcat (sql,ticker);
strcat (sql, "'");
Then use the sql string in SQLExecDirect line
Replace the statement text with sql.
Sorry my c syntax is rusty. The point is to retrieve ticker value and concatenate that to the sql string while surrounding the input (ticker) with single quotes. Then use this as your execution statement.
Hope it works.

What is the barebones C++ code necessary to put a jpeg into MySQL table?

I have created a MySQL table where one of the columns stores a BLOB type. (The Internet told me BLOB is the correct data type for images.)
I am pretty much a beginner with both C++ and MySQL. What I would like to do is to write a small program with a main() that puts a jpeg into that table. For the sake of this exercise, I do not want to store a reference to a directory that contains an image.
Am I wrong to think that it is as simple as filling out the part in BLOCK 2 below?
#include <iostream>
#include <string>
#include <mysql.h>
using namespace std;
int main(int argc, char **argv)
{
//BLOCK 1: INIT
MYSQL *connection, mysql;
MYSQL_RES *result;
MYSQL_ROW row;
int query_state;
mysql_init(&mysql);
connection = mysql_real_connect(&mysql, "localhost", "root", "secret", "beginner_db",0,0,0);
//BLOCK 2: SEND QUERY
/* do something to insert image to table */
//BLOCK 3: DISPLAY QUERY RESULTS
result = mysql_store_result(connection);
/* do something with result */
//BLOCK 4: FREE
mysql_free_result(result);
mysql_close(connection);
return 0;
}
For this scenario, a good solution would be to use the mysql_stmt_send_long_data() function.
There is an example on the MySQL Manual page that I linked to, but here is a more relevant example of sending file contents:
#ifdef _WIN32
#include <windows.h>
#endif
#include <cstddef>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <boost/scope_exit.hpp>
#include <mysql.h>
#define ARR_LEN(arr_id) ((sizeof (arr_id))/(sizeof (arr_id)[0]))
int main()
{
using namespace std;
MYSQL *pconn = mysql_init(NULL);
BOOST_SCOPE_EXIT( (pconn) ) {
mysql_close(pconn);
} BOOST_SCOPE_EXIT_END
const char *db_name = "test";
if (!mysql_real_connect(pconn, "localhost", "test", "********", db_name, 0, NULL, CLIENT_COMPRESS)) {
cerr << "Error: mysql_real_connect() failed to connect to `" << db_name << "`." << endl;
return EXIT_FAILURE;
}
MYSQL_STMT *pinsert_into_images_stmt = mysql_stmt_init(pconn);
BOOST_SCOPE_EXIT( (pinsert_into_images_stmt) ) {
mysql_stmt_close(pinsert_into_images_stmt);
} BOOST_SCOPE_EXIT_END
const char sql1[] = "INSERT INTO images(data) VALUES (?)";
if (mysql_stmt_prepare(pinsert_into_images_stmt, sql1, strlen(sql1)) != 0) {
cerr << "Error: mysql_stmt_prepare() failed to prepare `" << sql1 << "`." << endl;
return EXIT_FAILURE;
}
MYSQL_BIND bind_structs[] = {
{ 0 } // One for each ?-placeholder
};
unsigned long length0;
bind_structs[0].length = &length0;
bind_structs[0].buffer_type = MYSQL_TYPE_BLOB;
bind_structs[0].is_null_value = 0;
if (mysql_stmt_bind_param(pinsert_into_images_stmt, bind_structs) != 0) {
cerr << "Error: mysql_stmt_bind_param() failed." << endl;
return EXIT_FAILURE;
}
const char *file_name = "image.jpg";
FILE *fp = fopen(file_name, "rb");
BOOST_SCOPE_EXIT( (fp) ) {
fclose(fp);
} BOOST_SCOPE_EXIT_END
// Use mysql_stmt_send_long_data() to send the file data in chunks.
char buf[10*1024];
while (!ferror(fp) && !feof(fp)) {
size_t res = fread(buf, 1, ARR_LEN(buf), fp);
if (mysql_stmt_send_long_data(pinsert_into_images_stmt, 0, buf, res) != 0) {
cerr << "Error: mysql_stmt_send_long_data() failed." << endl;
return EXIT_FAILURE;
}
}
if (!feof(fp)) {
cerr << "Error: Failed to read `" << file_name << "` in its entirety." << endl;
return EXIT_FAILURE;
}
if (mysql_stmt_execute(pinsert_into_images_stmt) != 0) {
cerr << "Error: mysql_stmt_execute() failed." << endl;
return EXIT_FAILURE;
}
cout << "Inserted record #" << mysql_insert_id(pconn) << endl;
return EXIT_SUCCESS;
}
I am using the following definition of table `images`:
CREATE TABLE images (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
data MEDIUMBLOB NOT NULL,
PRIMARY KEY (id)
);
Upon running this program, it successfully sent the 38,339-byte JPEG image.jpg to the server and outputted "Inserted record #1".
You can verify that the correct number of bytes were sent:
mysql> SELECT octet_length(data) FROM images WHERE id=1;
+--------------------+
| octet_length(data) |
+--------------------+
| 38339 |
+--------------------+
1 row in set (0.00 sec)
I found this solution that worked... for images under 10kb.
//http://zetcode.com/tutorials/mysqlcapitutorial/
//g++ -o output source.cpp $(mysql_config --cflags) $(mysql_config --libs)
#include <stdio.h>
#include <iostream>
#include <mysql.h>
int main(int argc, char **argv)
{
MYSQL *conn;
int len, size;
char data[1000*1024];
char chunk[2*1000*1024+1];
char query[1024*5000];
FILE *fp;
conn = mysql_init(NULL);
mysql_real_connect(conn, "localhost", "root", "secret", "beginner_db", 0, NULL, 0);
fp = fopen("filename.png", "rb");
size = fread(data, 1, 1024*1000, fp);
mysql_real_escape_string(conn, chunk, data, size);
char *stat = "INSERT INTO pic_tbl(name, pic) VALUES('cexample', '%s')";
len = snprintf(query, sizeof(stat)+sizeof(chunk) , stat, chunk);
mysql_real_query(conn, query, len);
fclose(fp);
mysql_close(conn);
}
Something like this:
CString SaveFile( CMemoryFile& File )
{
*pFileKey = -1;
SQLRETURN retcode;
SQLHSTMT hstmt;
CLoggEntryList LoggEntryList( this ); // logg entries cannot be made while busy inside the hstmt, use this class to add them later
SQLINTEGER cbDocumentBlock; // For binding the main image
long lDocumentBufferSize = 0;
unsigned char* pDocumentBuffer; // Will be set to point to the buffer that should be written into the document blob field
unsigned char pDummyChar[] = {'0'}; // Dummy buffer to write in the image/thumbnail blob fields when using external storage
lDocumentBufferSize = File.m_lBufferSize;
pDocumentBuffer = File.m_pFileBuffer;
// Allocate statement handle
retcode = SQLAllocHandle(SQL_HANDLE_STMT, m_Database.m_hdbc, &hstmt);
if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
{ // Create a result set
CString szSQL;
szSQL = ( "INSERT INTO ObjectTable (ObjectData) VALUES ( ? )");
retcode = SQLPrepare(hstmt, (SQLCHAR*)szSQL.GetBuffer(), SQL_NTS);
if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
{
// Bind the parameters. For parameter 1, pass the parameter number in ParameterValuePtr instead of a buffer address.
SQLINTEGER cbNULL = 0;
SQLINTEGER cbTEXT = SQL_NTS;
int nColumn = 1;
// Bind ObjectData
cbDocumentBlock = SQL_LEN_DATA_AT_EXEC(0); //SQL_LEN_DATA_AT_EXEC(lImageBufferSize);
retcode = SQLBindParameter(hstmt, nColumn++, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY,
0, 0, (SQLPOINTER) DOCUMENT, 0, &cbDocumentBlock);
if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
{
// Set values so data for parameter 1 will be passed at execution. Note that the length parameter in
// the macro SQL_LEN_DATA_AT_EXEC is 0. This assumes that the driver returns "N" for the
// SQL_NEED_LONG_DATA_LEN information type in SQLGetInfo.
retcode = SQLExecute(hstmt);
const long nMaxChunkSize = 400000;
// For data-at-execution parameters, call SQLParamData to get the parameter number set by SQLBindParameter.
// Call InitUserData. Call GetUserData and SQLPutData repeatedly to get and put all data for the parameter.
// Call SQLParamData to finish processing this parameter.
while (retcode == SQL_NEED_DATA)
{
SQLPOINTER pToken;
retcode = SQLParamData(hstmt, &pToken);
switch( (int)pToken )
{
case DOCUMENT:
{
if (retcode == SQL_NEED_DATA)
{
for( int nPos = 0; nPos < lDocumentBufferSize; nPos += nMaxChunkSize )
{
int nBufferSize = min( lDocumentBufferSize - nPos, nMaxChunkSize );
SQLRETURN retcode2 = SQLPutData(hstmt, pDocumentBuffer+nPos, nBufferSize );
if (retcode2 != SQL_SUCCESS && retcode2 != SQL_SUCCESS_WITH_INFO)
{
SQLCHAR Sqlstate[6];
SQLINTEGER NativeError;
SQLCHAR MessageText[201];
SQLSMALLINT TextLengthPtr;
retcode2 = SQLGetDiagRec( SQL_HANDLE_STMT, hstmt, 1, Sqlstate, &NativeError, MessageText, 200, &TextLengthPtr );
if (retcode2 == SQL_SUCCESS || retcode2 == SQL_SUCCESS_WITH_INFO)
{
MessageText[TextLengthPtr] = 0;
Sqlstate[5] = 0;
CString szSQLState( Sqlstate );
CString szMessageText( MessageText );
CString szMessage;
szMessage.Format("Error in SaveFile(). SQL State %s. Native %ld. Source: %s", szSQLState, NativeError, szMessageText );
SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
return szMessage;
}
}
}
}
break;
}
default:
{
CString szMessage;
szMessage.Format("Error in SaveBuffer(). Unknown parameter buffer.");
SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
return szMessage;
}
break;
}
}
}
SQLRETURN retcode3;
retcode3 = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
ASSERT(retcode3 == SQL_SUCCESS);
}
}
}
This code is not tested or even compiled, but it should point you in the right direction.