diff --git a/sources/container/qetcontainer.h b/sources/container/qetcontainer.h index 241716950..7b1e2c890 100644 --- a/sources/container/qetcontainer.h +++ b/sources/container/qetcontainer.h @@ -56,8 +56,8 @@ namespace QetContainer /// The format written; files of a later format are refused by join() /// unless their min-reader allows this one. constexpr int Format = 4; - /// 3: the database's rows keyed by the items' uuids, true/false as - /// 1/0, its PRAGMA user_version the format, the symbols' type left + /// 3: the database's rows keyed by the items' uuids, true/false and + /// show flags as 1/0, its PRAGMA user_version the format, the symbols' type left /// in the folio files, no column for where a block sat. /// 4: what QElectroTech computes left out of the folios /// (dropComputed()), the tables written without keys or indexes. diff --git a/sources/container/qetcontainerdb.cpp b/sources/container/qetcontainerdb.cpp index 8ddf4518a..e4f6a96fe 100644 --- a/sources/container/qetcontainerdb.cpp +++ b/sources/container/qetcontainerdb.cpp @@ -123,11 +123,11 @@ QString schema() { return QStringLiteral( "CREATE TABLE meta (key TEXT PRIMARY KEY, value TEXT);" - "CREATE TABLE project_property (ord INTEGER PRIMARY KEY, name TEXT, value TEXT, attrs TEXT);" + "CREATE TABLE project_property (ord INTEGER PRIMARY KEY, name TEXT, value TEXT, show INTEGER, attrs TEXT);" "CREATE TABLE folio (uuid TEXT PRIMARY KEY, pos INTEGER NOT NULL UNIQUE, %1," " props_attrs TEXT);" "CREATE TABLE folio_property (folio_uuid TEXT NOT NULL REFERENCES folio, ord INTEGER NOT NULL," - " name TEXT, value TEXT, attrs TEXT, PRIMARY KEY (folio_uuid, ord));" + " name TEXT, value TEXT, show INTEGER, attrs TEXT, PRIMARY KEY (folio_uuid, ord));" "CREATE TABLE element (uuid TEXT PRIMARY KEY, folio_uuid TEXT NOT NULL REFERENCES folio, %2," " info_attrs TEXT, links_attrs TEXT);" "CREATE TABLE element_info (element_uuid TEXT NOT NULL REFERENCES element, ord INTEGER NOT NULL," @@ -484,6 +484,21 @@ QList children(const QDomElement &parent, const QString &tag) return list; } + //A list entry -- a , an -- as its row: + //its name, value, show flag (0 or 1, else left in the JSON) and any + //other attribute as JSON, its tag too if it is not @p usual_tag +QVariantList entryRow(const QDomElement &e, const QString &usual_tag) +{ + QVariant value; + simpleValue(e, &value); + const QString show = e.attribute(QStringLiteral("show")); + const bool show_stored = show == QLatin1String("0") || show == QLatin1String("1"); + QStringList kept{QStringLiteral("name")}; + if (show_stored) kept << QStringLiteral("show"); + return {e.hasAttribute(QStringLiteral("name")) ? QVariant(e.attribute(QStringLiteral("name"))) : QVariant(), + value, show_stored ? QVariant(show.toInt()) : QVariant(), attributesJson(e, kept, usual_tag)}; +} + //The key of each of @p items: its uuid, when it has one no other of //@p items has and @p reserved does not hold, so the uuid the folio file //already holds says which row is the item's (@p own true). Else (F100: @@ -680,12 +695,12 @@ bool restoreDiagram(Database &db, QDomElement d, UsedRows &used, QString *error) if (hasMarker(d, QStringLiteral("properties"))) { QDomElement props = document.createElement(QStringLiteral("properties")); setAttributesJson(props, values.at(FolioData.size())); - Statement p(db, QStringLiteral("SELECT name, value, attrs FROM folio_property WHERE %1 = ? ORDER BY ord") - .arg(db.folioRef())); + Statement p(db, QStringLiteral("SELECT name, value, attrs, %1 FROM folio_property WHERE %2 = ? ORDER BY ord") + .arg(db.showColumn(), db.folioRef())); p.bind({id}); while (p.next()) props.appendChild(entry(p.value(0), QStringLiteral("name"), p.value(1), p.value(2), - QStringLiteral("property"))); + QStringLiteral("property"), p.value(3))); restoreWhitespace(props, values.at(FolioData.size())); if (!replaceMarker(d, props)) { setError(error, QStringLiteral("folio %1's properties have no place").arg(id)); @@ -832,9 +847,9 @@ QByteArray QetContainerDb::extract(QDomElement project, QString *error, const QB db.exec(QStringLiteral("BEGIN")); Statement meta(db, QStringLiteral("INSERT INTO meta VALUES (?,?)")); - Statement project_property(db, QStringLiteral("INSERT INTO project_property VALUES (?,?,?,?)")); + Statement project_property(db, QStringLiteral("INSERT INTO project_property VALUES (?,?,?,?,?)")); Statement folio(db, QStringLiteral("INSERT INTO folio VALUES (?,?,%1,?)").arg(placeholders(FolioData.size()))); - Statement folio_property(db, QStringLiteral("INSERT INTO folio_property VALUES (?,?,?,?,?)")); + Statement folio_property(db, QStringLiteral("INSERT INTO folio_property VALUES (?,?,?,?,?,?)")); Statement element(db, QStringLiteral("INSERT INTO element VALUES (?,?,%1,?,?)").arg(placeholders(ElementData.size()))); Statement element_info(db, QStringLiteral("INSERT INTO element_info VALUES (?,?,?,?,?,?)")); Statement link(db, QStringLiteral("INSERT INTO link VALUES (?,?,?,?)")); @@ -848,44 +863,22 @@ QByteArray QetContainerDb::extract(QDomElement project, QString *error, const QB // of a block, as rows of @p rows (with @p prefix values) auto moveProperties = [&](QDomElement block, Statement &rows, const QVariantList &prefix) { int ord = 0; - for (QDomElement p : children(block, QStringLiteral("property"))) { - QVariant value; - simpleValue(p, &value); - ok = ok && rows.run(QVariantList(prefix) << ord++ << (p.hasAttribute(QStringLiteral("name")) - ? QVariant(p.attribute(QStringLiteral("name"))) : QVariant()) - << value << attributesJson(p, {QStringLiteral("name")}, QStringLiteral("property"))); - } + for (QDomElement p : children(block, QStringLiteral("property"))) + ok = ok && rows.run(QVariantList(prefix) << ord++ << entryRow(p, QStringLiteral("property"))); }; //The entries of @p block, as the rows of the //symbol @p key auto moveInformation = [&](const QDomElement &block, const QString &key) { int ord = 0; - for (QDomElement i : children(block, QStringLiteral("elementInformation"))) { - QVariant value; - simpleValue(i, &value); - const QString show = i.attribute(QStringLiteral("show")); - const bool show_stored = show == QLatin1String("0") || show == QLatin1String("1"); - QStringList kept{QStringLiteral("name")}; - if (show_stored) kept << QStringLiteral("show"); - ok = ok && element_info.run({key, ord++, i.hasAttribute(QStringLiteral("name")) - ? QVariant(i.attribute(QStringLiteral("name"))) : QVariant(), - value, show_stored ? QVariant(show.toInt()) : QVariant(), - attributesJson(i, kept, QStringLiteral("elementInformation"))}); - } + for (QDomElement i : children(block, QStringLiteral("elementInformation"))) + ok = ok && element_info.run(QVariantList{key, ord++} << entryRow(i, QStringLiteral("elementInformation"))); }; //Project properties const QDomElement project_props = project.firstChildElement(QStringLiteral("properties")); if (!project_props.isNull() && listBlock(project_props, QStringLiteral("property"))) { ok = ok && meta.run({QStringLiteral("props_attrs"), blockJson(project_props)}); - int ord = 0; - for (QDomElement p : children(project_props, QStringLiteral("property"))) { - QVariant value; - simpleValue(p, &value); - ok = ok && project_property.run({ord++, p.hasAttribute(QStringLiteral("name")) - ? QVariant(p.attribute(QStringLiteral("name"))) : QVariant(), - value, attributesJson(p, {QStringLiteral("name")}, QStringLiteral("property"))}); - } + moveProperties(project_props, project_property, {}); leaveMarker(project_props); } @@ -1090,10 +1083,11 @@ bool QetContainerDb::restore(QDomElement project, const QByteArray &database, QS if (hasMarker(project, QStringLiteral("properties"))) { QDomElement props = document.createElement(QStringLiteral("properties")); setAttributesJson(props, meta.value(QStringLiteral("props_attrs"))); - Statement s(db, QStringLiteral("SELECT name, value, attrs FROM project_property ORDER BY ord")); + Statement s(db, QStringLiteral("SELECT name, value, attrs, %1 FROM project_property ORDER BY ord") + .arg(db.showColumn())); while (s.next()) props.appendChild(entryElement(document, s.value(0), QStringLiteral("name"), s.value(1), s.value(2), - QStringLiteral("property"))); + QStringLiteral("property"), s.value(3))); restoreWhitespace(props, meta.value(QStringLiteral("props_attrs"))); if (!replaceMarker(project, props)) { setError(error, QStringLiteral("the project's properties have no place")); @@ -1143,7 +1137,8 @@ bool QetContainerDb::currentFormat(const QByteArray &database) const QList> columns = { {QStringLiteral("folio"), int(FolioData.size()) + 3}, {QStringLiteral("element"), int(ElementData.size()) + 4}, - {QStringLiteral("conductor"), int(ConductorData.size()) + 2}}; + {QStringLiteral("conductor"), int(ConductorData.size()) + 2}, + {QStringLiteral("folio_property"), 6}, {QStringLiteral("element_info"), 6}}; for (const auto &table : columns) { Statement s(db, QStringLiteral("SELECT count(*) FROM pragma_table_info('%1')").arg(table.first)); if (!s.next() || s.value(0).toInt() != table.second) diff --git a/tests/qttest/tst_qetcontainer.cpp b/tests/qttest/tst_qetcontainer.cpp index ef5914eb2..89b56e57d 100644 --- a/tests/qttest/tst_qetcontainer.cpp +++ b/tests/qttest/tst_qetcontainer.cpp @@ -416,6 +416,26 @@ private slots: QCOMPARE(query(database, "SELECT count(*) FROM meta WHERE key = 'props_at'"), QStringLiteral("0")); } + // A project as QElectroTech saves it needs no JSON: every attribute it + // writes has a column, the show flag of a property too + void savedProjectNeedsNoJson() + { + QString error; + const QList entries = QetContainer::split(savedProject(), QString(), &error); + QVERIFY2(!entries.isEmpty(), qPrintable(error)); + const QByteArray database = entry(entries, QStringLiteral("project.sqlite")); + QVERIFY(query(database, "SELECT count(*) FROM project_property").toInt() > 0); + QCOMPARE(query(database, "SELECT count(*) FROM project_property WHERE show IS NOT NULL"), + query(database, "SELECT count(*) FROM project_property")); + for (const char *sql : {"SELECT count(*) FROM project_property WHERE attrs IS NOT NULL", + "SELECT count(*) FROM folio_property WHERE attrs IS NOT NULL", + "SELECT count(*) FROM element_info WHERE attrs IS NOT NULL", + "SELECT count(*) FROM link WHERE attrs IS NOT NULL", + "SELECT count(*) FROM folio WHERE props_attrs IS NOT NULL", + "SELECT count(*) FROM element WHERE info_attrs IS NOT NULL OR links_attrs IS NOT NULL"}) + QVERIFY2(query(database, sql) == QLatin1String("0"), sql); + } + void laterFormatRefused() { QDomDocument original;