Write a symbol's link rows in the order it saves them, with its waiting partners

The project database's link table should say what each symbol saves:
QETZ-DB-FIRST stage 2a moves link reading and writing to it. Measured over
the corpus and 25 kinds of edit, its rows disagreed with the file for
three reasons; this fixes the one in the database:

- rows followed the order partners were built in, not the order the
  symbol saves them (11 symbols, mostly .qetz files where an edit built a
  partner's folio, with lazy folios);
- the rows were written by three pieces of code with three rules: the
  rebuild and the flush from the built partners then the waiting ones,
  the save from its own list.

Element::linksToSave() is now that one list: the order the symbol was
saved with, then partners linked since, a partner on a folio not built
included. toXml() writes it and the database's rows are written from it
(projectDataBase::insertLinkRows()), with their place in a new ord
column of the in-memory table (the .qetz file's own link table is not
touched). A built partner the project no longer has, on a removed folio,
still gets no row, as before.

The other two reasons are not here: symbols sharing a uuid (PR #1522)
and links saved to symbols on a removed folio (F113), which the save will
stop writing once it writes from the database (stage 2a-2).

QET_CHECK_LINK_ROWS=1 says at each save whether the rows are what the
symbols save, without building any folio. tst_resaveunchanged
linkRowsAfterLazyEdit moves a symbol of m_000 as a .qetz with lazy
folios: with the previous order, 2 symbols differ.

No saved byte changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XCdg2Er2vYhB8Vwa5G7fSR
This commit is contained in:
ispyisail
2026-10-18 15:47:59 +13:00
parent abd1c8df2f
commit ee2a060b16
6 changed files with 184 additions and 78 deletions
+43 -25
View File
@@ -1437,32 +1437,11 @@ QDomElement Element::toXml(
element.appendChild(inputs);
//if this element is linked to other elements,
//save the uuid of each other elements
//A partner on a folio not built yet (lazy folios), which this
//symbol will be linked to, is saved too, in the order the symbol was
//saved with: saving needs no partner built (stage D5a)
//save the uuid of each other elements (linksToSave())
QList<std::pair<QUuid, int>> links;
if (keepsSavedLinks() && diagram() && diagram()->project()) {
const QSet<QUuid> waiting = diagram()->project()->waitingLinkTargets();
QSet<Element *> written;
for (const auto &link_info : std::as_const(tmp_uuids_link)) {
Element *linked = nullptr;
for (Element *elmt : std::as_const(connected_elements))
if (elmt->uuid() == link_info.uuid && !written.contains(elmt)) { linked = elmt; break; }
if (linked) {
written.insert(linked);
links.append({linked->uuid(), m_group_index_map.value(linked, -1)});
} else if (waiting.contains(link_info.uuid)) {
links.append({link_info.uuid, link_info.group_index});
}
}
for (Element *elmt : std::as_const(connected_elements))
if (!written.contains(elmt))
links.append({elmt->uuid(), m_group_index_map.value(elmt, -1)});
} else {
for (Element *elmt : std::as_const(connected_elements))
links.append({elmt->uuid(), m_group_index_map.value(elmt, -1)});
}
for (const SavedLink &link : linksToSave(diagram() && diagram()->project()
? diagram()->project()->waitingLinkTargets() : QSet<QUuid>()))
links.append({link.uuid, link.group_index});
if (!links.isEmpty()) {
QDomElement links_uuids = document.createElement(QStringLiteral("links_uuids"));
for (const auto &link : std::as_const(links)) {
@@ -1869,6 +1848,45 @@ void Element::initLink(QETProject *prj)
}
}
/**
@brief Element::linksToSave
The partners this symbol saves, in the order it saves them: the order
it was saved with (tmp_uuids_link), then partners linked since. A
partner on a folio not built yet (lazy folios), which this symbol will
be linked to, is in the list too: saving needs no partner built (stage
D5a). toXml() writes this list, and the project database's link rows
are written from it, so the two are the same (QETZ-DB-FIRST stage 2a).
@param waiting the symbols on folios not built: a saved partner among
them is kept (QETProject::symbolsOnUnbuiltFolios())
@return each partner's uuid, its contact group (-1 if none), and the
partner itself when it is built (nullptr for one waiting)
*/
QList<Element::SavedLink> Element::linksToSave(const QSet<QUuid> &waiting) const
{
QList<SavedLink> links;
if (keepsSavedLinks() && diagram() && diagram()->project()) {
QSet<Element *> written;
for (const auto &link_info : std::as_const(tmp_uuids_link)) {
Element *linked = nullptr;
for (Element *elmt : std::as_const(connected_elements))
if (elmt->uuid() == link_info.uuid && !written.contains(elmt)) { linked = elmt; break; }
if (linked) {
written.insert(linked);
links.append({linked->uuid(), m_group_index_map.value(linked, -1), linked});
} else if (waiting.contains(link_info.uuid)) {
links.append({link_info.uuid, link_info.group_index, nullptr});
}
}
for (Element *elmt : std::as_const(connected_elements))
if (!written.contains(elmt))
links.append({elmt->uuid(), m_group_index_map.value(elmt, -1), elmt});
} else {
for (Element *elmt : std::as_const(connected_elements))
links.append({elmt->uuid(), m_group_index_map.value(elmt, -1), elmt});
}
return links;
}
/**
@brief Element::pendingLinks
@return the uuids of the partners this symbol was saved linked to and