Junk varchar entries in MSSQL database using ODBC - c++

I'm trying to insert a string-variable into a varchar(100)-field, but if the string is longer than 15 elements only junk is inserted (e.g. "0‰?").
First my setup:
Development: Win7 (64bit) / VS2013 / C++11 / 64bit Application
Database: Win8 (64bit) / Microsoft SQL Server Express 2014 (64bit)
Driver: SQL Server Native Client 11.0
Second the binding of the paramter:
std::string mMessageText;
SQLHANDLE mSqlStatementHandle;
std::string mExecString;
bool initConnection()
{
mExecString = "{? = CALL dbo.InsertTestProcedure(?, ?, ?, ?, ?)}";
(...)
// bind parameters
SQLBindParameter(mSqlStatementHandle, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_LONGVARCHAR, 100, 0, (SQLPOINTER)mMessageText.c_str(), mMessageText.length(), NULL);
(...)
// prepare handle with execution string
if (SQL_SUCCESS != SQLPrepare(mSqlStatementHandle, (SQLCHAR*)mExecString.c_str(), (SQLSMALLINT)mExecString.length()))
{
throwError(SQL_HANDLE_STMT, mSqlStatementHandle);
return false;
}
}
Third the query execution:
bool fillDb()
{
(...)
mMessageText = "This text is longer than 15";
// execute SQL statement
if (SQL_SUCCESS != SQLExecute(mSqlStatementHandle))
{
throwError(SQL_HANDLE_STMT, mSqlStatementHandle);
return false;
}
(...)
}
Header of the procedure:
ALTER PROCEDURE [dbo].[InsertTestProcedure]
#MessageComp VARCHAR(20),
#MessageType VARCHAR(20),
#MessageAction VARCHAR(20),
#MessageText VARCHAR(100),
#MessageName VARCHAR(20)
AS
If the string is shorter than 15 elements, it works fine. And calling the procedure from SQL Management Studio with value lengths > 15 works fine too.

One thing that comes to my mind is the procedure you are calling. Maybe you have table with varchar(100) column, but the procedure has only varchar(15) parameter. Could you post header of that procedure?

Thanks to #erg, here is the solution that worked for me:
char mMessageText[100];
bool initConnection()
{
(...)
// bind parameters
SQLBindParameter(mSqlStatementHandle, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_LONGVARCHAR, 100, 0, (SQLPOINTER)mMessageText, 100, NULL);
(...)
}
bool fillDb()
{
(...)
std::string lMessageText = "This text is longer than 15";
strcpy(mMessageText, lMessageText.c_str());
mMessageText[sizeof(mMessageText) - 1] = 0;
(...)
}

Related

How to retrieve multiple named output parameters in MS ODBC stored procedure call

I am trying to call a stored procedure which has two output arguments, and get the output programmatically in a C++ program.
Code snippet for executing a query:
char const* const query =
"DECLARE #iMM int, #sMM varchar(100); "
"EXEC sp_myproc #sFoo = 'abcdef', #sBar = 'ghij', #iQux = 1,"
"#iOutInt = #iMM output, #sOutString = #sMM output;"
"PRINT #iMM; PRINT #sMM;";
auto result = SQLExecDirectA(handle, (SQLCHAR*)query, SQL_NTS);
This returns SQL_SUCCESS_WITH_INFO ., but SQLGetDiagRecA does not retrieve the printed output . SQLMoreResults returns 1, SQLParamData returns -1.
The same query does execute successfully in SSMS but I can't figure out how to get the output parameter values programmatically. There are various code examples using SQLBindParameter to retrieve multiple output parameters but none of them use named parameters.
I managed to get the data this way (maybe not optimal but at least it works):
char const* const query =
"EXEC sp_myproc #sFoo = 'abcdef', #sBar = 'ghij', #iQux = 1,"
"#iOutInt = ? output, #sOutString = ? output;"
short out_int;
SQLBindParameter(handle, 1, SQL_PARAM_OUTPUT, SQL_C_SSHORT, SQL_SMALLINT,
0, 0, &out_int, 1, NULL);
char outbuf[61];
SQLBindParameter(handle, 2, SQL_PARAM_OUTPUT, SQL_C_CHAR, SQL_VARCHAR,
60, 0, outbuf, 60, NULL);
auto result = SQLExecDirectA(handle, (SQLCHAR*)query, SQL_NTS);
if ( result == SQL_SUCCESS_WITH_INFO )
{
std::cout << out_int << ", '" << outbuf << "'\n";
}
Using SQL_CHAR instead of SQL_VARCHAR meant the result string was always right-padded with spaces .

OLEDB reads data's type wrongly from xls file in x64 windows?

my os is windows server 2012 r2 (x64) and installed with excel 2013 (x64), in my cpp code i'm reading an .xls file as:
#import "C:/Program Files/Common Files/System/ado/msado15.dll" rename("EOF", "adoEOF") rename("BOF", "adoBOF")
String ConnStr = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\\my_data.xls;Extended Properties=\"Excel 8.0;HDR=YES;IMEX=1\"";
_bstr_t connStr = _bstr_t(ConnStr.c_str());
HRESULT hr = ::CoInitialize(NULL);
ADODB::_ConnectionPtr pCon;
pCon.CreateInstance(__uuidof(ADODB::Connection));
pCon->Open(connStr, "", "", NULL);
String SqlStr = "SELECT * FROM [data_sheet$]";
ADODB::_RecordsetPtr pRec;
pRec.CreateInstance(__uuidof(Recordset)));
pRec->Open(_SqlStr.c_str(), connStr, adOpenStatic, adLockOptimistic, adCmdText);
So far so good, but then, when I read the cell's content by
_variant_t v = pRec->Fields->GetItem(0)->Value;
, an error happened: the field read in is actually a numeric data, but the data type v.vt shows VT_BSTR instead of VT_R8, which are defined in wtypes.h as:
enum VARENUM
{
VT_EMPTY = 0,
VT_NULL = 1,
...
VT_R8 = 5,
...
VT_BSTR = 8,
, so a Double value is read in as a String -- v.bstrVal shows the double value in string, while v.dblVal shows something like 2.047048723726e-312#DEN.
Anyone met this issue before, how could the data type be read in correctly?

C++ SQLite3 prepared delete statement not working

I have a C++ application which loops through a SQLite3 database. Each row contains an ID which is checked against a vector. If the ID in the DB is not present in the vector, it should be deleted with a prepared statement. I use the following code, however the ID's won't get deleted. I Neither can get an error message from the sqlite3_step(stmt2) function.
//SETTINGS["Reference"] CONTAINS THE REFERENCE FOR THE ID's (IT's 1 FOR UNDERNEATH EXAMPLE)
vector<int> IDs; //THIS VECTOR CONTAINS THE ID's IN MY APPLICATION
rc = sqlite3_prepare_v2(db, "SELECT ID FROM Files WHERE Reference=?", -1, &stmt, 0);
sqlite3_bind_text(stmt, 1, Settings["Reference"].c_str(), Settings["Reference"].length(), 0);
CheckDBError(rc);
rc = sqlite3_step(stmt);
sqlite3_stmt* stmt2;
int rc2 = sqlite3_prepare_v2(db, "DELETE FROM Files WHERE ID=? AND Reference=?", -1, &stmt2, 0);
CheckDBError(rc2);
while(rc == SQLITE_ROW) {
string IDToCheck = NumberToString(sqlite3_column_int64(stmt, 0));
if (std::find(IDs.begin(), IDs.end(), IDToCheck) == IDs.end()) { //VERIFY AGAINST VECTOR WORKS AS EXPECTED
//I GET HERE WITH ALL MY ID's I HAVE CHECKED THAT ALREADY :)
sqlite3_bind_text(stmt2, 1, IDToCheck.c_str(), IDToCheck.length(), 0);
sqlite3_bind_text(stmt2, 2, Settings["Reference"].c_str(), Settings["Reference"].length(), 0);
rc2 = sqlite3_step(stmt2);
//CAN'T GET ANY ERROR MESSAGE (SO QUERY IS FINE, WHICH SEEMS LIKE IT?)
}
rc = sqlite3_step(stmt);
}
sqlite3_finalize(stmt);
sqlite3_finalize(stmt2);
You must not call the finalize function before the while block, because that way you finalize your statement before using it. As per SQLite documentation (emphasis mine):
It is a grievous error for the application to try to use a prepared
statement after it has been finalized. Any use of a prepared statement
after it has been finalized can result in undefined and undesirable
behavior such as segfaults and heap corruption.

Saving text in Sqlite3, c++

here is my second post in the community, excuse me if I'm forget to add something, just let me know:
I am trying to do a program in c++ able to save text (i want to save code) in a database using sqlite3. Currently I've made a wxWidget program that call some functions from a DLL and this ones interactuate with the database.
The database that I want to make is really simple, it has 3 columns in one table (id,name, ref). My problem comes when I want to save big amount of text that also contains simblos that can conflict with the sql queries (I would like to save files inside the database, for example in the "ref" column ).
I'm using mostly the sqlite3_exec function, because the functions sqlite3_prepare_v2, sqlite_bind, sqlite3_step crash me the DLL where I'm working.
My doubt: Can I directly save any text as big as I want, without taking care about if it has simbols or not? and how can I do it?.
More info: I am working in c++ with code:block(13.12) making a DLL of sqlite3 functions and using MinGW toolchain. (windows 7).
This is an example of an insert function that I'm using:
int DLL_EXPORT add_item(sqlite3* db, string tbname,string col,string item)
{
char* db_err = 0;
if (tbname==std::string()||col==std::string()||item==std::string())
throw std::invalid_argument( "stoi: invalid argument table name");
char buf[200];
sprintf(buf,"insert into %s (%s) values ('%s');", tbname.c_str(), col.c_str(),item.c_str());
int n = sqlite3_exec(db, buf, NULL, 0, &db_err);
dsperr(&db_err);
if( n != SQLITE_OK )
{
//throw something
}
return 0;
}
Thank you in advance.
Thank to CL. for the up commentary
// Add one text to a table
// The column must be specify
//
int DLL_EXPORT add_text(sqlite3* db, string tbname,string col,string id,string item)
{
char* db_err = 0;
if (tbname==std::string()||col==std::string()||item==std::string())
throw std::invalid_argument( "stoi: invalid argument table name");
char *zSQL = sqlite3_mprintf("UPDATE %q SET %q=(%Q) WHERE id=%q", tbname.c_str(),col.c_str() ,item.c_str(),id.c_str());
int n = sqlite3_exec(db, zSQL, NULL, 0, &db_err);
dsperr(&db_err);
sqlite3_free(zSQL);
if( n != SQLITE_OK )
{
// throw something
}
return 0;
}

Issues of getting indexes information - SQL Server

Trying to get table indexes information in SQL Server 2012 I identified a strange situation for one scenarion.
I have a table that contains two indexes referenced to some fields: Field_1 and Field_3 mapped over int, null columns (the number means the existing field order into the table designed few years ago...).
I am trying to get information about these indexes like this:
nRetCode = ::SQLStatistics(hstmtAux, NULL, 0, NULL, 0, (TCHAR*)(LPCTSTR)strTempTable, SQL_NTS, SQL_INDEX_ALL, SQL_ENSURE);
if (nRetCode == SQL_SUCCESS || nRetCode == SQL_SUCCESS_WITH_INFO)
{
nRetCode = ::SQLBindCol(hstmtAux, 4, SQL_C_SHORT, &swNonUnique, sizeof(SWORD), &cbNonUnique);
nRetCode = ::SQLBindCol(hstmtAux, 5, SQL_CHAR, szIdxQualif, sizeof(CHAR) * 130, &cbIdxQualif);
nRetCode = ::SQLBindCol(hstmtAux, 6, SQL_C_CHAR, szIdxName, sizeof(CHAR) * 130, &cbIdxName);
nRetCode = ::SQLBindCol(hstmtAux, 7, SQL_C_SHORT, &swType, sizeof(SWORD), &cbType);
nRetCode = ::SQLBindCol(hstmtAux, 8, SQL_C_SHORT, &swSeqInIdx, sizeof(SWORD), &cbSeqInIdx);
nRetCode = ::SQLBindCol(hstmtAux, 9, SQL_C_CHAR, szIdxColName, sizeof(CHAR) * 130, &cbIdxColName);
while (bNoFetch || (nRetCode = ::SQLExtendedFetch(hstmtAux, SQL_FETCH_NEXT, 1, &crow, &rgfRowStatus)) == SQL_SUCCESS || nRetCode == SQL_SUCCESS_WITH_INFO)
{
if (cbIdxName != SQL_NULL_DATA && _tcslen((TCHAR)szIdxName) > 0)
{
// rest of the code
}
// the rest of the code
Because SQLExtendedFetch() is deprecated I used SQLFetchScroll() but the behavior is the same from my interest point of view.
Usually, I get the right information about indexes but in one situation I encounte a strange behavior. It's about having a clustered index into a scenario.
When Field_1 is Non-Unique, Non-Clustered and Field_3 is Clusted index I get the right information.
But if the index Field_1 is Clustered and the Field_3 is Non-Unique, Non-Clustered I get no information about Field_1 index (eg. szIdxName and szIdxColName are "" and their length is -1 that means SQL_NULL_DATA).
So, I have no Index information. Within while loop, with the next iteration I get correct information about the second index Field_3.
I'm not sure whether the problem is with SQLStatistics, the bindings or SQLFetchScroll (they all always return SQL_SUCCESS). It looks like a problem with the driver when the first index is clustered.
Any ideas for fixing this problem or alternative ways for retrieving indexes information?
There are cases documented for SQLStatistics to return NULL for the 'index' name or 'column name':
COLUMN_NAME: Column name. If the column is based on an expression, such as SALARY + BENEFITS, the expression is returned; if the expression cannot be determined, an empty string is returned. NULL is returned if TYPE is SQL_TABLE_STAT.
INDEX_NAME: Index name; NULL is returned if TYPE is SQL_TABLE_STAT.
Are you sure you do not capture an auto-created stats for the table in your results set? What value is swType? You can view the object stats in sys.stats.
I solved it with the workaround: using a query through sys.tables and sys.indexes tables.