Commit Graph

8 Commits

Author SHA1 Message Date
ispyisail 60489c9893 Fix two faults the end-state gates found (stage D7)
A .qetz re-saved after opening from its index saved wrong folio numbers
in its database: reading each folio's header wrote its row with a
number counted from the folios read so far ("1/1", "2/2" for "1/3",
"2/3"), and an index-opened project keeps the rows it was saved with,
so nothing wrote them again. The folio table's rows are no longer
written while the project is being read (projectDataBase::
diagramInfoChanged()). Folio list tables showed it.

A .qetz whose project.sqlite is not a database crashed QElectroTech: the
first statement could not be prepared and SQLite was handed a null
statement. A statement that cannot be prepared now binds nothing and
steps to no row, and the file is refused.

Gates: 342 corpus projects,
written by QElectroTech 0.3 to 0.200, all open; .qet -> .qetz -> .qet
equals the plain resave with every folio built; a .qetz re-saved twice
holds the same bytes entry by entry but the save's own name, path and
time (the first fault made the first re-save differ); hostile .qetz
(truncated, garbage database, view for a table, trigger, escaping and
repeated names, later format, missing folio, 2 GiB zip bomb) neither
crash nor hang and none opens; an unknown extra entry still opens.
tst_lazyfolios: folio numbers with totals from the index as when built
(fails without the fix); tst_qetcontainer: garbage database refused
(segfaults without it). ctest 96/96 both ways; save and export gates
same on 342.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 23:03:51 +13:00
ispyisail 8ea051acfc A .qetz keeps nothing QElectroTech computes in its folio files (stage D3c)
Format 3 folio files lose what building a folio computes
(QetContainer::dropComputed()): a wire's description of its ends
(element1_label, element1_name, element1_linked, terminalname1 and the
same for end 2), the content of a symbol text showing the symbol's
information or a composition of it, and -- when the project database's
index is written, every symbol with a uuid of its own -- a symbol's list
of its terminals.

What may go was found by test, not by reading: on 342 projects saved by
this version, each kind taken out of the file, the project opened and
saved: the wire ends came back in all 244 that had them, the symbol texts
in all 201, the terminal lists in 305 of 306 -- not in the file whose
symbols share a uuid (#1408), where wires find their ends by them; such a
project gets no index, so it keeps them.

Stays, for now: labels made from a formula and wire numbers made from one,
in the database (opening from the index and numbering read them), and a
table's size (the user's as much as computed).

tst_qetcontainer compares a joined document with the original less what is
computed, and computedNotKept checks a file holding both kinds (one filled
in by the test) loses them -- it fails when nothing is dropped. Through
QElectroTech, 342 projects: a .qetz opened from its index and saved as .qet
= eager = master; every folio of industrial and f2099 drawn and linked as
eager (f2099 but its 3 render-noise folios). ctest 96/96 with and without
QET_LAZY_FOLIOS=1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 23:03:43 +13:00
ispyisail 2f98872d7a .qetz format 3: one project database (stage D3a)
project.sqlite now holds both what QElectroTech keeps in its project
database (the index D2 wrote as index.sqlite) and the document's
engineering data, the latter's tables named doc_<name> beside the
former's -- element, element_info, link and conductor exist in both
(QetContainerDb's SQL names them as before; Database::sql() puts the
prefix on). The manifest says format 3, min-reader 3: a format 2 reader
would find no tables of its names. Format 2 files, with or without an
index.sqlite entry, are read as before: the prefix is found from the
file (doc_meta).

A .qetz with an index opens from project.sqlite. Measured: the 136-folio
f2099 1.8 s, 5.2 MB (the two table sets still overlap: D3b).

Checked: tst_qetcontainer with the format 3 names, 34/34; a format 2 file
of the last commit opens from its index.sqlite and saves the same; corpus
342: 340 .qetz carry the index in project.sqlite and open from it, saved
back = 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>
2026-10-13 23:03:43 +13:00
ispyisail a44e816d59 Keep terminal strips in a .qetz project's database too
The project's terminal strips -- strip information, the terminals of
each physical terminal by level, bridges and what they bridge -- move to
four tables of project.sqlite, when rebuilding them from the rows gives
the same XML exactly (as QElectroTech writes them). Anything else, or
strips that carry text a save would not write, stays in project.xml.

Test: tst_qetcontainer::terminalStripsInDatabase (two strips with a
bridge and a two-level terminal on industrial.qet's own terminals: out
of project.xml, back exactly; stays in the XML when the rebuild is
broken). Outside ctest: a strip fixture saved as .qetz then .qet by
QElectroTech equals its straight resave, and the strips are rows any
SQLite tool reads.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Wga9B92BNurDEtxxtCDxf
2026-10-11 06:24:29 +13:00
ispyisail 739061b0f0 Test that a .qetz whose database hides a view behind a table name is refused
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Wga9B92BNurDEtxxtCDxf
2026-10-11 06:24:29 +13:00
ispyisail cbaf40000c Keep a .qetz project's engineering data in project.sqlite
Fourth stage of the zipped project file (#1440), format 2: what an
engineer would put in a list -- folio title-block fields and
properties, project properties, a symbol's uuid, type, prefix,
information and links, a wire's ends, number, function, colour,
section, cable and bus -- moves out of the XML into project.sqlite in
the .qetz, and lives only there. The folio files keep where things are
drawn. On opening it goes back into the document before the usual
reader runs, so nothing else in QElectroTech changes yet (the program
reading the database directly is the next step of the roadmap).

Each moved item keeps a key in the XML (qetz-key: its uuid where
unique); each moved block leaves a marker where it was, which keeps the
whitespace around it apart. A block of a shape this does not model
stays in the XML. A file is untrusted: the database is read with
trusted_schema off, query_only, and only from names that are plain
tables. Format 2 readers still open format 1; a format 1 reader
refuses format 2.

Tests (tst_qetcontainer): every example back exactly, whitespace
included; databaseHoldsTheData (industrial.qet: the folio files hold no
information, links, wire numbers or properties; the row counts match).
Outside ctest: 341 of 341 projects QET can open give the same .qet
through a format 2 .qetz.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Wga9B92BNurDEtxxtCDxf
2026-10-11 06:24:29 +13:00
ispyisail fac09253a0 Open and save projects as .qetz (zipped, opt-in)
Third stage of the zipped project file (#1440). A project is saved as a
.qetz when its file name ends in .qetz -- Save As offers it as a second
file type, and --resave, --set-titleblock, the script API's save() and
the live-mode snapshot follow the name too. Saving a .qet project still
writes a .qet; nothing changes unless the user picks .qetz.

Opening recognises a .qetz by its content (a zip), not its name, and
reads it as bytes: text mode would alter it on Windows. The project
document is joined back and read by the same code as a .qet. Crash
backups stay plain XML. Drag and drop, the open dialog and the
numbering import accept .qetz; the database export names its file from
the project's name without assuming ".qet".

Parts are now written without added indentation (one line each): the
symbol definitions are kept as read, whitespace included, and parts
that gained indentation made the next save differ (five forum projects).

Tests: tst_resaveunchanged::containerSavesTheSameProject (four examples
saved as .qetz, then as .qet, equal the straight .qet; pictures checked
to fail without the fix), tst_qetcontainer::noWhitespaceAdded. Outside
ctest: 341 of 341 projects QET can open (examples, bugtracker, GitHub,
forum) give the same .qet through a .qetz; 181.6 MB of .qet became
36.9 MB of .qetz.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Wga9B92BNurDEtxxtCDxf
2026-10-11 06:24:29 +13:00
ispyisail a475383915 Split a project into the parts of a .qetz file and join it back
Second stage of the zipped project file (#1440): QetContainer::split()
turns a project document into the entries of a .qetz -- mimetype first
and stored, a manifest with the format and the oldest format allowed to
read it, project.xml with each part replaced by <qetz-part file=.../>,
one file per folio, per symbol definition and per title block template,
and folio pictures as PNG files named by their SHA-256, stored once
each. join() puts it back and refuses a file of a later format, a
missing part or anything that is not a .qetz.

A picture leaves its folio only if its base64 comes back exactly, so
nothing here can change a project. Parts are written as QET writes a
.qet and read as it reads one. Still nothing opens or saves a .qetz.

Test: tst_qetcontainer, every example split, zipped, unzipped and
joined back to the same document (whitespace between elements aside);
pictures out of the folios once each; later format and missing part
refused. With QET_CONTAINER_CORPUS it also runs on a folder of projects:
341 of 341 readable projects collected from the bugtracker, GitHub and
the forum come back the same.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Wga9B92BNurDEtxxtCDxf
2026-10-11 06:24:16 +13:00