Setzt den OpenSpec-Change phase-3-runtime-bildschirm um (77/77 Aufgaben). Abdeckung laut Inventar: 195 implementiert, 37 offen, 53 Non-Feature. Vollstaendigkeits-Inventar - docs/inventar.md mit 285 Eintraegen aus den Themenlisten von bas7advr.hlp und qb45advr.hlp, je mit Status und Fundstelle - crates/tb-frontend/tests/inventar.rs haelt die Tabelle in beide Richtungen gegen den Code; der Abdeckungsstand kann nicht veralten Bruchschritt (Puffer, Host, Korpus) - Cell/TextScreen ziehen ratatui-frei nach tb-runtime::screen; tb-ui behaelt Farbabbildung, Widget und den neuen Terminal-Host - Host wechselt vom Zeichenstrom auf Anzeige des Zellenpuffers plus Ereignisse (Taste, Groessenaenderung, Abbruch) - Korpusvergleich auf getrimmten Bildschirm-Snapshot; die acht bestehenden Sollausgaben blieben dabei unveraendert Groessenunabhaengigkeit - 80x25 ist nirgends mehr eine feste Grenze; Groessenaenderungen waehrend der Ausfuehrung werden zugestellt (Inhalt oben links erhalten, Cursor und VIEW PRINT geklemmt) - Korpusnachweis: dasselbe Programm bei 80x25 und 120x40 mit je eigener Sollausgabe, plus ein Programm mit Groessenwechsel mitten im Lauf Bibliothek - Breite Unicode-Zeichen belegen zwei Zellen (Cursor, POS, Randumbruch) - Bildschirm: CLS, COLOR, LOCATE, WIDTH, VIEW PRINT, SCREEN, CSRLIN, POS - Tastatur: INKEY$, INPUT$, Funktionstasten-Makros (KEY n / LIST / ON) - PRINT USING, LPRINT USING, FORMAT$, SetFormatCC - Mathematik mit kompatiblem PRNG (gleiche Saat, gleiche Folge) - Datum und Zeit mit Serienwerten ab 1899 - Finanzmathematik: FV#, PV#, Pmt#, IPmt#, PPmt#, NPer#, Rate#, NPV#, IRR#, MIRR#, SLN#, SYD#, DDB# - Datei-E/A: sequenziell, RANDOM (Recordpuffer und UDT-Variablen), BINARY, FIELD/LSET/RSET, Statusfunktionen, Dateisystem, MK$/CV - System: ENVIRON, FRE, CLEAR, TRON/TROFF, STACK, ERDEV, ERR-Anweisung Altlasten aus Phase 2 - ON ERROR GOTO auf Modulebene ist aus Prozeduren ansprechbar (prozeduruebergreifender Fixup im Codegenerator) - DATA behaelt seinen Rohtext (Gross-/Kleinschreibung, innerer Leerraum) - Die vier TODO-verify-Zellen der Konvertierungsmatrix sind aufgeloest Vom Inventar aufgedeckte Fehler - Zwoelf Non-Features wies der Compiler entgegen der Phase-1-Spec nicht ab (CALLS, SSEG, POINT, VIEW, COM, PEN, STRIG, STICK, die String*-Routinen, LINE und OPEN "COMn:") -- geschlossen - Drei Gruppen fehlten im urspruenglichen Umfang: Finanzmathematik, Record-Konvertierung, Rest der Dateisystemfunktionen - bas7advr.hlp allein ist keine vollstaendige Quelle; das Inventar bildet die Vereinigung mit qb45advr.hlp Neue Changes - phase-3-isam: schliesst Phase 3 ab (Speicherschicht redb) - phase-3-ortszeit: zieht die UTC-Abweichung zurueck Neue Abhaengigkeit: unicode-width. Dokumentiert: sprachreferenz.md und tbvm-design.md sind TODO-frei, docs/bibliothek.md neu, PLAN.md fortgeschrieben. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
188 lines
9.8 KiB
Markdown
188 lines
9.8 KiB
Markdown
## 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.
|
|
|
|
## ADDED 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 und ausstehende
|
|
Änderungen festschreiben. `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: 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. 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.
|
|
|
|
#### 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: 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 ohne aktiven Index MUST
|
|
Laufzeitfehler 83 auslösen.
|
|
|
|
#### 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 Cursor als
|
|
unpositioniert gelten. 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
|
|
- **THEN** tritt Laufzeitfehler 85 auf
|
|
|
|
#### 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.
|
|
|
|
#### 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
|