I want to perform a simple query (drop table) with sqlite native interface and I persistently run into SQLITE_ERROR as I try to prepare the statement. I tried everything as I was afraid of string compatibility (qt strings can be a pain sometimes) but every it always gives me the same the code is the following:
sqlite3_stmt *query;
std::string tmp = "DROP TABLE ?";
if(sqlite3_prepare_v2(db, tmp.c_str(), tmp.size(), &query, NULL) != SQLITE_OK)return FALSE;
if(sqlite3_bind_text16(query, 1, str.utf16(), -1, SQLITE_TRANSIENT) != SQLITE_OK) return FALSE;
if(sqlite3_step(query) != SQLITE_OK) {
std::cerr << sqlite3_errmsg(db);
return FALSE;
}
sqlite3_finalize(query);
I hope sincerely someone out there can help.
You can't pass table names as parameters (it applies to most database APIs supporting parameters, maybe even all database APIs).
Related
I have been trying to use the SQLite tutorial here to learn how to use SQLite in C. I was having trouble figuring out exactly how to use it, as it only mentions the sqlite3_exec, which seems incredibly un-intuitive to use for basic querying (it uses a callback for everything). Then I came across this answer, https://stackoverflow.com/a/31168999/11954200, which addresses this same issue and suggests using the various four sqlite statements that are essentially merged into the sqlite3_exec shorthand form. Here is my understanding so far:
// Query to get the rows
// Part1 -- Prepare the query: cursor.execute()
// http://www.sqlite.org/c3ref/prepare.html
sqlite3_stmt *stmt; // this is the prepared statement that the sqlite3_prepare writes to
rc = sqlite3_prepare(db, "SELECT * FROM mytable LIMIT 20", -1, &stmt, NULL);
if (rc != SQLITE_OK) {
printf("error: %s", sqlite3_errmsg(db));
return 0;
}
// Part2 -- equivalent of cursor.fetchone()
// http://www.sqlite.org/c3ref/step.html
int rownum=1;
while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
// Part3 -- reference the values from the row
// http://www.sqlite.org/c3ref/column_blob.html
int id = sqlite3_column_int(stmt, 0);
const unsigned char * date = sqlite3_column_text(stmt, 1);
printf("Row: %d | ID: %d | Date: %s\n", rownum, id, date);
rownum++;
}
// Part4 -- equivalent of cursor.close()
// http://www.sqlite.org/c3ref/finalize.html
sqlite3_finalize(stmt);
In other words, in higher language parlance:
sqlite3_prepare(...) works like cursor.execute(...)
sqlite3_step(...) works like cursor.fetchone(...)
sqlite3_finalize(...) works like cursor.close(...);
Is this an accurate assessment of how these statements work in sqlite? Would the sqlite3_exec ever be used when fetching results?
Introduction
I'm attempting to incorporate variables into queries using C. I'm following this tutorial using sqlite tutorialspoint , and my first exposure to using SQL. The tutorial has shown me how to use Queries such as these:
Query
sql = "UPDATE COMPANY set SALARY = 25000.00 where ID=1; " \
"SELECT * from COMPANY";
*So how would i go about incorporating variables into this statement, for example if i wanted to replace 1 with a variable assigned to 'ID'.
For example(My failed attempt)
sql = "UPDATE COMPANY set SALARY = 25000.00 where ID=" + variable + ";" \
"SELECT * from COMPANY";
I've googling around however I couldn't really find any material on using variables in sql queries using the C language syntax. How would i go about this in the correct and safe way, to incorporate variables and without making a program vulnereable to SQL injection?
The C-API provides the functions sqlite3_prepare_v2 and sqlite3_bind so that you can bind parameters to prepared statements. What that means is, you can use a placeholder where you want to substitute parameters within a string.
Each placeholder is referenced by an index, so you can use as many parameters as you like (up to the compile-time limit set by SQLITE_MAX_VARIABLE_NUMBER). You then bind a parameter to the placeholder at a specified index.
There are a number of functions and methods to accomplish parameter substitution, but to get you started, here's an example which binds an integer to the 1st placeholder in an sql statement:
int rc;
sqlite3 *db;
sqlite3_stmt *stmt = NULL;
...
// here I assume you open the db, and provide any other working code as needed...
...
// the employee id number.
int id_num;
...
// create the sql statement, with a single placeholder marked by '?'.
char *sql = "UPDATE COMPANY set SALARY = 25000.00 where ID=?";
// prepare the sql statement.
rc = sqlite3_prepare_v2(db, sql, strlen(sql)+1, &stmt, NULL);
if (rc != SQLITE_OK) {
printf("Failed to prepare statement: %s\n\r", sqlite3_errstr(rc));
sqlite3_close(db);
return 1;
}
else {
printf("SQL statement prepared: OK\n\n\r");
}
// bind an integer to the parameter placeholder.
rc = sqlite3_bind_int(stmt, 1, id_num);
if (rc != SQLITE_OK) {
printf("Failed to bind parameter: %s\n\r", sqlite3_errstr(rc));
sqlite3_close(db);
return 1;
}
else {
printf("SQL bind integer param: OK\n\n\r");
}
// evaluate the prepared statement.
rc = sqlite3_step(stmt);
// other successful return codes are possible...
if (rc != SQLITE_DONE) {
printf("Failed to execute statement: %s\n\r", sqlite3_errstr(rc));
sqlite3_close(db);
return 1;
}
// deallocate/finalize the prepared statement when you no longer need it.
// you may also place this in any error handling sections.
sqlite3_finalize(stmt);
...
// close the db when finished.
sqlite3_close(db)
...
// finish your code.
I'm writing an application that uses the wxSQLite3 library, which is a wrapper around libsqlite3 for the wxWidgets cross-platform GUI programming framework. When attempting to reuse a prepared statement, a wxSQLite3Exception is thrown.
This example illustrates the problem:
#include <wx/string.h>
#include <wx/wxsqlite3.h>
int main() {
wxSQLite3Database::InitializeSQLite();
//create in-memory test database & populate it
wxSQLite3Database db;
db.Open(wxT(":memory:"));
db.ExecuteUpdate(wxT("CREATE TABLE SimpleTable (id INT PRIMARY KEY, val INT);"));
db.ExecuteUpdate(wxT("INSERT INTO SimpleTable VALUES (1, 10);"));
db.ExecuteUpdate(wxT("INSERT INTO SimpleTable VALUES (2, 20);"));
//create a prepared statement we can reuse
wxSQLite3Statement stmt;
stmt = db.PrepareStatement(wxT("SELECT * FROM SimpleTable WHERE id = ?;"));
//first use of statement (works)
stmt.Bind(1, 1);
wxSQLite3ResultSet r_set = stmt.ExecuteQuery();
if (r_set.NextRow()) {
wxPrintf(wxT("id: %i value: %i\n"), r_set.GetInt(wxT("id")), r_set.GetInt(wxT("val")));
}
r_set.Finalize();
//reset and reuse statement
stmt.Reset();
stmt.Bind(1, 2); //**EXCEPTION THROWN HERE**
wxSQLite3ResultSet r_set2 = stmt.ExecuteQuery();
if (r_set2.NextRow()) {
wxPrintf(wxT("id: %i value: %i\n"), r_set2.GetInt(wxT("id")), r_set2.GetInt(wxT("val")));
}
r_set2.Finalize();
//cleanup
stmt.Finalize();
db.Close();
wxSQLite3Database::ShutdownSQLite();
return 0;
}
The exception handling was removed for brevity, but the message from the exception is:
WXSQLITE_ERROR[1000]: Statement not accessible
I wrote roughly equivalent code in plain C using libsqlite3 and it ran without problem. Does anyone know what I'm doing wrong, or if this is a bug of some sort in wxSQLite3? Thank you in advance for your help!
In SQLite itself, a statement and a result set actually are the same object.
wxSQLite3 uses reference counting so that the statement is freed only when the last wxSQLite3Statement or wxSQLite3ResultSet object is freed.
This happens automatically in the respective destructors.
However, calling Finalize() explicitly bypasses the reference counting.
While not necessary, if you want to ensure that wxSQLite3ResultSet resources are freed correctly before the next statement execution, just destruct this object:
wxSQLite3Statement stmt = ...;
...
{
wxSQLite3ResultSet r_set = stmt.ExecuteQuery();
... r_set.NextRow() ...
// r_set destructed here
}
...
As long as you intend to reuse a prepared SQL statement, that is, to reset the statement and to bind new values to statement variables, you must not call method Finalize - neither on the prepared statement object itself nor on a result set retrieved from that statement.
As the method name, Finalize, suggests, the method finalizes the underlying SQLite statement object by calling sqlite3_finalize (quotation from the SQLite docs: "The sqlite3_finalize() function is called to delete a prepared statement.") After the underlying SQLite statement object has been deleted, it obviously can't be accessed anymore. Therefore you get the exception.
Usually you don't need to call method Finalize explicitly. wxSQLite3 takes care of finalizing statements through reference counting.
I was trying to run test_onefile.c example from sqlite3 VFS examples, and I get the following failure:
test_onefile: test_onefile.c:693: fsDelete: Assertion `strpcmp("-journal", &zPath[nName])==0' failed.
I'm running the code as follows:
int retval;
fs_register();
int q_cnt = 5,q_size = 150,ind = 0;
char **queries = (char**) malloc(sizeof(char) * q_cnt * q_size);
sqlite3_stmt *stmt;
sqlite3 *handle;
retval = sqlite3_open_v2( "sampledb.sqlite2", &handle, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE , "fs");
if(retval)
{
printf("Database connection failed\n");
return -1;
}
printf("Connection successful\n");
// Create the SQL query for creating a table
char create_table[100] = "CREATE TABLE IF NOT EXISTS users (uname TEXT,pass TEXT NOT NULL,activated INTEGER)";
// Execute the query for creating the table
retval = sqlite3_exec(handle,create_table,0,0,0);
// Insert first row and second row
queries[ind++] = "INSERT INTO users VALUES('manish','mani',1)";
retval = sqlite3_exec(handle,queries[ind-1],0,0,0);
queries[ind++] = "INSERT INTO users VALUES('mehul','pulsar',0)";
retval = sqlite3_exec(handle,queries[ind-1],0,0,0);
Edit:
The file it fails on is sampledb.sqlite2-wal, clearly not a journal file. However, I don't understand how it reached it.
Edit2:
Well, after removing the assertion in source file:
assert(strcmp("-journal", &zPath[nName])==0);
The code seems to work. However, I'm not a big fan of assertion deletion, as clearly it would lead to some unexpected behavior. The author had a reason to use the assertion.
The VFS implemented by test_onefile.c is quite old, and therefore does not support the additional files required for WAL mode.
To make it work with a modern SQLite, the fsDelete function should just ignore attempts to delete -wal or -shm files.
Using the C api with sqlite (v 3.7.13), I'm trying to list all attached databases of the current connection:
sqlite3_stmt* pCompiledSql;
if(SQLITE_OK == sqlite3_prepare_v2(database, "PRAGMA database_list;", -1, &pCompiledSql, nullptr))
{
while(SQLITE_ROW == sqlite3_step(pCompiledSql))
{
const char* pName = reinterpret_cast<const char*>(sqlite3_column_text(pCompiledSql, 1));
const char* pFile = reinterpret_cast<const char*>(sqlite3_column_text(pCompiledSql, 2));
// Using pName and PFile...
}
}
Where database is a handle to an existing database with several databases attached. The same code works fine with statements like "SELECT * FROM testtable;"
However with the pragma call the first step call will just return an SQLITE_DONE straight away.
I'm pretty sure I'm ovelooking something obvious but without much experience with SQLite I'm stuck now... What can possibly go wrong here?
The fourth parameter of sqlite3_prepare_v2 is a pointer to the statement pointer, so it must not be pCompiledSql but &pCompiledSql.
Your compiler should have warned you about this.