Use SQLite in Qt

SQLite is a lightweight, file-based database that needs no separate server process, which makes it a perfect fit for desktop applications that want to store data locally. Qt ships with a built-in SQLite driver as part of its SQL module, so you can talk to a database without pulling in any third-party dependency. This post walks through the few steps required to set it up and run your first queries.

Note: This post has been updated for Qt 6. Qt’s SQL API has stayed source-compatible across major releases, so the original steps still apply — QT += sql, the QSQLITE driver, and QSqlQuery behave the same on modern Qt as they did when this was first written.

Enabling the SQL module

Qt keeps its database classes in a separate module that is not linked by default. To pull it in, add the following line to your project’s .pro file and rebuild:

QT += sql

Wrapping the database in a manager class

Rather than scattering connection code across the project, it is cleaner to hide the database behind a small manager class. The header below exposes just enough to open the database, delete it, and report the last error that occurred:

#include <QtSql/QSqlDatabase>
#include <QtSql/QSqlError>

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

    public:
    bool openDB();
    bool deleteDB();
    QSqlError lastError();

    private:
    QSqlDatabase db;
};

Opening and removing the database

QSqlDatabase::addDatabase("QSQLITE") selects the SQLite driver, and setDatabaseName() points it at a file on disk. If that file does not exist yet, SQLite creates it the first time you call open(). Whenever a call fails, lastError() gives you a human-readable description through QSqlError::text(), so it is worth surfacing it to the caller. Because an SQLite database is nothing more than a single file, deleting it is just a matter of closing the connection and removing that file:

bool DatabaseManager::openDB() {
    // Find QSLite driver
    db = QSqlDatabase::addDatabase("QSQLITE");
    db.setDatabaseName("database_name_here");

    // Open database
    return db.open();
}

QSqlError DatabaseManager::lastError() {
    // If opening database has failed user can ask
    // error description by QSqlError::text()
    return db.lastError();
}

bool DatabaseManager::deleteDB() {
    // Close database
    db.close();
    return QFile::remove("database_name_here");
}

Running queries

Once the database is open, every statement goes through a QSqlQuery. For one-off statements such as creating or dropping a table, exec() takes the SQL string directly. When you insert user-supplied or otherwise dynamic data, however, prefer the prepared form with addBindValue(): it keeps the values properly escaped and protects you from SQL injection, instead of concatenating strings by hand. The example below creates a table, inserts a file record both ways, and finally drops the table:

QSqlQuery query;
// Create a new table
query.exec("CREATE TABLE filelist(fullpath VARCHAR PRIMARY KEY, timestamp VARCHAR)");

// Prepare file Information to insert
QFileInfo currentFile = ...;
QString filepath = currentFile.absoluteFilePath();
QString timestamp = currentFile.lastModified().date().toString("yyyy-MM");

// Insert a record by building the SQL string directly
query.exec("INSERT INTO filelist VALUES('" + filepath + "', '" + timestamp + "')");

// Or, more safely, bind the values to a prepared statement
query.prepare("INSERT INTO filelist VALUES(?, ?)");
query.addBindValue(filepath);
query.addBindValue(timestamp);
query.exec();

query.exec("DROP TABLE filelist");

That is all it takes: enable the SQL module, open a connection through the SQLite driver, and drive everything else with QSqlQuery. From here you can read rows back with query.next() and query.value() to display the stored data in your application.