mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-14 17:44:13 +02:00
Save a .qetz without building the folios not touched (stage D4)
With QET_LAZY_FOLIOS=1, a project opened from a .qetz's index saves a .qetz without building the folios it never built, when building them would write the same: their parts are copied as they were and their rows of the document's engineering data from the project.sqlite they were read from (QetContainerDb::extract() with kept_from, QetContainer::split() with the old entries, pictures included). Whether a folio may be kept follows the rules of stage 5.5 (QETProject::toXml() with kept_parts): nothing project-wide changed since opening; a folio whose own data changed is built with its partners; after an edit, folios holding a table filled from the database are built; the partners still waiting of every built folio are built. Trusted only for a project opened from a format 3 index written by this version whose folios are named by their uuid. Format 3 names a folio by its uuid in the database (doc_meta folio_ids = uuid) when every folio has one of its own, so that a kept folio keeps its rows whatever its place; copies keep the rows' order (ORDER BY rowid). A folio not built keeps the name of its part until it is built (Diagram::deferredPart()). Measured, the 136-folio f2099 opened from its .qetz and saved as a .qetz: 4.75 s with no folio built, 33 s with every folio built; its 136 folio parts copied byte for byte; a kept save of a kept save is the same bytes. industrial.qetz untouched: 28 of its 47 folios not built are built to save (link chains, as in 5.5); after everyday edits on its busiest folio, 7 stay not built. tst_lazyfolios: an untouched .qetz saved without building, read back, holds the project; edits saved without building hold, read back, what the same edits saved with every folio built hold (read back too: a wire numbered from its potential is saved with no sequential numbers whichever the way -- reading it back renumbers it, a separate issue); fails when a kept folio's symbol information is not copied. Corpus, 342 projects: a .qetz saved without building, read and saved as .qet = eager = master on all 342. ctest 96/96 with and without QET_LAZY_FOLIOS=1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -24,12 +24,15 @@
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QSet>
|
||||
#include <QUuid>
|
||||
#include <QVariant>
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
namespace {
|
||||
const QString KeyAttribute = QStringLiteral("qetz-key");
|
||||
//A folio written as it was read (stage D4): its part
|
||||
const QString KeptPartAttribute = QStringLiteral("qetz-kept-part");
|
||||
const QString FolioAttribute = QStringLiteral("qetz-folio");
|
||||
//What a moved block leaves where it was. Removing it instead would leave
|
||||
//the whitespace before and after it side by side, read back as one text
|
||||
@@ -604,7 +607,8 @@ bool restoreDiagram(Database &db, QDomElement d, QString *error)
|
||||
folios still in it) into a new database.
|
||||
@return the database file, or an empty array with @p error set
|
||||
*/
|
||||
QByteArray QetContainerDb::extract(QDomElement project, QString *error, const QByteArray &index)
|
||||
QByteArray QetContainerDb::extract(QDomElement project, QString *error, const QByteArray &index,
|
||||
const QByteArray &kept_from)
|
||||
{
|
||||
Database db;
|
||||
db.m_prefix = QStringLiteral("doc_");
|
||||
@@ -627,6 +631,28 @@ QByteArray QetContainerDb::extract(QDomElement project, QString *error, const QB
|
||||
if (!db.exec(schema())) {
|
||||
setError(error, QStringLiteral("cannot create the project database"));
|
||||
return {};
|
||||
}
|
||||
//The database of the .qetz this project was read from, for the folios
|
||||
//kept as they were (stage D4)
|
||||
if (!kept_from.isEmpty()) {
|
||||
unsigned char *copy = static_cast<unsigned char *>(sqlite3_malloc64(sqlite3_uint64(kept_from.size())));
|
||||
if (!copy) { setError(error, QStringLiteral("out of memory")); return {}; }
|
||||
memcpy(copy, kept_from.constData(), size_t(kept_from.size()));
|
||||
if (!db.exec(QStringLiteral("ATTACH DATABASE ':memory:' AS old"))
|
||||
|| sqlite3_deserialize(db.m_db, "old", copy, kept_from.size(), kept_from.size(),
|
||||
SQLITE_DESERIALIZE_FREEONCLOSE | SQLITE_DESERIALIZE_READONLY) != SQLITE_OK) {
|
||||
setError(error, QStringLiteral("cannot read the database the kept folios come from"));
|
||||
return {};
|
||||
}
|
||||
db.exec(QStringLiteral("PRAGMA trusted_schema = OFF"));
|
||||
for (const QString &table : DocTables) {
|
||||
Statement kind(db, QStringLiteral("SELECT type FROM old.sqlite_master WHERE name = ?"));
|
||||
kind.bind({db.table(table)});
|
||||
if (!kind.next() || kind.value(0).toString() != QLatin1String("table")) {
|
||||
setError(error, QStringLiteral("the kept folios' database has no table %1").arg(table));
|
||||
return {};
|
||||
}
|
||||
}
|
||||
}
|
||||
db.exec(QStringLiteral("BEGIN"));
|
||||
|
||||
@@ -683,10 +709,53 @@ QByteArray QetContainerDb::extract(QDomElement project, QString *error, const QB
|
||||
const QStringList conductor_keys = keys(all_conductors, QStringLiteral("c"));
|
||||
int e_index = 0, c_index = 0;
|
||||
|
||||
//A folio's id is its uuid when every folio has one of its own, so
|
||||
//that a folio kept as it was keeps its rows (format 3, D4)
|
||||
bool uuid_ids = true;
|
||||
{
|
||||
QSet<QString> seen;
|
||||
for (const QDomElement &d : diagrams) {
|
||||
const QString uuid = d.attribute(QStringLiteral("uuid"));
|
||||
if (QUuid(uuid).isNull() || seen.contains(uuid)) { uuid_ids = false; break; }
|
||||
seen.insert(uuid);
|
||||
}
|
||||
}
|
||||
if (uuid_ids) {
|
||||
ok = ok && meta.run({QStringLiteral("folio_ids"), QStringLiteral("uuid")});
|
||||
}
|
||||
|
||||
int pos = 0;
|
||||
for (QDomElement d : diagrams)
|
||||
{
|
||||
const QString id = QStringLiteral("f%1").arg(++pos);
|
||||
++pos;
|
||||
const QString id = uuid_ids ? d.attribute(QStringLiteral("uuid")) : QStringLiteral("f%1").arg(pos);
|
||||
//Kept as it was: its rows from the database it was read from
|
||||
if (d.hasAttribute(KeptPartAttribute)) {
|
||||
if (kept_from.isEmpty() || !uuid_ids) {
|
||||
setError(error, QStringLiteral("a folio kept as it was, without its database"));
|
||||
return {};
|
||||
}
|
||||
Statement copy(db, QStringLiteral("INSERT INTO folio SELECT id, ?, %1, props_at, props_attrs "
|
||||
"FROM old.folio WHERE id = ?")
|
||||
.arg(QStringLiteral("\"") + FolioData.join(QStringLiteral("\", \"")) + QStringLiteral("\"")));
|
||||
ok = ok && copy.run({pos, id}) && sqlite3_changes(db.m_db) == 1;
|
||||
for (const QString &sql : {
|
||||
QStringLiteral("INSERT INTO folio_property SELECT * FROM old.folio_property WHERE folio_id = ? ORDER BY rowid"),
|
||||
QStringLiteral("INSERT INTO element SELECT * FROM old.element WHERE folio_id = ? ORDER BY rowid"),
|
||||
QStringLiteral("INSERT INTO element_info SELECT * FROM old.element_info WHERE element_key IN "
|
||||
"(SELECT key FROM old.element WHERE folio_id = ?) ORDER BY rowid"),
|
||||
QStringLiteral("INSERT INTO link SELECT * FROM old.link WHERE element_key IN "
|
||||
"(SELECT key FROM old.element WHERE folio_id = ?) ORDER BY rowid"),
|
||||
QStringLiteral("INSERT INTO conductor SELECT * FROM old.conductor WHERE folio_id = ? ORDER BY rowid")}) {
|
||||
Statement rows(db, sql);
|
||||
ok = ok && rows.run({id});
|
||||
}
|
||||
if (!ok) {
|
||||
setError(error, QStringLiteral("folio %1 kept as it was is not in its database").arg(id));
|
||||
return {};
|
||||
}
|
||||
continue;
|
||||
}
|
||||
QVariantList row{id, pos};
|
||||
row += take(d, FolioData);
|
||||
QDomElement props = d.firstChildElement(QStringLiteral("properties"));
|
||||
@@ -859,3 +928,18 @@ bool QetContainerDb::restoreFolio(QDomElement diagram, const QByteArray &databas
|
||||
Database db;
|
||||
return openDatabase(db, database, error) && restoreDiagram(db, diagram, error);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief QetContainerDb::foliosByUuid
|
||||
@return true if @p database, a format 3 project.sqlite, names its
|
||||
folios by their uuid: a folio written as it was read keeps its rows
|
||||
under it (stage D4)
|
||||
*/
|
||||
bool QetContainerDb::foliosByUuid(const QByteArray &database)
|
||||
{
|
||||
Database db;
|
||||
if (!openDatabase(db, database, nullptr) || db.m_prefix.isEmpty())
|
||||
return false;
|
||||
Statement s(db, QStringLiteral("SELECT value FROM meta WHERE key = 'folio_ids'"));
|
||||
return s.next() && s.value(0).toString() == QLatin1String("uuid");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user