diff --git a/sources/container/qetcontainer.h b/sources/container/qetcontainer.h index fb0623f7e..72184045d 100644 --- a/sources/container/qetcontainer.h +++ b/sources/container/qetcontainer.h @@ -48,8 +48,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 = 3; - /// 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. Format 2 is /// still read; a later format whose min-reader allows this one is /// read too, and said so through join()'s @p format. diff --git a/sources/container/qetcontainerdb.cpp b/sources/container/qetcontainerdb.cpp index 37ed07749..66cf9cbf8 100644 --- a/sources/container/qetcontainerdb.cpp +++ b/sources/container/qetcontainerdb.cpp @@ -121,11 +121,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," @@ -455,6 +455,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, so the uuid the folio file already holds says which row //is the item's (@p own true). Else (F100: symbols copied with their @@ -510,9 +525,9 @@ QByteArray QetContainerDb::extract(QDomElement project, QString *error) db.exec(QStringLiteral("BEGIN")); Statement meta(db.m_db, QStringLiteral("INSERT INTO meta VALUES (?,?)")); - Statement project_property(db.m_db, QStringLiteral("INSERT INTO project_property VALUES (?,?,?,?)")); + Statement project_property(db.m_db, QStringLiteral("INSERT INTO project_property VALUES (?,?,?,?,?)")); Statement folio(db.m_db, QStringLiteral("INSERT INTO folio VALUES (?,?,%1,?)").arg(placeholders(FolioData.size()))); - Statement folio_property(db.m_db, QStringLiteral("INSERT INTO folio_property VALUES (?,?,?,?,?)")); + Statement folio_property(db.m_db, QStringLiteral("INSERT INTO folio_property VALUES (?,?,?,?,?,?)")); Statement element(db.m_db, QStringLiteral("INSERT INTO element VALUES (?,?,%1,?,?)").arg(placeholders(ElementData.size()))); Statement element_info(db.m_db, QStringLiteral("INSERT INTO element_info VALUES (?,?,?,?,?,?)")); Statement link(db.m_db, QStringLiteral("INSERT INTO link VALUES (?,?,?,?)")); @@ -526,27 +541,15 @@ QByteArray QetContainerDb::extract(QDomElement project, QString *error) // 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"))); }; //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); } @@ -597,18 +600,9 @@ QByteArray QetContainerDb::extract(QDomElement project, QString *error) ok = ok && element.run(erow); if (!info.isNull()) { int ord = 0; - for (QDomElement i : children(info, 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(info, QStringLiteral("elementInformation"))) + ok = ok && element_info.run(QVariantList{key, ord++} + << entryRow(i, QStringLiteral("elementInformation"))); } if (!links.isNull()) { int ord = 0; @@ -755,10 +749,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.m_db, QStringLiteral("SELECT name, value, attrs FROM project_property ORDER BY ord")); + Statement s(db.m_db, QStringLiteral("SELECT name, value, attrs, %1 FROM project_property ORDER BY ord") + .arg(show_column)); while (s.next()) props.appendChild(entry(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")); @@ -812,11 +807,12 @@ bool QetContainerDb::restore(QDomElement project, const QByteArray &database, QS if (hasMarker(d, QStringLiteral("properties"))) { QDomElement props = document.createElement(QStringLiteral("properties")); setAttributesJson(props, values.at(FolioData.size())); - Statement p(db.m_db, QStringLiteral("SELECT name, value, attrs FROM folio_property WHERE %1 = ? ORDER BY ord").arg(folio_ref)); + Statement p(db.m_db, QStringLiteral("SELECT name, value, attrs, %1 FROM folio_property WHERE %2 = ? ORDER BY ord") + .arg(show_column, folio_ref)); 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)); diff --git a/tests/qttest/tst_qetcontainer.cpp b/tests/qttest/tst_qetcontainer.cpp index a744bd2bc..82f0e8020 100644 --- a/tests/qttest/tst_qetcontainer.cpp +++ b/tests/qttest/tst_qetcontainer.cpp @@ -331,6 +331,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;