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:
@@ -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,
|
mit diesem Layout angelegt werden; existiert sie mit abweichendem Layout,
|
||||||
MUST Laufzeitfehler 88 „ISAM - Database inconsistent" auftreten. Ein
|
MUST Laufzeitfehler 88 „ISAM - Database inconsistent" auftreten. Ein
|
||||||
ungültiger Tabellen- oder Datenbankname MUST Fehler 81 „ISAM - Invalid
|
ungültiger Tabellen- oder Datenbankname MUST Fehler 81 „ISAM - Invalid
|
||||||
name" auslösen. `CLOSE` SHALL die Bindung lösen und ausstehende
|
name" auslösen. `CLOSE` SHALL die Bindung lösen; ausstehende Änderungen
|
||||||
Änderungen festschreiben. `DELETETABLE` SHALL eine Tabelle samt ihren
|
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 -
|
Indizes entfernen; eine nicht vorhandene Tabelle MUST Fehler 82 „ISAM -
|
||||||
Table not found" auslösen.
|
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
|
- **WHEN** eine bestehende Tabelle mit einem Typ geöffnet wird, dessen Felder nicht zu ihren Spalten passen
|
||||||
- **THEN** tritt Laufzeitfehler 88 auf
|
- **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
|
#### Scenario: Entfernte Tabelle
|
||||||
- **WHEN** `DELETETABLE` für eine nicht vorhandene Tabelle aufgerufen wird
|
- **WHEN** `DELETETABLE` für eine nicht vorhandene Tabelle aufgerufen wird
|
||||||
- **THEN** tritt Laufzeitfehler 82 auf
|
- **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.
|
absteigend geordnet. `DELETEINDEX` SHALL einen Index entfernen.
|
||||||
`SETINDEX` SHALL den aktiven Index einer Dateinummer setzen und damit die
|
`SETINDEX` SHALL den aktiven Index einer Dateinummer setzen und damit die
|
||||||
Reihenfolge bestimmen, in der Sätze durchlaufen werden; `GETINDEX$` SHALL
|
Reihenfolge bestimmen, in der Sätze durchlaufen werden; `GETINDEX$` SHALL
|
||||||
den Namen des aktiven Index liefern. Ein unbekannter Indexname MUST
|
den Namen des aktiven Index liefern. `SETINDEX` ohne Indexnamen (oder mit
|
||||||
Laufzeitfehler 83 „ISAM - Index not found" auslösen, ein unbekannter
|
leerem Namen) SHALL den NULL-Index wählen, also die Einfügereihenfolge;
|
||||||
Spaltenname Fehler 84 „ISAM - Invalid column". Nach `SETINDEX` MUST der
|
`GETINDEX$` liefert dafür einen leeren String. Ein unbekannter Indexname
|
||||||
Cursor als unpositioniert gelten.
|
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
|
#### 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
|
- **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
|
- **WHEN** `SETINDEX #1, "GibtsNicht"` ausgeführt wird
|
||||||
- **THEN** tritt Laufzeitfehler 83 auf
|
- **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
|
#### Scenario: Index über unbekannte Spalte
|
||||||
- **WHEN** `CREATEINDEX` eine Spalte nennt, die der Satztyp nicht führt
|
- **WHEN** `CREATEINDEX` eine Spalte nennt, die der Satztyp nicht führt
|
||||||
- **THEN** tritt Laufzeitfehler 84 auf
|
- **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
|
höchstens der Spaltenzahl des aktiven Index entsprechen; ein
|
||||||
mehrspaltiger Index SHALL mit einem Präfix der Spalten durchsucht werden
|
mehrspaltiger Index SHALL mit einem Präfix der Spalten durchsucht werden
|
||||||
können. Findet die Suche keinen Satz, SHALL `EOF` wahr werden und der
|
können. Findet die Suche keinen Satz, SHALL `EOF` wahr werden und der
|
||||||
Cursor unpositioniert bleiben. Eine Suche ohne aktiven Index MUST
|
Cursor unpositioniert bleiben. Eine Suche über den NULL-Index MUST
|
||||||
Laufzeitfehler 83 auslösen.
|
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
|
#### 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
|
- **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
|
Laufzeitfehler 85 „ISAM - No current record" auslösen. Ein `INSERT` oder
|
||||||
`UPDATE`, das einen als eindeutig angelegten Index verletzen würde, MUST
|
`UPDATE`, das einen als eindeutig angelegten Index verletzen würde, MUST
|
||||||
Laufzeitfehler 86 „ISAM - Duplicate value for unique index" auslösen und
|
Laufzeitfehler 86 „ISAM - Duplicate value for unique index" auslösen und
|
||||||
den Satzbestand unverändert lassen. Nach `DELETE` SHALL der Cursor als
|
den Satzbestand unverändert lassen. Nach `DELETE` SHALL der auf den
|
||||||
unpositioniert gelten. Alle Indizes der Tabelle SHALL nach jeder
|
gelöschten folgende Satz der aktuelle sein; war der gelöschte der letzte
|
||||||
Satzoperation die neue Ordnung widerspiegeln.
|
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
|
#### Scenario: Einfügen und Zurücklesen
|
||||||
- **WHEN** ein Satz eingefügt, per `SEEKEQ` gesucht und mit `RETRIEVE` gelesen wird
|
- **WHEN** ein Satz eingefügt, per `SEEKEQ` gesucht und mit `RETRIEVE` gelesen wird
|
||||||
- **THEN** stimmen alle Feldwerte mit den eingefügten überein
|
- **THEN** stimmen alle Feldwerte mit den eingefügten überein
|
||||||
|
|
||||||
#### Scenario: Operation ohne aktuellen Satz
|
#### 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
|
- **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
|
#### Scenario: Verstoß gegen eindeutigen Index
|
||||||
- **WHEN** ein Satz eingefügt wird, dessen Schlüssel in einem eindeutigen Index bereits vorkommt
|
- **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
|
- **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
|
`ROLLBACK ALL` SHALL alle Änderungen seit `BEGINTRANS` zurücknehmen und
|
||||||
die Transaktion beenden. Nach jeder Rücknahme MUST der Satzbestand
|
die Transaktion beenden. Nach jeder Rücknahme MUST der Satzbestand
|
||||||
einschließlich aller Indizes dem Zustand zum Bezugspunkt entsprechen und
|
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
|
#### Scenario: Rücknahme stellt den Bestand wieder her
|
||||||
- **WHEN** nach `BEGINTRANS` Sätze eingefügt und gelöscht werden und dann `ROLLBACK` folgt
|
- **WHEN** nach `BEGINTRANS` Sätze eingefügt und gelöscht werden und dann `ROLLBACK` folgt
|
||||||
@@ -1,78 +1,78 @@
|
|||||||
## 1. Umfang festlegen
|
## 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
|
- [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
|
||||||
- [ ] 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.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. Frontend: Grammatik und Signaturen
|
||||||
|
|
||||||
- [ ] 2.1 ISAM-Anweisungen in die Parser-Grammatik aufnehmen; verifiziert durch Frontend-Test, dass `SETINDEX #1, "Name"` diagnose-frei parst
|
- [x] 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
|
- [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
|
||||||
- [ ] 2.3 Satzargumente gegen den Tabellentyp prüfen; verifiziert durch Frontend-Test, dass `RETRIEVE #1, x$` „Type mismatch" meldet
|
- [x] 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
|
- [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
|
||||||
- [ ] 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.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. 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
|
- [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
|
||||||
- [ ] 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.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
|
- [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
|
||||||
- [ ] 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.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
|
- [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
|
||||||
- [ ] 3.6 Sortierordnung (Unicode-Codepoints, Groß-/Kleinschreibung unterschieden) in docs/sprachreferenz.md festhalten; verifiziert durch den Abschnittseintrag
|
- [x] 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.7 Datenbankdateiformat in docs/dateiformate.md beschreiben inklusive Formatversion und bewusster Inkompatibilität zum Vorbild; verifiziert durch den Abschnittseintrag
|
||||||
|
|
||||||
## 4. Tabellen
|
## 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
|
- [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
|
||||||
- [ ] 4.2 Tabelle beim ersten Öffnen anlegen; verifiziert durch Korpustest gegen eine Datenbank ohne diese Tabelle
|
- [x] 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
|
- [x] 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
|
- [x] 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
|
- [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
|
||||||
- [ ] 4.6 `DELETETABLE` entfernt Tabelle samt Indizes, Fehler 82 bei fehlender Tabelle; verifiziert durch Korpustest für beide Fälle
|
- [x] 4.6 `DELETETABLE` entfernt Tabelle samt Indizes, Fehler 82 bei fehlender Tabelle; verifiziert durch Korpustest für beide Fälle
|
||||||
|
|
||||||
## 5. Indizes
|
## 5. Indizes
|
||||||
|
|
||||||
- [ ] 5.1 Indextabellen `Schlüsselbytes → satz-id` je Index anlegen; verifiziert durch Test, dass die Einträge in Schlüsselreihenfolge stehen
|
- [x] 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
|
- [x] 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
|
- [x] 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
|
- [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
|
||||||
- [ ] 5.5 Fehler 83 bei unbekanntem Index und Fehler 84 bei unbekannter Spalte; verifiziert durch Korpustest für beide Fälle
|
- [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. 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
|
- [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
|
||||||
- [ ] 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.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
|
- [x] 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
|
- [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
|
||||||
- [ ] 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.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
|
- [x] 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.7 Suche über den NULL-Index löst Fehler 87 aus; verifiziert durch Korpustest
|
||||||
|
|
||||||
## 7. Satzoperationen
|
## 7. Satzoperationen
|
||||||
|
|
||||||
- [ ] 7.1 `INSERT` und `RETRIEVE`; verifiziert durch Korpustest, dass ein eingefügter Satz per `SEEKEQ` gefunden und feldgleich gelesen wird
|
- [x] 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
|
- [x] 7.2 `UPDATE` und `DELETE`, nach `DELETE` wird der folgende Satz der aktuelle (am Ende: kein aktueller Satz); verifiziert durch Korpustest
|
||||||
- [ ] 7.3 Fehler 85 bei `RETRIEVE`/`UPDATE`/`DELETE` ohne positionierten Cursor; verifiziert durch je einen Korpustest
|
- [x] 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
|
- [x] 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.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. Transaktionen
|
||||||
|
|
||||||
- [ ] 8.1 `BEGINTRANS`/`COMMITTRANS` auf einer Schreibtransaktion; verifiziert durch Korpustest, dass die Änderungen nach erneutem Öffnen vorhanden sind
|
- [x] 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
|
- [x] 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
|
- [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
|
||||||
- [ ] 8.4 `SAVEPOINT` als Funktion (liefert Kennung) und `ROLLBACK kennung`; verifiziert durch Korpustest, dass Änderungen vor dem Punkt erhalten bleiben
|
- [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. Ü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
|
- [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
|
||||||
- [ ] 9.3 Vorgabewert und Bedeutung von `SETMEM` sowie die Abweichung vom DOS-Speichermodell in docs/sprachreferenz.md festhalten; verifiziert durch den Abschnittseintrag
|
- [x] 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.4 Indexordnung gegen den Stringvergleich des Dialekts prüfen; verifiziert durch Korpustest, dass Indexreihenfolge und `<`-Vergleich übereinstimmen
|
||||||
|
|
||||||
## 10. Abschluss
|
## 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
|
- [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
|
||||||
- [ ] 10.2 `docs/bibliothek.md` um die ISAM-Elemente erweitern; verifiziert dadurch, dass jedes umgesetzte Element dort einen Eintrag hat
|
- [x] 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
|
- [x] 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
|
- [x] 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.5 PLAN.md Phase 3 als abgeschlossen markieren; verifiziert durch den aktualisierten Planabschnitt
|
||||||
@@ -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.
|
||||||
211
openspec/specs/isam-datenbank/spec.md
Normal file
211
openspec/specs/isam-datenbank/spec.md
Normal 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
|
||||||
@@ -173,3 +173,29 @@ bleiben.
|
|||||||
#### Scenario: Leerraum an den Rändern
|
#### Scenario: Leerraum an den Rändern
|
||||||
- **WHEN** ein Modul `DATA ab cd ` enthält und gelesen wird
|
- **WHEN** ein Modul `DATA ab cd ` enthält und gelesen wird
|
||||||
- **THEN** lautet der Wert `ab cd`
|
- **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
|
||||||
|
|||||||
Reference in New Issue
Block a user