0

I'm trying to make a class using sqlite3 database with a method that should insert the data in the table when the user adds the files to the application, but it is not working as intended,

    void Database::InsertSample(int Favorite, std::string Filename,
                                std::string SamplePack, int Channels, int Length,
                                int SampleRate, int Bitrate, std::string Comment,
                                std::string Path)
    {
        try
        {
            rc = sqlite3_open("Samples.db", &DB);
    
        sql = "INSERT INTO SAMPLES (FAVORITE, FILENAME, SAMPLEPACK, CHANNELS, \
                                    LENGTH, SAMPLERATE, BITRATE, BITSPERSAMPLE, PATH) \
            VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);";
    
        rc = sqlite3_prepare_v2(DB, sql.c_str(), 0, &stmt, 0);   // create the prepared statement
    
        rc = sqlite3_bind_int(stmt, 1, Favorite);
    
        rc = sqlite3_bind_text(stmt, 2, Filename.c_str(), Filename.size(), SQLITE_STATIC);
    
        rc = sqlite3_bind_text(stmt, 3, SamplePack.c_str(), SamplePack.size(), SQLITE_STATIC);
    
        rc = sqlite3_bind_int(stmt, 4, Channels);
    
        rc = sqlite3_bind_text(stmt, 5, Filename.c_str(), Filename.size(), SQLITE_STATIC);
    
        rc = sqlite3_bind_int(stmt, 6, Length);
    
        rc = sqlite3_bind_int(stmt, 7, SampleRate);
    
        rc = sqlite3_bind_int(stmt, 8, Bitrate);
    
        rc = sqlite3_bind_text(stmt, 9, Comment.c_str(), Comment.size(), SQLITE_STATIC);
    
        rc = sqlite3_bind_text(stmt, 10, Path.c_str(), Path.size(), SQLITE_STATIC);
    
        rc = sqlite3_step(stmt);
    
        rc = sqlite3_exec(DB, Sample.c_str(), NULL, 0, &ErrorMessage);
    
            if (rc != SQLITE_OK)
            {
                std::cerr << "Error! Cannot insert data into table." << std::endl;
                sqlite3_free(ErrorMessage);
            }
            else
            {
                std::cout << "Data inserted successfully." << std::endl;
            }
    
            sqlite3_close(DB);
        }
        catch (const std::exception &exception)
        {
            std::cerr << exception.what();
        }
    }

But this fails, throwing the error statement "Error! Cannot insert data into table.". Am I doing something wrong here.

I'm using this function in another class as,

void Browser::OnClickDirCtrl(wxCommandEvent& event)
{
    TagLib::FileRef File (DirCtrl->GetFilePath());
    TagLib::String Artist = File.tag()->artist();
    TagLib::String Album = File.tag()->album();
    TagLib::String Genre = File.tag()->genre();
    TagLib::String Title = File.tag()->title();
    TagLib::String Comment = File.tag()->comment();
    int Bitrate = File.audioProperties()->bitrate();
    int Channels = File.audioProperties()->channels();
    int Length = File.audioProperties()->lengthInMilliseconds();
    int LengthSec = File.audioProperties()->lengthInSeconds();
    int SampleRate = File.audioProperties()->sampleRate();

    wxVector<wxVariant> Data;
    Data.clear();
    Data.push_back(false);
    Data.push_back(TagLibTowx(Title));
    Data.push_back(TagLibTowx(Artist));
    Data.push_back(wxString::Format("%d",Channels));
    Data.push_back(wxString::Format("%d",LengthSec));
    Data.push_back(wxString::Format("%d",SampleRate));
    Data.push_back(wxString::Format("%d",Bitrate));
    Data.push_back(TagLibTowx(Comment));

    SampleListView->AppendItem(Data);

    db.InsertSample(0, Title.to8Bit(), Artist.to8Bit(), Channels, Length, SampleRate, Bitrate, Comment.to8Bit(), DirCtrl->GetFilePath().ToStdString());
}

This just a part of the function that should add the files to the database. As you can see, I am storing the path of the files in the database which is important data that I need for the project.

/---------/

EDIT: Adding a short sample,

main.cpp

#include "testdb.hpp"

int main()
{
    Database db;

    db.InsertData("Hello, World!");
    return 0;
}

testdb.hpp

#include <sqlite3.h>
#include <string>

class Database
{
    public:
        Database();
        ~Database();

    public:
        sqlite3* DB;
        int rc;
        char* ErrorMessage;
        std::string Test;
        std::string sql;

        sqlite3_stmt* stmt;

    public:
        void InsertData(std::string Path);
};

testdb.cpp

#include <exception>
#include <iostream>
#include <string>

#include "testdb.hpp"

Database::Database()
{
    /* Create SQL statement */
    Test = "CREATE TABLE TEST("
        "TEST           TEXT    NOT NULL);";

    try
    {
        rc = sqlite3_open("Test.db", &DB);
        rc = sqlite3_exec(DB, Test.c_str(), NULL, 0, &ErrorMessage);

        if (rc != SQLITE_OK)
        {
            std::cerr << "Error! Cannot create table." << std::endl;
            sqlite3_free(ErrorMessage);
        }
        else
        {
            std::cout << "Table created successfuly." << std::endl;
        }

        sqlite3_close(DB);
    }
    catch (const std::exception &exception)
    {
        std::cerr << exception.what();
    }
}

void Database::InsertData(std::string Test)
{
    try
    {
        rc = sqlite3_open("Test.db", &DB);

    sql = "INSERT INTO TEST (PATH) VALUES (?);";

    rc = sqlite3_prepare_v2(DB, sql.c_str(), 10, &stmt, 0);   // create the prepared statement
    // error handling goes here

    rc = sqlite3_bind_text(stmt, 10, Test.c_str(), Test.size(), SQLITE_STATIC);
    // error handling goes here

    rc = sqlite3_step(stmt);
    // error handling goes here

    rc = sqlite3_finalize(stmt);

        if (rc != SQLITE_OK)
        {
            std::cerr << "Error! Cannot insert data into table." << std::endl;
            sqlite3_free(ErrorMessage);
        }
        else if (rc == SQLITE_BUSY)
        {
            std::cout << "BUSY" << std::endl;
        }
        else if (rc == SQLITE_DONE)
        {
            std::cout << "DONE" << std::endl;
        }
        else if (rc == SQLITE_ERROR)
        {
            std::cout << "ERROR" << std::endl;
        }
        else if (rc == SQLITE_MISUSE)
        {
            std::cout << "MISUSE" << std::endl;
        }
        else
        {
            std::cout << "Data inserted successfully." << ErrorMessage << std::endl;
        }

        sqlite3_close(DB);
    }
    catch (const std::exception &exception)
    {
        std::cerr << exception.what();
    }
}

Database::~Database(){}

Compile using g++ main.cpp testdb.cpp -l sqlite3 -o db.

Same thing happening here, it says data inserted but database shows empty in sqlitebrowser.

apoorv569
  • 143
  • 1
  • 12
  • [Edit] your question to include the entire error message. – Code-Apprentice Jan 11 '21 at 15:44
  • Also, I suggest creating a simpler example that we can discuss here. Try doing something similar with a table that only has one column. If you can get that to work, then you can add to it from there. If you get a similar error, then you can edit your question to show the smaller code which makes it much easier for us to see what's going on. – Code-Apprentice Jan 11 '21 at 15:46
  • 1
    Tip: open a database connection once and reuse it throughout the life of your program, instead of every time you want to insert data. Much more efficient. You can save and reuse the prepared statement too. – Shawn Jan 11 '21 at 16:32
  • @Code-Apprentice Edited the post added short sample, with file names. – apoorv569 Jan 11 '21 at 17:35
  • @apoorv569 Does your short example cause the same error as the original example? – Code-Apprentice Jan 11 '21 at 20:36
  • @Code-Apprentice Yes. Opening the database in `sqlitebrowser` show as empty. -- https://github.com/sqlitebrowser/sqlitebrowser – apoorv569 Jan 11 '21 at 20:39
  • And what is the output when you run the short example? – Code-Apprentice Jan 11 '21 at 22:16
  • @Code-Apprentice https://imgur.com/g5Jq8NS – apoorv569 Jan 12 '21 at 06:21
  • @apoorv569 Please [edit] your question to include the output as text. – Code-Apprentice Jan 12 '21 at 16:12
  • I was able to fix it, you can check the comment section in the end of the page, it was because I had column name messed up, I was using filename twice, and in my prepare statement, I was not specifying the query size. – apoorv569 Jan 13 '21 at 09:54

1 Answers1

2

You have a sqlite3_step followed by sqlite3_exec, which is probably not what you intended. It's certainly not good. You must call sqlite3_reset or sqlite3_finalize to complete the prepared statement. It will also provide a specific error code that better describes the error if you get one from sqlite3_step.

See this explanation of `sqlite3_step'

Doug Currie
  • 40,708
  • 1
  • 95
  • 119
  • I tried using both `sqlite3_finalize` and `sqlite3_reset`, I get the message saying "Data inserted successfully." but when I try to view the database in `sqlitebrowser` it shows the database as empty. I also added `SQLITE_BUSY`, `SQLITE_DONE`, `SQLITE_ERROR` and `SQLITE_MISUSE` error codes, but I get nothing. – apoorv569 Jan 11 '21 at 16:39
  • Are you sure you're looking at the same "Test.db" file? Is the timestamp changing when you run your successful insert? – Doug Currie Jan 11 '21 at 21:07
  • I double checked , when I first run the program it says table created and data inserted successfully, but running again does not change the timestamp, but it says cannot create table. as it is already created but data inserted successfully, with these weird characters `��` after the message. – apoorv569 Jan 11 '21 at 21:19
  • The weird characters are from `ErrorMessage`, which is not initialized. If the timestamp is not changing, then you are looking at the wrong file. You can create the table with `CREATE TABLE IF NOT EXISTS` to avoid the error on table creation. – Doug Currie Jan 11 '21 at 21:59
  • Should be the right file, I created a different folder somewhere else which only has the 3 files I posted above, nothing else, after running the program that `Test.db` is created. – apoorv569 Jan 11 '21 at 22:06
  • Okay, I was able to fix it, by looking at this example - https://stackoverflow.com/a/11451953/1174378.. I had to change my prepare statement to - `rc = sqlite3_prepare_v2(DB, sql.c_str(), sql.size(), &stmt, NULL);`... But it is not inserting correct data according to what I passed in arguments. I tried `cout` those variables it prints fine, but in database it shows wrong value, for example the path is not path it inserts comment there, for length it should a `int` value but it inserts filename there. Also the program crashes after adding 4-5 records. – apoorv569 Jan 12 '21 at 07:05
  • I think I fixed wrong values being inserted too, it was because I mistyped a value 2 times (was inserting filename twice) and had a extra column that didn't exist. It adds data fine now. – apoorv569 Jan 12 '21 at 07:48