where is the problem with the following code? It's can not DROP my table. When I make this drop from sql browser (on the same databade) - all works fine.
int rcc = sqlite3_open_v2(str_sessions_file.c_str(), &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL );
if ( SQLITE_OK != rcc)
{
fprintf (stderr, "Can't open database: %s\n", sqlite3_errmsg (db));
sqlite3_close (db);
return;
}
// Drop
std::string sql_dropatable = "DROP TABLE IF EXISTS sessions";
if( sqlite3_exec(db, sql_dropatable.c_str(), 0, 0, 0) != SQLITE_OK ) { // or == -- same effect
std::cout << "SQLite can't drop sessions table" << std::endl;
sqlite3_close (db);
//exit (1);
return;
}
Related
I want to backup the SQLite database from my file system to in memory database using C++. I read this website and try the example 1. It compiled perfectly but I get an error "Segmentation Fault". The code looks like this:
Ps: The last line printf("foo1"); is not printed
#include <stdio.h>
#include <sqlite3.h>
#include <nlohmann/json.hpp>
using namespace std;
using json = nlohmann::json;
static int callback(void *NotUsed, int count, char **data, char **column) {
int i;
int lastEntry = count - 1;
printf("{");
for (i=0; i<count; i++) {
if (i == lastEntry ) {
printf("\"%s\" : \"%s\"", column[i], data[i] ? data[i] : "NULL");
} else {
printf("\"%s\" : \"%s\",", column[i], data[i] ? data[i] : "NULL");
}
}
printf("}\n");
return 0;
}
int loadOrSaveDb(sqlite3 *pFile, const char *zFilename, int isSave){
int rc; /* Function return code */
sqlite3 *pInMemory; /* Database connection opened on zFilename */
sqlite3_backup *pBackup; /* Backup object used to copy data */
sqlite3 *pTo; /* Database to copy to (pFile or pInMemory) */
sqlite3 *pFrom; /* Database to copy from (pFile or pInMemory) */
/* Open the database file identified by zFilename. Exit early if this fails
** for any reason. */
rc = sqlite3_open(zFilename, &pFile);
if( rc==SQLITE_OK ){
pFrom = (isSave ? pInMemory : pFile);
pTo = (isSave ? pFile : pInMemory);
pBackup = sqlite3_backup_init(pTo, "main", pFrom, "main");
if( pBackup ){
(void)sqlite3_backup_step(pBackup, -1);
(void)sqlite3_backup_finish(pBackup);
}
printf("foo");
rc = sqlite3_errcode(pTo);
}
/* Close the database connection opened on database file zFilename
** and return the result of this function. */
(void)sqlite3_close(pFile);
return rc;
}
int main(int argc, char* argv[]) {
sqlite3 *db;
char *zErrMsg = 0;
int rc;
string sql;
/* Open Database */
rc = sqlite3_open("test.db", &db);
if( rc ) {
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
return(0);
} else {
fprintf(stderr, "Opened database successfully\n");
}
/* Create SQL Statement */
sql = "CREATE TABLE COMPANY(" \
"ID INT PRIMARY KEY NOT NULL," \
"name TEXT NOT NULL," \
"AGE INT NOT NULL," \
"ADDRESS CHAR(50)," \
"SALARY REAL );";
/* Execute SQL statement */
rc = sqlite3_exec(db, sql.c_str(), callback, 0, &zErrMsg);
if( rc != SQLITE_OK ){
fprintf(stderr, "SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
} else {
fprintf(stdout, "Table created successfully\n");
}
/* Insert SQL statement */
sql = "INSERT INTO COMPANY " \
"VALUES (1, 'Paul', 32, 'California', 20000.00 ); " \
"INSERT INTO COMPANY " \
"VALUES (2, 'Allen', 25, 'Texas', 15000.00 ); " \
"INSERT INTO COMPANY " \
"VALUES (3, 'Teddy', 23, 'Norway', 20000.00 );" \
"INSERT INTO COMPANY " \
"VALUES (4, 'Mark', 25, 'Rich-Mond ', 65000.00 );";
/* Execute SQL statement */
rc = sqlite3_exec(db, sql.c_str(), callback, 0, &zErrMsg);
if( rc != SQLITE_OK ){
fprintf(stderr, "SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
} else {
fprintf(stdout, "Records created successfully\n");
}
/* Select SQL statement */
//sql = "SELECT * from COMPANY WHERE NAME LIKE 'Paul'";
sql = "SELECT * from COMPANY";
/* Execute SQL statement */
rc = sqlite3_exec(db, sql.c_str(), callback, 0, &zErrMsg);
if( rc != SQLITE_OK ) {
fprintf(stderr, "SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
} else {
fprintf(stdout, "Operation done successfully\n");
}
loadOrSaveDb(db, "test.db", 0);
sqlite3_close(db);
printf("foo1");
return 0;
}
The destination for sqlite3_backup_init has to be a database connection handle. In order to use sqlite3 *pInMemory; as the destination, you should initialise it with such a handle:
sqlite3 *pInMemory; /* Database connection opened on zFilename */
sqlite3_open(":memory:", &pInMemory);
I try to save Data in my esp32. In my table there should be a primary key so that I can´t save something twice with the same id. But I get an error
The Error Code:
SQL error: disk I/O error
That´s the whole Code :
#include <stdio.h>
#include <string.h>
#include <sys/unistd.h>
#include <sys/stat.h>
#include "esp_err.h"
#include "esp_log.h"
#include "esp_spiffs.h"
#include "esp_timer.h"
#include "sqlite3.h"
static const char *TAG = "sqlite3_spiffs";
const char* data = "Callback function called";
static int callback(void *data, int argc, char **argv, char **azColName) {
int i;
printf("%s: ", (const char*)data);
for (i = 0; i<argc; i++){
printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
}
printf("\n");
return 0;
}
int db_open(const char *filename, sqlite3 **db) {
int rc = sqlite3_open(filename, db);
if (rc) {
printf("Can't open database: %s\n", sqlite3_errmsg(*db));
return rc;
} else {
printf("Opened database successfully\n");
}
return rc;
}
char *zErrMsg = 0;
int db_exec(sqlite3 *db, const char *sql) {
printf("%s\n", sql);
int64_t start = esp_timer_get_time();
int rc = sqlite3_exec(db, sql, callback, (void*)data, &zErrMsg);
if (rc != SQLITE_OK) {
printf("SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
} else {
printf("Operation done successfully\n");
}
printf("Time taken: %lld\n", esp_timer_get_time()-start);
return rc;
}
void setup()
{
sqlite3 *db1;
int rc;
ESP_LOGI(TAG, "Initializing SPIFFS");
esp_vfs_spiffs_conf_t conf = {
.base_path = "/spiffs",
//.partition_label = "storage",
.partition_label = NULL,
.max_files = 5,
.format_if_mount_failed = true
};
// Use settings defined above to initialize and mount SPIFFS filesystem.
// Note: esp_vfs_spiffs_register is an all-in-one convenience function.
esp_err_t ret = esp_vfs_spiffs_register(&conf);
if (ret != ESP_OK) {
if (ret == ESP_FAIL) {
ESP_LOGE(TAG, "Failed to mount or format filesystem");
} else if (ret == ESP_ERR_NOT_FOUND) {
ESP_LOGE(TAG, "Failed to find SPIFFS partition");
} else {
ESP_LOGE(TAG, "Failed to initialize SPIFFS (%s)", esp_err_to_name(ret));
}
return;
}
size_t total = 0, used = 0;
ret = esp_spiffs_info(NULL, &total, &used);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to get SPIFFS partition information (%s)", esp_err_to_name(ret));
} else {
ESP_LOGI(TAG, "Partition size: total: %d, used: %d", total, used);
}
// remove existing file
unlink("/spiffs/test1.db");
unlink("/spiffs/test2.db");
sqlite3_initialize();
if (db_open("/spiffs/test1.db", &db1))
return;
rc = db_exec(db1, "CREATE TABLE entries(id STRING PRIMARY KEY, value ,section)");
if (rc != SQLITE_OK) {
sqlite3_close(db1);
return;
}
rc = db_exec(db1, "INSERT INTO entries VALUES ('ROOMNAME', 'HH180L23G', 'general');");
if (rc != SQLITE_OK) {
sqlite3_close(db1);
return;
}
rc = db_exec(db1, "SELECT * FROM entries");
if (rc != SQLITE_OK) {
sqlite3_close(db1);
return;
}
sqlite3_close(db1);
// All done, unmount partition and disable SPIFFS
esp_vfs_spiffs_unregister(NULL);
ESP_LOGI(TAG, "SPIFFS unmounted");
//while(1);
}
void loop() {
// put your main code here, to run repeatedly:
}
I have ideas like that it has something to do with spiffs or with the mounting ..
I tried both partition.labels .. it´s the same error ..
I can´t figure out why it doesn´t let me take a primary key .. the same thing as I can´t implement unique Text or something I get the same error..
Did someone have experienced it before ?
It would be such a huge support
Log Monitor:
Opened database successfully
CREATE TABLE entries(id STRING PRIMARY KEY, value ,section)
SQL error: disk I/O error
Time taken: 179044
Looks like a known problem: https://github.com/siara-cc/esp32_arduino_sqlite3_lib/issues/18
Somebody has posted a workaround/fix, which doesn't look very promising to me - they explicitly specify the label of the SPIFFS partition when mounting it. Try it, see if it works.
https://github.com/siara-cc/esp32-idf-sqlite3/issues/13
Considering:
int user_id = 0x01; //dummy
int size_id = 0x01; //dummy
sqlite3_stmt *stmt;
sqlite3 *db;
int rc = sqlite3_open("path_to_database.db", &db);
if( rc != SQLITE_OK )
{
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
return -1;
}
rc = sqlite3_prepare_v2(db, "SELECT id, type, time_registered"
" from myTestTable"
" where user = ? and size = ?", -1, &stmt, NULL);
if (rc != SQLITE_OK)
{
throw std::string(sqlite3_errmsg(db));
sqlite3_finalize(stmt);
return -1;
}
rc = sqlite3_bind_int(stmt, 1, user_id);
if (rc != SQLITE_OK)
{
std::string errmsg(sqlite3_errmsg(db));
sqlite3_finalize(stmt);
throw errmsg;
return -1;
}
rc = sqlite3_bind_int(stmt, 2, size_id);
if (rc != SQLITE_OK)
{
std::string errmsg(sqlite3_errmsg(db));
sqlite3_finalize(stmt);
throw errmsg;
return -1;
}
[...]
This does work, but it seems not user friendly. I could use snprintf to prepare the statement instead of bind, but I'ld loose safety (even if I shouldn't be facing injection attacks on my local db).
Is there a better way to use sqlite3_bind_TYPES to bind more than one value in statement, with same types (or not if possible) ?
You could use a c++ wrapper like SQLiteCpp. If you don't like that one, there's a list at the bottom of the github page.
If you prefer to use the c interface you could invert the logic of the bind statements. This would at least unclutter the code a bit.
int rc = sqlite3_open("path_to_database.db", &db);
if( rc != SQLITE_OK )
{
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
return -1;
}
rc = sqlite3_prepare_v2(db, "SELECT id, type, time_registered"
" from myTestTable"
" where user = ? and size = ?", -1, &stmt, NULL);
if (rc == SQLITE_OK)
rc = sqlite3_bind_int(stmt, 1, user_id);
if (rc == SQLITE_OK)
rc = sqlite3_bind_int(stmt, 2, size_id);
if (rc != SQLITE_OK)
{
throw std::string(sqlite3_errmsg(db));
sqlite3_finalize(stmt);
return -1;
}
[...]
Mike
I have pasted my code below. It gives seg fault at sqlite3_get_table (). I want to get the number of row in table and return that. If number of row is greater than 0, the program needs to read all rows.
int countRowsInTable()
{
sqlite3 *db;
char *zErrMsg = 0;
int rc;
char const *sql;
char ***pazResult;
int *pnRow; /* Number of result rows written here */
int *pnColumn; /* Number of result columns written here */
rc = sqlite3_open("DeviceDetails.db", &db);
if( rc )
{
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
return(0);
}
else
{
fprintf(stderr, "Opened database successfully\n");
}
/* Create SQL statement */
sql = "select * from Device_Details";
/* Execute SQL statement */
// rc = sqlite3_exec(db, sql, 0/*callback*/, 0/*(void*)data*/, &zErrMsg);
rc = sqlite3_get_table(db, sql, pazResult, pnRow, pnColumn, &zErrMsg);
void sqlite3_free_table(char **result);
if( rc != SQLITE_OK )
{
fprintf(stderr, "SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
}
else
{
fprintf(stdout, "Operation done successfully\n");
cout<<"rows="<<pnRow<<endl;
sleep(10);
}
sqlite3_close(db);
cout<<"num rows =" << *pnRow <<endl;
}
after reading your suggestion I have changed my code. Now I use select count (*), but where (which) variable will hold the number of rows. Do, I need to implement the callback function in sqlite3_exec. Also I need to get data if row count is greater than zero.If I get the data in call back function, how to pass it to the function calling countRows in table? may be I can keep a global variable. As soon as call back is executed , it will signal the calling function through condition variable. So I have to maintain countRowsInTable function in a new thread. I there any simpler approach?
int countRowsInTable()
{
sqlite3 *db;
char *zErrMsg = new char[64];
int rc;
char const *sql;
char ***pazResult;
int *pnRow = new int; /* Number of result rows written here */
int *pnColumn = new int; /* Number of result columns written here */
//*pnRow = 0;
**pazResult = new char[20480];
rc = sqlite3_open("DeviceDetails.db", &db);
if( rc )
{
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
return(0);
}
else
{
fprintf(stderr, "Opened database successfully\n");
}
/* Create SQL statement */
sql = "select count(*) from Device_Details";
/* Execute SQL statement */
rc = sqlite3_exec(db, sql, 0/*callback*/, 0/*(void*)data*/, &zErrMsg);
//rc = sqlite3_get_table(db, sql, pazResult, pnRow, pnColumn, &zErrMsg);
//void sqlite3_free_table(char **result);
if( rc != SQLITE_OK )
{
fprintf(stderr, "SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
}
else
{
fprintf(stdout, "Operation done successfully\n");
}
}
Your are not calling the function with the right parameters. You are passing uninitialized pointers, whereas you must create the variables and pass pointers on them so SQLite can write the values.
const char *sql = "select * from Device_Details";
char **result;
int nrow;
int ncolumn;
char *errmsg;
int rc;
rc = sqlite3_get_table(db, sql, &result, &nrow, &ncolumn, &errmsg);
However, you should be aware the SQLite manual discourage the use of the function: "This is a legacy interface that is preserved for backwards compatibility. Use of this interface is not recommended."
Also, you should remove void sqlite3_free_table(char **result) that you probably pasted by mistake.
I got this code snippet to copy a file database to a memory database. it works but it destroys the original file database. (By destroys I mean sets the file size to zero)
/**
* Exec an sql statement in values[0] against
* the database in pData.
*/
int process_ddl_row(void * pData, int nColumns, char **values, char **columns)
{
if (nColumns != 1) {
return 1; // Error
}
sqlite3 * db = (sqlite3*)pData;
if( SQLITE_OK != sqlite3_exec(db, values[0], NULL, NULL, NULL) ) {
return 1;
}
return 0;
}
/**
* Insert from a table named by backup.{values[0]}
* into main.{values[0]} in database pData.
*/
int process_dml_row(void *pData, int nColumns, char **values, char **columns)
{
if (nColumns != 1) {
return 1; // Error
}
sqlite3* db = (sqlite3*)pData;
char *stmt = sqlite3_mprintf("insert into main.%q " "select * from backup.%q", values[0], values[0]);
if( SQLITE_OK != sqlite3_exec(db, stmt, NULL, NULL, NULL) ) {
return 1;
}
sqlite3_free(stmt);
return 0;
}
bool CDatabase::LoadFromFile( const char * databaseFile )
{
if( databaseFile == NULL || this->m_db == NULL ) {
return false;
}
sqlite3 * memorydb = this->m_db->GetDb() ; // Gets the open memory database.
sqlite3 * backupdb = NULL ;
if( SQLITE_OK != sqlite3_open_v2(databaseFile, &backupdb, SQLITE_OPEN_READONLY, NETBURNER_VFS_NAME ) ) {
return false;
}
// Schema
// ---------------------------------------------------------------------------
// Create the in-memory schema from the backup
if( SQLITE_OK != sqlite3_exec(backupdb, "BEGIN", NULL, NULL, NULL) ) {
return false;
}
if( SQLITE_OK != sqlite3_exec(backupdb, "SELECT sql FROM sqlite_master WHERE sql NOT NULL", &process_ddl_row, memorydb, NULL) ) {
return false;
}
if( SQLITE_OK != sqlite3_exec(backupdb, "COMMIT", NULL, NULL, NULL) ) {
return false;
}
sqlite3_close(backupdb);
// DATA
// ---------------------------------------------------------------------------
// Attach the backup to the in memory
char sql[255];
sprintf( sql, "ATTACH DATABASE '%s' as backup", databaseFile );
// This after this line the file database is set to zero bytes.
if( SQLITE_OK != sqlite3_exec(memorydb, sql, NULL, NULL, NULL) ) {
return false;
}
// Copy the data from the backup to the in memory
if( SQLITE_OK != sqlite3_exec(memorydb, "BEGIN", NULL, NULL, NULL) ) {
return false;
}
if( SQLITE_OK != sqlite3_exec(memorydb, "SELECT name FROM backup.sqlite_master WHERE type='table'", &process_dml_row, memorydb, NULL) ) {
return false;
}
if( SQLITE_OK != sqlite3_exec(memorydb, "COMMIT", NULL, NULL, NULL) ) {
return false;
}
if( SQLITE_OK != sqlite3_exec(memorydb, "DETACH DATABASE backup", NULL, NULL, NULL) ) {
return false;
}
return true;
}
I have been looking at it for awhile now and I can seem to figure out why this is happening. Suggestions?
Your code worked fine for me (checked on WinXP).
I think you should try running it without specifying VFS object (if possible) - just replace NETBURNER_VFS_NAME with 0 in sqlite3_open_v2 call.
This will show whether the problem is in VFS customization or not.