OpenSpec: phase-3-isam archiviert, Delta-Specs gesynct

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>
This commit is contained in:
2026-09-04 11:38:10 +02:00
parent 9b58e0ec43
commit e49a231a68
9 changed files with 514 additions and 62 deletions

View File

@@ -14,8 +14,11 @@ sind die Spalten der Tabelle. Existiert die Tabelle noch nicht, SHALL sie
mit diesem Layout angelegt werden; existiert sie mit abweichendem Layout,
MUST Laufzeitfehler 88 „ISAM - Database inconsistent" auftreten. Ein
ungültiger Tabellen- oder Datenbankname MUST Fehler 81 „ISAM - Invalid
name" auslösen. `CLOSE` SHALL die Bindung lösen und ausstehende
Änderungen festschreiben. `DELETETABLE` SHALL eine Tabelle samt ihren
name" auslösen. `CLOSE` SHALL die Bindung lösen; ausstehende Änderungen
sind dabei bereits festgeschrieben, weil jede Operation außerhalb einer
Transaktion für sich eine Transaktion ist. Eine laufende Transaktion MUST
`CLOSE` unberührt lassen — über ihr Ende entscheiden allein `COMMITTRANS`
und `ROLLBACK ALL`. `DELETETABLE` SHALL eine Tabelle samt ihren
Indizes entfernen; eine nicht vorhandene Tabelle MUST Fehler 82 „ISAM -
Table not found" auslösen.
@@ -27,6 +30,10 @@ Table not found" auslösen.
- **WHEN** eine bestehende Tabelle mit einem Typ geöffnet wird, dessen Felder nicht zu ihren Spalten passen
- **THEN** tritt Laufzeitfehler 88 auf
#### Scenario: CLOSE lässt eine laufende Transaktion stehen
- **WHEN** innerhalb einer Transaktion eine andere Dateinummer mit `CLOSE` geschlossen wird
- **THEN** läuft die Transaktion weiter und ein folgendes `ROLLBACK ALL` nimmt ihre Änderungen zurück
#### Scenario: Entfernte Tabelle
- **WHEN** `DELETETABLE` für eine nicht vorhandene Tabelle aufgerufen wird
- **THEN** tritt Laufzeitfehler 82 auf
@@ -37,10 +44,13 @@ anlegen, wahlweise eindeutig oder mehrdeutig und je Spalte auf- oder
absteigend geordnet. `DELETEINDEX` SHALL einen Index entfernen.
`SETINDEX` SHALL den aktiven Index einer Dateinummer setzen und damit die
Reihenfolge bestimmen, in der Sätze durchlaufen werden; `GETINDEX$` SHALL
den Namen des aktiven Index liefern. Ein unbekannter Indexname MUST
Laufzeitfehler 83 „ISAM - Index not found" auslösen, ein unbekannter
Spaltenname Fehler 84 „ISAM - Invalid column". Nach `SETINDEX` MUST der
Cursor als unpositioniert gelten.
den Namen des aktiven Index liefern. `SETINDEX` ohne Indexnamen (oder mit
leerem Namen) SHALL den NULL-Index wählen, also die Einfügereihenfolge;
`GETINDEX$` liefert dafür einen leeren String. Ein unbekannter Indexname
MUST Laufzeitfehler 83 „ISAM - Index not found" auslösen, ein unbekannter
Spaltenname Fehler 84 „ISAM - Invalid column". Nach `SETINDEX` SHALL der
erste Satz in der Ordnung des gewählten Index der aktuelle Satz sein; ist
die Tabelle leer, gibt es keinen aktuellen Satz.
#### Scenario: Aktiver Index bestimmt die Reihenfolge
- **WHEN** ein Index über die Spalte `Name` gesetzt ist und die Sätze mit `MOVEFIRST` und `MOVENEXT` durchlaufen werden
@@ -50,6 +60,10 @@ Cursor als unpositioniert gelten.
- **WHEN** `SETINDEX #1, "GibtsNicht"` ausgeführt wird
- **THEN** tritt Laufzeitfehler 83 auf
#### Scenario: Aktueller Satz nach SETINDEX
- **WHEN** ein Index gesetzt wird, dessen Ordnung mit einem anderen Satz beginnt als die bisherige
- **THEN** ist dieser Satz ohne weitere Bewegung der aktuelle Satz
#### Scenario: Index über unbekannte Spalte
- **WHEN** `CREATEINDEX` eine Spalte nennt, die der Satztyp nicht führt
- **THEN** tritt Laufzeitfehler 84 auf
@@ -77,8 +91,9 @@ größer-gleich dem angegebenen Wert ist. Die Zahl der Schlüsselwerte MUST
höchstens der Spaltenzahl des aktiven Index entsprechen; ein
mehrspaltiger Index SHALL mit einem Präfix der Spalten durchsucht werden
können. Findet die Suche keinen Satz, SHALL `EOF` wahr werden und der
Cursor unpositioniert bleiben. Eine Suche ohne aktiven Index MUST
Laufzeitfehler 83 auslösen.
Cursor unpositioniert bleiben. Eine Suche über den NULL-Index MUST
Laufzeitfehler 87 „ISAM - Invalid operation on NULL index" auslösen — er
führt keine Schlüssel, sondern nur die Einfügereihenfolge.
#### Scenario: Suche nach größerem Schlüssel
- **WHEN** Sätze mit den Schlüsseln 10, 20 und 30 vorliegen und `SEEKGT #1, 20` ausgeführt wird
@@ -100,18 +115,24 @@ aus einer solchen Variablen überschreiben und `DELETE` ihn entfernen.
Laufzeitfehler 85 „ISAM - No current record" auslösen. Ein `INSERT` oder
`UPDATE`, das einen als eindeutig angelegten Index verletzen würde, MUST
Laufzeitfehler 86 „ISAM - Duplicate value for unique index" auslösen und
den Satzbestand unverändert lassen. Nach `DELETE` SHALL der Cursor als
unpositioniert gelten. Alle Indizes der Tabelle SHALL nach jeder
Satzoperation die neue Ordnung widerspiegeln.
den Satzbestand unverändert lassen. Nach `DELETE` SHALL der auf den
gelöschten folgende Satz der aktuelle sein; war der gelöschte der letzte
in der Ordnung des aktiven Index, SHALL der Cursor am Ende der Tabelle
stehen und es SHALL keinen aktuellen Satz geben. Alle Indizes der Tabelle
SHALL nach jeder Satzoperation die neue Ordnung widerspiegeln.
#### Scenario: Einfügen und Zurücklesen
- **WHEN** ein Satz eingefügt, per `SEEKEQ` gesucht und mit `RETRIEVE` gelesen wird
- **THEN** stimmen alle Feldwerte mit den eingefügten überein
#### Scenario: Operation ohne aktuellen Satz
- **WHEN** nach `SETINDEX` ohne Positionierung `RETRIEVE #1, k` ausgeführt wird
- **WHEN** hinter dem letzten Satz — also nach `MOVENEXT` am Ende — `RETRIEVE #1, k` ausgeführt wird
- **THEN** tritt Laufzeitfehler 85 auf
#### Scenario: Aktueller Satz nach DELETE
- **WHEN** ein Satz gelöscht wird, auf den in der Ordnung des aktiven Index ein weiterer folgt
- **THEN** ist dieser folgende Satz der aktuelle
#### Scenario: Verstoß gegen eindeutigen Index
- **WHEN** ein Satz eingefügt wird, dessen Schlüssel in einem eindeutigen Index bereits vorkommt
- **THEN** tritt Laufzeitfehler 86 auf und der Satzbestand bleibt unverändert
@@ -129,7 +150,8 @@ SHALL auf diesen Punkt zurücksetzen, ohne die Transaktion aufzugeben.
`ROLLBACK ALL` SHALL alle Änderungen seit `BEGINTRANS` zurücknehmen und
die Transaktion beenden. Nach jeder Rücknahme MUST der Satzbestand
einschließlich aller Indizes dem Zustand zum Bezugspunkt entsprechen und
der Cursor als unpositioniert gelten.
der Cursor als unpositioniert gelten. Eine beim Programmende nicht
festgeschriebene Transaktion MUST wirkungslos bleiben.
#### Scenario: Rücknahme stellt den Bestand wieder her
- **WHEN** nach `BEGINTRANS` Sätze eingefügt und gelöscht werden und dann `ROLLBACK` folgt

View File

@@ -1,78 +1,78 @@
## 1. Umfang festlegen
- [ ] 1.1 ISAM-Abschnitt der Original-Hilfe auswerten und alle Anweisungen, Funktionen und Klauseln gegen die ISAM-Einträge in `docs/inventar.md` abgleichen; verifiziert dadurch, dass jedes gefundene Element im Inventar mit Status `offen` steht und im Change notiert ist, falls es über die Aufzählung im Proposal hinausgeht
- [ ] 1.2 Argumentformen je Element aus der Original-Hilfe festhalten (insbesondere die Schreibweise der Spaltenliste bei `CREATEINDEX`); verifiziert durch die Notiz im Change, die jede Signatur mit ihrer Quelle nennt
- [x] 1.1 ISAM-Abschnitt der Original-Hilfe auswerten und alle Anweisungen, Funktionen und Klauseln gegen die ISAM-Einträge in `docs/inventar.md` abgleichen; verifiziert dadurch, dass jedes gefundene Element im Inventar mit Status `offen` steht und im Change notiert ist, falls es über die Aufzählung im Proposal hinausgeht
- [x] 1.2 Argumentformen je Element aus der Original-Hilfe festhalten (insbesondere die Schreibweise der Spaltenliste bei `CREATEINDEX`); verifiziert durch die Notiz im Change, die jede Signatur mit ihrer Quelle nennt
## 2. Frontend: Grammatik und Signaturen
- [ ] 2.1 ISAM-Anweisungen in die Parser-Grammatik aufnehmen; verifiziert durch Frontend-Test, dass `SETINDEX #1, "Name"` diagnose-frei parst
- [ ] 2.2 Signaturen (Argumentanzahl und -typen) für alle ISAM-Elemente in der Semantik führen; verifiziert durch Frontend-Test, dass `SEEKGT #1` ohne Schlüsselwert eine Diagnose erzeugt, die `SEEKGT` namentlich nennt
- [ ] 2.3 Satzargumente gegen den Tabellentyp prüfen; verifiziert durch Frontend-Test, dass `RETRIEVE #1, x$` „Type mismatch" meldet
- [ ] 2.4 `Unsupported`-Absenkung von `OPEN … FOR ISAM` entfernen und durch die reguläre Absenkung ersetzen; verifiziert dadurch, dass ein ISAM-Programm nicht mehr mit Laufzeitfehler 73 endet
- [ ] 2.5 Prüfen, dass kein ISAM-Element mehr als unbekannter Bezeichner behandelt wird; verifiziert durch Frontend-Test über ein Programm, das jedes Element einmal verwendet, ohne Diagnose „unbekannter Bezeichner"
- [x] 2.1 ISAM-Anweisungen in die Parser-Grammatik aufnehmen; verifiziert durch Frontend-Test, dass `SETINDEX #1, "Name"` diagnose-frei parst
- [x] 2.2 Signaturen (Argumentanzahl und -typen) für alle ISAM-Elemente in der Semantik führen; verifiziert durch Frontend-Test, dass `SEEKGT #1` ohne Schlüsselwert eine Diagnose erzeugt, die `SEEKGT` namentlich nennt
- [x] 2.3 Satzargumente gegen den Tabellentyp prüfen; verifiziert durch Frontend-Test, dass `RETRIEVE #1, x$` „Type mismatch" meldet
- [x] 2.4 `Unsupported`-Absenkung von `OPEN … FOR ISAM` entfernen und durch die reguläre Absenkung ersetzen; verifiziert dadurch, dass ein ISAM-Programm nicht mehr mit Laufzeitfehler 73 endet
- [x] 2.5 Prüfen, dass kein ISAM-Element mehr als unbekannter Bezeichner behandelt wird; verifiziert durch Frontend-Test über ein Programm, das jedes Element einmal verwendet, ohne Diagnose „unbekannter Bezeichner"
## 3. Speicherschicht
- [ ] 3.1 `redb` als Abhängigkeit aufnehmen, auf das ISAM-Modul in `tb-runtime` beschränkt; verifiziert dadurch, dass `cargo build -p tb-runtime` durchläuft
- [ ] 3.2 Datenbankdatei mit Formatversion anlegen und öffnen; verifiziert durch Test, dass eine Datei mit höherer Formatversion mit benanntem Fehler abgewiesen und eine strukturell beschädigte Datei mit Laufzeitfehler 88 abgelehnt wird
- [ ] 3.3 Satztabelle `satz-id → Satzbytes` mit monoton vergebenen, nie wiederverwendeten IDs; verifiziert durch Test, dass eine nach `DELETE` neu vergebene ID nicht vorkommt
- [ ] 3.4 Satzserialisierung auf den Recordpuffern und der UTF-32-Festtextkodierung aus `datei-eio` aufsetzen; verifiziert durch Roundtrip-Test über alle Feldtypen des Satztyps
- [ ] 3.5 Ordnungserhaltende Schlüsselkodierung je Spaltentyp (Zahlen vorzeichenrichtig fester Breite, Text nach Codepoints, mehrspaltig verkettet mit eindeutigem Trenner, absteigend durch Byte-Invertierung); verifiziert durch Eigenschaftstest über zufällige Wertepaare, dass der Byte-Vergleich der Kodierung dem fachlichen Vergleich entspricht
- [ ] 3.6 Sortierordnung (Unicode-Codepoints, Groß-/Kleinschreibung unterschieden) in docs/sprachreferenz.md festhalten; verifiziert durch den Abschnittseintrag
- [ ] 3.7 Datenbankdateiformat in docs/dateiformate.md beschreiben inklusive Formatversion und bewusster Inkompatibilität zum Vorbild; verifiziert durch den Abschnittseintrag
- [x] 3.1 `redb` als Abhängigkeit aufnehmen, auf das ISAM-Modul in `tb-runtime` beschränkt; verifiziert dadurch, dass `cargo build -p tb-runtime` durchläuft
- [x] 3.2 Datenbankdatei mit Formatversion anlegen und öffnen; verifiziert durch Test, dass eine Datei mit höherer Formatversion mit benanntem Fehler abgewiesen und eine strukturell beschädigte Datei mit Laufzeitfehler 88 abgelehnt wird
- [x] 3.3 Satztabelle `satz-id → Satzbytes` mit monoton vergebenen, nie wiederverwendeten IDs; verifiziert durch Test, dass eine nach `DELETE` neu vergebene ID nicht vorkommt
- [x] 3.4 Satzserialisierung auf den Recordpuffern und der UTF-32-Festtextkodierung aus `datei-eio` aufsetzen; verifiziert durch Roundtrip-Test über alle Feldtypen des Satztyps
- [x] 3.5 Ordnungserhaltende Schlüsselkodierung je Spaltentyp (Zahlen vorzeichenrichtig fester Breite, Text nach Codepoints, mehrspaltig verkettet mit eindeutigem Trenner, absteigend durch Byte-Invertierung); verifiziert durch Eigenschaftstest über zufällige Wertepaare, dass der Byte-Vergleich der Kodierung dem fachlichen Vergleich entspricht
- [x] 3.6 Sortierordnung (Unicode-Codepoints, Groß-/Kleinschreibung unterschieden) in docs/sprachreferenz.md festhalten; verifiziert durch den Abschnittseintrag
- [x] 3.7 Datenbankdateiformat in docs/dateiformate.md beschreiben inklusive Formatversion und bewusster Inkompatibilität zum Vorbild; verifiziert durch den Abschnittseintrag
## 4. Tabellen
- [ ] 4.1 `OPEN … FOR ISAM typname tabellenname AS #n` an die Dateitabelle binden, Satzlayout aus dem benannten Typ ableiten; verifiziert durch Korpustest, dass die Dateinummer danach nutzbar ist
- [ ] 4.2 Tabelle beim ersten Öffnen anlegen; verifiziert durch Korpustest gegen eine Datenbank ohne diese Tabelle
- [ ] 4.3 Layoutabgleich bei bestehender Tabelle mit Fehler 88 bei Abweichung; verifiziert durch Korpustest mit unpassendem Typ
- [ ] 4.4 Ungültige Datenbank- und Tabellennamen mit Fehler 81 abweisen; verifiziert durch Korpustest
- [ ] 4.5 `CLOSE` löst die Bindung und schreibt ausstehende Änderungen fest; verifiziert durch Korpustest, dass die Sätze nach erneutem Öffnen vorhanden sind
- [ ] 4.6 `DELETETABLE` entfernt Tabelle samt Indizes, Fehler 82 bei fehlender Tabelle; verifiziert durch Korpustest für beide Fälle
- [x] 4.1 `OPEN … FOR ISAM typname tabellenname AS #n` an die Dateitabelle binden, Satzlayout aus dem benannten Typ ableiten; verifiziert durch Korpustest, dass die Dateinummer danach nutzbar ist
- [x] 4.2 Tabelle beim ersten Öffnen anlegen; verifiziert durch Korpustest gegen eine Datenbank ohne diese Tabelle
- [x] 4.3 Layoutabgleich bei bestehender Tabelle mit Fehler 88 bei Abweichung; verifiziert durch Korpustest mit unpassendem Typ
- [x] 4.4 Ungültige Datenbank- und Tabellennamen mit Fehler 81 abweisen; verifiziert durch Korpustest
- [x] 4.5 `CLOSE` löst die Bindung und lässt eine laufende Transaktion unberührt; verifiziert durch Korpustest, dass die Sätze nach erneutem Öffnen vorhanden sind
- [x] 4.6 `DELETETABLE` entfernt Tabelle samt Indizes, Fehler 82 bei fehlender Tabelle; verifiziert durch Korpustest für beide Fälle
## 5. Indizes
- [ ] 5.1 Indextabellen `Schlüsselbytes → satz-id` je Index anlegen; verifiziert durch Test, dass die Einträge in Schlüsselreihenfolge stehen
- [ ] 5.2 `CREATEINDEX` mit einer und mit mehreren Spalten, eindeutig und mehrdeutig, auf- und absteigend; verifiziert durch Korpustest je Variante
- [ ] 5.3 `DELETEINDEX`; verifiziert durch Korpustest, dass der Index danach nicht mehr gesetzt werden kann
- [ ] 5.4 `SETINDEX` setzt den aktiven Index und den Cursor auf unpositioniert, `GETINDEX$` liefert ihn zurück; verifiziert durch Korpustest
- [ ] 5.5 Fehler 83 bei unbekanntem Index und Fehler 84 bei unbekannter Spalte; verifiziert durch Korpustest für beide Fälle
- [x] 5.1 Indextabellen `Schlüsselbytes → satz-id` je Index anlegen; verifiziert durch Test, dass die Einträge in Schlüsselreihenfolge stehen
- [x] 5.2 `CREATEINDEX` mit einer und mit mehreren Spalten, eindeutig und mehrdeutig, auf- und absteigend; verifiziert durch Korpustest je Variante
- [x] 5.3 `DELETEINDEX`; verifiziert durch Korpustest, dass der Index danach nicht mehr gesetzt werden kann
- [x] 5.4 `SETINDEX` setzt den aktiven Index und stellt den Cursor auf den ersten Satz seiner Ordnung, `GETINDEX$` liefert ihn zurück; verifiziert durch Korpustest
- [x] 5.5 Fehler 83 bei unbekanntem Index und Fehler 84 bei unbekannter Spalte; verifiziert durch Korpustest für beide Fälle
## 6. Cursor
- [ ] 6.1 Cursor je Dateinummer als Wert führen (aktiver Index, letzte Satz-ID, Kennzeichen unpositioniert); verifiziert durch Unit-Test, dass der Cursor eine Satzänderung überlebt
- [ ] 6.2 `MOVEFIRST`, `MOVELAST`, `MOVENEXT`, `MOVEPREVIOUS` in der Ordnung des aktiven Index; verifiziert durch Korpustest, dass ein Durchlauf die Sätze in Indexreihenfolge liefert
- [ ] 6.3 Bewegung ohne aktiven Index folgt der Einfügereihenfolge; verifiziert durch Korpustest
- [ ] 6.4 `EOF` am Ende und `BOF` am Anfang, Cursor danach jeweils unpositioniert; verifiziert durch je einen Korpustest für `MOVENEXT` am Ende und `MOVEPREVIOUS` am Anfang
- [ ] 6.5 `SEEKEQ`, `SEEKGT`, `SEEKGE` inklusive Präfixsuche über einen mehrspaltigen Index; verifiziert durch Korpustest mit den Schlüsseln 10, 20, 30 und einer Präfixsuche
- [ ] 6.6 Erfolglose Suche setzt `EOF` und lässt den Cursor unpositioniert; verifiziert durch Korpustest
- [ ] 6.7 Suche ohne aktiven Index löst Fehler 83 aus; verifiziert durch Korpustest
- [x] 6.1 Cursor je Dateinummer als Wert führen (aktiver Index, letzte Satz-ID, Kennzeichen unpositioniert); verifiziert durch Unit-Test, dass der Cursor eine Satzänderung überlebt
- [x] 6.2 `MOVEFIRST`, `MOVELAST`, `MOVENEXT`, `MOVEPREVIOUS` in der Ordnung des aktiven Index; verifiziert durch Korpustest, dass ein Durchlauf die Sätze in Indexreihenfolge liefert
- [x] 6.3 Bewegung ohne aktiven Index folgt der Einfügereihenfolge; verifiziert durch Korpustest
- [x] 6.4 `EOF` am Ende und `BOF` am Anfang, Cursor danach jeweils unpositioniert; verifiziert durch je einen Korpustest für `MOVENEXT` am Ende und `MOVEPREVIOUS` am Anfang
- [x] 6.5 `SEEKEQ`, `SEEKGT`, `SEEKGE` inklusive Präfixsuche über einen mehrspaltigen Index; verifiziert durch Korpustest mit den Schlüsseln 10, 20, 30 und einer Präfixsuche
- [x] 6.6 Erfolglose Suche setzt `EOF` und lässt den Cursor unpositioniert; verifiziert durch Korpustest
- [x] 6.7 Suche über den NULL-Index löst Fehler 87 aus; verifiziert durch Korpustest
## 7. Satzoperationen
- [ ] 7.1 `INSERT` und `RETRIEVE`; verifiziert durch Korpustest, dass ein eingefügter Satz per `SEEKEQ` gefunden und feldgleich gelesen wird
- [ ] 7.2 `UPDATE` und `DELETE`, Cursor nach `DELETE` unpositioniert; verifiziert durch Korpustest
- [ ] 7.3 Fehler 85 bei `RETRIEVE`/`UPDATE`/`DELETE` ohne positionierten Cursor; verifiziert durch je einen Korpustest
- [ ] 7.4 Eindeutigkeitsprüfung mit Fehler 86 und unverändertem Satzbestand; verifiziert durch Korpustest, der nach dem Fehler den Bestand nachzählt
- [ ] 7.5 Alle Indizes nach jeder Satzoperation nachführen; verifiziert durch Korpustest, dass ein Satz nach Änderung der indizierten Spalte an neuer Position im Durchlauf erscheint
- [x] 7.1 `INSERT` und `RETRIEVE`; verifiziert durch Korpustest, dass ein eingefügter Satz per `SEEKEQ` gefunden und feldgleich gelesen wird
- [x] 7.2 `UPDATE` und `DELETE`, nach `DELETE` wird der folgende Satz der aktuelle (am Ende: kein aktueller Satz); verifiziert durch Korpustest
- [x] 7.3 Fehler 85 bei `RETRIEVE`/`UPDATE`/`DELETE` ohne positionierten Cursor; verifiziert durch je einen Korpustest
- [x] 7.4 Eindeutigkeitsprüfung mit Fehler 86 und unverändertem Satzbestand; verifiziert durch Korpustest, der nach dem Fehler den Bestand nachzählt
- [x] 7.5 Alle Indizes nach jeder Satzoperation nachführen; verifiziert durch Korpustest, dass ein Satz nach Änderung der indizierten Spalte an neuer Position im Durchlauf erscheint
## 8. Transaktionen
- [ ] 8.1 `BEGINTRANS`/`COMMITTRANS` auf einer Schreibtransaktion; verifiziert durch Korpustest, dass die Änderungen nach erneutem Öffnen vorhanden sind
- [ ] 8.2 Rücknahmeprotokoll der Änderungen innerhalb einer Transaktion; verifiziert durch Unit-Test, dass jede Satzoperation einen Protokolleintrag erzeugt
- [ ] 8.3 `ROLLBACK ALL` stellt Sätze und Indizes wieder her, beendet die Transaktion und setzt den Cursor auf unpositioniert; verifiziert durch Korpustest mit Einfügen, Löschen und anschließendem Durchlauf
- [ ] 8.4 `SAVEPOINT` als Funktion (liefert Kennung) und `ROLLBACK kennung`; verifiziert durch Korpustest, dass Änderungen vor dem Punkt erhalten bleiben
- [x] 8.1 `BEGINTRANS`/`COMMITTRANS` auf einer Schreibtransaktion; verifiziert durch Korpustest, dass die Änderungen nach erneutem Öffnen vorhanden sind
- [x] 8.2 Rücknahmeprotokoll der Änderungen innerhalb einer Transaktion; verifiziert durch Unit-Test, dass jede Satzoperation einen Protokolleintrag erzeugt
- [x] 8.3 `ROLLBACK ALL` stellt Sätze und Indizes wieder her, beendet die Transaktion und setzt den Cursor auf unpositioniert; verifiziert durch Korpustest mit Einfügen, Löschen und anschließendem Durchlauf
- [x] 8.4 `SAVEPOINT` als Funktion (liefert Kennung) und `ROLLBACK kennung`; verifiziert durch Korpustest, dass Änderungen vor dem Punkt erhalten bleiben
## 9. Übrige Elemente
- [ ] 9.2 `SETMEM` als Funktion mit echter Puffergrenze und Fehler 89 beim Überschreiten; verifiziert durch Korpustest, der die Grenze niedrig setzt und den Fehler auslöst
- [ ] 9.3 Vorgabewert und Bedeutung von `SETMEM` sowie die Abweichung vom DOS-Speichermodell in docs/sprachreferenz.md festhalten; verifiziert durch den Abschnittseintrag
- [ ] 9.4 Indexordnung gegen den Stringvergleich des Dialekts prüfen; verifiziert durch Korpustest, dass Indexreihenfolge und `<`-Vergleich übereinstimmen
- [x] 9.2 `SETMEM` als Funktion mit echter Puffergrenze und Fehler 89 beim Überschreiten; verifiziert durch Korpustest, der die Grenze niedrig setzt und den Fehler auslöst
- [x] 9.3 Vorgabewert und Bedeutung von `SETMEM` sowie die Abweichung vom DOS-Speichermodell in docs/sprachreferenz.md festhalten; verifiziert durch den Abschnittseintrag
- [x] 9.4 Indexordnung gegen den Stringvergleich des Dialekts prüfen; verifiziert durch Korpustest, dass Indexreihenfolge und `<`-Vergleich übereinstimmen
## 10. Abschluss
- [ ] 10.1 ISAM-Einträge in `docs/inventar.md` auf `implementiert` setzen; verifiziert dadurch, dass der Abgleichstest aus `phase-3-runtime-bildschirm` grün bleibt
- [ ] 10.2 `docs/bibliothek.md` um die ISAM-Elemente erweitern; verifiziert dadurch, dass jedes umgesetzte Element dort einen Eintrag hat
- [ ] 10.3 Korpus-ISAM-Programme in einem temporären Arbeitsverzeichnis ausführen; verifiziert dadurch, dass nach `cargo test` `git status` sauber ist
- [ ] 10.4 Vollständige Suite auf allen Zielplattformen ausführen; verifiziert dadurch, dass `cargo test --workspace` grün ist
- [ ] 10.5 PLAN.md Phase 3 als abgeschlossen markieren; verifiziert durch den aktualisierten Planabschnitt
- [x] 10.1 ISAM-Einträge in `docs/inventar.md` auf `implementiert` setzen; verifiziert dadurch, dass der Abgleichstest aus `phase-3-runtime-bildschirm` grün bleibt
- [x] 10.2 `docs/bibliothek.md` um die ISAM-Elemente erweitern; verifiziert dadurch, dass jedes umgesetzte Element dort einen Eintrag hat
- [x] 10.3 Korpus-ISAM-Programme in einem temporären Arbeitsverzeichnis ausführen; verifiziert dadurch, dass nach `cargo test` `git status` sauber ist
- [x] 10.4 Vollständige Suite auf allen Zielplattformen ausführen; verifiziert dadurch, dass `cargo test --workspace` grün ist
- [x] 10.5 PLAN.md Phase 3 als abgeschlossen markieren; verifiziert durch den aktualisierten Planabschnitt

View File

@@ -0,0 +1,193 @@
# Umfang und Argumentformen der ISAM-Elemente (Aufgaben 1.1, 1.2)
**Quelle.** `bas7advr.hlp` (BASIC 7 Advisor), Themenseiten unter
https://dos-help.soulsphere.org/bas7advr.hlp/ (abgerufen 2026-09-04).
Die Themenliste selbst ist bereits in
`openspec/changes/archive/2026-09-04-phase-3-runtime-bildschirm/rohliste-original-hilfe.md`
wortgetreu festgehalten; hier kommen die Syntaxzeilen der Einzelthemen
dazu.
## 1.1 Umfangsabgleich
Der ISAM-Abschnitt der Original-Hilfe führt genau diese Themen (Zeilen der
Rohliste in Klammern):
| Thema der Rohliste | Zeile | Elemente |
|---|---|---|
| `BEGINTRANS Statement` | 26 | `BEGINTRANS` |
| `BOF Function` | 28 | `BOF` |
| `COMMITTRANS Statement` | 47 | `COMMITTRANS` |
| `CREATEINDEX Statement` | 51 | `CREATEINDEX` |
| `DELETE Statement` | 64 | `DELETE` |
| `DELETEINDEX Statement` | 65 | `DELETEINDEX` |
| `DELETETABLE Statement` | 66 | `DELETETABLE` |
| `GETINDEX$ Function` | 99 | `GETINDEX$` |
| `INSERT Statement` | 110 | `INSERT` |
| `MOVEFIRST, MOVELAST, MOVENEXT, MOVEPREVIOUS Statements` | 145 | 4 Elemente |
| `RETRIEVE Statement` | 195 | `RETRIEVE` |
| `ROLLBACK, ROLLBACK ALL Statements` | 200 | `ROLLBACK` |
| `SAVEPOINT Function` | 206 | `SAVEPOINT` |
| `SEEKGT, SEEKGE, SEEKEQ Statements` | 211 | 3 Elemente |
| `SETINDEX Statement` | 213 | `SETINDEX` |
| `SETMEM Function` | 214 | `SETMEM` |
| `UPDATE Statement` | 259 | `UPDATE` |
Das sind **22 Elemente**. `docs/inventar.md` führt in der Gruppe `ISAM`
genau dieselben 22 Namen, alle mit Status `offen`. Der Umfang geht damit
**nicht** über die Aufzählung im Proposal hinaus — die Liste im Proposal
ist deckungsgleich.
**Kein eigenes Element:** `TEXTCOMP` kommt in der Themenliste nicht vor
(bestätigt den Befund aus `design.md`, D4). Ebenso wenig ein Thema für
Sicherungspunkte außer `SAVEPOINT` oder für Datenbankwartung.
**Elemente mit zusätzlichem ISAM-Verhalten**, die schon anderweitig
implementiert sind und deshalb keinen eigenen Inventareintrag der Gruppe
`ISAM` haben: `OPEN` (Klausel `FOR ISAM`), `CLOSE`, `EOF`, `LOF`, `LOC`.
## 1.2 Argumentformen je Element
Syntaxzeilen wortgetreu aus der jeweiligen Themenseite der Original-Hilfe.
| Element | Syntax (Original-Hilfe) | Themenseite |
|---|---|---|
| `OPEN … FOR ISAM` | `OPEN database$ FOR ISAM tabletype tablename$ AS [#]filenumber%` | `x_dot_opfior.html` |
| `CREATEINDEX` | `CREATEINDEX [#]filenumber%,indexname$,unique%,columnname$[,columnname$]` | `x_dot_CREATEINDEXr.html` |
| `DELETEINDEX` | `DELETEINDEX [#]filenumber%,indexname$` | `x_dot_DELETEINDEXr.html` |
| `SETINDEX` | `SETINDEX [#]filenumber%[,indexname$]` | `x_dot_SETINDEXr.html` |
| `GETINDEX$` | `GETINDEX$ (filenumber%)` | `x_dot_GETINDEX$r.html` |
| `INSERT` | `INSERT [#]filenumber%,recordvariable` | `x_dot_INSERTr.html` |
| `RETRIEVE` | `RETRIEVE [#]filenumber%,recordvariable` | `x_dot_RETRIEVEr.html` |
| `UPDATE` | `UPDATE [#]filenumber%,recordvariable` | `x_dot_UPDATEr.html` |
| `DELETE` | `DELETE [#]filenumber%` | `x_dot_DELETEr.html` |
| `DELETETABLE` | `DELETETABLE database$,tablename$` | `x_dot_DELETETABLEr.html` |
| `MOVEFIRST` u. a. | `MOVEFIRST [#]filenumber%` (ebenso `MOVELAST`, `MOVENEXT`, `MOVEPREVIOUS`) | `x_dot_MOVEFIRSTr.html` |
| `SEEKGT`/`SEEKGE`/`SEEKEQ` | `SEEKGT [#]filenumber% ,keyvalue [,keyvalue]…` | `x_dot_seekisamr.html` |
| `BEGINTRANS` | `BEGINTRANS` | `x_dot_BEGINTRANSr.html` |
| `COMMITTRANS` | `COMMITTRANS` | `x_dot_COMMITTRANSr.html` |
| `ROLLBACK` | `ROLLBACK [savepoint%]` bzw. `ROLLBACK ALL` | `x_dot_ROLLBACKr.html` |
| `SAVEPOINT` | `SAVEPOINT` (Funktion, liefert eine ganze Zahl) | `x_dot_SAVEPOINTr.html` |
| `SETMEM` | `SETMEM(numeric-expression&)` | `x_dot_setmemr.html` |
| `BOF` | `BOF(filenumber%)` | `x_dot_BOFr.html` |
### Befund zur offenen Frage aus design.md
Die Spaltenliste bei `CREATEINDEX` ist **keine** Zeichenkette mit
Trennzeichen, sondern eine **Folge einzelner Stringargumente**:
`columnname$[,columnname$]…`. Die Absenkung übernimmt diese Form
unverändert; `CREATEINDEX` ist damit variadisch (4 bis 12 Argumente,
also bis zu neun Indexspalten).
### Ergänzung über die Original-Hilfe hinaus: absteigende Spalten
Die Original-Hilfe kennt bei `CREATEINDEX` **keine** Angabe der
Sortierrichtung — Indizes sind dort immer aufsteigend. Die Anforderung
„Indexverwaltung" dieses Changes verlangt aber auf- **und** absteigende
Spalten. Gewählte Schreibweise: ein vorangestelltes `-` am Spaltennamen.
```basic
CREATEINDEX #1, "NachPreis", 0, "Nachname", "-Preis"
```
Sie ist eindeutig und kollisionsfrei, weil ein Feldname einer
`TYPE`-Anweisung nie mit `-` beginnen kann; Programme des Vorbilds
bleiben dadurch unverändert gültig. Festgehalten in
docs/sprachreferenz.md und docs/bibliothek.md.
### Cursor-Semantik folgt dem Vorbild
Die Delta-Spezifikation schrieb ursprünglich an zwei Stellen eine andere
Cursorlage vor, als die Original-Hilfe beschreibt. Entscheidung vom
2026-09-04: **bei Semantik und Syntax gewinnt das Vorbild, wo immer das
möglich ist.** Die Spezifikation wurde daraufhin angeglichen, nicht die
Umsetzung:
| Situation | Original-Hilfe (und jetzt auch Terminal Basic) |
|---|---|
| nach `SETINDEX` | „the current record is the first record according to that index" — der erste Satz der neuen Ordnung ist der aktuelle |
| nach `DELETE` | „the record following the deleted record becomes the current record"; war der gelöschte der letzte, steht der Cursor am Ende der Tabelle ohne aktuellen Satz |
`SETINDEX` ohne Indexnamen bzw. mit `""` wählt den NULL-Index: „If you
omit the `indexname$` argument or specify double quotes for it, the
current index is the NULL index" — er „represents the order in which
records were added to the file".
### Präfixsuche: Verhalten je Suchart
Die Original-Hilfe unterscheidet die Sucharten bei zu wenigen
Schlüsselwerten ausdrücklich; die Umsetzung übernimmt das:
- `SEEKEQ` mit unvollständigem Schlüssel **schlägt immer fehl**
(„SEEKEQ with insufficient keyvalues always fails").
- `SEEKGE` sucht mit den vorhandenen Werten als Präfix.
- `SEEKGT` mit unvollständigem Schlüssel positioniert auf dem **ersten
passenden** Satz — also wie `SEEKGE`, nicht hinter der Präfixgruppe.
### Fehler 87: Auslöser ist die Suche über den NULL-Index
Der Proposal-Abschnitt „ISAM-Fehlersemantik" nennt Code 87 („ISAM -
Invalid operation on NULL index"). Die Themenseite zur `SEEK`-Familie
schweigt zur Lage ohne aktiven Index — der Fehlerkatalog des Vorbilds
benennt sie aber genau: der NULL-Index führt keine Schlüssel, sondern nur
die Einfügereihenfolge, eine Schlüsselsuche über ihn ist deshalb die
„unzulässige Operation auf dem NULL-Index".
Die Delta-Spezifikation nannte hier zunächst Fehler 83; sie ist mit der
Entscheidung vom 2026-09-04 („bei Semantik gewinnt das Vorbild") auf 87
angeglichen. **83** bleibt dem wirklich unbekannten Indexnamen vorbehalten
und wird von `SETINDEX` und `DELETEINDEX` ausgelöst. Damit hat jeder Code
von 81 bis 89 einen Auslöser.
Bewegung über den NULL-Index bleibt zulässig (`MOVE`-Familie in
Einfügereihenfolge) — die Original-Hilfe beschreibt den NULL-Index
ausdrücklich als Reihenfolge, nicht als Fehlerzustand.
## Befunde der Verifikation (2026-09-04)
Vier Befunde aus `/opsx:verify`, alle umgesetzt.
### `CLOSE` beendet keine Transaktion
Ursprünglich schrieb `CLOSE` eine laufende Transaktion fest, um Aufgabe 4.5
(„schreibt ausstehende Änderungen fest") zu erfüllen. Das war zu weit
gegriffen und hatte zwei beobachtbare Folgen:
```basic
BEGINTRANS : INSERT #1, p : CLOSE #1 ' Satz blieb erhalten
BEGINTRANS : INSERT #1, p : END ' Satz verfiel
```
und, schlimmer, über Dateinummern hinweg:
```basic
OPEN AS #1 : OPEN AS #2
BEGINTRANS : INSERT #1, p : CLOSE #2 ' schrieb die Änderung an #1 fest
ROLLBACK ALL ' → Fehler 5, Satz überlebte
```
Aufgabe 4.5 ist auch ohne diese Festschreibung erfüllt, weil jede Operation
außerhalb einer Transaktion für sich eine Transaktion ist. `CLOSE` löst
seither nur die Bindung; über das Ende einer Transaktion entscheiden allein
`COMMITTRANS` und `ROLLBACK ALL`, und eine beim Programmende offene
Transaktion verfällt — in beiden Wegen gleich. Delta-Spec, Aufgabe 4.5 und
die Referenzen sind angeglichen; zwei Einheitentests halten es fest.
### `CREATEINDEX` ohne Obergrenze der Spaltenzahl
Die Signatur begrenzte den Index auf neun Spalten. Die Original-Hilfe nennt
keine Obergrenze, also darf die Signatur auch keine setzen: die
Spaltenargumente werden jetzt wie bei `INSTR` gesondert geprüft (ab dem
vierten Argument je ein String), die Argumentzahl ist offen.
### Kein Index-Torso nach Fehler 86
Scheiterte `CREATEINDEX` an der Eindeutigkeit, blieb innerhalb einer
Transaktion eine halb gefüllte Indextabelle zurück, die keine
Indexdefinition mehr nannte. `index_anlegen` bildet jetzt erst alle
Schlüssel und prüft die Eindeutigkeit, bevor es schreibt — dieselbe
Reihenfolge wie in `satz_schreiben`.
### Toter Klon in `RETRIEVE`
`satz_lesen` klonte das Tabellenlayout je Aufruf, ohne es zu benutzen.
Entfernt.

View File

@@ -0,0 +1,211 @@
# isam-datenbank Specification
## Purpose
Die ISAM-Unterstützung bietet indizierten Satzzugriff auf Tabellen einer
Datenbankdatei: Sätze werden über benannte Indizes geordnet durchlaufen
und über Schlüsselvergleiche gesucht, Änderungen laufen in Transaktionen
und Verstöße melden die ISAM-Fehlercodes des Vorbilds.
## Requirements
### Requirement: Datenbank- und Tabellenbindung an eine Dateinummer
`OPEN datenbank$ FOR ISAM typname tabellenname AS #n` SHALL eine Tabelle
einer Datenbankdatei an eine Dateinummer binden. Das Satzlayout SHALL
sich aus dem benannten benutzerdefinierten Typ ergeben; dessen Felder
sind die Spalten der Tabelle. Existiert die Tabelle noch nicht, SHALL sie
mit diesem Layout angelegt werden; existiert sie mit abweichendem Layout,
MUST Laufzeitfehler 88 „ISAM - Database inconsistent" auftreten. Ein
ungültiger Tabellen- oder Datenbankname MUST Fehler 81 „ISAM - Invalid
name" auslösen. `CLOSE` SHALL die Bindung lösen; ausstehende Änderungen
sind dabei bereits festgeschrieben, weil jede Operation außerhalb einer
Transaktion für sich eine Transaktion ist. Eine laufende Transaktion MUST
`CLOSE` unberührt lassen — über ihr Ende entscheiden allein `COMMITTRANS`
und `ROLLBACK ALL`. `DELETETABLE` SHALL eine Tabelle samt ihren
Indizes entfernen; eine nicht vorhandene Tabelle MUST Fehler 82 „ISAM -
Table not found" auslösen.
#### Scenario: Tabelle wird beim ersten Öffnen angelegt
- **WHEN** eine Datenbank ohne die Tabelle `Kunden` mit `OPEN "db" FOR ISAM KundeTyp "Kunden" AS #1` geöffnet wird
- **THEN** entsteht die Tabelle mit den Feldern des Typs und die Dateinummer ist nutzbar
#### Scenario: Abweichendes Satzlayout
- **WHEN** eine bestehende Tabelle mit einem Typ geöffnet wird, dessen Felder nicht zu ihren Spalten passen
- **THEN** tritt Laufzeitfehler 88 auf
#### Scenario: CLOSE lässt eine laufende Transaktion stehen
- **WHEN** innerhalb einer Transaktion eine andere Dateinummer mit `CLOSE` geschlossen wird
- **THEN** läuft die Transaktion weiter und ein folgendes `ROLLBACK ALL` nimmt ihre Änderungen zurück
#### Scenario: Entfernte Tabelle
- **WHEN** `DELETETABLE` für eine nicht vorhandene Tabelle aufgerufen wird
- **THEN** tritt Laufzeitfehler 82 auf
### Requirement: Indexverwaltung
`CREATEINDEX` SHALL einen benannten Index über eine oder mehrere Spalten
anlegen, wahlweise eindeutig oder mehrdeutig und je Spalte auf- oder
absteigend geordnet. `DELETEINDEX` SHALL einen Index entfernen.
`SETINDEX` SHALL den aktiven Index einer Dateinummer setzen und damit die
Reihenfolge bestimmen, in der Sätze durchlaufen werden; `GETINDEX$` SHALL
den Namen des aktiven Index liefern. `SETINDEX` ohne Indexnamen (oder mit
leerem Namen) SHALL den NULL-Index wählen, also die Einfügereihenfolge;
`GETINDEX$` liefert dafür einen leeren String. Ein unbekannter Indexname
MUST Laufzeitfehler 83 „ISAM - Index not found" auslösen, ein unbekannter
Spaltenname Fehler 84 „ISAM - Invalid column". Nach `SETINDEX` SHALL der
erste Satz in der Ordnung des gewählten Index der aktuelle Satz sein; ist
die Tabelle leer, gibt es keinen aktuellen Satz.
#### Scenario: Aktiver Index bestimmt die Reihenfolge
- **WHEN** ein Index über die Spalte `Name` gesetzt ist und die Sätze mit `MOVEFIRST` und `MOVENEXT` durchlaufen werden
- **THEN** erscheinen sie in aufsteigender Reihenfolge der Spalte `Name`
#### Scenario: Unbekannter Index
- **WHEN** `SETINDEX #1, "GibtsNicht"` ausgeführt wird
- **THEN** tritt Laufzeitfehler 83 auf
#### Scenario: Aktueller Satz nach SETINDEX
- **WHEN** ein Index gesetzt wird, dessen Ordnung mit einem anderen Satz beginnt als die bisherige
- **THEN** ist dieser Satz ohne weitere Bewegung der aktuelle Satz
#### Scenario: Index über unbekannte Spalte
- **WHEN** `CREATEINDEX` eine Spalte nennt, die der Satztyp nicht führt
- **THEN** tritt Laufzeitfehler 84 auf
### Requirement: Cursorbewegung in der Ordnung des aktiven Index
`MOVEFIRST`, `MOVELAST`, `MOVENEXT` und `MOVEPREVIOUS` SHALL den Cursor
in der Ordnung des aktiven Index bewegen. Läuft der Cursor über das Ende
hinaus, SHALL `EOF` für diese Dateinummer wahr werden; läuft er über den
Anfang hinaus, SHALL `BOF` wahr werden. In beiden Fällen SHALL der Cursor
als unpositioniert gelten. Eine Bewegung ohne aktiven Index SHALL der
Einfügereihenfolge folgen.
#### Scenario: Cursor läuft über das Ende hinaus
- **WHEN** nach dem letzten Satz `MOVENEXT` ausgeführt wird
- **THEN** liefert `EOF` für diese Dateinummer wahr
#### Scenario: Rückwärts über den Anfang hinaus
- **WHEN** auf dem ersten Satz `MOVEPREVIOUS` ausgeführt wird
- **THEN** liefert `BOF` für diese Dateinummer wahr
### Requirement: Schlüsselsuche
`SEEKEQ`, `SEEKGT` und `SEEKGE` SHALL den Cursor auf den ersten Satz
setzen, dessen Schlüssel im aktiven Index gleich, größer bzw.
größer-gleich dem angegebenen Wert ist. Die Zahl der Schlüsselwerte MUST
höchstens der Spaltenzahl des aktiven Index entsprechen; ein
mehrspaltiger Index SHALL mit einem Präfix der Spalten durchsucht werden
können. Findet die Suche keinen Satz, SHALL `EOF` wahr werden und der
Cursor unpositioniert bleiben. Eine Suche über den NULL-Index MUST
Laufzeitfehler 87 „ISAM - Invalid operation on NULL index" auslösen — er
führt keine Schlüssel, sondern nur die Einfügereihenfolge.
#### Scenario: Suche nach größerem Schlüssel
- **WHEN** Sätze mit den Schlüsseln 10, 20 und 30 vorliegen und `SEEKGT #1, 20` ausgeführt wird
- **THEN** steht der Cursor auf dem Satz mit Schlüssel 30
#### Scenario: Erfolglose Suche
- **WHEN** `SEEKGT #1, 30` bei größtem Schlüssel 30 ausgeführt wird
- **THEN** liefert `EOF` wahr
#### Scenario: Suche über Präfix eines mehrspaltigen Index
- **WHEN** der aktive Index über `Nachname` und `Vorname` geht und mit nur einem Wert gesucht wird
- **THEN** positioniert die Suche auf dem ersten Satz mit passendem `Nachname`
### Requirement: Satzoperationen auf dem aktuellen Satz
`INSERT` SHALL einen Satz aus einer Variablen des Tabellentyps einfügen,
`RETRIEVE` den aktuellen Satz in eine solche Variable lesen, `UPDATE` ihn
aus einer solchen Variablen überschreiben und `DELETE` ihn entfernen.
`RETRIEVE`, `UPDATE` und `DELETE` ohne positionierten Cursor MUST
Laufzeitfehler 85 „ISAM - No current record" auslösen. Ein `INSERT` oder
`UPDATE`, das einen als eindeutig angelegten Index verletzen würde, MUST
Laufzeitfehler 86 „ISAM - Duplicate value for unique index" auslösen und
den Satzbestand unverändert lassen. Nach `DELETE` SHALL der auf den
gelöschten folgende Satz der aktuelle sein; war der gelöschte der letzte
in der Ordnung des aktiven Index, SHALL der Cursor am Ende der Tabelle
stehen und es SHALL keinen aktuellen Satz geben. Alle Indizes der Tabelle
SHALL nach jeder Satzoperation die neue Ordnung widerspiegeln.
#### Scenario: Einfügen und Zurücklesen
- **WHEN** ein Satz eingefügt, per `SEEKEQ` gesucht und mit `RETRIEVE` gelesen wird
- **THEN** stimmen alle Feldwerte mit den eingefügten überein
#### Scenario: Operation ohne aktuellen Satz
- **WHEN** hinter dem letzten Satz — also nach `MOVENEXT` am Ende — `RETRIEVE #1, k` ausgeführt wird
- **THEN** tritt Laufzeitfehler 85 auf
#### Scenario: Aktueller Satz nach DELETE
- **WHEN** ein Satz gelöscht wird, auf den in der Ordnung des aktiven Index ein weiterer folgt
- **THEN** ist dieser folgende Satz der aktuelle
#### Scenario: Verstoß gegen eindeutigen Index
- **WHEN** ein Satz eingefügt wird, dessen Schlüssel in einem eindeutigen Index bereits vorkommt
- **THEN** tritt Laufzeitfehler 86 auf und der Satzbestand bleibt unverändert
#### Scenario: Index folgt der Änderung
- **WHEN** die indizierte Spalte eines Satzes per `UPDATE` geändert wird
- **THEN** erscheint der Satz beim nächsten Durchlauf an seiner neuen Position in der Indexordnung
### Requirement: Transaktionen mit Rücknahme
`BEGINTRANS` SHALL eine Transaktion eröffnen, `COMMITTRANS` alle
Änderungen seit ihrem Beginn festschreiben und `ROLLBACK` sie
zurücknehmen. `SAVEPOINT` SHALL als **Funktion** einen Sicherungspunkt
setzen und dessen Kennung liefern; `ROLLBACK` mit einer solchen Kennung
SHALL auf diesen Punkt zurücksetzen, ohne die Transaktion aufzugeben.
`ROLLBACK ALL` SHALL alle Änderungen seit `BEGINTRANS` zurücknehmen und
die Transaktion beenden. Nach jeder Rücknahme MUST der Satzbestand
einschließlich aller Indizes dem Zustand zum Bezugspunkt entsprechen und
der Cursor als unpositioniert gelten. Eine beim Programmende nicht
festgeschriebene Transaktion MUST wirkungslos bleiben.
#### Scenario: Rücknahme stellt den Bestand wieder her
- **WHEN** nach `BEGINTRANS` Sätze eingefügt und gelöscht werden und dann `ROLLBACK` folgt
- **THEN** entspricht der Bestand beim erneuten Durchlauf dem Zustand vor `BEGINTRANS`
#### Scenario: Rücknahme auf einen Sicherungspunkt
- **WHEN** innerhalb einer Transaktion `s = SAVEPOINT` ausgewertet, danach geändert und mit `ROLLBACK s` zurückgesetzt wird
- **THEN** bleiben die Änderungen vor dem Sicherungspunkt erhalten und die danach sind zurückgenommen
#### Scenario: ROLLBACK ALL beendet die Transaktion
- **WHEN** nach mehreren Sicherungspunkten `ROLLBACK ALL` ausgeführt wird
- **THEN** entspricht der Bestand dem Zustand vor `BEGINTRANS` und die Transaktion ist beendet
### Requirement: Vergleichsreihenfolge für Indizes
Textspalten SHALL in Indizes nach einer festgelegten, dokumentierten
Sortierordnung geordnet werden. Diese Ordnung MUST dieselbe sein, die der
Vergleichsoperator des Dialekts auf Strings anwendet, damit ein Programm
die Satzreihenfolge vorhersagen kann. Die gewählte Ordnung MUST in
docs/sprachreferenz.md festgehalten sein, einschließlich der Behandlung
von Groß- und Kleinschreibung und von Zeichen jenseits von ASCII.
#### Scenario: Indexordnung entspricht dem Stringvergleich
- **WHEN** zwei Texte als Schlüssel indiziert und dieselben Werte mit `<` verglichen werden
- **THEN** entspricht ihre Reihenfolge im Index dem Ergebnis des Vergleichs
### Requirement: Pufferverwaltung mit benanntem Fehler
`SETMEM` SHALL als **Funktion** die für ISAM verfügbare Puffergröße um
den übergebenen Betrag ändern und die danach verfügbare Größe liefern.
Reicht
der verfügbare Puffer für eine Operation nicht aus, MUST Laufzeitfehler 89
„ISAM - Insufficient ISAM buffers" auftreten statt eines generischen
Fehlers oder eines stillen Abbruchs. Weicht die Bedeutung von `SETMEM`
mangels DOS-Speichermodell vom Vorbild ab, MUST die Abweichung in
docs/sprachreferenz.md dokumentiert sein.
#### Scenario: Pufferfehler wird benannt
- **WHEN** eine Operation den verfügbaren ISAM-Puffer überschreitet
- **THEN** tritt Laufzeitfehler 89 auf
### Requirement: Eigenes Datenbankdateiformat
Die Datenbankdatei SHALL ein eigenes, in docs/dateiformate.md
beschriebenes Format verwenden und ist bewusst nicht binärkompatibel zu
Datenbankdateien des Vorbilds — dieselbe Linie wie bei den
UTF-32-Festtextfeldern. Das Format SHALL eine Formatversion tragen; eine
unbekannte Version MUST mit einem benannten Fehler abgewiesen werden
statt fehlinterpretiert zu werden. Eine strukturell beschädigte Datei
MUST Laufzeitfehler 88 auslösen.
#### Scenario: Unbekannte Formatversion
- **WHEN** eine Datenbankdatei mit einer höheren Formatversion geöffnet wird
- **THEN** wird sie mit einem benannten Fehler abgewiesen und nicht gelesen
#### Scenario: Beschädigte Datei
- **WHEN** eine strukturell beschädigte Datenbankdatei geöffnet wird
- **THEN** tritt Laufzeitfehler 88 auf

View File

@@ -173,3 +173,29 @@ bleiben.
#### Scenario: Leerraum an den Rändern
- **WHEN** ein Modul `DATA ab cd ` enthält und gelesen wird
- **THEN** lautet der Wert `ab cd`
### Requirement: ISAM-Anweisungen und -Funktionen in Grammatik und Signaturprüfung
Der Parser SHALL die ISAM-Anweisungen und -Funktionen des Vorbilds als
eigene Konstrukte erkennen, und die Semantik SHALL für jede eine Signatur
führen (Argumentanzahl und -typen). Der maßgebliche Umfang ergibt sich
aus dem Sprachinventar; er umfasst mindestens `CREATEINDEX`,
`DELETEINDEX`, `SETINDEX`, `GETINDEX$`, `INSERT`, `RETRIEVE`, `UPDATE`,
`DELETE`, `DELETETABLE`, `MOVEFIRST`, `MOVELAST`, `MOVENEXT`,
`MOVEPREVIOUS`, `SEEKEQ`, `SEEKGT`, `SEEKGE`, `BEGINTRANS`,
`COMMITTRANS`, `ROLLBACK`, `ROLLBACK ALL`, `SAVEPOINT`, `SETMEM` und
`BOF`. Eine fehlerhafte Verwendung MUST eine Diagnose erzeugen, die
das Element namentlich nennt; ein ISAM-Element MUST NOT als unbekannter
Bezeichner oder mit einem generischen Syntaxfehler abgewiesen werden
(Guiding Principle).
#### Scenario: ISAM-Anweisung wird als solche erkannt
- **WHEN** ein Modul `SETINDEX #1, "NachName"` enthält
- **THEN** entsteht keine Diagnose „unbekannter Bezeichner" und die Anweisung wird als ISAM-Anweisung erfasst
#### Scenario: Falsche Argumentanzahl wird namentlich gemeldet
- **WHEN** ein Modul `SEEKGT #1` ohne Schlüsselwert enthält
- **THEN** nennt die Diagnose `SEEKGT` und die erwartete Argumentanzahl
#### Scenario: Typprüfung des Satzarguments
- **WHEN** `RETRIEVE #1, x$` mit einem String statt einer Variablen des Tabellentyps geprüft wird
- **THEN** wird „Type mismatch" mit Quelltextposition gemeldet