Repository: nomic-ai/gpt4all
File: gpt4all-chat/src/database.cpp (C++)

Code before:
        info.file = file;
        info.title = title;
        info.author = author;
        info.date = date;
        info.text = chunk_text;
        info.page = page;
        info.from = from;
        info.to = to;
        tempResults.insert(rowid, info);
#if defined(DEBUG)
        qDebug() << "retrieve rowid:" << rowid
                 << "chunk_text:" << chunk_text;
#endif
    }

    for (int id : searchResults)
        if (tempResults.contains(id))
            results->append(tempResults.value(id));
}

bool Database::ftsIntegrityCheck()
{
    bool isConsistent = false;

    QSqlQuery q(m_db);
    if (!q.prepare("insert into chunks_fts(chunks_fts, rank) values('integrity-check', 1);")) {
        qWarning() << "ERROR: Cannot prepare sql for fts integrity check" << q.lastError();
        return false;
    }

    // Returns an error executing sql if it the integrity check fails
    // See: https://www.sqlite.org/fts5.html#the_integrity_check_command
    isConsistent = q.exec();

    if (!isConsistent) {
        if (!q.prepare("insert into chunks_fts(chunks_fts) values('rebuild');")) {
            qWarning() << "ERROR: Cannot prepare sql for fts rebuild" << q.lastError();
            return false;
        }

        if (!q.exec()) {
            qWarning() << "ERROR: Cannot exec sql for fts rebuild" << q.lastError();
            return false;
        }
    }

    return true;
}

// FIXME This is very slow and non-interruptible and when we close the application and we're
// cleaning a large table this can cause the app to take forever to shut down. This would ideally be

Review comment (bug):
We should use global constants for these query strings, as we already do with the others.

Also, wouldn't it be good to check for SQLITE_CORRUPT_VTAB specifically (possibly via nativeErrorCode)? This way we could also avoid the need for the separate prepare() because we can disambiguate between preparation failure and a corrupt table.

Code after:
        const int from = q.value(8).toInt();
        const int to = q.value(9).toInt();
        const QString collectionName = q.value(10).toString();
        ResultInfo info;
        info.collection = collectionName;
        info.path = document_path;
        info.file = file;
        info.title = title;
        info.author = author;
        info.date = date;
        info.text = chunk_text;
        info.page = page;
        info.from = from;
        info.to = to;
        tempResults.insert(rowid, info);
#if defined(DEBUG)
        qDebug() << "retrieve rowid:" << rowid
                 << "chunk_text:" << chunk_text;
#endif
    }

    for (int id : searchResults)
        if (tempResults.contains(id))
            results->append(tempResults.value(id));
}

bool Database::ftsIntegrityCheck()
{
    QSqlQuery q(m_db);

    // Returns an error executing sql if it the integrity check fails
    // See: https://www.sqlite.org/fts5.html#the_integrity_check_command
    const bool success = q.exec(FTS_INTEGRITY_SQL);
    if (!success && q.lastError().nativeErrorCode() != "267" /*SQLITE_CORRUPT_VTAB from sqlite header*/) {
        qWarning() << "ERROR: Cannot prepare sql for fts integrity check" << q.lastError();
        return false;
    }

    if (!success && !q.exec(FTS_REBUILD_SQL)) {
        qWarning() << "ERROR: Cannot exec sql for fts rebuild" << q.lastError();
        return false;
    }

    return true;
}

// FIXME This is very slow and non-interruptible and when we close the application and we're
// cleaning a large table this can cause the app to take forever to shut down. This would ideally be
// interruptible and we'd continue 'cleaning' when we restart
bool Database::cleanDB()
{