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.
Related
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.
Help me, I want to make a generic function that get the results of any query string and puts all rows into another variable. When iterating over a row, how can I know the numbers of columns available?
Environment *env = Environment::createEnvironment();
Connection *conn = env->createConnection("user","pass");
quantLinhas = 0;
if( conn != NULL ) {
Statement *stmt = conn->createStatement(query);
ResultSet *rs = stmt->executeQuery();
resultadoSQL->linhas.clear();
while (rs->next()) {
aux.campos.clear();
numbers_colums = rs->whatever_method() //WHAT DO I DO HERE??
for(i = 0; i < numbers_colums ; i++) {
aux.campos.push_back( rs->getString(i) );
quantLinhas++;
}
resultadoSQL->linhas.push_back( aux );
}
stmt->closeResultSet(rs);
conn->terminateStatement(stmt);
env->terminateConnection(conn);
}
According the documentation you can use getColumnListMetaData function. The number of elements in the returned vector might be what you want. You may check if each element is of PTYPE_COL to ensure it.
try this
vector<MetaData> columnList = rs->getColumnListMetaData();
numbers_colums = columnList.size();
columnList.clear();
it worked for me.
source link
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.
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.
C++ SQLite3 how to know if select return 0 rows
I have a select statement for SQLite3, how do I know that if after executing the sql statement, the result is 0 rows , no match found etc..
How can i modify my code so that if 0 rows found, it will not execute the part where it put the result into a vector.
My code below:
sqlstatement = "SELECT * from abe_account where department="+quotesql(department)+" AND name="+quotesql(name)+";";
std::vector< std::vector < std:: string > > result;
for( int i = 0; i < 4; i++ )
result.push_back(std::vector< std::string >());
sqlite3_prepare( db, sqlstatement.c_str() , -1, &stmt2, NULL );//preparing the statement
sqlite3_step( stmt2 );//executing the statement
while( sqlite3_column_text( stmt2, 0 ) )
{
for( int i = 0; i < 4; i++ )
result[i].push_back( std::string( (char *)sqlite3_column_text( stmt2, i ) ) );
sqlite3_step( stmt2 );
counter ++;
}
sqlite3_step returns SQLITE_DONE when there are no (more) rows to process:
int stat = sqlite3_step(stmt2);
if (stat == SQLITE_DONE) {
// no rows to process
}
Remember to check for errors as well, e.g.:
if (stat != SQLITE_DONE && stat != SQLITE_ROW) {
// there's probably a problem
}
You can find the complete list of result codes in the manual.
Lastly, you should use the "v2" interface when using SQLite3. From the manual:
The sqlite3_prepare_v2() and sqlite3_prepare16_v2() interfaces are recommended for all new programs. The two older interfaces are retained for backwards compatibility, but their use is discouraged.
Do a SQL Count first, if it returns 0 you can just stop execution without needing to query the select statement.