LoganBerryCommandsImpl.mesa
Copyright Ó 1986, 1992 by Xerox Corporation. All rights reserved.
Doug Terry, July 17, 1992 10:44 am PDT
Brian Oki, May 29, 1990 9:43 am PDT
Registers a collection of CommandTool commands for performing operations on a LoganBerry database.
Willie-s, April 27, 1992 11:52 am PDT
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];
LoganBerryCommandsImpl: CEDAR PROGRAM
IMPORTS Commander, CommanderOps, IO, LoganBerry, LoganQuery, PFS, Process, Rope, SystemNames
~ BEGIN
ROPE: TYPE ~ Rope.ROPE;
STREAM: TYPE ~ IO.STREAM;
currentName: ROPE ¬ "#";
currentDB: LoganBerry.OpenDB ¬ LoganBerry.nullDB; -- cache of most recently used db
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"];
EXITS
End => RETURN;
};
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];
EXITS
End => RETURN;
};
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];
EXITS
End => RETURN;
};
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];
EXITS
End => RETURN;
};
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]];
EXITS
End => RETURN;
};
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]];
EXITS
End => RETURN;
};
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];
EXITS
End => RETURN;
};
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]];
EXITS
End => RETURN;
};
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}];
EXITS
End => RETURN;
};
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}];
EXITS
End => RETURN;
};
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> ..." ];
END.
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
Doug Terry, October 31, 1986 2:58:29 pm PST
Exports LoganBerryBrowser.
changes to: DIRECTORY, EXPORTS, QueryCmd, WriteCmd, DeleteCmd, ParseArgs, ReadAttributePatterns, ReadAttributePattern (local of ReadAttributePatterns), WriteAttributePatterns, WriteAttributePattern (local of WriteAttributePatterns), PatternsToEntry, EntryToPatterns, FilteredQuery
Doug Terry, November 2, 1986 6:38:02 pm PST
Added support for subrange filter.
changes to: DIRECTORY, OptimalStart, NamedFilter, ToDash, Subrange, GetAttributeValue
Doug Terry, November 23, 1986 8:59:16 pm PST
Moved support for filtered queries to LoganBerryBrowserImpl; added command for creating a browser. Now this is simply a client of LoganBerry and LoganBerryBrowser.
changes to: ~, ParseArgs, Open, Commander, MakeBrowserTool
Doug Terry, January 28, 1987 9:09:29 pm PST
changes to: MakeBrowserTool, Open, CheckOps
Doug Terry, April 16, 1987 12:11:28 pm PDT
Tracked migration of query facilities from LoganBerryBrowser to LoganQuery.
changes to: DIRECTORY, QueryCmd, WriteCmd, DeleteCmd, ParseArgs
Brian Oki, January 26, 1990 9:30:00 am PST
added new command tool command, following Doug Terry's changes: LBCreate
Doug Terry, May 8, 1991 4:02:25 pm PDT
Moved MakeBrowserTool to LoganBerryViewerCommandsImpl.mesa.
Doug Terry, July 17, 1992 10:42:18 am PDT
Changed LBQuery and LBDelete commands to take boolean queries.