Files
TerminalBasic/openspec/changes/phase-3-isam/specs/isam-datenbank/spec.md
Chili Palmer 52ccbb5848 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>
2026-09-04 06:54:19 +02:00

9.8 KiB

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