I am developing a multithreaded cpp app that reads data from an sqlite database. I am running into a problem with multithreaded read attempts causing an "unknown error" to be thrown. I assume this has to be something like database locking.
My question is the following - Why does the database lock on read operations? I would understand if it was reading/writing, but this app only executes select statements.
Potential solutions to my problem: Throw some mutexes at the sections, switch from sqlite to some other database..
I realize sqlite is not an incredibly strong database, so am leaning more toward switching to some other database. Any suggestions?
Thanks!
The code:
//generate a db connection//
bool open(string filename){
if(sqlite3_open(filename.c_str(), &database) == SQLITE_OK)
{
return true;
}
return false;
}
//Query database//
vector<vector<string>> query(string query){
sqlite3_stmt *statement;
vector<vector<string>> results;
if(sqlite3_prepare_v2(database, query.c_str(), -1, &statement, 0) == SQLITE_OK)
{
int cols = sqlite3_column_count(statement);
int result = 0;
while(true)
{
result = sqlite3_step(statement);
if(result == SQLITE_ROW)
{
vector<string> values;
for(int col = 0; col < cols; col++)
{
auto temp_val = (char*)sqlite3_column_text(statement, col);
if(!temp_val){
cout << "FAIL" << endl;
}
else{
values.emplace_back(temp_val);
}
}
results.push_back(values);
}
else
{
break;
}
}
sqlite3_finalize(statement);
}
string error = sqlite3_errmsg(database);
if(error != "not an error") cout << query << " " << error << endl;
return results;
}
void close(){
sqlite3_close(database);
}
You say you are using a single connection from multiple threads. This is probably not the best idea, and depending on the options configured in your particular SQLite installation, it may not work at all. For more on this, see here: https://www.sqlite.org/threadsafe.html
As you can see, a full diagnosis of your problem requires looking at how your SQLite package was built...but you can try setting runtime options to see if that helps, as described on the above page.
Related
bool run_query(sqlite3* db, const std::string& sql, std::vector< user_record >& records)
{
// clear any prior results
records.clear();
char* error_message;
if(sqlite3_exec(db, sql.c_str(), callback, &records, &error_message) != SQLITE_OK)
{
std::cout << "Data failed to be queried from USERS table. ERROR = " << error_message << std::endl;
sqlite3_free(error_message);
return false;
}
return true;
}
How do I fix this method to fail and display an error if there is a suspected SQL Injection?
I am working on a school project which involves SQLite3.
I've created a bunch of functions for my DatabaseManager class which all work quite good.
Now, after adding a function with a DELETE statement, sqlite3_step() returns SQLITE_BUSY and the sqlite3_errmsg() says "Database is locked".
Before opening this function I have already called sqlite3_finalize() and sqlite3_close().
int errorcode;
sqlite3 *db;
//sql statement
std::string statement = "DELETE FROM tbl_follower WHERE flw_ID = " + std::to_string(row);
sqlite3_stmt *binStatement;
errorcode = sqlite3_open(m_filePath.c_str(), &db);
if (errorcode != SQLITE_OK)
std::cerr << sqlite3_errmsg(db) << std::endl;
errorcode = sqlite3_prepare_v2(db, statement.c_str(), statement.size(), &binStatement, NULL);
if(errorcode != SQLITE_OK)
std::cerr << sqlite3_errmsg(db) << std::endl;
errorcode = sqlite3_step(binStatement);
if (errorcode == SQLITE_DONE)
{
sqlite3_finalize(binStatement);
sqlite3_close(db);
}
else
{
//error
std::cerr << sqlite3_errmsg(db) << std::endl;
sqlite3_finalize(binStatement);
sqlite3_close(db);
}
I am pretty sure the issue is somewhere in this code because I have already checked all of my other functions for mistakes (which all work without giving an error). My SQL statement is also correct.
"Database is locked" means that there is some other active transaction.
You probably forgot to finalize a statement in some other part of the program.
I managed to get the wpa_supplicant C API to work. But it behaves completly different each time I restart my Program.
The Connection succeeds every time. But then the troubles begin:
Sometimes SCAN replies an empty String but returns 0 (Ok).
In another run it replies "OK\n" and returns 0. When I loop and wait for an return of 0 and a "OK\n"-reply it runs forever with an empty reply and a 0 return.
In rare cases when SCAN returns 0 and replies "OK\n" I move on and wait for SCAN_RESULTS to return 0. At this point it behaves completely random. Sometimes it replies the whole Scan-Results. Sometimes it replies nothing but return 0 and the Scan-results are in my Event-Pipeline.
Or like in most cases: It returns 0 but does nothing. No reply, no Events. Nothing.
For debugging I reduced my Code to this snippet and try to figure out whats wrong. Im done, tried everything and I am somewhat frustrated with the Documentation of the ctrl-interface which doesn't define any workflow or tips. Im sick of reverse engineering the wpa_cli.c to figure out their flow.
I have to attach that mostly the first PING works well. Every other PING results in empty Strings.
/* some includes */
wpa_ctrl* _wpac;
static void callback(char* rply, size_t rplylen){
std::cout << std::string(rply,rplylen) << std::endl;
}
bool ScanResults() {
if(_wpac)
{
char rply[4096]; //same as in wpa_cli.c
size_t rplylen;
int retval = wpa_ctrl_request(_wpac,"SCAN_RESULTS",12,rply,&rplylen,callback);
if(retval == 0) {
std::string rplystring = std::string(rply,rplylen);
std::string message = std::string("wpa_ctrl(SCAN_RESULTS) replied: '").append(rplystring).append("' (").append(std::to_string(retval)).append(")");
std::cout << message << std::cout;
std::cout << std::string("wpa_ctrl(SCAN_RESULTS): Available (").append(std::to_string(retval)).append(")") << std::endl;
return true;
}
else
std::cout << std::string("wpa_ctrl(SCAN_RESULTS): Unavailable (").append(std::to_string(retval)).append(")") << std::endl;
return false;
}
return false;
}
bool InitScan() {
if(_wpac)
{
char rply[4096]; //same as in wpa_cli.c
size_t rplylen;
int retval = wpa_ctrl_request(_wpac,"SCAN",4,rply,&rplylen,callback);
if(retval == 0) {
std::string rplystring = std::string(rply,rplylen);
std::string message = std::string("wpa_ctrl(SCAN) replied: '").append(rplystring).append("' (").append(std::to_string(retval)).append(")");
std::cout << message << std::endl;
if(rplystring == "OK\n") {
std::string message = std::string("wpa_ctrl(SCAN): Scan initiated (").append(std::to_string(retval)).append(")");
std::cout << message << std::endl;
return true;
}
}
std::string message = std::string("wpa_ctrl(SCAN) failed: (").append(std::to_string(retval)).append(")");
std::cout << message << std::endl;
}
return false;
}
int main(){
std::string connection_string = std::string("/var/run/wpa_supplicant/").append(_interface);
wpa_ctrl* _wpac = wpa_ctrl_open(connection_string.c_str());
if(!_wpac)
return 1;
/* Well Working Attach to as Eventlistener omitted */
while(!InitScan())
sleep(1);
while(!ScanResults())
sleep(1)
return 0;
}
Try doing something like this in the appropriate places in your code
char rply[4096];
size_t rplylen = sizeof(rply);
static char cmd[] = "SCAN"; //maybe a bit easier to deal with since you need a command length
int retval = wpa_ctrl_request(_wpac, cmd, sizeof(cmd)-1, rply, &rplylen, NULL);
NULL, because I suspect you really don't need a callback routine. But put one in if you want to.
I am trying to retrieve blob encrypted data from a sqlite file. Here is my current code:
sqlite3 *db; char *error;
int rc;
rc = sqlite3_open("db.sq", &db);
if (rc)
{
cout << "Connection Error";
sqlite3_close(db);
}
else {
char **results = NULL;
int rows, columns;
const char *sqlSelect = "SELECT password FROM users";
sqlite3_get_table(db, sqlSelect, &results, &rows, &columns, &error);
if (rc) {
system("color 0c");
cout << "Query Error";
}
else {
for (int i = 2; i < rows*2; i += 1) {
cout << results[i] << endl;
}
}
}
The only thing this prints, however, is a bunch of smiley faces. I assume that I am getting the data wrong, as I remember seeing a way to get blob information in the documentation, but I am not sure how to implement that.
The documentation for blob: https://www.sqlite.org/c3ref/column_blob.html
I have a table "SiteCode" with following schema :
CREATE TABLE SiteCode(
ID INTEGER PRIMARY KEY,
Code TEXT(3) UNIQUE NOT NULL DEFAULT 0);
Through the following code I can Open a DB & Access the elements of DB and perform the query execution perfectly.
Now I wish to add a small code snippet which could check if the element that a user wish to delete exists .
For example: Suppose table SiteCode have following Entries in Code column : 400, 401, 402 and 403 and user enters 444 as the input to DELETE query then it shall return an error.
At present, if a user enters 444 as input to the query then that query gets executed successfully without checking the Code column and without any Error.
How do i approach this problem. Kindly Help.
void DELETE_Site_Code()
{
int CodeA;
int rc = sqlite3_open("/DBsqlite3/empdbv3.db", &db);
if (rc != SQLITE_OK) {
cerr << "Cannot open database [ " << sqlite3_errmsg(db) << " ]" << endl;
sqlite3_close(db);
}
sql = "DELETE FROM SiteCode WHERE Code= #Code;";
rc = sqlite3_prepare_v2(db, sql, -1, &stmt4, 0);
if(SQLITE_OK != rc) {
cerr << "Failed to PREPARE DELETE statement for SiteCode Table [ " << sqlite3_errmsg(db) << " ]" << endl;
sqlite3_close(db);
exit(1);
}
else{
int Code_x = sqlite3_bind_parameter_index(stmt4, "#Code");
cout<< "Enter the Site Code That you wish to DELETE from table-SiteCode\n"<< endl;
cin>>CodeA;
if(cin.fail()) {
cout<<"\nPlease enter only digits\n"<<endl;
}
else {
if((CodeA >= 000) && (CodeA <= 998)) {
sqlite3_bind_int(stmt4, Code_x, CodeA);
}
else {
cout<< "Valid Site Code Should be between 000 to 998\n" <<endl;
}
} //else loop of (cin.fail) ends here
}
int step = sqlite3_step(stmt4);
if(SQLITE_DONE != step) {
cout<<"\nERROR while inserting into table\n"<< endl;
}
else {
cout<<"\nRecord DELETION completed"<<endl;
}
if(step == SQLITE_ROW){
cout<< sqlite3_column_text(stmt4, 0) << endl;
cout<< sqlite3_column_text(stmt4, 1) << endl;
}
sqlite3_finalize(stmt4);
sqlite3_close(db);
}
Use sqlite3_changes() function to query how many rows were affected by the query executed most recently:
// ...
int step = sqlite3_step(stmt4);
if(SQLITE_DONE != step) {
cout<<"\nERROR while inserting into table\n"<< endl;
}
else if (sqlite3_changes(db) == 0) {
cout<<"\nNo records deleted"<<endl;
}
else {
cout<<"\nRecord DELETION completed"<<endl;
}