Neue Capability isam-datenbank mit 9 Anforderungen und 25 Szenarien: Tabellenbindung an eine Dateinummer, Indexverwaltung, Cursorbewegung, Schluesselsuche, Satzoperationen, Transaktionen mit Ruecknahme, Vergleichsreihenfolge, Pufferverwaltung und das eigene Dateiformat. sprach-frontend um eine Anforderung erweitert: ISAM-Anweisungen und -Funktionen in Grammatik und Signaturpruefung. Die Delta-Spec wurde vor dem Archivieren an die Original-Hilfe angeglichen -- Cursorlage nach SETINDEX und DELETE, Fehlercode der Suche ueber den NULL-Index. Die Change-Notiz umfang-und-signaturen.md haelt Umfangsabgleich, Argumentformen mit Quellenangabe je Themenseite und die Verifikationsbefunde fest. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
210 lines
9.3 KiB
Markdown
210 lines
9.3 KiB
Markdown
## Context
|
||
|
||
Siehe proposal.md — Why. Technisch relevant ist der Zustand nach
|
||
`phase-3-runtime-bildschirm`:
|
||
|
||
- Die Dateitabelle, die Recordpuffer und die UTF-32-Serialisierung fester
|
||
Textfelder existieren (Capability `datei-eio`) und sind die Grundlage,
|
||
auf der ISAM die Sätze ablegt.
|
||
- Die Laufzeitfehler 81–89 sind in `tb_runtime::errors` bereits
|
||
vorhanden; dieser Change liefert ihre Auslöser.
|
||
- Vom ISAM-Sprachumfang existiert heute nur die Parser-Klausel
|
||
`OPEN … FOR ISAM` (`parser.rs`). Die ISAM-Anweisungen haben weder
|
||
Grammatik noch Signatur — sie enden in „unbekannter Bezeichner".
|
||
- Das Sprachinventar (`docs/inventar.md`) führt die ISAM-Elemente mit
|
||
Status `offen` und ist der maßgebliche Umfangsnachweis.
|
||
|
||
## Goals / Non-Goals
|
||
|
||
**Goals:**
|
||
|
||
- Der volle dokumentierte ISAM-Sprachumfang läuft, nicht eine Auswahl.
|
||
- Die Cursor-Semantik des Vorbilds ist exakt abgebildet — sie ist der
|
||
Teil, den keine Fremdbibliothek liefert.
|
||
- Speicher, Indizes und Transaktionen kommen aus einer erprobten
|
||
Bibliothek statt aus Eigenbau.
|
||
- Phase 3 ist mit diesem Change abgeschlossen.
|
||
|
||
**Non-Goals:**
|
||
|
||
- Leistungsoptimierung des Satzzugriffs — ISAM-Programme des Vorbilds
|
||
arbeiten auf Datenmengen, die jede heutige Speicherschicht mühelos
|
||
trägt.
|
||
- Nebenläufiger Zugriff mehrerer Prozesse auf dieselbe Datenbankdatei
|
||
über die Satzsperren aus `datei-eio` hinaus.
|
||
- Lesen von Datenbankdateien des Vorbilds.
|
||
|
||
## Decisions
|
||
|
||
### D1: `redb` als Speicherschicht
|
||
|
||
Die Datenbankdatei ist eine `redb`-Datei. Sätze und Indexeinträge liegen
|
||
darin als Key-Value-Paare; `redb` liefert B-Baum, Transaktionen und
|
||
Crash-Sicherheit.
|
||
|
||
*Warum nicht Eigenbau:* Seitenverwaltung, B-Baum und crash-sichere
|
||
Transaktionen sind gelöste Probleme mit langen Fehlerkatalogen. Der
|
||
projektspezifische Teil ist die ISAM-Semantik, nicht der Baum darunter.
|
||
|
||
*Warum nicht SQLite:* Zöge eine C-Bibliothek in jedes von `tbc build
|
||
--exe` erzeugte Binary. Der eigentliche Grund ist aber semantisch: ISAM
|
||
hat einen *stabilen, benannten Cursor*, der Änderungen überlebt und sich
|
||
vorwärts wie rückwärts bewegt — ein SQL-Cursor ist ein
|
||
Ergebnismengen-Iterator. Die Abbildung wäre aufwendiger als der Gewinn.
|
||
Die für Stufe 2 geplante SQL-Erweiterung bleibt davon unberührt; sie ist
|
||
eine eigene Sprachebene und kann später ihren eigenen Speicher wählen.
|
||
|
||
*Was `redb` nicht liefert und wir bauen:* Sekundärindizes,
|
||
Savepoints innerhalb einer Transaktion, die Cursor-Semantik.
|
||
|
||
### D2: Datenmodell — Sätze und Indizes als getrennte Tabellen
|
||
|
||
Je Tabelle eine Satztabelle `satz-id → Satzbytes` und je Index eine
|
||
Tabelle `Schlüsselbytes → satz-id`. Satz-IDs sind monoton vergebene
|
||
Zahlen und werden nie wiederverwendet.
|
||
|
||
*Warum getrennt:* `SETINDEX` wechselt die Durchlaufordnung zur Laufzeit;
|
||
mit Indextabellen ist das ein Wechsel der zu lesenden Tabelle, ohne die
|
||
Sätze anzufassen. `MOVEPREVIOUS` und die `SEEK`-Familie sind dann
|
||
Bereichsabfragen auf einer geordneten Tabelle.
|
||
|
||
*Warum IDs nicht wiederverwendet werden:* Der Cursor merkt sich eine
|
||
Satz-ID. Würde sie nach `DELETE` neu vergeben, könnte ein Cursor still
|
||
auf einen fremden Satz zeigen.
|
||
|
||
### D3: Schlüsselkodierung muss ordnungserhaltend sein
|
||
|
||
Indexschlüssel werden so kodiert, dass die Byte-Reihenfolge der Kodierung
|
||
der fachlichen Reihenfolge entspricht — Zahlen mit vorzeichenrichtiger
|
||
Kodierung fester Breite, Texte nach der in D4 festgelegten Ordnung,
|
||
mehrspaltige Schlüssel als Verkettung mit eindeutigem Trenner.
|
||
Absteigende Spalten werden durch Invertierung der Bytes abgebildet.
|
||
|
||
*Warum das die kritische Stelle ist:* `redb` ordnet nach Bytes. Jede
|
||
Ordnungsverletzung in der Kodierung äußert sich als falsche
|
||
Satzreihenfolge — ein Fehler, der erst bei bestimmten Datenwerten
|
||
sichtbar wird. Ein Eigenschaftstest über zufällige Wertepaare
|
||
(Kodierungsvergleich = fachlicher Vergleich) gehört deshalb zur
|
||
Umsetzung.
|
||
|
||
*Mehrspaltige Präfixsuche* fällt aus dieser Kodierung ab: Suche mit
|
||
weniger Werten als Spalten ist eine Bereichsabfrage über das
|
||
Schlüsselpräfix.
|
||
|
||
### D4: Sortierordnung explizit festlegen
|
||
|
||
Texte werden nach Unicode-Codepoints geordnet, Groß- und Kleinschreibung
|
||
unterschieden. Es ist dieselbe Ordnung, die der Vergleichsoperator des
|
||
Dialekts auf Strings anwendet.
|
||
|
||
`TEXTCOMP` gibt es in der Original-Hilfe nicht (Befund aus Aufgabe 1.1,
|
||
2026-09-03); die Ordnung braucht deshalb keine eigene Vergleichsfunktion,
|
||
sondern muss nur mit dem Stringvergleich der Sprache übereinstimmen.
|
||
|
||
*Warum nicht sprachabhängige Sortierung:* Sie machte die Reihenfolge
|
||
plattform- und locale-abhängig und damit die Korpustests unreproduzierbar.
|
||
Das Vorbild ordnete nach Zeichencodes einer festen Codepage; Codepoints
|
||
sind die direkte Entsprechung unter der bereits getroffenen
|
||
Unicode-Entscheidung. Die Ordnung wird in docs/sprachreferenz.md
|
||
festgehalten, weil sie beobachtbar ist.
|
||
|
||
### D5: Cursor als Wert, nicht als Bibliotheks-Iterator
|
||
|
||
Der Cursor je Dateinummer ist ein eigener Zustand: aktiver Index,
|
||
zuletzt besuchte Satz-ID und ein Kennzeichen „unpositioniert". Jede
|
||
Bewegung ist eine frische Bereichsabfrage ab dem gemerkten Schlüssel.
|
||
|
||
*Warum keine gehaltenen Iteratoren:* Der ISAM-Cursor überlebt
|
||
Satzänderungen und Transaktionsgrenzen; ein gehaltener Iterator über eine
|
||
Transaktion tut das nicht. Ein Cursor als Wert bildet außerdem
|
||
„unpositioniert" (Fehler 85) direkt ab, statt es aus einem
|
||
Iteratorzustand ableiten zu müssen.
|
||
|
||
*Preis:* Jede Bewegung kostet eine Suche statt eines Schritts. Bei den
|
||
Datenmengen dieser Programme irrelevant; siehe Non-Goals.
|
||
|
||
### D6: Savepoints als Rücknahmeprotokoll
|
||
|
||
`BEGINTRANS` öffnet eine Schreibtransaktion. Innerhalb davon führt die
|
||
Laufzeit ein Protokoll der Änderungen; die Funktion `SAVEPOINT` merkt
|
||
sich eine Position darin und liefert deren Kennung, `ROLLBACK` mit dieser
|
||
Kennung spielt das Protokoll bis dorthin rückwärts ab. `ROLLBACK ALL` und
|
||
`COMMITTRANS` nutzen die Transaktion der Bibliothek direkt.
|
||
|
||
*Warum ein eigenes Protokoll:* Verschachtelte Rücknahmepunkte gehören
|
||
nicht zum Funktionsumfang von `redb`. Das Protokoll ist der kleinste
|
||
Weg dorthin und bleibt auf den Umfang einer laufenden Transaktion
|
||
begrenzt.
|
||
|
||
### D7: Fehler 89 und `SETMEM` bekommen eine echte Grenze
|
||
|
||
`SETMEM` ist eine Funktion: sie ändert die Obergrenze des ISAM-Puffers um
|
||
den übergebenen Betrag und liefert die danach verfügbare Größe.
|
||
Überschreiten der Grenze löst Fehler 89 aus. Ohne Grenze wäre der Code nie
|
||
erreichbar und die Anweisung eine Attrappe.
|
||
|
||
*Abweichung:* Die Bedeutung in Bytes entspricht nicht dem
|
||
DOS-Speichermodell des Vorbilds und wird in docs/sprachreferenz.md
|
||
dokumentiert.
|
||
|
||
### D8: Reihenfolge der Umsetzung
|
||
|
||
```
|
||
1. Frontend: Grammatik + Signaturen -> Elemente werden benannt
|
||
2. Speicher: Datei, Satztabelle, Schluesselkodierung (+ Eigenschaftstest)
|
||
3. Indizes: CREATEINDEX/SETINDEX/GETINDEX$/DELETEINDEX
|
||
4. Cursor: MOVE-Familie, SEEK-Familie, EOF
|
||
5. Saetze: INSERT/RETRIEVE/UPDATE/DELETE, Eindeutigkeit
|
||
6. Transaktionen: BEGINTRANS/COMMITTRANS/ROLLBACK[ ALL]/SAVEPOINT
|
||
7. Rest: SETMEM, DELETETABLE, BOF, Formatversion
|
||
```
|
||
|
||
Schritt 1 zuerst, weil er allein schon den Guiding-Principle-Verstoß
|
||
behebt: ISAM-Programme werden ab da namentlich abgewiesen statt mit
|
||
„unbekannter Bezeichner". Schritt 2 vor allem anderen, weil die
|
||
Schlüsselkodierung jede spätere Ordnung bestimmt.
|
||
|
||
## Risks / Trade-offs
|
||
|
||
- **Fehlerhafte Schlüsselkodierung fällt erst bei bestimmten Daten auf**
|
||
→ Eigenschaftstest über zufällige Wertepaare je Spaltentyp:
|
||
Byte-Vergleich der Kodierung MUSS dem fachlichen Vergleich entsprechen.
|
||
Läuft vor allem Weiteren.
|
||
- **Der genaue ISAM-Sprachumfang stammt aus der Original-Hilfe und ist
|
||
bislang nur aus zweiter Hand belegt** → Das Inventar aus
|
||
`phase-3-runtime-bildschirm` ist die maßgebliche Liste; findet die
|
||
Erhebung dort weitere Elemente, gehören sie in diesen Change. Die
|
||
Aufzählung im Proposal ist ein Mindestumfang, keine Obergrenze.
|
||
- **Cursorverhalten an den Rändern ist die häufigste Fehlerquelle**
|
||
(`EOF` nach `MOVENEXT` am Ende, Cursor nach `DELETE`, nach `SETINDEX`,
|
||
nach `ROLLBACK`) → je ein Korpustest pro Randfall, nicht ein
|
||
Sammeltest.
|
||
- **Neue Abhängigkeit in `tb-runtime`, die bisher keine hatte** →
|
||
bewusst; die Alternative wäre ein eigener transaktionaler B-Baum.
|
||
Die Abhängigkeit bleibt auf das ISAM-Modul beschränkt.
|
||
- **Das Rücknahmeprotokoll kann bei langen Transaktionen wachsen** →
|
||
begrenzt auf die laufende Transaktion; `COMMITTRANS` beendet sie. Falls
|
||
eine Grenze nötig wird, ist Fehler 89 der dafür vorgesehene Ausgang.
|
||
- **Eigenes Dateiformat heißt: keine Vorbild-Datenbanken lesbar** →
|
||
bewusst entschieden, dieselbe Linie wie UTF-32-Records; steht in
|
||
docs/dateiformate.md und in der Sprachreferenz.
|
||
|
||
## Migration Plan
|
||
|
||
Rein additiv — es gibt heute keine ISAM-Funktionalität und keine
|
||
Datenbankdateien im Feld. Die einzige sichtbare Änderung an bestehendem
|
||
Verhalten: `OPEN … FOR ISAM` löst nicht mehr Laufzeitfehler 73 aus,
|
||
sondern arbeitet.
|
||
|
||
Die Datenbankdatei trägt von der ersten Fassung an eine Formatversion,
|
||
damit spätere Änderungen abgewiesen statt fehlinterpretiert werden.
|
||
|
||
## Open Questions
|
||
|
||
- Genaue Argumentformen einzelner ISAM-Anweisungen (etwa die Schreibweise
|
||
der Spaltenliste bei `CREATEINDEX`): bei der Umsetzung aus der
|
||
Original-Hilfe zu übernehmen. Berührt weder Datenmodell noch
|
||
Aufgabenschnitt.
|
||
- Konkreter Vorgabewert der `SETMEM`-Grenze: bei der Umsetzung zu wählen
|
||
und zu dokumentieren.
|