C++ SQLite3 prepared delete statement not working - c++

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.

Related

WHERE column = value, only work with INTEGER value

I use sqlite on a c++ project, but I have a problem when i use WHERE on a column with TEXT values
I created a sqlite database:
CREATE TABLE User( id INTEGER PRIMARY KEY AUTOINCREMENT, name VARCHAR(24))
When i try to get the value of the column with VARCHAR values, it doesn't work, and return me a STATUS_CODE 101 just after the sqlite3_step :
int res = 0;
sqlite3_stmt *request;
char *sqlSelection = (char *)"SELECT * FROM User WHERE name='bob' ";
int id = 0;
res = sqlite3_prepare_v2(db, sqlSelection, strlen(sqlSelection), &request, NULL);
if (!res){
while (res == SQLITE_OK || res == SQLITE_ROW){
res = sqlite3_step(request);
if (res == SQLITE_OK || res == SQLITE_ROW ){
id = sqlite3_column_int(request, 0);
printf("User exist %i \n",id);
}
}
sqlite3_finalize(request);
I also tried with LIKE but it also doesn't work
SELECT * FROM User WHERE name LIKE '%bob%'
But when I execute the same code but for an INTERGER value
SELECT * FROM User WHERE id=1
It work fine.
In DB Browser for SQLite all requests work fine.
To solve the problem I searched what status code 101 means.
Here is what they said.
(101) SQLITE_DONE
The SQLITE_DONE result code indicates that an operation has completed.
The SQLITE_DONE result code is most commonly seen as a return value
from sqlite3_step() indicating that the SQL statement has run to
completion. But SQLITE_DONE can also be returned by other multi-step
interfaces such as sqlite3_backup_step().
https://sqlite.org/rescode.html
So, you're getting 101 because there is no more result from SELECT SQL.
The solution was to replace the VARCHAR fields by TEXT.
SQLite for c++ seems to don't manage VARCHAR fields when they are used after the WHERE

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;
}

Junk varchar entries in MSSQL database using ODBC

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;
(...)
}

Using sqlite3_bind_XXX inside a loop

I want to do multiple parameterized inserts with SQLite in my code. For this :
I have a single prepare statement outside of my loop as :
error = sqlite3_prepare(connection, insert_sql, strlen(insert_sql), &stmt, NULL);
I want inserts within a loop as:
while ( not reached end of datafile ) {
// Insert into server table
sqlite3_bind_int(stmt, 1, id1);
sqlite3_bind_double(stmt, 2, latitude);
sqlite3_bind_double(stmt, 3, longitude);
sqlite3_step(stmt);
}
The API docs for the function : https://www.sqlite.org/c3ref/bind_blob.html
mention that :
sqlite3_step() has been called more recently than sqlite3_reset(), then the call will return SQLITE_MISUSE
Bindings are not cleared by the sqlite3_reset() routine
If any sqlite3_bind_() routine is passed a prepared statement that has
been finalized, the result is undefined and probably harmful.
I am really confused as to how do I do repeated inserts with parameterized query in SQLite?
Just call sqlite3_reset() after sqlite3_step().

C++ and sqlite3- sqlite3_prepare_v2 is not working

I'm using sqlite3 with C++ . But the problem is that when I debugged the code I realised that it does not, I mean sqlite3_prepare_v2 does not extract the data from the database.
When I printed the value it extracted, it printed some garbage value. At the same time, I noticed the following warning:
Warning 4 warning LNK4248: unresolved typeref token (01000028) for
'sqlite3_stmt'; image may not run .
I'm working on MS visual studio 2010 windows forms application.
Can anyone help?
I think the linker complaint is ok. See http://social.msdn.microsoft.com/Forums/en-US/8376b2f0-cc36-48c8-9021-f30bda41f410/linker-warning-lnk4248-possible-problem
The following does not take into account that you're programming in MS managed C++, however, I hope it still provides some guidance.
sqlite3_prepare_v2 prepares the statement but does not execute anything.
You need to call sqlite3_step to execute (or partly execute) the SQL.
For an SQL statement that returns nothing (e.g. UPDATE, DELETE, INSERT) you call sqlite3_step once and it should return SQLITE_DONE if it worked.
For a statement that can return multiple rows you call sqlite3_step repeatedly. Each time the return code is SQLITE_ROW you have been returned a row of data that you can access with the
sqlite3_column_* set of statements. When all data has been returned sqlite3_step returns SQLITE_DONE.
Code might look something like this
sqlite3* db;
int rc = sqlite3_open(databasePath.c_str(), &db);
std::string sql = "SELECT Id, Url FROM Url WHERE Page_Download_Reqd <> 0;";
sqlite3_stmt* statementPtr;
const char* tailPtr;
int rc = sqlite3_prepare_v2(db, sql.c_str(), sql.size(), &statementPtr, &tailPtr);
bool finished = false;
do {
int rc = sqlite3_step(statementPtr);
switch (rc) {
case SQLITE_ROW: {
__int64 id = sqlite3_column_int(statementPtr, 0);
std::string url = reinterpret_cast<const char*>(sqlite3_column_text(statementPtr, 1));
// Do something with your data
}
break;
case SQLITE_DONE:
finished = true;
break;
default:
assert(false);
}
} while (!finished);
If you have variables in you SQL, e.g.
SELECT Id, Url FROM Url WHERE Count <> :count;
then you need to use one of the sqlite3_bind_* functions between the sqlite3_prepare_v2 and sqlite3_step functions. For example
__int count = 6;
std::string bindVarName = ":count";
rc = sqlite3_bind_int(
statementPtr,
sqlite3_bind_parameter_index(statementPtr, bindVarName.c_str()),
count);