Phase 3 (Kern): Laufzeitbibliothek, Bildschirm und Datei-E/A
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>
This commit is contained in:
187
openspec/changes/phase-3-isam/specs/isam-datenbank/spec.md
Normal file
187
openspec/changes/phase-3-isam/specs/isam-datenbank/spec.md
Normal file
@@ -0,0 +1,187 @@
|
||||
## 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
|
||||
Reference in New Issue
Block a user