# rdb Ein Nachbau von **/rdb**, dem relationalen Datenbanksystem der Mark Williams Company für COHERENT (um 1991), in Ruby. Das Original-Handbuch und die Man-Pages stammen aus den freigegebenen MWC-Quellen ([nesssoftware.com/home/mwc/source.php](https://www.nesssoftware.com/home/mwc/source.php)). Der Programmcode von /rdb selbst war darin nicht enthalten. Alle Befehle hier sind nach dem Handbuch neu geschrieben. ## Einrichten ``` mise install # Ruby 3.4 mise run install # legt die Symlinks row, column, total ... in bin/ an mise run test # 76 Tests ``` Danach steht `bin/` im PATH, solange man im Projektordner ist (`mise.toml`). Jeder Befehl ist ein Symlink auf `bin/rdb`, das am Aufrufnamen erkennt, was zu tun ist. Es geht auch ohne Symlinks: `rdb row 'Cost > 5' < demo/inventory`. Vier Namen verdecken im Projektordner Systembefehle: `column`, `rename`, `display` und `whatis`. ## Das Prinzip Eine **Tabelle** ist eine Textdatei: Kopfzeile mit Spaltennamen, Strichzeile, dann eine Zeile pro Datensatz, Spalten durch Tabulator getrennt. Jeder Befehl liest eine Tabelle von stdin und schreibt eine Tabelle nach stdout. Befehle lassen sich mit Pipes kombinieren. ``` cd demo row 'Cost > 5' < inventory | column Description Cost | sorttable -nr Cost compute 'Value = Cost * Amount' < inventory | total Value jointable inventory owners ``` Dasselbe gilt für **Listen** (ein Datensatz als `SpalteWert`-Zeilen, die Datei beginnt mit einer Leerzeile). `tabletolist` und `listtotable` wandeln um, die meisten Befehle lesen beides. `row`, `compute` und `validate` übergeben ihren Ausdruck an awk, nachdem sie Spaltennamen durch `$N` ersetzt haben. Es gilt also awk-Syntax. Spaltennamen unterscheiden Groß- und Kleinschreibung. Mit `RDB_ICASE=1` wird sie ignoriert (Details unter „Unterschiede zum Original“): ``` RDB_ICASE=1 compute 'wert = cost * amount' < inventory ``` ## Hilfe ``` rdb alle Befehle rdb man search die Original-Man-Page als Text (auch Themen: backup, dbase, dashline ...) rdb --version Versionsnummer (Änderungen siehe CHANGELOG.md) rdb man -g search dieselbe Seite mit groff formatiert (Beispiele sauber eingerückt) whatis column Beschreibung und Syntax whatwill total Befehle, die zu einem Stichwort passen commands Tabelle aller Befehle ``` ## Beispiele zu allen Befehlen `demo/testall.cmds` enthält zu jedem Befehl ein Beispiel mit kurzer Erklärung. `testall` führt es in einer Kopie von `demo/` aus und zeigt jede Zeile samt Ausgabe: ``` testall | less alle Beispiele ansehen testall > testall.old Lauf als Vergleich festhalten testall | diff testall.old - später prüfen, was sich geändert hat (mise run testall) ``` Eine Zeile mit `##` erscheint als Überschrift in der Ausgabe, eine mit `#` ist ein stiller Kommentar. Neue Befehle brauchen hier ein Beispiel, sonst schlägt der Test `test_testall_mentions_every_command` fehl. ## Vorlagen: `form` und `entry` - `demo/form` ist eine Vorlage für `report` und `letter`: sie setzt Spaltenwerte in einen Text ein. Sie hat keine Beschriftungen und nennt `Name` zweimal. - `demo/entry` ist eine Eingabemaske für `screen`: jedes Feld kommt einmal vor und hat eine Beschriftung. `screen < entry > maske.sh` erzeugt das Skript. Starten: `CLEAR=$(clear.rdb) sh maske.sh > tabelle 2>&1`. Die Tabelle geht auf stderr (wie im Original), daher das `2>&1`. Mit `form` würde die Maske leer und doppelt aussehen. ## Eingabemasken: `mask` `mask` ist die Variante von `screen` mit eigener Oberfläche (nur Ruby, kein ncurses). Es zeichnet die Vorlage (von der Standardeingabe, wie `screen`; ein Dateiname geht auch) selbst und schreibt die Eingabe als Tabelle auf die Standardausgabe, also pipefähig: ``` mask -b < demo/maskform Rahmen (-b), Felder, Schaltflächen mask < demo/maskform > neu Tabelle in eine Datei mask < demo/maskform | append -h kunden oder direkt in eine Tabelle mask -s < demo/maskform nur das Bild und die Feldpositionen, ohne Terminal ``` In der Vorlage: `` oder `` ist ein Feld (mit Breite), `[ Speichern ]` eine Schaltfläche, `[ Abbrechen ]` (oder Cancel, Abort, Quit) bricht ab, `` fügt Befehlsausgabe ein. Kommt eine Spalte mehrfach vor, ist es ein Feld. Tab/Pfeile wechseln, Enter bestätigt, Esc bricht ab (Status 1, keine Ausgabe). ## Zeilen auswählen: `pick` `pick` zeigt eine Tabelle auf dem Terminal, man scrollt, filtert und wählt eine oder mehrere Zeilen. Die Tabelle kommt von der Standardeingabe, die gewählten Zeilen gehen als Tabelle auf die Standardausgabe, also mitten in der Pipe: ``` pick < inventory eine Zeile wählen pick -m < inventory | total Amount Value mehrere (Leertaste markiert, a alle) column Item Description < inventory | pick -m | jointable - owners pick -m -k 'down space down enter' < inventory Tasten aus einem String (Skripte, Tests) ``` Pfeile, Bild auf/ab, Pos1/Ende scrollen; `/` filtert (Enter behält den Filter, Esc löscht ihn); Enter wählt; Esc bricht ab (Status 1, keine Ausgabe). Ohne Markierung gilt die Zeile unter dem Cursor. ## Befehle | Gruppe | Befehle | |---|---| | Auswahl | `row` `select` `column` `project` `sorttable` `record` `number` `Number` | | Spalten | `compute` `addcol` `rename` | | Mengen, Verbund | `jointable` `union` `intersect` `difference` `paste.rdb` `splittable` | | Auswertung | `total` `mean` `maximum` `minimum` `subtotal` `width` `widest` `precision` `datatype` `schema` `dbdict` | | Schnellzugriff | `index` `search` `seek` `append` `delete` `replace` `enter` `lock` `unlock` `vilock` `update.rdb` `ve` `vindex` | | Prüfen | `check.rdb` `domain` `validate` `fd` `searchtree` `tableorlist` | | Format | `justify` `trim` `compresss` `pad` `rmblank` `blank` `headon` `headoff` `insertdash` `display` `see` | | Text | `cap` `uppercase` `lowercase` `substitute` `trimblank` `padstring` `length` `filesize` `ascii` `chr` `word` `prompt` `not` `path` | | Umwandeln | `tabletolist` `listtotable` `listtosh` `tabletofix` `fixtotable` `tabletostruct` `tabletotbl` `tabletofact` `tabletorule` `tabletom4` `tabletosed` `translate` `uniondict` | | Datum, Namen | `julian` `gregorian` `computedate` `todaysdate` `soundex` `like` `hashkey` | | Berichte | `report` `reportwriter` `screen` `mask` `pick` `label` `letter` | | Terminal, Hilfe | `termput` `tset` `clear.rdb` `cursor` `menu` `rdb` `commands` `helpme` `whatis` `whatwill` `howmany` `man` | | Betrieb | `testall` `testsearch` `timesearch` `rmcore` `cpdir.rdb` | | Buchhaltung (/act) | `act` `chartdup` `foot` `adjust` `balance` `close` `consolidate` `getjournal` `fillform` `calculate` `tax` `cashflow` `calcpay` `w2` `bom` `explode` `sale` `invoice` `cstate` `update.inv` | ## Schnellzugriff Wie im Original sucht `search` über Byte-Offsets. Indizes bauen: | Option | Methode | Datei | |---|---|---| | `-ms` | sequentiell (Vorgabe) | keine | | `-mb` | binär: `index` sortiert die Tabelle selbst | keine | | `-mr` | Datensatznummer | `tabelle.r` | | `-mi` | invertiert (Schlüssel und Offset, sortiert) | `tabelle.i` | | `-mh` | Hash, doppelt so viele Plätze wie Zeilen | `tabelle.h` | ``` index -mi inventory Description echo scales | search -mi inventory Description echo cl | search -mi -x inventory Description # Teiltreffer, Groß/klein egal echo 5 | seek -mi inventory Item # von bis xvon xbis ``` `delete` überschreibt einen Satz mit Leerzeichen, ohne die Datei zu verschieben. `replace` schreibt an dieselbe Stelle zurück und hängt den Satz hinten an, wenn er nicht mehr passt (außer mit `-a`). Beide halten die Indizes aktuell. `rmblank` räumt später auf. ## Unterschiede zum Original - Es gibt kein COHERENT-Programmmaterial, daher sind die **Ausgaben nur anhand der Man-Pages geprüft**, nicht gegen das echte /rdb. Die Beispiele aus dem Handbuch sind als Tests enthalten (`test/rdb_test.rb`). - Die Indexdateien haben ein eigenes, einfaches Format (das Original ist nicht dokumentiert). Sie sind nicht mit alten /rdb-Dateien austauschbar. - Spaltennamen unterscheiden wie im Original Groß- und Kleinschreibung. Mit `RDB_ICASE=1` (z. B. in `mise.toml` unter `[env]`) wird sie ignoriert: ein exakter Treffer gewinnt, mehrere ähnliche sind ein Fehler. In `row`/`compute`/`validate` bleiben awk-Wörter und Funktionsaufrufe unberührt (eine Spalte `Length` verdeckt also nicht `length()`). Die Man-Pages von `row`, `compute`, `column`, `sorttable`, `jointable`, `rename`, `validate` und `rdb` haben dazu den Abschnitt „Case of Column Names“; er ist die einzige Ergänzung gegenüber den Original-Seiten. - `select` ist in bash, zsh und ksh ein reserviertes Wort. `select 'Cost > 5' < inventory` endet dort mit `syntax error near unexpected token '<'`, weil die Shell das Schlüsselwort vor dem `PATH` auswertet. Es gibt dasselbe unter dem Namen `row`. Außerdem gehen `\select ...`, `command select ...` und `rdb select ...`. - `jointable` braucht keine vorsortierten Tabellen. - `ve` ist ein zeilenorientierter Editor, kein Bildschirmeditor. - `soundex` nutzt den Standard-Algorithmus. Die Beispielwerte im Handbuch weichen davon ab. - Die /act-Befehle arbeiten auf Tabellen im aktuellen Ordner. Welche das sind und welche Spalten sie brauchen, steht jeweils in der Man-Page. Das Paket ist wie im Original nur ein Beispiel für Datenbankprogrammierung. Die Befehle, zu denen das Handbuch nichts sagt (`post`, `purchase`, `ship` ...), fehlen. ## Aufbau ``` bin/rdb das eine Programm (und die Symlinks darauf) lib/rdb.rb Registry der Befehle lib/rdb/table.rb Tabellen und Listen lesen und schreiben lib/rdb/fast.rb Schnellzugriff über Byte-Offsets lib/rdb/cmd/*.rb die Befehle, nach Gruppen man/ Man-Pages (troff-Quellen aus den MWC-Archiven) demo/ Beispieltabellen; testall.cmds = je ein Beispiel pro Befehl, testall.old = letzter Lauf test/ Tests ``` Die Man-Pages und die Beispieltabellen stammen aus den MWC-Quellen. Lizenz der Originale: BSD, Copyright 1977-1995 Robert Swartz.