diff --git a/sources/container/qetcontainerdb.cpp b/sources/container/qetcontainerdb.cpp index f455f945a..3d706e975 100644 --- a/sources/container/qetcontainerdb.cpp +++ b/sources/container/qetcontainerdb.cpp @@ -149,7 +149,11 @@ class Statement } ~Statement() { sqlite3_finalize(m_stmt); } bool ok() const { return m_stmt; } + //A statement that could not be prepared (the database is not + //one, a table is missing) binds nothing and steps to no row: + //SQLite's own calls are not all safe on a null statement void bind(const QVariantList &values) { + if (!m_stmt) return; sqlite3_reset(m_stmt); sqlite3_clear_bindings(m_stmt); for (int i = 0 ; i < values.size() ; ++i) { @@ -165,11 +169,13 @@ class Statement } } bool run(const QVariantList &values) { + if (!m_stmt) return false; bind(values); return sqlite3_step(m_stmt) == SQLITE_DONE; } - bool next() { return sqlite3_step(m_stmt) == SQLITE_ROW; } + bool next() { return m_stmt && sqlite3_step(m_stmt) == SQLITE_ROW; } QVariant value(int column) const { + if (!m_stmt) return QVariant(); switch (sqlite3_column_type(m_stmt, column)) { case SQLITE_NULL: return QVariant(); case SQLITE_INTEGER: return QVariant(sqlite3_column_int(m_stmt, column)); diff --git a/sources/dataBase/projectdatabase.cpp b/sources/dataBase/projectdatabase.cpp index 15da3aeae..d73675c59 100644 --- a/sources/dataBase/projectdatabase.cpp +++ b/sources/dataBase/projectdatabase.cpp @@ -2052,6 +2052,13 @@ void projectDataBase::removeDiagram(Diagram *diagram) void projectDataBase::diagramInfoChanged(Diagram *diagram) { m_content_changed = true; + //Not while the project is being read: a folio's header read gives + //it a folio number counted from the folios read so far ("1/1" for + //the first of three); the rows are filled once every folio is + //read, from the document or from the index a .qetz was saved with + if (m_update_blocked) { + return; + } bindDiagramInfoValues(m_update_diagram_info_query, diagram); if (!m_update_diagram_info_query.exec()) { diff --git a/tests/qttest/lazy_folios_probe.cpp b/tests/qttest/lazy_folios_probe.cpp index 09630399c..2a3349ad5 100644 --- a/tests/qttest/lazy_folios_probe.cpp +++ b/tests/qttest/lazy_folios_probe.cpp @@ -765,6 +765,37 @@ int main(int argc, char **argv) check(indexed.unloadedFolioCount() == folios.size() - strip_folios.size(), "the .qetz opens building only the folios of the terminal strip's terminals"); check(numbering(indexed) == numbering(eager), "symbols' numbering from the index as when built"); + //The folios' rows as saved, not as reading their headers one + //by one numbers them ("1/1" for the first of three, D7) + { + auto folioRows = [](QETProject &project) { + QStringList rows; + for (const QString &row : tables(project)) + if (row.startsWith("diagram")) rows << row; + return rows; + }; + check(folioRows(indexed) == folioRows(eager), "the folios' rows from the index as when built"); + //Folio numbers that count the folios ("%id/%total") + const QString totals = output + "/totals.qetz"; + qputenv("QET_EAGER_FOLIOS", "1"); + { + QETProject numbered(zipped); + QetScriptApi api(&numbered); + for (int i = 0 ; i < api.folioCount() ; ++i) + api.setFolioProperty(i, "folio", "%id/%total"); + check(numbered.writeContainerFile(totals, &error), "folio numbers with totals saved"); + } + QETProject usual_totals(totals); + qunsetenv("QET_EAGER_FOLIOS"); + QETProject lazy_totals(totals); + check(lazy_totals.openedFromIndex(), "opened from its index"); + const QStringList lazy_rows = folioRows(lazy_totals), usual_rows = folioRows(usual_totals); + if (lazy_rows != usual_rows) + for (int i = 0 ; i < lazy_rows.size() && i < 3 ; ++i) + std::printf(" lazily %s\n built %s\n", qPrintable(lazy_rows.at(i).left(140)), + qPrintable(usual_rows.value(i).left(140))); + check(lazy_rows == usual_rows, "folio numbers with totals from the index as when built"); + } for (int i = 0; i < folios.size(); ++i) same(indexed, i, "shown from the .qetz"); check(indexed.toXml().toString() == eager.toXml().toString(), "the .qetz saves as when built"); diff --git a/tests/qttest/tst_qetcontainer.cpp b/tests/qttest/tst_qetcontainer.cpp index 1bccb0ce0..592d011e6 100644 --- a/tests/qttest/tst_qetcontainer.cpp +++ b/tests/qttest/tst_qetcontainer.cpp @@ -319,6 +319,25 @@ private slots: // A crafted project.sqlite whose "element" is a view, not a table, is // refused: nothing in a file's schema is trusted. + void garbageDatabaseRefused() + { + //Bytes that are no database: refused, not read (crashed reading + //the first statement until stage D7) + QDomDocument original; + QList entries = splitFile(QStringLiteral(QET_EXAMPLES_DIR "/perceuse.qet"), &original); + bool replaced = false; + for (QetZip::Entry &entry : entries) + if (entry.name == QLatin1String("project.sqlite")) { + entry.data = QByteArray("not a database").repeated(100); + replaced = true; + } + QVERIFY(replaced); + QDomDocument back; + QString error; + QVERIFY(!QetContainer::join(entries, &back, &error)); + QVERIFY(!error.isEmpty()); + } + void viewInsteadOfTableRefused() { QDomDocument original;