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>
212 lines
11 KiB
Markdown
212 lines
11 KiB
Markdown
# 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
|