<> <> <> <> <<>> <> <<>> <> <> <> <<(Added NewMail and Archive log position info for restart)>> <<(Changed to be consistent with new WalnutSchema)>> <<>> DIRECTORY BasicTime USING [GMT, nullGMT, OutOfRange], Convert USING [TimeFromRope], DB USING [Boolean, CreateRelship, Error, FieldSpec, FirstRelship, DeclareDomain, DeclareIndex, DeclareProperty, DeclareRelation, GetF, Integer, L2F, L2FS, L2VS, SetF, String, T2V, Time, TypeForDomain, TypeSpec, V2T, Domain, FieldSequence, Index, Relation, Relship, TypeCode, ValueSequence], IO, Rope USING [ROPE, Cat], WalnutDefs USING [Error, SchemaMismatch, Segment], WalnutRoot USING [CommitAndContinue], WalnutSchema; WalnutSchemaImpl: CEDAR PROGRAM IMPORTS Convert, DB, IO, Rope, WalnutDefs, WalnutRoot EXPORTS WalnutSchema, WalnutDefs = BEGIN OPEN WalnutSchema; <<>> <> GMT: TYPE = BasicTime.GMT; ROPE: TYPE = Rope.ROPE; <<>> <<>> <> <<>> SchemaMismatch: PUBLIC SIGNAL[explanation: ROPE _ NIL] = CODE; VersionMismatch: PUBLIC SIGNAL[explanation: ROPE _ NIL] = CODE; Error: PUBLIC SIGNAL [who, code: ATOM, explanation: ROPE _ NIL] = CODE; <> <<>> schemaVersionDate: GMT = Convert.TimeFromRope["April 15, 1986 2:14:01 pm PST"]; <> <<>> gRootInfo: PUBLIC DB.Relation; gRootInfoFields: DB.FieldSpec = DB.L2FS[LIST[ [name: "RootFileStamp", type: DB.Time], [name: "gRootFileKey", type: DB.String, lengthHint: 20], [name: "MailFor", type: DB.String, lengthHint: 20] ]]; gLogInfo: PUBLIC DB.Relation; gLogInfoFields: DB.FieldSpec = DB.L2FS[LIST[ [name: "LogFileID", type: DB.Integer], [name: "OpInProgressPos", type: DB.Integer], [name: "FirstDestroyedMsgPos", type: DB.Integer], [name: "BytesInDestroyedMsgs", type: DB.Integer], [name: "TimeOfLastScavenge", type: DB.Time] ]]; gParseLogInfo: PUBLIC DB.Relation; gParseLogInfoFields: DB.FieldSpec = DB.L2FS[LIST[ [name: "ParseLogInProgress", type: DB.Boolean], [name: "ParseLogPos", type: DB.Integer] ]]; gExpungeInfo: PUBLIC DB.Relation; gExpungeLogInfoFields: DB.FieldSpec = DB.L2FS[LIST[ [name: "LogExpungePhase", type: DB.Integer], [name: "gExpungeFileID", type: DB.Integer], [name: "CurrentLogPos", type: DB.Integer], [name: "TempLogPos", type: DB.Integer], [name: "TimeOfLastExpunge", type: DB.Time] ]]; gNewMailInfo: PUBLIC DB.Relation; gNewMailInfoFields: DB.FieldSpec = DB.L2FS[LIST[ [name: "NewMailLogLength", type: DB.Integer], [name: "CopyNewMailLogPos", type: DB.Integer], [name: "AcceptNewMailLogPos", type: DB.Integer], [name: "AddingServerMsgs", type: DB.Boolean], [name: "LastNewMailTimeStamp", type: DB.Time] ]]; gReadArchiveInfo: PUBLIC DB.Relation; gReadArchiveInfoFields: DB.FieldSpec = DB.L2FS[LIST[ [name: "ReadArchiveLogPos", type: DB.Integer], [name: "CopyReadArchiveLogPos", type: DB.Integer] ]]; gVersionInfo: PUBLIC DB.Relation; gVersionInfoFields: DB.FieldSpec = DB.L2FS[LIST[ [name: "MsgCount", type: DB.Integer], [name: "MsgSetCount", type: DB.Integer], [name: "MsgSetsVersion", type: DB.Integer] ]]; <> MsgDomain: PUBLIC DB.Domain; MsgTypeProc: PROC[] RETURNS[DB.TypeCode] = { RETURN[DB.TypeForDomain[MsgDomain]] }; MsgType: DB.TypeSpec = [indirect[MsgTypeProc]]; MsgSetDomain: PUBLIC DB.Domain; MsgSetTypeProc: PROC[] RETURNS[DB.TypeCode] = { RETURN[DB.TypeForDomain[MsgSetDomain]] }; MsgSetType: DB.TypeSpec = [indirect[MsgSetTypeProc]]; ServerDomain: PUBLIC DB.Domain; ServerTypeProc: PROC[] RETURNS[DB.TypeCode] = { RETURN[DB.TypeForDomain[ServerDomain]] }; ServerType: DB.TypeSpec = [indirect[ServerTypeProc]]; UnacceptedDomain: PUBLIC DB.Domain; UnacceptedTypeProc: PROC[] RETURNS[DB.TypeCode] = { RETURN[DB.TypeForDomain[UnacceptedDomain]] }; UnacceptedType: DB.TypeSpec = [indirect[UnacceptedTypeProc]]; <> <<>> <> <<>> sBasicInfo: PUBLIC DB.Relation; -- One per Server sBasicType: DB.FieldSpec = DB.L2FS[LIST[ [name: "of", type: ServerType], [name: "messages", type: DB.Integer] ]]; <<>> <> msBasicInfo: PUBLIC DB.Relation; -- One per MsgSet msBasicType: DB.FieldSpec = DB.L2FS[LIST[ [name: "of", type: MsgSetType], [name: "count", type: DB.Integer], [name: "version", type: DB.Integer], [name: "printName", type: DB.String] ]]; <> mTextInfo: PUBLIC DB.Relation; -- One per Msg mTextType: DB.FieldSpec = DB.L2FS[LIST[ [name: "of", type: MsgType], [name: "herald", type: DB.String, lengthHint: 60], [name: "shortNameLen", type: DB.Integer], [name: "entryStart", type: DB.Integer], [name: "textOffset", type: DB.Integer], [name: "textLen", type: DB.Integer], [name: "formatLen", type: DB.Integer] ]]; <<>> <> mDisplayInfo: PUBLIC DB.Relation; -- One per Msg mDisplayType: DB.FieldSpec = DB.L2FS[LIST[ [name: "of", type: MsgType], [name: "tocEntry", type: DB.String, lengthHint: 70], [name: "startOfSubject", type: DB.Integer], [name: "hasBeenRead", type: DB.Boolean] ]]; <<>> <> mInfo: PUBLIC DB.Relation; -- One per Msg mInfoType: DB.FieldSpec = DB.L2FS[LIST[ [name: "of", type: MsgType], [name: "date", type: DB.Time], [name: "show", type: UnacceptedType] ]]; <> cdRelation: PUBLIC DB.Relation; -- One per Msg / MsgSet pair cdRelationType: DB.FieldSpec = DB.L2FS[LIST[ [name: "of", type: MsgType], [name: "msgSet", type: MsgSetType], [name: "date", type: DB.Time] ]]; cdIndex: PUBLIC DB.Index; cdIndexFieldSequence: DB.FieldSequence = DB.L2F[LIST[WalnutSchema.cdMsgSet, WalnutSchema.cdDate]]; schemaDateField: DB.FieldSpec = DB.L2FS[LIST[[name: "schemaDate", type: DB.Time]]]; gSchemaDateInfoName: ROPE = "gSchemaDateInfo"; <> Initialize: PUBLIC PROC [segment: WalnutDefs.Segment] = { <> DO BEGIN ENABLE DB.Error => IF code = ProtectionViolation THEN GOTO readOnly ELSE GOTO badSchema; gSchemaDateInfo: DB.Relation = DB.DeclareRelation[gSchemaDateInfoName, segment, schemaDateField]; schemaRelship: DB.Relship _ DB.FirstRelship[gSchemaDateInfo]; IF schemaRelship = NIL THEN schemaRelship _ DB.CreateRelship[gSchemaDateInfo, DB.L2VS[LIST[DB.T2V[schemaVersionDate]]]] ELSE { date: BasicTime.GMT = DB.V2T[DB.GetF[schemaRelship, 0]]; IF date = schemaVersionDate THEN EXIT; ERROR WalnutDefs.SchemaMismatch[TimesToRope[date, schemaVersionDate]] }; EXITS badSchema => ERROR WalnutDefs.SchemaMismatch[ TimesToRope[BasicTime.nullGMT, schemaVersionDate]]; readOnly => ERROR WalnutDefs.Error[ $db, $readOnly, "You only have read permission for this database"]; END; ENDLOOP; <<-- Define the Global Relations>> <<>> gRootInfo _ DB.DeclareRelation["RootInfo", segment, gRootInfoFields]; gLogInfo _ DB.DeclareRelation["LogInfo", segment, gLogInfoFields]; gParseLogInfo _ DB.DeclareRelation["ParseLogInfo", segment, gParseLogInfoFields]; gExpungeInfo _ DB.DeclareRelation["ExpungeInfo", segment, gExpungeLogInfoFields]; gNewMailInfo _ DB.DeclareRelation["NewMailInfo", segment, gNewMailInfoFields]; gReadArchiveInfo _ DB.DeclareRelation["ReadArchiveInfo", segment, gReadArchiveInfoFields]; gVersionInfo _ DB.DeclareRelation["VersionInfo", segment, gVersionInfoFields]; <<-- Define the Global Domains>> MsgDomain _ DB.DeclareDomain["Msg", segment]; MsgSetDomain _ DB.DeclareDomain["MsgSet", segment]; ServerDomain _ DB.DeclareDomain["Server", segment]; UnacceptedDomain _ DB.DeclareDomain["Unaccepted", segment]; sBasicInfo _ DB.DeclareProperty["sBasicInfo", segment, sBasicType]; msBasicInfo _ DB.DeclareProperty["msBasicInfo", segment, msBasicType]; mTextInfo _ DB.DeclareProperty["mTextInfo", segment, mTextType]; mDisplayInfo _ DB.DeclareProperty["mDisplayInfo", segment, mDisplayType]; mInfo _ DB.DeclareProperty["mInfo", segment, mInfoType]; cdRelation _ DB.DeclareRelation["cdRelation", segment, cdRelationType]; <<>> <<-- Define the indicies>> cdIndex _ DB.DeclareIndex[cdRelation, cdIndexFieldSequence]; <> <<>> BEGIN gr: DB.Relship _ DB.FirstRelship[gRootInfo]; IF gr = NIL THEN { init: DB.ValueSequence _ DB.L2VS[ LIST[ [time[BasicTime.nullGMT]], [rope[NIL]], [rope[NIL]] ] ]; [] _ DB.CreateRelship[gRootInfo, init]; init _ DB.L2VS[ LIST[ [integer[0]], [integer[0]], [integer[0]], [integer[0]], [time[BasicTime.nullGMT]] ] ]; [] _ DB.CreateRelship[gLogInfo, init]; init _ DB.L2VS[ LIST[ [boolean[FALSE]], [integer[0]] ] ]; [] _ DB.CreateRelship[gParseLogInfo, init]; init _ DB.L2VS[ LIST[ [integer[0]], [integer[0]], [integer[0]], [integer[0]], [time[BasicTime.nullGMT]] ] ]; [] _ DB.CreateRelship[gExpungeInfo, init]; init _ DB.L2VS[LIST[ [integer[0]], [integer[0]], [integer[0]], [boolean[FALSE]], [time[BasicTime.nullGMT]] ] ]; [] _ DB.CreateRelship[gNewMailInfo, init]; init _ DB.L2VS[ LIST[ [integer[0]], [integer[0]] ] ]; [] _ DB.CreateRelship[gReadArchiveInfo, init]; <> init _ DB.L2VS[ LIST[ [integer[0]], [integer[2]], [integer[1]] ] ]; [] _ DB.CreateRelship[gVersionInfo, init]; }; END; <<>> <<-- Commit what we've done>> <<>> WalnutRoot.CommitAndContinue[]; <<>> }; SetSchemaVersion: PUBLIC PROC[segment: WalnutDefs.Segment] = { <> gSchemaDateInfo: DB.Relation = DB.DeclareRelation[gSchemaDateInfoName, segment, schemaDateField]; schemaRelship: DB.Relship _ DB.FirstRelship[gSchemaDateInfo]; IF schemaRelship # NIL THEN DB.SetF[schemaRelship, 0, DB.T2V[schemaVersionDate]] ELSE schemaRelship _ DB.CreateRelship[gSchemaDateInfo, DB.L2VS[LIST[DB.T2V[schemaVersionDate]]]]; <> }; TimesToRope: PROC[is, shouldBe: BasicTime.GMT] RETURNS[ROPE] = { isR, shouldR: ROPE; isR _ IO.PutFR[NIL, IO.time[is ! BasicTime.OutOfRange => isR _ "OutOfRange"]]; shouldR _ IO.PutFR[NIL, IO.time[shouldBe ! BasicTime.OutOfRange => shouldR _ "OutOfRange"]]; RETURN[Rope.Cat["\nSchema is of: ", isR, " but should be of: ", shouldR]]; }; END.