ISAM-Datenbankunterstuetzung
Setzt den OpenSpec-Change phase-3-isam um (49/49 Aufgaben) und schliesst damit Phase 3 ab. Alle 22 ISAM-Elemente des Inventars sind implementiert; der Abdeckungsstand steigt auf 217 implementiert / 15 offen / 53 Non-Feature. Frontend - ISAM-Anweisungen als eigener Zweig im Parser: NAME [#]n [, arg ...], Sonderform ROLLBACK ALL ueber eine Sentinel-Kennung - 22 Signaturen in builtin_stmt/builtin_fn, neue Argumentart R (Satzvariable eines benutzerdefinierten Typs) - Satzargumente von INSERT/RETRIEVE/UPDATE werden gegen den Typ der Dateinummer geprueft, sofern beide literal bekannt sind - Die Unsupported-Absenkung von OPEN ... FOR ISAM entfaellt Speicherschicht (tb-runtime::isam, einziger Ort mit redb) - Je Tabelle eine Satztabelle satz-id -> Satzbytes, je Index eine Tabelle Schluesselbytes -> satz-id. Satz-IDs sind monoton und werden nie wiederverwendet: der Cursor merkt sich eine ID, eine neu vergebene koennte still auf einen fremden Satz zeigen - Satzbytes entstehen mit fileio::wert_schreiben/wert_lesen, also mit den Recordpuffern und der UTF-32-Festtextkodierung aus datei-eio - Ordnungserhaltende Schluesselkodierung je Spaltentyp; Text als UTF-8, dessen Bytereihenfolge die Codepoint-Reihenfolge ist und damit dieselbe Ordnung wie CmpStr der VM. Ein Eigenschaftstest ueber 5000 zufaellige Wertepaare je Typ haelt fest, dass der Bytevergleich dem fachlichen Vergleich entspricht -- eine Ordnungsverletzung faellt sonst erst bei bestimmten Datenwerten auf - Cursor als Wert (aktiver Index, letzte Satz-ID, unpositioniert), nicht als gehaltener Iterator: er ueberlebt Satzaenderungen und Transaktionsgrenzen - Sicherungspunkte ueber ein eigenes Ruecknahmeprotokoll; ROLLBACK ALL bricht die Bibliothekstransaktion direkt ab Semantik durchweg aus der Original-Hilfe - Die Argumentformen stammen erstmals aus den Einzelseiten, nicht nur aus der Themenliste. Das korrigierte eine Annahme des Entwurfs: die Spaltenliste von CREATEINDEX ist keine Zeichenkette mit Trennzeichen, sondern eine Folge einzelner Stringargumente - SEEKEQ mit unvollstaendigem Schluessel schlaegt immer fehl, SEEKGT mit Teilschluessel positioniert wie SEEKGE - Nach SETINDEX ist der erste Satz der neuen Ordnung aktuell, nach DELETE der folgende - Suche ueber den NULL-Index meldet 87, nicht 83; damit hat jeder Code von 81 bis 89 einen Ausloeser - Einzige Erweiterung: ein - vor dem Spaltennamen ordnet absteigend. Die Original-Hilfe kennt bei CREATEINDEX keine Sortierrichtung, die Anforderung verlangt sie. Kollisionsfrei, weil ein TYPE-Feldname nie mit - beginnen kann CLOSE beendet keine Transaktion - Erst schrieb es sie fest, womit das Schliessen irgendeiner Dateinummer die Transaktion aller anderen mit beendete und ein folgendes ROLLBACK ALL ins Leere lief. Ausstehende Aenderungen sind ohnehin festgeschrieben, weil jede Operation ausserhalb einer Transaktion fuer sich eine ist - Ueber das Ende entscheiden allein COMMITTRANS und ROLLBACK ALL; eine beim Programmende offene Transaktion verfaellt, in beiden Wegen gleich SETMEM und Fehler 89 - Ohne DOS-Speichermodell ist SETMEM die Obergrenze des ISAM-Puffers (Vorgabe 65536 Bytes); ohne echte Grenze waere Code 89 nie erreichbar und die Anweisung eine Attrappe Tests - 18 Einheitentests (Kodierung, Formatversion, Satz-IDs, Roundtrip ueber alle Feldtypen, Indexordnung, Cursor, Protokoll) - 6 Korpusprogramme: Tabellen, Indizes, Cursor, Saetze, Transaktionen, Puffer und Vergleichsordnung -- je in einem temporaeren Arbeits- verzeichnis, der Projektbaum bleibt sauber - Der VM-Test zu Fehler 73 prueft ISAM nicht mehr, sondern SETUEVENT; dafuer belegt ein neuer Test, dass OPEN ... FOR ISAM arbeitet Dokumentation: Datenbankdateiformat in dateiformate.md, Abschnitt 10a samt Sortierordnung und SETMEM-Abweichung in sprachreferenz.md, ISAM-Abschnitt in bibliothek.md, Inventar auf implementiert, PLAN.md schaerft die Leitplanke Referenzverhalten -- sie gilt auch gegenueber den eigenen Spezifikationen. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
126
tests/compat/isamcursor.bas
Normal file
126
tests/compat/isamcursor.bas
Normal file
@@ -0,0 +1,126 @@
|
||||
' tb-screen: 80x25
|
||||
' tb-tempdir
|
||||
' ISAM (Spec isam-datenbank, Anforderungen "Cursorbewegung in der Ordnung
|
||||
' des aktiven Index" und "Schluesselsuche"): MOVE-Familie, Einfuegereihen-
|
||||
' folge ohne aktiven Index, EOF/BOF an den Raendern, SEEK-Familie mit
|
||||
' Praefixsuche, erfolglose Suche und Suche ohne aktiven Index.
|
||||
' Laeuft in einem temporaeren Verzeichnis.
|
||||
TYPE Satz
|
||||
Schluessel AS INTEGER
|
||||
Nachname AS STRING * 6
|
||||
Vorname AS STRING * 6
|
||||
END TYPE
|
||||
DIM s AS Satz
|
||||
|
||||
ON ERROR GOTO Handler
|
||||
OPEN "cursor.isam" FOR ISAM Satz "Werte" AS #1
|
||||
|
||||
' Einfuegereihenfolge 30, 10, 20 — damit sie sich von der Indexordnung
|
||||
' unterscheidet.
|
||||
s.Schluessel = 30
|
||||
s.Nachname = "Cesar"
|
||||
s.Vorname = "Carl"
|
||||
INSERT #1, s
|
||||
s.Schluessel = 10
|
||||
s.Nachname = "Adam"
|
||||
s.Vorname = "Anna"
|
||||
INSERT #1, s
|
||||
s.Schluessel = 20
|
||||
s.Nachname = "Adam"
|
||||
s.Vorname = "Bert"
|
||||
INSERT #1, s
|
||||
|
||||
CREATEINDEX #1, "NachSchluessel", 1, "Schluessel"
|
||||
CREATEINDEX #1, "NachName", 0, "Nachname", "Vorname"
|
||||
|
||||
' --- 6.3: Bewegung ohne aktiven Index folgt der Einfuegereihenfolge ---
|
||||
SETINDEX #1
|
||||
PRINT "ohne Index:";
|
||||
MOVEFIRST #1
|
||||
DO WHILE NOT EOF(1)
|
||||
RETRIEVE #1, s
|
||||
PRINT s.Schluessel;
|
||||
MOVENEXT #1
|
||||
LOOP
|
||||
PRINT
|
||||
|
||||
' --- 6.2: Durchlauf in der Ordnung des aktiven Index ---
|
||||
SETINDEX #1, "NachSchluessel"
|
||||
PRINT "vorwaerts:";
|
||||
MOVEFIRST #1
|
||||
DO WHILE NOT EOF(1)
|
||||
RETRIEVE #1, s
|
||||
PRINT s.Schluessel;
|
||||
MOVENEXT #1
|
||||
LOOP
|
||||
PRINT
|
||||
|
||||
PRINT "rueckwaerts:";
|
||||
MOVELAST #1
|
||||
DO WHILE NOT BOF(1)
|
||||
RETRIEVE #1, s
|
||||
PRINT s.Schluessel;
|
||||
MOVEPREVIOUS #1
|
||||
LOOP
|
||||
PRINT
|
||||
|
||||
' --- 6.4: MOVENEXT am Ende setzt EOF, Cursor unpositioniert ---
|
||||
MOVELAST #1
|
||||
MOVENEXT #1
|
||||
PRINT "EOF nach MOVENEXT am Ende:"; EOF(1); "BOF:"; BOF(1)
|
||||
RETRIEVE #1, s
|
||||
|
||||
' --- 6.4: MOVEPREVIOUS am Anfang setzt BOF, Cursor unpositioniert ---
|
||||
MOVEFIRST #1
|
||||
MOVEPREVIOUS #1
|
||||
PRINT "BOF nach MOVEPREVIOUS am Anfang:"; BOF(1); "EOF:"; EOF(1)
|
||||
RETRIEVE #1, s
|
||||
|
||||
' --- 6.5: SEEKGT, SEEKGE, SEEKEQ mit den Schluesseln 10, 20, 30 ---
|
||||
SETINDEX #1, "NachSchluessel"
|
||||
SEEKGT #1, 20
|
||||
RETRIEVE #1, s
|
||||
PRINT "SEEKGT 20 ->"; s.Schluessel
|
||||
|
||||
SEEKGE #1, 20
|
||||
RETRIEVE #1, s
|
||||
PRINT "SEEKGE 20 ->"; s.Schluessel
|
||||
|
||||
SEEKGE #1, 15
|
||||
RETRIEVE #1, s
|
||||
PRINT "SEEKGE 15 ->"; s.Schluessel
|
||||
|
||||
SEEKEQ #1, 10
|
||||
RETRIEVE #1, s
|
||||
PRINT "SEEKEQ 10 ->"; s.Schluessel
|
||||
|
||||
' --- 6.5: Praefixsuche ueber einen mehrspaltigen Index ---
|
||||
SETINDEX #1, "NachName"
|
||||
SEEKGE #1, "Adam"
|
||||
RETRIEVE #1, s
|
||||
PRINT "Praefix Adam ->"; RTRIM$(s.Nachname); "/"; RTRIM$(s.Vorname)
|
||||
' SEEKEQ mit unvollstaendigem Schluessel schlaegt immer fehl (Original-Hilfe).
|
||||
SEEKEQ #1, "Adam"
|
||||
PRINT "SEEKEQ mit Praefix, EOF:"; EOF(1)
|
||||
' Mit beiden Werten findet SEEKEQ den Satz.
|
||||
SEEKEQ #1, "Adam", "Bert"
|
||||
RETRIEVE #1, s
|
||||
PRINT "SEEKEQ Adam/Bert ->"; s.Schluessel
|
||||
|
||||
' --- 6.6: erfolglose Suche setzt EOF, Cursor unpositioniert ---
|
||||
SETINDEX #1, "NachSchluessel"
|
||||
SEEKGT #1, 30
|
||||
PRINT "SEEKGT 30, EOF:"; EOF(1)
|
||||
RETRIEVE #1, s
|
||||
|
||||
' --- 6.7: Suche ueber den NULL-Index meldet 87 ---
|
||||
SETINDEX #1
|
||||
SEEKEQ #1, 10
|
||||
|
||||
CLOSE #1
|
||||
PRINT "fertig"
|
||||
END
|
||||
|
||||
Handler:
|
||||
PRINT "ERR="; ERR
|
||||
RESUME NEXT
|
||||
18
tests/compat/isamcursor.out
Normal file
18
tests/compat/isamcursor.out
Normal file
@@ -0,0 +1,18 @@
|
||||
ohne Index: 30 10 20
|
||||
vorwaerts: 10 20 30
|
||||
rueckwaerts: 30 20 10
|
||||
EOF nach MOVENEXT am Ende:-1 BOF: 0
|
||||
ERR= 85
|
||||
BOF nach MOVEPREVIOUS am Anfang:-1 EOF: 0
|
||||
ERR= 85
|
||||
SEEKGT 20 -> 30
|
||||
SEEKGE 20 -> 20
|
||||
SEEKGE 15 -> 20
|
||||
SEEKEQ 10 -> 10
|
||||
Praefix Adam ->Adam/Anna
|
||||
SEEKEQ mit Praefix, EOF:-1
|
||||
SEEKEQ Adam/Bert -> 20
|
||||
SEEKGT 30, EOF:-1
|
||||
ERR= 85
|
||||
ERR= 87
|
||||
fertig
|
||||
100
tests/compat/isamindizes.bas
Normal file
100
tests/compat/isamindizes.bas
Normal file
@@ -0,0 +1,100 @@
|
||||
' tb-screen: 80x25
|
||||
' tb-tempdir
|
||||
' ISAM (Spec isam-datenbank, Anforderung "Indexverwaltung"): CREATEINDEX
|
||||
' mit einer und mit mehreren Spalten, eindeutig und mehrdeutig, auf- und
|
||||
' absteigend; SETINDEX/GETINDEX$, DELETEINDEX und die Fehler 83 und 84.
|
||||
' Laeuft in einem temporaeren Verzeichnis.
|
||||
TYPE Person
|
||||
Nachname AS STRING * 8
|
||||
Vorname AS STRING * 8
|
||||
Alter AS INTEGER
|
||||
END TYPE
|
||||
DIM p AS Person
|
||||
|
||||
ON ERROR GOTO Handler
|
||||
OPEN "leute.isam" FOR ISAM Person "Leute" AS #1
|
||||
|
||||
p.Nachname = "Meier"
|
||||
p.Vorname = "Anna"
|
||||
p.Alter = 30
|
||||
INSERT #1, p
|
||||
p.Nachname = "Meier"
|
||||
p.Vorname = "Bert"
|
||||
p.Alter = 50
|
||||
INSERT #1, p
|
||||
p.Nachname = "Adam"
|
||||
p.Vorname = "Cara"
|
||||
p.Alter = 40
|
||||
INSERT #1, p
|
||||
|
||||
' --- 5.2: eine Spalte, mehrdeutig, aufsteigend ---
|
||||
CREATEINDEX #1, "NachName", 0, "Nachname"
|
||||
SETINDEX #1, "NachName"
|
||||
PRINT "einspaltig aufsteigend:";
|
||||
GOSUB Durchlauf
|
||||
|
||||
' --- 5.2: eine Spalte, absteigend ---
|
||||
CREATEINDEX #1, "AlterAb", 0, "-Alter"
|
||||
SETINDEX #1, "AlterAb"
|
||||
PRINT "absteigend nach Alter:";
|
||||
GOSUB Durchlauf
|
||||
|
||||
' --- 5.2: mehrspaltig, eindeutig ---
|
||||
CREATEINDEX #1, "VollName", 1, "Nachname", "Vorname"
|
||||
SETINDEX #1, "VollName"
|
||||
PRINT "mehrspaltig eindeutig:";
|
||||
GOSUB Durchlauf
|
||||
|
||||
' --- 5.2: mehrspaltig gemischte Richtung ---
|
||||
CREATEINDEX #1, "NachAbAlter", 0, "Nachname", "-Alter"
|
||||
SETINDEX #1, "NachAbAlter"
|
||||
PRINT "Nachname auf, Alter ab:";
|
||||
GOSUB Durchlauf
|
||||
|
||||
' --- 5.4: GETINDEX$ liefert den aktiven Index zurueck ---
|
||||
SETINDEX #1, "NachName"
|
||||
PRINT "aktiv: ["; GETINDEX$(1); "]"
|
||||
' Ohne Namen gilt der NULL-Index (Einfuegereihenfolge).
|
||||
SETINDEX #1
|
||||
PRINT "NULL-Index: ["; GETINDEX$(1); "]"
|
||||
|
||||
' --- 5.4: nach SETINDEX ist der erste Satz der neuen Ordnung aktuell ---
|
||||
SETINDEX #1, "NachName"
|
||||
RETRIEVE #1, p
|
||||
PRINT "erster nach NachName: "; RTRIM$(p.Nachname); "/"; RTRIM$(p.Vorname)
|
||||
SETINDEX #1, "AlterAb"
|
||||
RETRIEVE #1, p
|
||||
PRINT "erster nach AlterAb: "; RTRIM$(p.Nachname); "/"; RTRIM$(p.Vorname)
|
||||
SETINDEX #1
|
||||
RETRIEVE #1, p
|
||||
PRINT "erster im NULL-Index: "; RTRIM$(p.Nachname); "/"; RTRIM$(p.Vorname)
|
||||
|
||||
' --- 5.5: unbekannter Index meldet 83 ---
|
||||
SETINDEX #1, "GibtsNicht"
|
||||
|
||||
' --- 5.5: unbekannte Spalte meldet 84 ---
|
||||
CREATEINDEX #1, "Falsch", 0, "GibtsNicht"
|
||||
|
||||
' --- 5.3: DELETEINDEX; danach ist der Index nicht mehr setzbar ---
|
||||
DELETEINDEX #1, "NachName"
|
||||
PRINT "Index geloescht"
|
||||
SETINDEX #1, "NachName"
|
||||
|
||||
CLOSE #1
|
||||
PRINT "fertig"
|
||||
END
|
||||
|
||||
' Sätze in der Ordnung des aktiven Index ausgeben.
|
||||
Durchlauf:
|
||||
MOVEFIRST #1
|
||||
DO WHILE NOT EOF(1)
|
||||
RETRIEVE #1, p
|
||||
PRINT " "; RTRIM$(p.Nachname); "/"; RTRIM$(p.Vorname); p.Alter;
|
||||
MOVENEXT #1
|
||||
LOOP
|
||||
PRINT
|
||||
RETURN
|
||||
|
||||
Handler:
|
||||
PRINT "ERR="; ERR
|
||||
RESUME NEXT
|
||||
14
tests/compat/isamindizes.out
Normal file
14
tests/compat/isamindizes.out
Normal file
@@ -0,0 +1,14 @@
|
||||
einspaltig aufsteigend: Adam/Cara 40 Meier/Anna 30 Meier/Bert 50
|
||||
absteigend nach Alter: Meier/Bert 50 Adam/Cara 40 Meier/Anna 30
|
||||
mehrspaltig eindeutig: Adam/Cara 40 Meier/Anna 30 Meier/Bert 50
|
||||
Nachname auf, Alter ab: Adam/Cara 40 Meier/Bert 50 Meier/Anna 30
|
||||
aktiv: [NachName]
|
||||
NULL-Index: []
|
||||
erster nach NachName: Adam/Cara
|
||||
erster nach AlterAb: Meier/Bert
|
||||
erster im NULL-Index: Meier/Anna
|
||||
ERR= 83
|
||||
ERR= 84
|
||||
Index geloescht
|
||||
ERR= 83
|
||||
fertig
|
||||
76
tests/compat/isampuffer.bas
Normal file
76
tests/compat/isampuffer.bas
Normal file
@@ -0,0 +1,76 @@
|
||||
' tb-screen: 80x25
|
||||
' tb-tempdir
|
||||
' ISAM (Spec isam-datenbank, Anforderungen "Pufferverwaltung mit benanntem
|
||||
' Fehler" und "Vergleichsreihenfolge fuer Indizes"): SETMEM aendert die
|
||||
' Puffergrenze und liefert die verfuegbare Groesse, ein Ueberschreiten meldet
|
||||
' 89; die Indexordnung stimmt mit dem Stringvergleich des Dialekts ueberein.
|
||||
' Laeuft in einem temporaeren Verzeichnis.
|
||||
TYPE Wort
|
||||
Text AS STRING * 6
|
||||
END TYPE
|
||||
DIM w AS Wort
|
||||
|
||||
ON ERROR GOTO Handler
|
||||
OPEN "worte.isam" FOR ISAM Wort "Worte" AS #1
|
||||
CREATEINDEX #1, "NachText", 0, "Text"
|
||||
SETINDEX #1, "NachText"
|
||||
|
||||
' --- 9.4: Gross-/Kleinschreibung und Zeichen jenseits von ASCII ---
|
||||
' Eingefuegt in willkuerlicher Reihenfolge; der Index ordnet nach
|
||||
' Unicode-Codepoints, also genau wie der Vergleichsoperator des Dialekts.
|
||||
w.Text = "birne"
|
||||
INSERT #1, w
|
||||
w.Text = "Apfel"
|
||||
INSERT #1, w
|
||||
w.Text = "Äpfel"
|
||||
INSERT #1, w
|
||||
w.Text = "apfel"
|
||||
INSERT #1, w
|
||||
w.Text = "Birne"
|
||||
INSERT #1, w
|
||||
|
||||
PRINT "Indexordnung:";
|
||||
DIM voriges AS STRING
|
||||
voriges = ""
|
||||
passt% = -1
|
||||
MOVEFIRST #1
|
||||
DO WHILE NOT EOF(1)
|
||||
RETRIEVE #1, w
|
||||
PRINT " "; RTRIM$(w.Text);
|
||||
' Jedes Paar in Folge muss auch dem Operator < genuegen.
|
||||
IF voriges <> "" THEN
|
||||
IF NOT (voriges < w.Text) THEN passt% = 0
|
||||
END IF
|
||||
voriges = w.Text
|
||||
MOVENEXT #1
|
||||
LOOP
|
||||
PRINT
|
||||
PRINT "Indexordnung passt zu <:"; passt%
|
||||
|
||||
' --- 9.2: SETMEM liefert die verfuegbare Groesse ---
|
||||
frei& = SETMEM(0)
|
||||
PRINT "Puffer vorgabe:"; frei&
|
||||
frei& = SETMEM(-1024)
|
||||
PRINT "nach SETMEM(-1024):"; frei&
|
||||
frei& = SETMEM(1024)
|
||||
PRINT "nach SETMEM(1024):"; frei&
|
||||
|
||||
' --- 9.2: unter die Satzgroesse gesenkt meldet ein INSERT 89 ---
|
||||
frei& = SETMEM(-65530)
|
||||
PRINT "knapper Puffer:"; frei&
|
||||
w.Text = "zuviel"
|
||||
INSERT #1, w
|
||||
|
||||
' Wieder aufgemacht, geht es weiter.
|
||||
frei& = SETMEM(65530)
|
||||
w.Text = "zebra"
|
||||
INSERT #1, w
|
||||
PRINT "nach Aufstocken Saetze:"; LOF(1)
|
||||
|
||||
CLOSE #1
|
||||
PRINT "fertig"
|
||||
END
|
||||
|
||||
Handler:
|
||||
PRINT "ERR="; ERR
|
||||
RESUME NEXT
|
||||
9
tests/compat/isampuffer.out
Normal file
9
tests/compat/isampuffer.out
Normal file
@@ -0,0 +1,9 @@
|
||||
Indexordnung: Apfel Birne apfel birne Äpfel
|
||||
Indexordnung passt zu <:-1
|
||||
Puffer vorgabe: 65536
|
||||
nach SETMEM(-1024): 64512
|
||||
nach SETMEM(1024): 65536
|
||||
knapper Puffer: 6
|
||||
ERR= 89
|
||||
nach Aufstocken Saetze: 6
|
||||
fertig
|
||||
121
tests/compat/isamsaetze.bas
Normal file
121
tests/compat/isamsaetze.bas
Normal file
@@ -0,0 +1,121 @@
|
||||
' tb-screen: 80x25
|
||||
' tb-tempdir
|
||||
' ISAM (Spec isam-datenbank, Anforderung "Satzoperationen auf dem aktuellen
|
||||
' Satz"): INSERT/RETRIEVE feldgleich, UPDATE/DELETE mit unpositioniertem
|
||||
' Cursor danach, Fehler 85 ohne aktuellen Satz, Fehler 86 bei Verstoss gegen
|
||||
' einen eindeutigen Index mit unveraendertem Bestand, und Indexnachfuehrung
|
||||
' nach jeder Satzoperation. Laeuft in einem temporaeren Verzeichnis.
|
||||
TYPE Artikel
|
||||
Nr AS LONG
|
||||
Name AS STRING * 8
|
||||
Preis AS DOUBLE
|
||||
Menge AS INTEGER
|
||||
END TYPE
|
||||
DIM a AS Artikel, g AS Artikel
|
||||
|
||||
ON ERROR GOTO Handler
|
||||
OPEN "lager.isam" FOR ISAM Artikel "Artikel" AS #1
|
||||
CREATEINDEX #1, "NachNr", 1, "Nr"
|
||||
CREATEINDEX #1, "NachName", 0, "Name"
|
||||
|
||||
' --- 7.1: einfuegen, per SEEKEQ finden und feldgleich zurueckelesen ---
|
||||
a.Nr = 100
|
||||
a.Name = "Schraube"
|
||||
a.Preis = 1.25
|
||||
a.Menge = 7
|
||||
INSERT #1, a
|
||||
a.Nr = 200
|
||||
a.Name = "Mutter"
|
||||
a.Preis = 0.5
|
||||
a.Menge = 3
|
||||
INSERT #1, a
|
||||
|
||||
SETINDEX #1, "NachNr"
|
||||
SEEKEQ #1, 100
|
||||
RETRIEVE #1, g
|
||||
PRINT "gelesen:"; g.Nr; RTRIM$(g.Name); g.Preis; g.Menge
|
||||
PRINT "feldgleich:"; (g.Nr = 100) AND (RTRIM$(g.Name) = "Schraube") AND (g.Preis = 1.25) AND (g.Menge = 7)
|
||||
|
||||
' --- 7.3: RETRIEVE ohne positionierten Cursor meldet 85 ---
|
||||
' Hinter dem letzten Satz gibt es keinen aktuellen Satz mehr.
|
||||
MOVELAST #1
|
||||
MOVENEXT #1
|
||||
RETRIEVE #1, g
|
||||
|
||||
' --- 7.3: UPDATE ohne positionierten Cursor meldet 85 ---
|
||||
MOVELAST #1
|
||||
MOVENEXT #1
|
||||
UPDATE #1, a
|
||||
|
||||
' --- 7.3: DELETE ohne positionierten Cursor meldet 85 ---
|
||||
MOVELAST #1
|
||||
MOVENEXT #1
|
||||
DELETE #1
|
||||
|
||||
' --- 7.5: UPDATE der indizierten Spalte fuehrt den Index nach ---
|
||||
SETINDEX #1, "NachName"
|
||||
PRINT "vor UPDATE:";
|
||||
GOSUB Namen
|
||||
SEEKEQ #1, "Schraube"
|
||||
RETRIEVE #1, g
|
||||
g.Name = "Anker"
|
||||
UPDATE #1, g
|
||||
PRINT "nach UPDATE:";
|
||||
GOSUB Namen
|
||||
|
||||
' --- 7.4: Verstoss gegen einen eindeutigen Index meldet 86 ---
|
||||
PRINT "Bestand vor 86:"; LOF(1)
|
||||
a.Nr = 200
|
||||
a.Name = "Doppelt"
|
||||
a.Preis = 9
|
||||
a.Menge = 1
|
||||
INSERT #1, a
|
||||
PRINT "Bestand nach 86:"; LOF(1)
|
||||
|
||||
' --- 7.2: UPDATE und DELETE; Cursor danach unpositioniert ---
|
||||
SETINDEX #1, "NachNr"
|
||||
SEEKEQ #1, 200
|
||||
RETRIEVE #1, g
|
||||
g.Menge = 99
|
||||
UPDATE #1, g
|
||||
SEEKEQ #1, 200
|
||||
RETRIEVE #1, g
|
||||
PRINT "nach UPDATE Menge:"; g.Menge
|
||||
|
||||
' Der Satz mit Nr 200 ist im Index NachNr der letzte; nach seinem Loeschen
|
||||
' steht der Cursor am Ende der Tabelle und es gibt keinen aktuellen Satz.
|
||||
SEEKEQ #1, 200
|
||||
DELETE #1
|
||||
PRINT "nach DELETE Bestand:"; LOF(1); "EOF:"; EOF(1)
|
||||
RETRIEVE #1, g
|
||||
|
||||
' Wird dagegen ein Satz mit Nachfolger geloescht, wird dieser der aktuelle.
|
||||
SETINDEX #1, "NachNr"
|
||||
a.Nr = 300
|
||||
a.Name = "Duebel"
|
||||
a.Preis = 2
|
||||
a.Menge = 5
|
||||
INSERT #1, a
|
||||
SEEKEQ #1, 100
|
||||
DELETE #1
|
||||
RETRIEVE #1, g
|
||||
PRINT "nach DELETE ist aktuell:"; g.Nr
|
||||
|
||||
CLOSE #1
|
||||
PRINT "fertig"
|
||||
END
|
||||
|
||||
' Namen in der Ordnung des Index NachName ausgeben.
|
||||
Namen:
|
||||
MOVEFIRST #1
|
||||
DO WHILE NOT EOF(1)
|
||||
RETRIEVE #1, g
|
||||
PRINT " "; RTRIM$(g.Name);
|
||||
MOVENEXT #1
|
||||
LOOP
|
||||
PRINT
|
||||
RETURN
|
||||
|
||||
Handler:
|
||||
PRINT "ERR="; ERR
|
||||
RESUME NEXT
|
||||
15
tests/compat/isamsaetze.out
Normal file
15
tests/compat/isamsaetze.out
Normal file
@@ -0,0 +1,15 @@
|
||||
gelesen: 100 Schraube 1.25 7
|
||||
feldgleich:-1
|
||||
ERR= 85
|
||||
ERR= 85
|
||||
ERR= 85
|
||||
vor UPDATE: Mutter Schraube
|
||||
nach UPDATE: Anker Mutter
|
||||
Bestand vor 86: 2
|
||||
ERR= 86
|
||||
Bestand nach 86: 2
|
||||
nach UPDATE Menge: 99
|
||||
nach DELETE Bestand: 1 EOF:-1
|
||||
ERR= 85
|
||||
nach DELETE ist aktuell: 300
|
||||
fertig
|
||||
66
tests/compat/isamtabellen.bas
Normal file
66
tests/compat/isamtabellen.bas
Normal file
@@ -0,0 +1,66 @@
|
||||
' tb-screen: 80x25
|
||||
' tb-tempdir
|
||||
' ISAM (Spec isam-datenbank, Anforderung "Datenbank- und Tabellenbindung an
|
||||
' eine Dateinummer"): Tabelle entsteht beim ersten Oeffnen, Layoutabgleich
|
||||
' meldet 88, ungueltige Namen melden 81, CLOSE schreibt fest, DELETETABLE
|
||||
' entfernt die Tabelle und meldet 82, wenn es sie nicht gibt.
|
||||
' Laeuft in einem temporaeren Verzeichnis.
|
||||
TYPE Kunde
|
||||
Nr AS LONG
|
||||
Name AS STRING * 8
|
||||
END TYPE
|
||||
TYPE Anders
|
||||
Nr AS INTEGER
|
||||
END TYPE
|
||||
DIM k AS Kunde, a AS Anders
|
||||
|
||||
ON ERROR GOTO Handler
|
||||
|
||||
' --- 4.1/4.2: Tabelle wird beim ersten Oeffnen angelegt ---
|
||||
OPEN "kunden.isam" FOR ISAM Kunde "Kunden" AS #1
|
||||
PRINT "angelegt, Saetze:"; LOF(1)
|
||||
k.Nr = 1
|
||||
k.Name = "Anton"
|
||||
INSERT #1, k
|
||||
k.Nr = 2
|
||||
k.Name = "Berta"
|
||||
INSERT #1, k
|
||||
PRINT "nach INSERT:"; LOF(1)
|
||||
|
||||
' --- 4.5: CLOSE loest die Bindung und schreibt fest ---
|
||||
CLOSE #1
|
||||
OPEN "kunden.isam" FOR ISAM Kunde "Kunden" AS #1
|
||||
PRINT "nach erneutem Oeffnen:"; LOF(1)
|
||||
CLOSE #1
|
||||
|
||||
' --- 4.3: abweichendes Satzlayout meldet 88 ---
|
||||
OPEN "kunden.isam" FOR ISAM Anders "Kunden" AS #2
|
||||
|
||||
' --- 4.4: ungueltiger Tabellenname meldet 81 ---
|
||||
OPEN "kunden.isam" FOR ISAM Kunde "1Ungueltig" AS #3
|
||||
|
||||
' --- 4.4: ungueltiger Datenbankname meldet 81 ---
|
||||
OPEN "" FOR ISAM Kunde "Kunden" AS #4
|
||||
|
||||
' --- 4.6: DELETETABLE fuer eine fehlende Tabelle meldet 82 ---
|
||||
DELETETABLE "kunden.isam", "GibtsNicht"
|
||||
|
||||
' --- 4.6: DELETETABLE entfernt die Tabelle samt Index ---
|
||||
OPEN "kunden.isam" FOR ISAM Kunde "Kunden" AS #1
|
||||
CREATEINDEX #1, "NachName", 0, "Name"
|
||||
CLOSE #1
|
||||
DELETETABLE "kunden.isam", "Kunden"
|
||||
PRINT "geloescht"
|
||||
OPEN "kunden.isam" FOR ISAM Kunde "Kunden" AS #1
|
||||
PRINT "neu angelegt, Saetze:"; LOF(1)
|
||||
' Der Index ist mit der Tabelle verschwunden.
|
||||
SETINDEX #1, "NachName"
|
||||
|
||||
Ende:
|
||||
CLOSE
|
||||
PRINT "fertig"
|
||||
END
|
||||
|
||||
Handler:
|
||||
PRINT "ERR="; ERR
|
||||
RESUME NEXT
|
||||
11
tests/compat/isamtabellen.out
Normal file
11
tests/compat/isamtabellen.out
Normal file
@@ -0,0 +1,11 @@
|
||||
angelegt, Saetze: 0
|
||||
nach INSERT: 2
|
||||
nach erneutem Oeffnen: 2
|
||||
ERR= 88
|
||||
ERR= 81
|
||||
ERR= 81
|
||||
ERR= 82
|
||||
geloescht
|
||||
neu angelegt, Saetze: 0
|
||||
ERR= 83
|
||||
fertig
|
||||
88
tests/compat/isamtrans.bas
Normal file
88
tests/compat/isamtrans.bas
Normal file
@@ -0,0 +1,88 @@
|
||||
' tb-screen: 80x25
|
||||
' tb-tempdir
|
||||
' ISAM (Spec isam-datenbank, Anforderung "Transaktionen mit Ruecknahme"):
|
||||
' BEGINTRANS/COMMITTRANS schreibt fest, ROLLBACK ALL stellt Saetze und
|
||||
' Indizes wieder her und beendet die Transaktion, SAVEPOINT liefert eine
|
||||
' Kennung und ROLLBACK kennung setzt nur bis dorthin zurueck.
|
||||
' Laeuft in einem temporaeren Verzeichnis.
|
||||
TYPE Posten
|
||||
Nr AS INTEGER
|
||||
Text AS STRING * 6
|
||||
END TYPE
|
||||
DIM p AS Posten
|
||||
|
||||
ON ERROR GOTO Handler
|
||||
OPEN "buch.isam" FOR ISAM Posten "Posten" AS #1
|
||||
CREATEINDEX #1, "NachNr", 1, "Nr"
|
||||
SETINDEX #1, "NachNr"
|
||||
|
||||
' --- 8.1: COMMITTRANS schreibt fest; nach erneutem Oeffnen ist alles da ---
|
||||
BEGINTRANS
|
||||
p.Nr = 1
|
||||
p.Text = "eins"
|
||||
INSERT #1, p
|
||||
p.Nr = 2
|
||||
p.Text = "zwei"
|
||||
INSERT #1, p
|
||||
COMMITTRANS
|
||||
CLOSE #1
|
||||
OPEN "buch.isam" FOR ISAM Posten "Posten" AS #1
|
||||
SETINDEX #1, "NachNr"
|
||||
PRINT "nach COMMITTRANS:";
|
||||
GOSUB Durchlauf
|
||||
|
||||
' --- 8.3: ROLLBACK ALL nimmt Einfuegen und Loeschen zurueck ---
|
||||
BEGINTRANS
|
||||
p.Nr = 3
|
||||
p.Text = "drei"
|
||||
INSERT #1, p
|
||||
SEEKEQ #1, 1
|
||||
DELETE #1
|
||||
PRINT "in der Transaktion:";
|
||||
GOSUB Durchlauf
|
||||
ROLLBACK ALL
|
||||
PRINT "nach ROLLBACK ALL:";
|
||||
GOSUB Durchlauf
|
||||
' Die Transaktion ist beendet — ein weiteres ROLLBACK ist unzulaessig.
|
||||
ROLLBACK ALL
|
||||
|
||||
' --- 8.4: SAVEPOINT und ROLLBACK kennung ---
|
||||
BEGINTRANS
|
||||
p.Nr = 4
|
||||
p.Text = "vier"
|
||||
INSERT #1, p
|
||||
s% = SAVEPOINT
|
||||
PRINT "Sicherungspunkt:"; s%
|
||||
p.Nr = 5
|
||||
p.Text = "fuenf"
|
||||
INSERT #1, p
|
||||
p.Nr = 6
|
||||
p.Text = "sechs"
|
||||
INSERT #1, p
|
||||
PRINT "vor ROLLBACK:";
|
||||
GOSUB Durchlauf
|
||||
ROLLBACK s%
|
||||
PRINT "nach ROLLBACK s:";
|
||||
GOSUB Durchlauf
|
||||
' Die Transaktion laeuft weiter und laesst sich festschreiben.
|
||||
COMMITTRANS
|
||||
PRINT "nach COMMITTRANS:";
|
||||
GOSUB Durchlauf
|
||||
|
||||
CLOSE #1
|
||||
PRINT "fertig"
|
||||
END
|
||||
|
||||
Durchlauf:
|
||||
MOVEFIRST #1
|
||||
DO WHILE NOT EOF(1)
|
||||
RETRIEVE #1, p
|
||||
PRINT " "; RTRIM$(p.Text);
|
||||
MOVENEXT #1
|
||||
LOOP
|
||||
PRINT
|
||||
RETURN
|
||||
|
||||
Handler:
|
||||
PRINT "ERR="; ERR
|
||||
RESUME NEXT
|
||||
9
tests/compat/isamtrans.out
Normal file
9
tests/compat/isamtrans.out
Normal file
@@ -0,0 +1,9 @@
|
||||
nach COMMITTRANS: eins zwei
|
||||
in der Transaktion: zwei drei
|
||||
nach ROLLBACK ALL: eins zwei
|
||||
ERR= 5
|
||||
Sicherungspunkt: 1
|
||||
vor ROLLBACK: eins zwei vier fuenf sechs
|
||||
nach ROLLBACK s: eins zwei vier
|
||||
nach COMMITTRANS: eins zwei vier
|
||||
fertig
|
||||
Reference in New Issue
Block a user