<> <> <> <> 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 BOOL _ FALSE; segmentTable: RefTab.Ref = RefTab.Create[]; <> <> Initialize: PUBLIC PROC[nCachePages: NAT, cacheFileName: ROPE] = { <> IF initialized THEN RETURN; initialized _ TRUE; IF cacheFileName=NIL THEN cacheFileName_ "DBSegment.VM"; DBStorage.Initialize[nCachePages, cacheFileName]; DBModelPrivate.InitializeDomainSchema[]; DBModelPrivate.InitializeRelationSchema[]; DBModelPrivate.InitializeIndexSchema[] }; <> 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 }; <> OpenTransaction: PUBLIC PROC[segment: Segment, useTrans: TransactionHandle, eraseAfterOpen: BOOL _ FALSE] 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]; <> BEGIN dbSchemaStamp: INT = DBSegment.GetVersionStamp[segment]; <> 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 { <> 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; <> DBStorage.FinishTransaction[handle: trans, abort: FALSE, continue: continue]; <> 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 { <> sh.domainTable _ sh.relationTable _ NIL; sh.attributeTable _ sh.domainsByTID _ NIL; sh.schemaUpdated _ FALSE; sh.cacheTrashed _ TRUE } ENDLOOP; <> trans.segments _ NIL } }; CloseTransaction: PUBLIC PROC[trans: TransactionHandle] = { FinishTransaction[trans: trans, continue: FALSE]; <> trans.segments _ NIL }; <> 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]; <> 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] <> ]; END.