SQLite3 in c++ outputing empty list on SELECT - c++

I am currently working on c++ with SQLite3, everything works fine, i can add stuff in the table, but when i do a SELECT * FROM myTable; is returns me (null)...
the variable rc of
sqlite3 *db;
char *zErrMsg;
int rc;
std::string sql = "SELECT * FROM users;";
char csql[sql.size()+1];
strcpy(csql, csql.c_str()); // String to char*
rc = sqlite3_exec(db, csql, callback, 0, &zErrMsg); // rc = 21 Error
...
is 21.. According to https://www.sqlite.org/c3ref/c_abort.html it means that I "Used the library incorrectly".. I then checked with python and online sql on the same .db file and it outputs me what is good..
If anyone could help me and explain me what I did wrong and how to correct it?
Thank you very much !
ps:
here is my function addUser in case the problem is in the add..
bool addUser(std::string username, std::string password){
char cpassword[password.size()+1];
strcpy(cpassword, password.c_str());
std::string shashedP = hashPass(cpassword); // hashPass returns std::string
std::string sql = "INSERT INTO users (username, passw) VALUES ('" + username + "', " + shashedP + ");";
char csql[sql.size()+1];
strcpy(csql, sql.c_str());
rc = sqlite3_exec(db, csql, callback, 0, &zErrMsg); // rc = SQLITE3_OK = 0 everytime
...

That's... strange... code (Plus as was already mentioned, not really valid C++, though some compilers do support C style VLAs as an extension). The normal workflow is to use a prepared statement for anything returning rows, or taking user-supplied arguments in the form of placeholders. sqlite3_exec() is mostly only good for creating tables and such where there's no results and no runtime defined arguments to pass to the query.
For example:
std::string query = "SELECT foo FROM bar WHERE name = ?";
std::string name = "Bob";
sqlite3_stmt *stmt;
if (sqlite3_prepare_v2(db, query.c_str(), query.size(), &stmt, nullptr) != SQLITE_OK) {
// Error reporting and handling
}
sqlite3_bind_text(stmt, 1, name.c_str(), name.size(), SQLITE_STATIC);
while (sqlite3_step(stmt) == SQLITE_ROW) {
int foo = sqlite3_column_int(stmt, 0);
// Do stuff with the current row's foo
}
sqlite3_finalize(stmt);
More reading.

Related

C++ SQLite return some values?

I am coding a serverside program that uses SQLite. When a client logs in, I want to get its informations from a Userinfo table.
Its columns are
Userno(int),
money(int),
bankmoney(int),
gender(string),
name,
surname
I want to write these informations to a user structure
struct userstruct
{
int userno;
int money;
int bankmoney
....
}
When a user logs in I want to create a new struct and set infos of user from this table via SQLite.
The following:
static int callback(void *data, int argc, char **argv, char **azColName)
void *data - can be used to give pointer to an object - aka this, or anything else, can be null
int argc - column count - number of selected records
char **argv - ARRAY of column values.
char **azColName - ARRAY of column names
btw: better use prepare - bind - step, like the following sample, this way it works without a callback:
sqlite3_prepare_v2(db, "select distinct name, age from demo where age > ? order by 2,1;", -1, &stmt, NULL);
sqlite3_bind_int(stmt, 1, 16); /* 1 */
while ( (rc = sqlite3_step(stmt)) == SQLITE_ROW) { /* 2 */
printf("%s is %d years old\n", sqlite3_column_text(stmt, 0), sqlite3_column_int(stmt, 1)); /* 3 */
}

Getting error in inserting a record in sqlite3 using vectors C++

I am working on a project for my Unix class. So as you can see from my code, I create functions to read a text file and parse the text file so that I may store the string values into a vector.
After storing the values into a vector, I use that vector to insert the records into my database. For some reason, I am not able to insert my vector and I receive this error:
error: assigning to 'char *' from incompatible type
'std::__1::basic_string'
sql = "INSERT INTO ARTIST(ID, NAME) " \
Here is a snippet of my code:
int main(int argc, char* argv[])
{
sqlite3 *db;
int rc;
char *zErrMsg = 0;
char *sql;
// initialize all text files
ifstream artistFile("artist.txt"); //Input file
ifstream cd("cd.txt");
ifstream track("track.txt");
//string sqlArtistInsert;
string sqlCDInsert;
string sqlTrackInsert;
std::vector<std::string> artistVector;
artistVector = readFile(artistVector, artistFile);
createArtistTable();
// Open database
rc = sqlite3_open("test.db", &db);
if( rc ){
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
exit(0);
}else{
fprintf(stderr, "Opened database successfully\n");
}
// Create SQL statement
sql = "INSERT INTO ARTIST(ID, NAME) " \
"VALUES(" + artistVector[0] + "," + artistVector[1] +"); " \
"VALUES(" + artistVector[2] + "," + artistVector[3] +"); " \
"VALUES(" + artistVector[4] + "," + artistVector[5] +"); " \
"VALUES(" + artistVector[6] + "," + artistVector[7] + ");";
// Execute SQL statement
rc = sqlite3_exec(db, sql, callback, 0, &zErrMsg);
if( rc != SQLITE_OK ){
fprintf(stderr, "SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
}else{
fprintf(stdout, "Records created successfully\n");
}
sqlite3_close(db);
return 0;
}
For some reason, it says that the error is at the
sql = insert
^
Also, the text file I'm parsing and storing it the vector is this below:
1|Pink Floyd //artistVector[0] & artistVector[1]
2|Genesis //artistVector[2] & artistVector[3]
3|Einaudi //artistVector[4] & artistVector[5]
4|Melanie C //artistVector[6] & artistVector[7]
Any help would be greatly appreciated! Thank you.
You could do something like this:
// Create SQL statement
sql = "INSERT INTO ARTIST(ID, NAME) VALUES(";
sql += artistVector[0];
sql += ',';
sql += artistVector[1];
sql += ");";
but note the following:
the sql variable should be defined as a std::string. If, later, you need a const char *, use the c_str() member function to get it. You can't concatenate const char * variables.
Using += avoids creating temporary variables when you have multiple concatenations of std:string variables so it's more efficient.
- I don't think you can insert multiple records at once. I think it's invalid SQL but check this.
It's probably more efficient to make an sql string that has variable place holders and use bind instead of constructing sql strings as you are doing.

How to prepare sql statements and bind parameters?

Unfortunatelly, the documentation completely lacks examples (what is really strange), as if it assumes all its readers to be good programmers. Whereas, I'm quite new to C++ and can not really figure out from the documentation how to really prepare and execute statements. I love the way how it is implemented in PDO for PHP. Normally, I just do it like this:
$s = $db->prepare("SELECT id FROM mytable WHERE id = :id");
$s->bindParam(':id', $id);
$s->execute();
or do it using ? tokens:
$data = array();
$data[] = 1;
$data[] = 2;
$s = $db->prepare("SELECT id FROM mytable WHERE id = ? or id = ?");
$s->execute($data);
Now, I have C++ and sqlite3.h in my hands. At this moment, I know just how to connect to database - I do it and get no errors:
sqlite3 * conn;
int rc = sqlite3_open(db_name, &conn);
Please, give some instructions (with clear tiny examples) about how to implement similar things that PDO does in PHP - prepare a statement using named arguments and using ? tokens.
You can find a decent amount of documentation here: sqlite.org
This example does not explain the sqlite3 function calls and parameters in detail, as that is quite a lot of information to cover - instead refer to the given link for more in-depth detail.
This example binds values to the statement in your question multiple times, and reads all query results after each bind:
sqlite3* conn;
sqlite3_stmt* stmt = 0;
int rc = sqlite3_open(db_name, &conn);
// Good idea to always check the return value of sqlite3 function calls.
// Only done once in this example:
if ( rc != SQLITE_OK ) { // Do something }
rc = sqlite3_prepare_v2( conn, "SELECT id FROM myTable WHERE id = ? or id = ?", -1, &stmt, 0 );
// Optional, but will most likely increase performance.
rc = sqlite3_exec( conn, "BEGIN TRANSACTION", 0, 0, 0 );
for ( int bindIndex = 0; bindIndex < number_of_times_you_wish_to_bind; bindIndex++ ) {
// Binding integer values in this example.
// Bind functions for other data-types are available - see end of post.
// Bind-parameter indexing is 1-based.
rc = sqlite3_bind_int( stmt, 1, int_you_wish_to_bind ); // Bind first parameter.
rc = sqlite3_bind_int( stmt, 2, int_you_wish_to_bind ); // Bind second parameter.
// Reading interger results in this example.
// Read functions for other data-types are available - see end of post.
while ( sqlite3_step( stmt ) == SQLITE_ROW ) { // While query has result-rows.
// In your example the column count will be 1.
for ( int colIndex = 0; colIndex < sqlite3_column_count( stmt ); colIndex++ ) {
int result = sqlite3_column_int( stmt, colIndex );
// Do something with the result.
}
}
// Step, Clear and Reset the statement after each bind.
rc = sqlite3_step( stmt );
rc = sqlite3_clear_bindings( stmt );
rc = sqlite3_reset( stmt );
}
char *zErrMsg = 0; // Can perhaps display the error message if rc != SQLITE_OK.
rc = sqlite3_exec( conn, "END TRANSACTION", 0, 0, &zErrMsg ); // End the transaction.
rc = sqlite3_finalize( stmt ); // Finalize the prepared statement.
More Bind Functions
More Read Functions
As I understand from http://hoogli.com/items/Avoid_sqlite3_clear_bindings().html, the step rc = sqlite3_clear_bindings( stmt ); is not necessary in this case.
Unfortunately, I am not yet allowed to post this as a comment to the very good previous answer.

How do I bind a variable to a prepared statement with the SOCI libary?

Currently my application only supports SQLite databases, but I would like to support both SQLite and MySQL databases, so I'm testing out the SOCI library to see if it does what I need. However, despite the examples and documentation, I can't figure out how SOCI handles prepared statements.
When using the SQLite C API, you prepare statement:
sqlite3_stmt* statement;
sqlite3_prepare_v2( database_handle_pointer,
"SELECT * FROM table WHERE user_id=:id;",
-1,
&statement,
NULL );
And later you bind a value to the :id place holder, execute the statement and step through the results:
const sqlite3_int64 user_id = some_function_that_returns_a_user_id();
const int index = sqlite3_bind_parameter_index( statement, ":id" );
sqlite3_bind_int64( statement, index, user_id );
while ( sqlite3_step( statement ) == SQLITE_ROW )
{
// Do something with the row
}
How do I do this with SOCI? It looks like the prepare and bind concepts are not separated like with the native SQLite API. Does the bind have to happen during the prepare using soci::use()?
Update 1: In case I'm not explaining the question well enough: Here's a small, working, C++ example using the SQLite C API. If I could see this re-implemented using SOCI, it would answer the question.
#include <sqlite3.h>
#include <iostream>
// Tables and data
const char* table = "CREATE TABLE test ( user_id INTEGER, name CHAR );";
const char* hank = "INSERT INTO test (user_id,name) VALUES(1,'Hank');";
const char* bill = "INSERT INTO test (user_id,name) VALUES(2,'Bill');";
const char* fred = "INSERT INTO test (user_id,name) VALUES(3,'Fred');";
// Create a SQLite prepared statement to select a user from the test table.
sqlite3_stmt* make_statement( sqlite3* database )
{
sqlite3_stmt* statement;
sqlite3_prepare_v2( database,
"SELECT name FROM test WHERE user_id=:id;",
-1, &statement, NULL );
return statement;
}
// Bind the requested user_id to the prepared statement.
void bind_statement( sqlite3_stmt* statement, const sqlite3_int64 user_id )
{
const int index = sqlite3_bind_parameter_index( statement, ":id" );
sqlite3_bind_int64( statement, index, user_id );
}
// Execute the statement and print the name of the selected user.
void execute_statement( sqlite3_stmt* statement )
{
while ( sqlite3_step( statement ) == SQLITE_ROW )
{
std::cout << sqlite3_column_text( statement, 0 ) << "\n";
}
}
int main()
{
// Create an in-memory database.
sqlite3* database;
if ( sqlite3_open( ":memory:", &database ) != SQLITE_OK )
{
std::cerr << "Error creating database" << std::endl;
return -1;
}
// Create a table and some rows.
sqlite3_exec( database, table, NULL, NULL, NULL );
sqlite3_exec( database, hank, NULL, NULL, NULL );
sqlite3_exec( database, bill, NULL, NULL, NULL );
sqlite3_exec( database, fred, NULL, NULL, NULL );
sqlite3_stmt* statement = make_statement( database );
bind_statement( statement, 2 );
execute_statement( statement );
// Cleanup
sqlite3_finalize( statement );
sqlite3_close( database );
return 1;
}
The same program partially implemented using SOCI (Note the two stub functions marked as HELPME)
#include <soci/soci.h>
#include <iostream>
const char* table = "CREATE TABLE test ( user_id INTEGER, name CHAR );";
const char* hank = "INSERT INTO test (user_id,name) VALUES(1,'Hank');";
const char* bill = "INSERT INTO test (user_id,name) VALUES(2,'Bill');";
const char* fred = "INSERT INTO test (user_id,name) VALUES(3,'Fred');";
soci::statement make_statement( soci::session& database )
{
soci::statement statement =
database.prepare << "SELECT name FROM test WHERE user_id=:id";
return statement;
}
void bind_statement( soci::statement& statement, const int user_id )
{
// HELPME: What goes here?
}
void execute_statement( soci::statement& statement )
{
// HELPME: What goes here?
}
int main()
{
soci::session database( "sqlite3", ":memory:" );
database << table;
database << hank;
database << bill;
database << fred;
soci::statement statement = make_statement( database );
bind_statement( statement, 2 );
execute_statement( statement );
}
Update 2: I ended up ditching SOCI when I found the cppdb library. Unlike SOCI, it is just a very thin wrapper around the native C APIs, which suits my needs at this time.
The documentation explains how to use prepared statements with parameters:
int user_id;
string name;
statement st = (database.prepare << "SELECT name FROM test WHERE user_id = :id",
use(user_id),
into(name));
user_id = 1;
st.execute(true);
Please note that the lifetime of the user_id and name variables must be at least as long as that of st.

sqlite query not prepared row insterted but empty values

I am writing a function to prepare a sql query and execute it against a sqlite db. I am using a query to insert values in to a table, the query looks like this
"insert into files values (?, ?, ?, ?, ?, 0)";
A row is inserted but all values for text fields are empty except the last field which is 0. The first 5 fields are of type TEXT
// If I hard code a value for value.data() then the row is inserted correctly with my hardcoded data, the exact line is below
status = sqlite3_bind_text(ppStmt, index, /* if I hardcode it works*/ value.data(), -1, SQLITE_STATIC);
The full function is shown below.The list is created on stack by the calller, I am not sure why it is not replacing question marks with my string args, hardcoded ones work though.. no error codes are returned
//db is already open before I call this
void MediaCache::prepareAndExecuteQuery(string query, list<string> args)
{
sqlite3_stmt *ppStmt = 0;
const char **pzTail = 0;
int status = 0;
if( sqlite3_prepare_v2(db, query.data(), query.length(), &ppStmt, pzTail) != SQLITE_OK )
{
string error = sqlite3_errmsg(db);
//throw an exception
}
if(ppStmt)
{
list<string>::iterator current = args.begin();
int index = 1;
for(current = args.begin() ; current != args.end(); current++)
{
string value = *current;
status = sqlite3_bind_text(ppStmt, index, value.data(), -1, SQLITE_STATIC);
if(status != SQLITE_OK)
{
//log error;
}
index++;
}
status = sqlite3_step(ppStmt);
status = sqlite3_finalize(ppStmt);
//sqlite3_exec(db, "COMMIT", NULL, NULL, NULL);
}
else
{
//ppStmt is null
//throw an exception
}
}
I suspect the problem is here:
string value = *current;
status = sqlite3_bind_text(ppStmt, index, value.data(), -1, SQLITE_STATIC);
In this case value goes out of scope before the execution of the statement takes place. So the pointers given to sqlite3_bind_text are no longer valid. To "fix" that, you could use SQLITE_TRANSIENT, which would force the library to make its own copy of the data before returning.
Also, if you are not using C++11, I don't believe string::data() is guaranteed to be NULL terminated in which case the -1 parameter could be incorrect. It should maybe be value.length() instead.