Home > Software engineering >  Passing SQLite3 database pointer to a Function having a sqlite3_close(db) gets a 21 error - bad para
Passing SQLite3 database pointer to a Function having a sqlite3_close(db) gets a 21 error - bad para

Time:10-19

My goal is to have a successful SQLite3 close after an open from functions I created.
I expected the close to return a code of zero.

I'm passing the SQLite3 *db pointer after a successful open to a function of my
construction having an rc = sqlite3_close(db). Seems in the act of passing the
db pointer something is lost and the close errors out. As a design feature I intend
to have many functions built around SQLite's db call functions so getting just a
simple open and close to work helps me in the future. This may just be a
miss-understanding on how to pass a pointer to a function.

The errors generated after code run:

Return code: 0 |Error code: 0 |Error message: not an error |Message: Database open success
New message -->Return code: 0 |Message: Database close success
Old message -->Return code: 21 |Error code: 21 |Error message: bad parameter or other API misuse |Message: Database close failed 

My code:

#include <iostream>
#include <string>
#include <sqlite3.h>
using namespace std;

// Function prototypes
int openDB(sqlite3**, string); // Mod - Added additional *
int closeDB(sqlite3**); // Mod - Added additional *


int main()
{    
    **// Open database**
    sqlite3 *db;
    string errmsg;
    int rc;
    string dbStr = "/home/steven/sparks-robotics/data/myDB.db";
    const char* database = dbStr.c_str();
    rc = openDB(&db,database); // Mod - Added &
    if (rc != EXIT_SUCCESS) {return EXIT_FAILURE;}

    **// Close database**
    rc = closeDB(&db); // Mod - Added &
    if (rc != EXIT_SUCCESS) {return EXIT_FAILURE;}

return 0;
};

int openDB(sqlite3 **db, string dbStr) { // Mod - Added *
    int rc;         // SQLite return code
    const char* em; // SQLite error message 
    int ec;         // SQLite error code
    
    string errmsg;
    const char* database = dbStr.c_str();
    rc = sqlite3_open_v2(database, &*db, SQLITE_OPEN_READONLY, NULL); // Mod - Added *
    errmsg = "Database open success";
    ec = sqlite3_errcode(*db); // Mod - Added *
    em = sqlite3_errmsg(*db);  // Mod - Added *
    if(rc != SQLITE_OK) {errmsg = "Database open failed";}
    cout << "Return code: " << rc <<" |Error code: " << ec << " |Error message: " << em << " |Message: "   errmsg << endl;
    return rc;
};

int closeDB(sqlite3 **db) { // Mod - Added *
    int rc;         // SQLite return code
    //const char* em; // SQLite error message // Mod - Deleted
    //int ec;         // SQLite error code // Mod - Deleted

    string errmsg;
    rc = sqlite3_close(*db); // Mod - Added *
    errmsg = "Database close success";
    //ec = sqlite3_errcode(*db); // Mod - deleted, db already closed
    //em = sqlite3_errmsg(*db);  // Mod - deleted, db already closed
    if(rc != SQLITE_OK) {errmsg = "Database close failed";}
    //cout << "Return code: " << rc <<" |Error code: " << ec << " |Error message: " << em << " |Message: "   errmsg << endl; 
    cout << "Return code: " << rc << " |Message: "   errmsg << endl;  
    return rc;
};

CodePudding user response:

As I wrote in my comment, changes to db in openDB do not show up in the variable db in main. There are two important things to understand:

  1. Pointer variables have an address like any other variable, but their value is also an address.
  2. C defaults to "pass by value"

I will explain the problem by interpreting your code myself:

  • Before the call to openDB, the db in main (henceforth main_db) has address=0xMAIN_DB and value=GARBAGE (because you failed to initialize it)
  • At the start of openDB, openDB_db has address=0xOPENDB_DB (different from main_db) and value=GARBAGE (because C is pass by value)
  • You call sqlite3_open with &openDB_db, so it dutifully writes a new value to address 0xOPENDB_DB. 0xMAIN_DB is untouched.

By contrast, if you change the signature of openDB to take a sqlite3 *&, openDB_db is a reference to main_db, which means that openDB_db will have the same address as main_db: 0xMAIN_DB. When you now pass &openDB_db to sqlite3_open, it will effectively write a pointer value into 0xMAIN_DB, which means that main_db also has that pointer value.

  • Related