DIRECTORY
Commander USING [CommandProc, Handle, Register],
CommanderOps USING [ParseToList],
IO,
LoganBerry USING [AttributeType, AttributeValue, BuildIndices, Close, CompactLogs, DeleteEntry, Describe, Entry, EntryProc, Error, FlushDBCache, IndexInfo, LogInfo, Open, OpenDB, SchemaInfo, WriteEntry, nullDB],
LoganQuery USING [AttributePatterns, BooleanFilterEntries, Cursor, EndGenerate, NextEntry, ParseBooleanQuery, ParseTree, PatternsToEntry, QueryEntries, ReadAttributePatterns, SyntaxError],
PFS USING [PathFromRope, StreamOpen],
Process USING [CheckForAbort],
Rope USING [Concat, Equal, Find, Index, Length, ROPE, SkipTo, Substr],
SystemNames USING [UserName];
LoganBerry commands
DescribeCmd: Commander.CommandProc = {
[cmd: Commander.Handle] RETURNS [result: REF ANY ← NIL, msg: ROPE ← NIL]
ENABLE LoganBerry.Error => {
IO.PutF[cmd.err, "Error: %g - %g\n", IO.atom[ec], IO.rope[explanation]];
GOTO End;
};
dbname: ROPE;
info: LoganBerry.SchemaInfo;
[dbname,,msg] ¬ ParseArgs[cmd, FALSE];
IF msg#NIL THEN RETURN;
info ¬ LoganBerry.Describe[db: Open[dbname]];
IO.PutF1[cmd.out, "Database: %g\n", IO.rope[info.dbName]];
IO.PutRope[cmd.out, " keys:"];
FOR l:
LIST
OF LoganBerry.IndexInfo ¬ info.indices, l.rest
WHILE l #
NIL
DO
IO.PutF1[cmd.out, " %g", IO.atom[l.first.key]];
ENDLOOP;
IO.PutRope[cmd.out, "\n"];
IO.PutRope[cmd.out, " index files:"];
FOR l:
LIST
OF LoganBerry.IndexInfo ¬ info.indices, l.rest
WHILE l #
NIL
DO
IO.PutF1[cmd.out, " %g", IO.rope[l.first.file]];
ENDLOOP;
IO.PutRope[cmd.out, "\n"];
IO.PutRope[cmd.out, " log files:"];
FOR l:
LIST
OF LoganBerry.LogInfo ¬ info.logs, l.rest
WHILE l #
NIL
DO
IO.PutF1[cmd.out, " %g", IO.rope[l.first.file]];
ENDLOOP;
IO.PutRope[cmd.out, "\n"];
};
QueryCmd: Commander.CommandProc = {
[cmd: Commander.Handle] RETURNS [result: REF ANY ← NIL, msg: ROPE ← NIL]
ENABLE LoganBerry.Error => {
IO.PutF[cmd.err, "Error: %g - %g\n", IO.atom[ec], IO.rope[explanation]];
GOTO End;
};
PrintEntry: LoganBerry.EntryProc = {
[entry: LoganBerry.Entry] RETURNS [continue: BOOL]
Process.CheckForAbort[];
FOR e: LoganBerry.Entry ¬ entry, e.rest
UNTIL e =
NIL
DO
IO.PutF[cmd.out, "%g: %g\n", IO.atom[e.first.type], IO.rope[e.first.value]];
ENDLOOP;
IO.PutRope[cmd.out, "\n"];
RETURN[TRUE];
};
dbname: ROPE;
queryTree: LoganQuery.ParseTree;
db: LoganBerry.OpenDB;
cursor: LoganQuery.Cursor;
entry: LoganBerry.Entry;
primaryKey: LoganBerry.AttributeType;
[dbname, queryTree, msg] ¬ ParseArgsQuery[cmd];
IF msg#NIL THEN RETURN;
db ¬ Open[dbname];
primaryKey ¬ LoganBerry.Describe[db: db].info.indices.first.key;
cursor ¬ LoganQuery.QueryEntries[db: db, patterns: NIL, baseIndex: primaryKey].cursor;
Note: the complete database is enumerated and fed into the boolean filter.
cursor ¬ LoganQuery.BooleanFilterEntries[input: cursor, query: queryTree, inputOrder: primaryKey, primaryKey: primaryKey];
entry ¬ LoganQuery.NextEntry[cursor];
WHILE entry #
NIL
DO
[] ¬ PrintEntry[entry];
entry ¬ LoganQuery.NextEntry[cursor];
ENDLOOP;
LoganQuery.EndGenerate[cursor];
};
WriteCmd: Commander.CommandProc = {
[cmd: Commander.Handle] RETURNS [result: REF ANY ← NIL, msg: ROPE ← NIL]
ENABLE LoganBerry.Error => {
IO.PutF[cmd.err, "Error: %g - %g\n", IO.atom[ec], IO.rope[explanation]];
GOTO End;
};
dbname: ROPE;
ap: LoganQuery.AttributePatterns;
db: LoganBerry.OpenDB;
entry: LoganBerry.Entry;
[dbname, ap, msg] ¬ ParseArgs[cmd];
IF msg#NIL THEN RETURN;
db ¬ Open[dbname];
entry ¬ LoganQuery.PatternsToEntry[ap];
LoganBerry.WriteEntry[db: db, entry: entry];
};
DeleteCmd: Commander.CommandProc = {
[cmd: Commander.Handle] RETURNS [result: REF ANY ← NIL, msg: ROPE ← NIL]
ENABLE LoganBerry.Error => {
IO.PutF[cmd.err, "Error: %g - %g\n", IO.atom[ec], IO.rope[explanation]];
GOTO End;
};
GetAttributeValue:
PROC [entry: LoganBerry.Entry, type: LoganBerry.AttributeType]
RETURNS [LoganBerry.AttributeValue] ~ {
FOR e: LoganBerry.Entry ¬ entry, e.rest
WHILE e #
NIL
DO
IF e.first.type = type
THEN
RETURN[e.first.value];
ENDLOOP;
RETURN[NIL];
};
DeleteEntry: LoganBerry.EntryProc = {
[entry: LoganBerry.Entry] RETURNS [continue: BOOL]
value: LoganBerry.AttributeValue ¬ GetAttributeValue[entry, primaryKey];
Process.CheckForAbort[];
LoganBerry.DeleteEntry[db: db, key: primaryKey, value: value];
IO.PutF[cmd.out, "Deleted %g: %g\n", IO.atom[primaryKey], IO.rope[value]];
RETURN[TRUE];
};
dbname: ROPE;
queryTree: LoganQuery.ParseTree;
db: LoganBerry.OpenDB;
cursor: LoganQuery.Cursor;
entry: LoganBerry.Entry;
primaryKey: LoganBerry.AttributeType;
[dbname, queryTree, msg] ¬ ParseArgsQuery[cmd];
IF msg#NIL THEN RETURN;
db ¬ Open[dbname];
primaryKey ¬ LoganBerry.Describe[db: db].info.indices.first.key;
cursor ¬ LoganQuery.QueryEntries[db: db, patterns: NIL, baseIndex: primaryKey].cursor;
Note: the complete database is enumerated and fed into the boolean filter.
cursor ¬ LoganQuery.BooleanFilterEntries[input: cursor, query: queryTree, inputOrder: primaryKey, primaryKey: primaryKey];
entry ¬ LoganQuery.NextEntry[cursor];
WHILE entry #
NIL
DO
[] ¬ DeleteEntry[entry];
entry ¬ LoganQuery.NextEntry[cursor];
ENDLOOP;
LoganQuery.EndGenerate[cursor];
};
CloseCmd: Commander.CommandProc = {
[cmd: Commander.Handle] RETURNS [result: REF ANY ← NIL, msg: ROPE ← NIL]
ENABLE LoganBerry.Error => {
IO.PutF[cmd.err, "Error: %g - %g\n", IO.atom[ec], IO.rope[explanation]];
GOTO End;
};
dbname: ROPE;
[dbname,,msg] ¬ ParseArgs[cmd, FALSE];
IF msg#NIL THEN RETURN;
LoganBerry.Close[db: Open[dbname]];
};
FlushCacheCmd: Commander.CommandProc = {
[cmd: Commander.Handle] RETURNS [result: REF ANY ← NIL, msg: ROPE ← NIL]
ENABLE LoganBerry.Error => {
IO.PutF[cmd.err, "Error: %g - %g\n", IO.atom[ec], IO.rope[explanation]];
GOTO End;
};
dbname: ROPE;
[dbname,,msg] ¬ ParseArgs[cmd, FALSE];
IF msg#NIL THEN RETURN;
IF Rope.Equal[dbname, "ALL",
FALSE]
THEN LoganBerry.FlushDBCache[]
ELSE LoganBerry.FlushDBCache[db: Open[dbname]];
};
BuildIndicesCmd: Commander.CommandProc = {
[cmd: Commander.Handle] RETURNS [result: REF ANY ← NIL, msg: ROPE ← NIL]
ENABLE LoganBerry.Error => {
IO.PutF[cmd.err, "Error: %g - %g\n", IO.atom[ec], IO.rope[explanation]];
GOTO End;
};
dbname: ROPE;
db: LoganBerry.OpenDB;
retried: BOOLEAN ¬ FALSE;
[dbname,,msg] ¬ ParseArgs[cmd, FALSE];
IF msg#NIL THEN RETURN;
db ¬ Open[dbname ! LoganBerry.Error =>
IF ec=$BadIndex AND NOT retried THEN {retried ¬ TRUE; RETRY};
];
LoganBerry.BuildIndices[db: db];
};
CompactLogsCmd: Commander.CommandProc = {
[cmd: Commander.Handle] RETURNS [result: REF ANY ← NIL, msg: ROPE ← NIL]
ENABLE LoganBerry.Error => {
IO.PutF[cmd.err, "Error: %g - %g\n", IO.atom[ec], IO.rope[explanation]];
GOTO End;
};
dbname: ROPE;
[dbname,,msg] ¬ ParseArgs[cmd, FALSE];
IF msg#NIL THEN RETURN;
LoganBerry.CompactLogs[db: Open[dbname]];
};
CreateCmd: Commander.CommandProc = {
[cmd: Commander.Handle] RETURNS [result: REF ANY ← NIL, msg: ROPE ← NIL]
Usage: LBCreate <dbname> <primaryKey> <secondaryKey> ...
dbname, shortname, mediumname, fullname, logname: ROPE;
username: ROPE;
i: INT;
s: STREAM;
args: LIST OF ROPE ¬ CommanderOps.ParseToList[cmd].list;
IF args = NIL THEN GOTO Usage;
Get database name and derived names
dbname ¬ args.first;
args ¬ args.rest;
i ¬ Rope.Find[s1: dbname, s2: "."];
IF i # -1
THEN {
-- fullname given, e.g. SampleLB.df or Sample.df
fullname ¬ dbname;
mediumname ¬ Rope.Substr[base: dbname, start: 0, len: i];
i ¬ Rope.Find[s1: mediumname, s2: "LB", case: FALSE];
IF i # -1
THEN shortname ¬ Rope.Substr[base: mediumname, start: 0, len: i]
ELSE shortname ¬ mediumname;
}
ELSE {
-- name given without suffix, e.g. SampleLB or just Sample
i ¬ Rope.Find[s1: dbname, s2: "LB", case: FALSE];
IF i # -1
THEN {
mediumname ¬ dbname;
shortname ¬ Rope.Substr[base: mediumname, start: 0, len: i];
}
ELSE {
shortname ¬ dbname;
mediumname ¬ Rope.Concat[base: shortname, rest: "LB"];
};
fullname ¬ Rope.Concat[base: mediumname, rest: ".df"];
};
Get user name
username ¬ SystemNames.UserName[];
username ¬ Rope.Substr[base: username, start: 0, len: Rope.Index[username, 0, "."]];
Create log file
logname ¬ Rope.Concat[base: shortname, rest: ".lblog"];
s ¬ PFS.StreamOpen[fileName: PFS.PathFromRope[logname], accessOptions: $create];
IO.PutRope[s, "\377"];
IO.Close[s];
Create database schema file
IF args = NIL THEN args ¬ LIST["Key"]; -- default primary key
s ¬ PFS.StreamOpen[fileName: PFS.PathFromRope[fullname], accessOptions: $create];
IO.PutF1[s, "-- %g\n", IO.rope[fullname]];
IO.PutRope[s, "-- A LoganBerry schema file\n\n"];
IO.PutF1[s, "Directory [User]<%g>Top>\n", IO.rope[username]];
IO.PutF1[s, " %g\n\n", IO.rope[fullname]];
IO.PutF[s, "Directory [User]<%g>%g>\n", IO.rope[username], IO.rope[mediumname]];
IO.PutRope[s, " --> log 0 readwrite\n"];
IO.PutF1[s, " %g\n\n", IO.rope[logname]];
IO.PutF1[s, " --> index \"%g\" primary\n", IO.rope[args.first]];
IO.PutF[s, " %g%g.lbindex\n", IO.rope[shortname], IO.rope[args.first]];
FOR skeys:
LIST
OF
ROPE ¬ args.rest, skeys.rest
WHILE skeys #
NIL
DO
--add secondary indices
IO.PutF1[s, " --> index \"%g\" secondary\n", IO.rope[skeys.first]];
IO.PutF[s, " %g%g.lbindex\n", IO.rope[shortname], IO.rope[skeys.first]];
ENDLOOP;
IO.Close[s];
msg ¬ IO.PutFR["Created database schema file %g and log file %g\n", IO.rope[fullname], IO.rope[logname]];
EXITS
Usage => IO.PutRope[cmd.err, "Usage: LBCreate <dbname> <primaryKey> <secondaryKey> ...\n e.g. LBCreate SampleLB.df Name Phone Address\n"];
};
ParseArgs:
PROC [cmd: Commander.Handle, readAttributePatterns:
BOOLEAN ¬
TRUE]
RETURNS [dbname:
ROPE ¬
NIL, ap: LoganQuery.AttributePatterns ¬
NIL, errMsg:
ROPE ¬
NIL] ~ {
argStream: IO.STREAM ¬ IO.RIS[cmd.commandLine];
dbname ¬ IO.GetTokenRope[argStream, IO.IDProc ! IO.EndOfStream => {errMsg ¬ "No database name supplied.\n"; GOTO End}].token;
IF readAttributePatterns
THEN
ap ¬ LoganQuery.ReadAttributePatterns[argStream ! LoganQuery.SyntaxError => {errMsg ¬ explanation; GOTO End}];
};
ParseArgsQuery:
PROC [cmd: Commander.Handle]
RETURNS [dbname:
ROPE ¬
NIL, query: LoganQuery.ParseTree ¬
NIL, errMsg:
ROPE ¬
NIL] ~ {
argStream: IO.STREAM ¬ IO.RIS[cmd.commandLine];
dbname ¬ IO.GetTokenRope[argStream, IO.IDProc ! IO.EndOfStream => {errMsg ¬ "No database name supplied.\n"; GOTO End}].token;
query ¬ LoganQuery.ParseBooleanQuery[argStream ! LoganQuery.SyntaxError => {errMsg ¬ Rope.Concat["Bad query: ", explanation]; GOTO End}];
};
Open:
PROC [dbname:
ROPE]
RETURNS [db: LoganBerry.OpenDB] ~ {
Opens the database with the given name; a distinquished name "#" indicates that the most recently opened database should be used. A cache of opened databases could be maintained, but we don't bother for now. If the name is missing an explicit extension and version then ".df!H" is assumed.
prev, loc: INT ¬ 0;
IF Rope.Equal[dbname, currentName] THEN RETURN[currentDB];
WHILE loc < Rope.Length[dbname]
DO
prev ¬ loc;
loc ¬ Rope.SkipTo[s: dbname, pos: prev+1, skip: "/>]"];
ENDLOOP;
IF Rope.Find[dbname, "!", prev] = -1
AND Rope.Find[dbname, ".", prev] = -1
THEN {
dbname ¬ Rope.Concat[dbname, ".df"];
};
db ¬ currentDB ¬ LoganBerry.Open[dbName: dbname];
};
Command registrations
Commander.Register[key: "LBDescribe", proc: DescribeCmd, interpreted:
TRUE,
doc: "Get schema information about a LoganBerry database.\n usage: LBDescribe <dbname>" ];
Commander.Register[key: "LBQuery", proc: QueryCmd, interpreted:
FALSE,
doc: "Query a LoganBerry database.\n usage: LBQuery <dbname> <booleanQuery>" ];
Commander.Register[key: "LBRead", proc: QueryCmd, interpreted:
FALSE,
doc: "synonym for LBQuery." ];
Commander.Register[key: "LBWrite", proc: WriteCmd, interpreted:
FALSE,
doc: "Write an entry into a LoganBerry database.\n usage: LBWrite <dbname> <atype:avalue> ..." ];
Commander.Register[key: "LBDelete", proc: DeleteCmd, interpreted:
FALSE,
doc: "Delete entries from a LoganBerry database.\n usage: LBDelete <dbname> <booleanQuery>" ];
Commander.Register[key: "LBClose", proc: CloseCmd, interpreted:
TRUE,
doc: "Close a LoganBerry database.\n usage: LBClose <dbname>" ];
Commander.Register[key: "LBFlushCache", proc: FlushCacheCmd, interpreted:
TRUE,
doc: "Flush one or all open database handles from LoganBerry's cache.\n usage: LBFlushCache {<dbname> or ALL}" ];
Commander.Register[key: "LBBuildIndices", proc: BuildIndicesCmd, interpreted:
TRUE,
doc: "Rebuild the indices for a LoganBerry database.\n usage: LBBuildIndices <dbname>" ];
Commander.Register[key: "LBCompactLogs", proc: CompactLogsCmd, interpreted:
TRUE,
doc: "Compact a LoganBerry database.\n usage: LBCompactLogs <dbname>" ];
Commander.Register[key: "LBCreate", proc: CreateCmd,
doc: "Create a LoganBerry database.\n usage: LBCreate <dbname> <primaryKey> <secondaryKey> ..." ];
Doug Terry, October 20, 1986 10:45:54 am PDT
Created.
changes to: DIRECTORY, LoganBerryCommandsImpl, IMPORTS, EXPORTS, DescribeCmd, QueryCmd, WriteCmd, DeleteCmd, CloseCmd, BuildIndicesCmd, CompactLogsCmd, ParseArgs, Commander, Commander, Commander, Commander, Commander, Commander, Commander, Commander, END, ~, ParseArgs, AttributeBreakProc (local of ReadAttributePatterns), ReadAttributePattern (local of ReadAttributePatterns), Open, WriteAttributePatterns, DoQuery, NamedFilter, WriteAttributePattern (local of WriteAttributePatterns), OptimalStart, PrintEntry (local of QueryCmd), DeleteEntry (local of DeleteCmd), GetAttributeValue, FilteredQuery