File: DBModelSegmentImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Last edited by:
Donahue, April 15, 1986 3:43:33 pm PST
DIRECTORY
AlpineEnvironment USING [LockOption],
Atom USING [MakeAtom],
CardTable USING[Create],
DBCommon,
DBFileAlpine,
DBSegment,
DBStorage,
DBDefs,
DB,
DBModel,
DBModelPrivate,
FS,
RefTab,
Rope,
SymTab;
DBModelSegmentImpl: CEDAR PROGRAM
IMPORTS Atom, CardTable, DBSegment, DBStorage, DB, DBModelPrivate, FS, RefTab, Rope, SymTab
EXPORTS DBModel, DBModelPrivate =
BEGIN OPEN DBCommon, DBDefs;
initialized: PUBLIC BOOLFALSE;
segmentTable: RefTab.Ref = RefTab.Create[];
This table contains the segment handles for each currently in use segment; these segment handles have all of the cached schema information.
Initialization of database system
Initialize: PUBLIC PROC[nCachePages: NAT, cacheFileName: ROPE] = {
Initializes database system
IF initialized THEN RETURN;
initialized ← TRUE;
IF cacheFileName=NIL THEN cacheFileName← "DBSegment.VM";
DBStorage.Initialize[nCachePages, cacheFileName];
DBModelPrivate.InitializeDomainSchema[];
DBModelPrivate.InitializeRelationSchema[];
DBModelPrivate.InitializeIndexSchema[]
};
Management of the schema caches for each segment
SegmentToHandle: PUBLIC PROC [s: Segment] RETURNS [handle: SegmentHandle] ~ {
found: BOOL;
val: REF ANY;
[found, val] ← RefTab.Fetch[segmentTable, s];
IF found THEN {handle ← NARROW[val]; RETURN};
handle ← NEW[SegmentObject ← [segment: s]];
[] ← RefTab.Insert[segmentTable, s, handle]
};
CacheKey: PUBLIC PROC[segment: Segment] RETURNS[key: INT] = {
key ← SegmentToHandle[segment].key };
Transaction operations
OpenTransaction: PUBLIC PROC[segment: Segment, useTrans: TransactionHandle, eraseAfterOpen: BOOLFALSE] RETURNS[trans: TransactionHandle, schemaInvalid: BOOL] = {
sh: SegmentHandle = SegmentToHandle[segment];
schemaInvalid ← FALSE;
IF NOT initialized THEN ERROR DB.Error[DatabaseNotInitialized];
trans ← IF useTrans # NIL THEN useTrans
ELSE NEW[TransactionObject ← [trans: DBStorage.MakeTransaction[server: sh.server]]];
DBStorage.OpenTransaction[segment, trans, eraseAfterOpen];
sh.indices ← DBStorage.RootIndicesFromSegment[segment];
Check the database version of the schema version stamp and flush the segment caches if the version stamps disagree
BEGIN
dbSchemaStamp: INT = DBSegment.GetVersionStamp[segment];
The schema cache is invalid (ie., the key will change) iff either the database is being erased, the cache is currently empty (caused by an aborted transaction), or the stamps in the database and cache disagree
schemaInvalid ← eraseAfterOpen OR sh.cacheTrashed OR (sh.schemaVersionStamp # dbSchemaStamp);
IF schemaInvalid THEN {
sh.key ← sh.key + 1;
sh.domainTable ← SymTab.Create[];
sh.relationTable ← SymTab.Create[];
sh.attributeTable ← CardTable.Create[];
sh.domainsByTID ← CardTable.Create[];
sh.cacheTrashed ← FALSE; -- The cache is empty but valid now
sh.schemaVersionStamp ← dbSchemaStamp;
sh.schemaUpdated ← FALSE }
END;
RETURN[trans, schemaInvalid] };
MarkTransaction: PUBLIC PROC[trans: TransactionHandle] = {
FinishTransaction[trans: trans, continue: TRUE] };
FinishTransaction: PROC[trans: TransactionHandle, continue: BOOL] = {
IF trans # NIL THEN {
Write the updated version stamps for segments that have a changed schema
FOR segments: DBCommon.SegmentList ← trans.segments, segments.next UNTIL segments = NIL DO
sh: SegmentHandle ← SegmentToHandle[segments.segment];
IF sh.schemaUpdated THEN {
DBSegment.SetVersionStamp[segments.segment, sh.schemaVersionStamp + 1] };
ENDLOOP;
Commit the transaction (The fact that the schema was changed is still known at this point so that if the transaction aborts, the schema cache is flushed. In the case of an abort, we can't assume anything about the state of the cache relative to the database!)
DBStorage.FinishTransaction[handle: trans, abort: FALSE, continue: continue];
If the transaction successfully commits, update the cached version stamps of all segments that changed the schema as a result of the transaction
FOR segments: DBCommon.SegmentList ← trans.segments, segments.next UNTIL segments = NIL DO
sh: SegmentHandle ← SegmentToHandle[segments.segment];
IF sh.schemaUpdated THEN {
sh.schemaVersionStamp ← sh.schemaVersionStamp + 1;
sh.schemaUpdated ← FALSE };
ENDLOOP } };
AbortTransaction: PUBLIC PROC[trans: TransactionHandle] = {
IF trans#NIL THEN {
DBStorage.FinishTransaction[trans, TRUE, FALSE];
FOR segments: DBCommon.SegmentList ← trans.segments, segments.next UNTIL segments = NIL DO
sh: SegmentHandle ← SegmentToHandle[segments.segment];
IF sh.schemaUpdated THEN {
Bad news. The only thing that can be done here is to flush all of the cached information about the schema and recompute it when a new transaction is opened
sh.domainTable ← sh.relationTable ← NIL;
sh.attributeTable ← sh.domainsByTID ← NIL;
sh.schemaUpdated ← FALSE;
sh.cacheTrashed ← TRUE }
ENDLOOP;
Throw away away the segments for the transaction; it is now disconnected from everything
trans.segments ← NIL } };
CloseTransaction: PUBLIC PROC[trans: TransactionHandle] = {
FinishTransaction[trans: trans, continue: FALSE];
Throw away away the segments for the transaction; it is now disconnected from everything
trans.segments ← NIL };
Segment operations
DeclareSegment: PUBLIC PROC[filePath: ROPE, segment: Segment, number: DBCommon.SegmentIndex, lock: AlpineEnvironment.LockOption, readonly: BOOL, createIfNotFound: BOOL, nPagesInitial, nPagesPerExtent: INT] = {
fileVersion: DBStorage.VersionOptions = IF readonly OR NOT createIfNotFound THEN OldFileOnly ELSE None;
nameComponents: FS.ComponentPositions;
server, baseName: Rope.ROPE;
IF NOT initialized THEN ERROR DB.Error[DatabaseNotInitialized];
[fullFName ~ filePath, cp ~ nameComponents] ← FS.ExpandName[name: filePath, wDir: "///"];
server ← Rope.Substr[filePath, nameComponents.server.start, nameComponents.server.length];
baseName ← Rope.Substr[filePath, nameComponents.base.start, nameComponents.base.length];
IF Rope.Length[server] = 0 OR Rope.Length[baseName] = 0 THEN ERROR DB.Error[IllegalFileName];
IF segment = NIL THEN segment ← Atom.MakeAtom[baseName];
IF number= 0 THEN number ← MapSegmentToNumber[segment];
Save away the server name for future reference
BEGIN
handle: SegmentHandle ← SegmentToHandle[segment];
newAttachment: BOOL = DBStorage.AttachSegment[filePath, segment, number, lock, readonly, fileVersion, nPagesInitial, nPagesPerExtent];
IF NOT newAttachment THEN RETURN;
handle.server ← server;
handle.cacheTrashed ← TRUE; -- whatever is hanging around in the schema cache for the segment will become invalid when the next transaction is opened
END };
EraseSegment: PUBLIC PROC[segment: Segment, useTrans: TransactionHandle] RETURNS [trans: TransactionHandle] = {
RETURN[OpenTransaction[segment: segment, useTrans: useTrans, eraseAfterOpen: TRUE].trans] };
GetSegmentInfo: PUBLIC PROC[segment: Segment] RETURNS [filePath: ROPE, readOnly: BOOL] = {
IF segment = NIL THEN ERROR DB.Error[NILArgument];
[filePath, readOnly] ← DBSegment.GetSegmentInfo[segment] };
MapSegmentToNumber: PROC [segment: Segment] RETURNS [NAT] = {
FOR i: CARDINAL IN [0..builtinSegmentCount) DO
IF segment = segmentArray[i].segment THEN RETURN[segmentArray[i].index];
ENDLOOP;
RETURN WITH ERROR DB.Error[CannotDefaultSegment]; -- seg number not given & can't guess
};
SegmentAndIndex: TYPE = RECORD[segment: Segment, index: SegmentIndex];
builtinSegmentCount: CARDINAL = 8;
segmentArray: ARRAY [0..builtinSegmentCount) OF SegmentAndIndex =
[ [$Icons, 140B], [$Walnut, 200B], [$Nuthatch, 250B], [$Finger, 260B], [$Test, 300B], [$Whiteboard, 310B], [$Tool, 320B], [$WalnutSortDef, 330B]
reserve [330B .. 350B) for Pasadena
];
END.