【发布时间】:2023-04-10 18:57:01
【问题描述】:
在 Windows 10 x64 上使用 Qt 5.15.2
我的 sqlite3.h 说
#define SQLITE_VERSION "3.34.0"
#define SQLITE_VERSION_NUMBER 3034000
#define SQLITE_SOURCE_ID "2020-12-01 16:14:00 a26b6597e3ae272231b96f9982c3bcc17ddec2f2b6eb4df06a224b91089fed5b"
根据我在本网站上找到的 sqlite3 文档和其他资料,我正在将临时数据库复制到磁盘(:memory: 如果发布,tmp 文件如果调试)。
How to backup/store between sqlite memory database and file database in Qt?
How to access sqlite3 directly from Qt without linking sqlite3.dll a second time
问题是如果数据库超过给定大小,db.close() 行会导致程序崩溃,并显示这个。
数据库是这样创建的。如果我使用 100 而不是 1000,它似乎可以正常工作,不会崩溃。
void SymbolLibDocument::init() {
if (m_activeState) {
return;
} else {
// Creates temp database to prime save, save as, etc.
// Does not create anything with full filename
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", QString::fromStdString(m_connName));
#if defined(QT_DEBUG)
db.setDatabaseName("tmp_" + QString::fromStdString(m_name));
#elif defined(QT_NO_DEBUG)
db.setDatabaseName(":memory:");
#endif
if(!db.open()) {
qDebug() << "Can't create database";
}
QSqlQuery query(db);
const
QStringList qsl = {"DROP TABLE IF EXISTS hello;",
"CREATE TABLE hello (ID INTEGER PRIMARY KEY AUTOINCREMENT, \n"
" name TEXT CHECK(length(name) > 0)); ",
"INSERT INTO hello (name) VALUES ('giraffe');"};
dbutils::executeList(query, qsl, "Could not init", __LINE__);
query.exec("BEGIN");
QString s = QString("INSERT INTO hello (name) VALUES (:v);");
query.prepare(s);
for (int i = 0; i < 10000; i++) {
query.bindValue(":v", QVariant(i));
query.exec();
}
query.exec("COMMIT");
}
m_activeState = true;
}
void dbSaveFromTo(const std::string & connFrom, const std::string & fileTo) {
// Uses sqlite3 backup mechanism to write database connFrom to fileTo
//auto secs = std::chrono::milliseconds(50);
QString qsConnFrom = QString::fromStdString(connFrom);
// Need to clone db so it can be used from this function, which is callable
// as another thread.
// Also this needs to go in another scope so db object is destroyed
// at exit, prior to close and removal
{
QSqlDatabase db = QSqlDatabase::cloneDatabase(qsConnFrom, "CloneDb");
db.open();
QVariant qvhandle = db.driver()->handle();
if (qvhandle.isValid() && qstrcmp(qvhandle.typeName(), "sqlite3*") == 0) {
sqlite3 *pFrom = *static_cast<sqlite3 **>(qvhandle.data());
sqlite3 *pTo;
sqlite3_open(fileTo.c_str(), &pTo);
sqlite3_backup *pBackup = sqlite3_backup_init(pTo, "main", pFrom, "main");
if (pBackup) {
int pagesPerCycle = 1;
int pageCount = 0;
int pagesCopied = 0;
int pagesRemaining = 0;
do {
(void) sqlite3_backup_step(pBackup, pagesPerCycle);
if (sqlite3_errcode(pFrom) != SQLITE_OK) {
qDebug() << sqlite3_errmsg(pFrom);
}
if (sqlite3_errcode(pTo) != SQLITE_OK) {
qDebug() << sqlite3_errmsg(pTo);
}
pageCount = sqlite3_backup_pagecount(pBackup);
pagesRemaining = sqlite3_backup_remaining(pBackup);
pagesCopied = pageCount - pagesRemaining;
emit intEmitter.emitInt(pagesCopied, pageCount);
qDebug() << "emitting" << pagesCopied << "/" << pageCount;
//std::this_thread::sleep_for(secs);
} while(pagesRemaining > 0);
(void) sqlite3_backup_finish(pBackup);
} else {
throw std::logic_error("sqlite3_backup_init(...) failed");
}
// causes error 21 bad parameter or other API misuse,
// But this occurs even if open and close are called back-to-back
// with no operations in between
sqlite3_close(pTo);
if (sqlite3_errcode(pFrom) != SQLITE_OK) {
qDebug() << sqlite3_errmsg(pFrom);
}
if (sqlite3_errcode(pTo) != SQLITE_OK) {
qDebug() << sqlite3_errmsg(pTo);
}
} else {
throw std::logic_error("invalid driver handle");
}
db.close();
}
QSqlDatabase::removeDatabase("CloneDb");
}
我担心这是因为我使用了内置的 Qt Sql 功能(.pro 文件中的.pro 中的db.open、db.close、QSqlDatabase::cloneDatabase 等),但是我正在使用单独的 sqlite3 预编译 .dll 来访问 sqlite3_backup_* 函数。
当我执行db.close() 时为什么会崩溃?
【问题讨论】:
-
我认为问题是在 cloneDatabase 之后你没有检查打开与否
-
使用单例来避免这个问题
-
你能否详细说明单例如何避免这个问题?哪个类的单身人士?
-
正确的做法是不要保留QSqlDatabase成员,而是使用静态函数来访问它
-
静态函数不能用于我需要它做的事情。我只是对整个事情使用了原始 api,而且效果很好。