Show a folio without building its partners' folios (stage D5b)

Showing a folio built it and every folio holding a partner of its
symbols. Now a partner is built alone, as a symbol, into its folio's
scene (Diagram::buildSymbols()), and reused when that folio is built.

- A linked folio report built alone had no wires when it was linked,
  so its texts showing a wire's properties stayed empty: after any
  build, linked reports on the built folios and their partners look at
  their wires again, as opening does. Unlinked reports keep their texts,
  as opening leaves them.
- A report whose partner was built alone builds the partner's folio
  when its potential is followed.
- tableChainFolios() parsed every unbuilt folio's file. The index now
  lists the drawn tables and the table each continues (folio_table),
  and a .qetz opened from it uses that list. An index without it falls
  back to the folio files.
- A .qetz's project.sqlite is opened once per project
  (QetContainerDb::FolioReader), not once per folio built.

nuri (136 folios), opened fresh, one folio shown: median 1104 -> 184 ms,
each show builds one folio; industrial 234 -> 87 ms.

tst_lazyfolios: a folio whose table continues another is shown first
from a .qetz (fails when the index's chains are ignored). ctest 96/96
eager and lazy; 342-project corpus: master, lazy, .qetz lazy and .qetz
kept all the same.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ispyisail
2026-10-12 02:22:25 +13:00
parent 2f94724df3
commit b891b6354a
13 changed files with 371 additions and 76 deletions
+32 -5
View File
@@ -907,11 +907,14 @@ QStringList indexTables(const QSqlDatabase &db)
This database written to an SQLite file, for a .qetz to be opened from
(stage D2): every table but the drawing items, brought
up to date first, with the folios' own data @p folio_headers (in folio
order, each as Diagram::headerXml() writes it) and the stamp of the
schema it was written with.
order, each as Diagram::headerXml() writes it), the drawn tables
@p tables and how they continue one another (which folios a folio's
tables must be built with), and the stamp of the schema it was written
with.
@return the file, or nothing if it could not be written
*/
QByteArray projectDataBase::indexFile(const QList<std::pair<QUuid, QString>> &folio_headers)
QByteArray projectDataBase::indexFile(const QList<std::pair<QUuid, QString>> &folio_headers,
const QList<FolioTable> &tables)
{
if (m_content_changed) {
updateDB();
@@ -952,7 +955,9 @@ QByteArray projectDataBase::indexFile(const QList<std::pair<QUuid, QString>> &fo
&& query.exec(QStringLiteral("DROP TABLE image"))
&& query.exec(QStringLiteral("CREATE TABLE index_meta (key TEXT PRIMARY KEY, value TEXT)"))
&& query.exec(QStringLiteral("CREATE TABLE folio_header (pos INTEGER PRIMARY KEY, "
"uuid TEXT NOT NULL, xml TEXT NOT NULL)"));
"uuid TEXT NOT NULL, xml TEXT NOT NULL)"))
&& query.exec(QStringLiteral("CREATE TABLE folio_table (uuid TEXT NOT NULL, "
"folio_uuid TEXT NOT NULL, previous_uuid TEXT)"));
file.transaction();
query.prepare(QStringLiteral("INSERT INTO index_meta (key, value) VALUES (?, ?)"));
for (const auto &meta : {std::pair<QString, QString>{QStringLiteral("schema"), stamp},
@@ -970,6 +975,14 @@ QByteArray projectDataBase::indexFile(const QList<std::pair<QUuid, QString>> &fo
query.addBindValue(header.second);
ok = ok && query.exec();
}
query.prepare(QStringLiteral("INSERT INTO folio_table (uuid, folio_uuid, previous_uuid) "
"VALUES (?, ?, ?)"));
for (const FolioTable &table : tables) {
query.addBindValue(table.table.toString());
query.addBindValue(table.folio.toString());
query.addBindValue(table.previous.isNull() ? QVariant() : QVariant(table.previous.toString()));
ok = ok && query.exec();
}
ok = file.commit() && ok;
ok = query.exec(QStringLiteral("VACUUM")) && ok;
file.close();
@@ -1015,12 +1028,15 @@ void projectDataBase::discardFill()
when written with this database's schema, its tables plain tables, and
nothing in its schema run.
@param folio_headers : the folios' own data, in folio order
@param folio_tables : if not null, the drawn tables indexFile() wrote, or
nothing if the index predates them
@return false, with @p why set and nothing changed in the tables, if
the file cannot be read
*/
bool projectDataBase::fillFromIndex(const QByteArray &index,
QList<std::pair<QUuid, QString>> *folio_headers,
QString *why)
QString *why,
std::optional<QList<FolioTable>> *folio_tables)
{
auto refuse = [why](const QString &reason) {
if (why) *why = reason;
@@ -1093,6 +1109,17 @@ bool projectDataBase::fillFromIndex(const QByteArray &index,
query.exec(QStringLiteral("SELECT uuid, xml FROM idx.folio_header ORDER BY pos"));
while (query.next())
folio_headers->append({QUuid(query.value(0).toString()), query.value(1).toString()});
if (folio_tables) {
folio_tables->reset();
if (isTable(QStringLiteral("folio_table"))) {
QList<FolioTable> read;
query.exec(QStringLiteral("SELECT uuid, folio_uuid, previous_uuid FROM idx.folio_table"));
while (query.next())
read.append({QUuid(query.value(0).toString()), QUuid(query.value(1).toString()),
QUuid(query.value(2).toString())});
*folio_tables = read;
}
}
detach();
//The stores, from their tables, as the document would fill them