DIRECTORY AlpineEnvironment USING [LockOption], DB USING[Aborted], DBCommon, DBFile, DBIndex USING [CallAfterFinishTransaction], DBSegment, DBStats, DBStorage, DBStorageConcrete USING[SystemTuplesetObject], DBStorageField USING[TuplesetFieldHandle], DBStorageGroup USING[GroupListEntry], DBStorageGroupScan USING [Init, CallAfterFinishTransaction], DBStoragePage, DBStoragePrivate USING[GetNWordBase], DBStorageTuple USING[InitSegmentTuples, ConsTupleObject, CallAfterFinishTransaction, TIDOfTuple, InvalidateMatchingTuples], DBStorageTuplesetScan USING[Init, CallAfterFinishTransaction, NoticeDeletion], PrincOpsUtils USING[LongCopy], Rope USING [ROPE]; DBStorageImplA: CEDAR PROGRAM IMPORTS DB, DBCommon, DBFile, DBIndex, DBSegment, DBStats, DBStorage, DBStorageField, DBStorageGroupScan, DBStoragePage, DBStoragePrivate, DBStorageTuple, DBStorageTuplesetScan, PrincOpsUtils EXPORTS DBCommon, DBStorage, DBStoragePage = BEGIN OPEN DBCommon; DoChecks: BOOLEAN = TRUE; UnlikelySlotIndex: CARDINAL = LAST[CARDINAL]; UserTupleCreateThreshold: CARDINAL = DBCommon.WordsPerPage/8; SystemTupleCreateThreshold: CARDINAL = DBCommon.WordsPerPage/4; AllocThreshold: CARDINAL = DBCommon.WordsPerPage/2; TupleObject: PUBLIC TYPE = DBCommon.TupleObject; TupleHandle: TYPE = REF TupleObject; SystemTuplesetObject: PUBLIC TYPE = DBStorageConcrete.SystemTuplesetObject; SystemTuplesetHandle: TYPE = REF SystemTuplesetObject; InternalError: PUBLIC ERROR = CODE; Segment: TYPE = DBCommon.Segment; SegmentIndex: TYPE = DBCommon.SegmentIndex; SegmentID: TYPE = DBCommon.SegmentID; VersionOptions: TYPE = DBCommon.VersionOptions; Trans: TYPE = DBCommon.Transaction; TID: TYPE = DBCommon.TID; ROPE: TYPE = Rope.ROPE; Initialize: PUBLIC PROC [nCachePages: NAT, cacheFileName: ROPE] = { DBSegment.Initialize[nCachePages, cacheFileName, CreateIndexes]; DBStorageGroupScan.Init[]; DBStorageTuplesetScan.Init[] }; CreateIndexes: PROC [s: Segment] RETURNS [indices: ARRAY [0..DBCommon.systemIndexCount) OF TID] = { indexTS: DBStorage.SystemTuplesetHandle = DBStorage.CreateSystemTupleset[1]; indexFH: DBStorage.FieldHandle = DBStorage.AddSystemField[indexTS, [NWord[DBStorage.IndexObjectSize]]]; handles: ARRAY [0..DBCommon.systemIndexCount) OF TupleHandle; FOR i: CARDINAL IN [0..DBCommon.systemIndexCount) DO handles[i] _ DBStorage.CreateSystemPageTuple[indexTS, IF i=0 THEN NIL ELSE handles[0], s]; DBStorage.CreateIndex[handles[i]]; indices[i] _ DBStorageTuple.TIDOfTuple[handles[i]] ENDLOOP; }; MakeTransaction: PUBLIC PROC[server: Rope.ROPE] RETURNS[t: DBCommon.Transaction] = { t _ DBFile.CreateTransaction[server] }; OpenTransaction: PUBLIC PROC[segment: Segment, handle: DBCommon.TransactionHandle, eraseAfterOpening: BOOL _ FALSE] = { DBSegment.OpenSegment[segment, handle.trans, eraseAfterOpening]; FOR segments: DBCommon.SegmentList _ handle.segments, segments.next UNTIL segments = NIL DO IF segment = segments.segment THEN RETURN ENDLOOP; handle.segments _ NEW[DBCommon.SegmentListObject _ [segment: segment, next: handle.segments]]; }; FinishTransaction: PUBLIC PROC [handle: DBCommon.TransactionHandle, abort: BOOL, continue: BOOL] = { stopping: BOOL = abort OR NOT continue; FOR segs: DBCommon.SegmentList _ handle.segments, segs.next UNTIL segs=NIL DO IF NOT abort THEN DBSegment.WriteOutCache[segs.segment]; ENDLOOP; DBFile.FinishTransaction[t: handle.trans, abort: abort, continue: TRUE]; IF stopping THEN { DBStorageTuplesetScan.CallAfterFinishTransaction[]; DBStorageGroupScan.CallAfterFinishTransaction[]; FOR segs: DBCommon.SegmentList _ handle.segments, segs.next UNTIL segs=NIL DO DBIndex.CallAfterFinishTransaction[segs.segment]; DBStorageTuple.CallAfterFinishTransaction[segs.segment]; DBSegment.FlushCache[segs.segment]; IF NOT abort THEN DBSegment.CloseSegment[segs.segment] ENDLOOP; IF NOT abort THEN DBFile.FinishTransaction[t: handle.trans, abort: FALSE, continue: FALSE ! DB.Aborted => CONTINUE] }; }; AttachSegment: PUBLIC PROC [fileName: ROPE, s: Segment, segmentIndex: SegmentIndex, lock: AlpineEnvironment.LockOption, readonly: BOOL, version: VersionOptions, nPagesInitial, nPagesPerExtent: NAT] RETURNS[newAttachment: BOOL] = { newAttachment _ DBSegment.AttachSegment[fileName, s, segmentIndex, lock, readonly, version, nPagesInitial, nPagesPerExtent]; DBStorageTuple.InitSegmentTuples[s]; }; RootIndicesFromSegment: PUBLIC PROC[s: Segment] RETURNS [indices: ARRAY[0..DBCommon.systemIndexCount) OF DBStorage.IndexHandle] = { indexTIDs: ARRAY[0..DBCommon.systemIndexCount) OF TID = DBSegment.RootIndicesFromSegment[s]; FOR i: CARDINAL IN [0..DBCommon.systemIndexCount) DO indices[i] _ DBStorageTuple.ConsTupleObject[tid: indexTIDs[i]] ENDLOOP }; CreateTuple: PUBLIC PROC[x: TupleHandle] RETURNS[TupleHandle] = TRUSTED { dbPage: DBCommon.DBPage; dbPagePtr: LONG POINTER TO DBStoragePage.VecPage; dbPageHdl: DBCommon.CacheHandle _ NIL; slotIndex: CARDINAL; success, mustSucceed: BOOLEAN _ FALSE; tsObjPtr: LONG POINTER TO DBStoragePage.TuplesetObject; tsObjHdl: DBCommon.CacheHandle; [tsObjPtr, tsObjHdl] _ DBStoragePrivate.GetNWordBase[x, DBStorageField.TuplesetFieldHandle[]]; DBStats.Inc[StorageCreateTuple]; DO --until tuple created IF tsObjPtr.allocList = NotOnList THEN ERROR DBCommon.InternalError; IF tsObjPtr.allocList = NullDBPage THEN { [dbPage, dbPageHdl, dbPagePtr] _ DBSegment.AllocPage[tsObjPtr.pageAllocator.segmentID]; DBSegment.WriteLockedPage[dbPageHdl]; --about to write dbPagePtr^ InitializeTuplePage[p: dbPagePtr, tsID: tsObjPtr.searchList.tuplesetID, pageTag: DBStoragePage.Tuple]; DBSegment.WriteLockedPage[tsObjHdl]; tsObjPtr.allocList _ dbPage; { ts: TupleHandle _ DBStorageTuple.ConsTupleObject[tsObjPtr.searchList.tuplesetID, tsObjHdl]; WriteTSDictField[dbPage, ts, prev, NullDBPage]; WriteTSDictField[dbPage, ts, next, tsObjPtr.searchList.next]; WriteTSDictField[tsObjPtr.searchList.next, ts, prev, dbPage]; WriteTSDictField[NullDBPage, ts, next, dbPage]; }; DBSegment.UnlockPage[dbPageHdl]; mustSucceed _ TRUE; };--IF dbPage _ tsObjPtr.allocList; [dbPageHdl, dbPagePtr] _ DBSegment.ReadPage[dbPage, dbPageHdl]; DBStoragePage.AssertPageIsTuplePage[dbPagePtr]; IF ~PageFullP[dbPagePtr, tsObjPtr] THEN [slotIndex, success] _ TryAllocTuple[dbPagePtr, dbPageHdl, tsObjPtr.searchList.tuplesetID, tsObjPtr.wordsForTupleFields]; IF mustSucceed AND ~success THEN ERROR DBCommon.InternalError; IF ~success OR PageFullP[dbPagePtr, tsObjPtr] THEN { tsDict: LONG POINTER TO DBStoragePage.TSDict _ TSDictBaseFromPagePtr[dbPagePtr]; IF tsDict.allocLink = NotOnList THEN ERROR DBCommon.InternalError; DBSegment.WriteLockedPage[tsObjHdl]; tsObjPtr.allocList _ tsDict.allocLink; DBSegment.WriteLockedPage[dbPageHdl]; tsDict.allocLink _ NotOnList; };--IF DBSegment.UnlockPage[dbPageHdl]; IF success THEN EXIT; ENDLOOP; DBSegment.UnlockPage[tsObjHdl]; RETURN[DBStorageTuple.ConsTupleObject[tid: ConsTID[dbPage, slotIndex], cacheHint: dbPageHdl]]; };--CreateTuple PageFullP: PROC[ dbPagePtr: LONG POINTER TO DBStoragePage.VecPage, tsObjPtr: LONG POINTER TO DBStoragePage.TuplesetObject] RETURNS[BOOLEAN] = TRUSTED { fixedPageOverhead: CARDINAL = --VecPage overhead, including FreeSlot and FreeVec SIZE[DBStoragePage.VecPage] + SIZE[DBStoragePage.Slot] + SIZE[DBStoragePage.VecHeader]; localTSID: CARDINAL _ 1; --this is a cheat, works only no colocation (one ts/page) wordsForOneTuple: CARDINAL = tsObjPtr.wordsForTupleFields + SIZE[DBStoragePage.Slot] + DBStoragePage.SizeOfNullTuple + 3 * SIZE[DBStorageGroup.GroupListEntry]; wordsInUse: CARDINAL _ fixedPageOverhead; FOR i: CARDINAL IN [1..DBStoragePage.HighSlotIndexOfPage[dbPagePtr]] DO slotType: CARDINAL = DBStoragePage.TypeOfSlot[dbPagePtr, i]; wordsInVec: CARDINAL = DBStoragePage.LengthOfVec[DBStoragePage.VecOfSlot[dbPagePtr,i]]; SELECT slotType FROM IN [1..DBStoragePage.MaxTuplesetPerPage] => { IF slotType = localTSID THEN wordsInUse _ wordsInUse + MAX[wordsForOneTuple, wordsInVec] ELSE ERROR;--you implemented colocation without fixing this procedure! }; DBStoragePage.LStringType, DBStoragePage.TSDictType => { wordsInUse _ wordsInUse + wordsInVec; }; DBStoragePage.FreeType => { }; DBStoragePage.TSDictType => { }; --already counted above ENDCASE => ERROR; ENDLOOP; RETURN[WordsPerPage <= UserTupleCreateThreshold + wordsInUse + LOOPHOLE[ (wordsForOneTuple + tsObjPtr.nVarFields*DBStoragePage.SizeOfNullLString), CARDINAL] ] };--PageFullP SystemPageFullP: PROC[dbPagePtr: LONG POINTER TO DBStoragePage.VecPage] RETURNS[BOOLEAN] = INLINE { RETURN[DBStoragePage.WordsLeftOnPage[dbPagePtr] <= SystemTupleCreateThreshold] };--SystemPageFullP CreateSystemPageTuple: PUBLIC PROC [x: SystemTuplesetHandle, y: TupleHandle, s: DBCommon.Segment] RETURNS[--newTuple-- TupleHandle] = TRUSTED { dbPage: DBPage; dbPagePtr: LONG POINTER TO DBStoragePage.VecPage; dbPageHdl: DBCommon.CacheHandle; slotIndex: CARDINAL; segment: DBPage; --Internal ID of segment where tuple goes. DBStats.Inc[StorageCreateSystemTuple]; BEGIN--block for EXITS success: BOOLEAN; IF y # NIL THEN BEGIN ys: REF TupleObject = NARROW[y]; [dbPage,] _ DecomposeTID[ys.tid]; [dbPageHdl, dbPagePtr] _ DBSegment.ReadPage[dbPage, ys.cacheHint]; ys.cacheHint _ dbPageHdl; DBStoragePage.AssertPageIsSystemTuplePage[dbPagePtr]; IF ~SystemPageFullP[dbPagePtr] THEN BEGIN [slotIndex, success] _ TryAllocTuple[dbPagePtr, dbPageHdl, x.tuplesetID, x.wordsForTupleFields]; IF success THEN GOTO Finish; END;--IF DBSegment.UnlockPage[dbPageHdl]; segment _ DBSegment.SegmentIDFromDBPage[dbPage]; END ELSE BEGIN segment _ DBSegment.SegmentIDFromSegment[s]; END;--IF [dbPage, dbPageHdl, dbPagePtr] _ DBSegment.AllocPage[segment]; DBSegment.WriteLockedPage[dbPageHdl]; InitializeTuplePage[dbPagePtr, x.tuplesetID, DBStoragePage.SystemTuple]; [slotIndex, success] _ TryAllocTuple[dbPagePtr, dbPageHdl, x.tuplesetID, x.wordsForTupleFields]; IF success THEN GOTO Finish; ERROR DBCommon.InternalError; EXITS Finish => BEGIN result: TupleHandle _ DBStorageTuple.ConsTupleObject[tid: ConsTID[dbPage, slotIndex], cacheHint: dbPageHdl]; DBSegment.UnlockPage[dbPageHdl]; RETURN[result]; END;--Finish END; };--CreateSystemPageTuple InitializeTuplePage: PROC[p: --writeable--LONG POINTER TO DBStoragePage.VecPage, tsID: TID, pageTag: CARDINAL] = TRUSTED { slotIndex: INTEGER; success: BOOLEAN; tsDict: LONG POINTER TO DBStoragePage.TSDict; DBStoragePage.InitializeVecPage[p, pageTag]; [slotIndex, success] _ DBStoragePage.AllocVec[p, DBStoragePage.SizeOfInitialTSDict]; IF DoChecks AND (~success OR slotIndex # DBStoragePage.TSDictSlotIndex) THEN ERROR DBCommon.InternalError; tsDict _ LOOPHOLE[DBStoragePage.VecOfSlot[p, DBStoragePage.TSDictSlotIndex]]; tsDict.allocLink _ SELECT pageTag FROM DBStoragePage.Tuple => NullDBPage, DBStoragePage.SystemTuple => NotOnList, ENDCASE => ERROR; tsDict.seq[1] _ [tuplesetID: tsID, next: NotOnList, prev: NotOnList]; DBStoragePage.SetTypeOfSlot[p, DBStoragePage.TSDictSlotIndex, DBStoragePage.TSDictType]; };--InitializeTuplePage TryAllocTuple: PROC[p: LONG POINTER TO DBStoragePage.VecPage, pValidHint: DBCommon.CacheHandle, tsID: TID, wordsForTupleFields: CARDINAL] RETURNS[--slotIndex-- CARDINAL, --success-- BOOLEAN] = TRUSTED { slotIndex: CARDINAL; entry: CARDINAL; BEGIN--block for EXITS BEGIN--block for tsDict variables success: BOOLEAN; tsIDFound: BOOLEAN; [entry, tsIDFound] _ GetIndex[p, tsID]; IF ~tsIDFound AND entry > DBStoragePage.MaxTuplesetPerPage THEN GOTO Failure; IF (wordsForTupleFields + (DBStoragePage.SizeOfNullTuple+SIZE[DBStoragePage.Slot]) + (IF tsIDFound THEN 0 ELSE SIZE[DBStoragePage.TSDictEntry])) > DBStoragePage.WordsLeftOnPage[p] THEN GOTO Failure; DBSegment.WriteLockedPage[pValidHint]; IF ~tsIDFound THEN { [success] _ DBStoragePage.ModifyVec[p, DBStoragePage.TSDictSlotIndex, SIZE[DBStoragePage.TSDictEntry], TRUE]; IF DoChecks AND ~success THEN ERROR DBCommon.InternalError; LOOPHOLE[DBStoragePage.VecOfSlot[p, DBStoragePage.TSDictSlotIndex], LONG POINTER TO DBStoragePage.TSDict].seq[entry] _ [tuplesetID: tsID, next: NotOnList, prev: NotOnList]; };--IF END;--block for tsDict variables BEGIN--block for tuple variables success: BOOLEAN; tuplePtr: LONG POINTER TO DBStoragePage.TupleBody; [slotIndex, success] _ DBStoragePage.AllocVec[p, wordsForTupleFields + DBStoragePage.SizeOfNullTuple]; IF DoChecks AND ~success THEN ERROR DBCommon.InternalError; tuplePtr _ LOOPHOLE[DBStoragePage.VecOfSlot[p, slotIndex]]; tuplePtr.groupOffset _ DBStoragePage.SizeOfNullTuple + wordsForTupleFields; IF wordsForTupleFields # 0 THEN BEGIN --zero out the tuple tuplePtr.fields[0] _ 0; PrincOpsUtils.LongCopy[from: @tuplePtr.fields[0], nwords: wordsForTupleFields-1, to: @tuplePtr.fields[1]]; END;--IF DBStoragePage.SetTypeOfSlot[p, slotIndex, entry]; GOTO Success; END;--block for tuple variables EXITS Success => RETURN[slotIndex, TRUE]; Failure => RETURN[UnlikelySlotIndex, FALSE]; END; };--TryAllocTuple GetIndex: PUBLIC PROC[p: LONG POINTER TO DBStoragePage.VecPage, tsID: TID] RETURNS[CARDINAL, BOOLEAN] = TRUSTED { tsDictPtr: LONG POINTER TO DBStoragePage.TSDict _ TSDictBaseFromPagePtr[p]; nEntries: CARDINAL _ NEntries[tsDictPtr]; FOR entry: CARDINAL IN [1..nEntries] DO IF tsDictPtr.seq[entry].tuplesetID = tsID THEN GOTO FoundEntry; REPEAT FoundEntry => RETURN[entry, TRUE]; FINISHED => RETURN[nEntries+1, FALSE]; ENDLOOP; };--IndexFromID NEntries: PUBLIC PROC[tsDictPtr: LONG POINTER TO DBStoragePage.TSDict] RETURNS[CARDINAL] = TRUSTED { dictLen: CARDINAL _ tsDictPtr.header.length - DBStoragePage.SizeOfNullTSDict; IF DoChecks AND (dictLen = 0 OR dictLen MOD SIZE[DBStoragePage.TSDictEntry] # 0) THEN ERROR DBCommon.InternalError; -- BadTSDictLength RETURN[dictLen/SIZE[DBStoragePage.TSDictEntry]]; };--NEntries GetEntry: PUBLIC PROC[p: LONG POINTER TO DBStoragePage.VecPage, tsID: TID] RETURNS[LONG POINTER TO DBStoragePage.TSDictEntry] = TRUSTED { tsDictPtr: LONG POINTER TO DBStoragePage.TSDict _ TSDictBaseFromPagePtr[p]; entry: CARDINAL; success: BOOLEAN; [entry, success] _ GetIndex[p, tsID]; IF success THEN RETURN[@tsDictPtr.seq[entry]]; ERROR DBCommon.InternalError; -- TSDictEntryNotFound };--GetEntry TSDictBaseFromPagePtr: PROC[dbPagePtr: LONG POINTER TO DBStoragePage.VecPage] RETURNS[LONG POINTER TO DBStoragePage.TSDict] = INLINE { RETURN[LOOPHOLE[DBStoragePage.VecOfSlot[dbPagePtr, DBStoragePage.TSDictSlotIndex]]]; };--TSDictBaseFromPagePtr EntryFromIndex: PUBLIC PROC[ dbPagePtr: LONG POINTER TO DBStoragePage.VecPage, index: CARDINAL] RETURNS[LONG POINTER TO DBStoragePage.TSDictEntry] = TRUSTED { tsDict: LONG POINTER TO DBStoragePage.TSDict _ TSDictBaseFromPagePtr[dbPagePtr]; IF index > NEntries[tsDict] THEN ERROR DBCommon.InternalError; -- TSDictEntryNotFound RETURN[@(tsDict.seq[index])]; };--EntryFromIndex DestroyTuple: PUBLIC PROC[x: TupleHandle] = TRUSTED { dbPage: DBPage; slotIndex, localTSID: CARDINAL; dbPagePtr: LONG POINTER TO DBStoragePage.VecPage; dbPageHdl: DBCommon.CacheHandle; pageEmpty: BOOLEAN _ FALSE; xs: REF TupleObject = NARROW[x]; DBStats.Inc[StorageDestroyTuple]; [dbPage, slotIndex] _ DecomposeTID[xs.tid]; [dbPageHdl, dbPagePtr] _ DBSegment.WritePage[dbPage, xs.cacheHint]; DBStoragePage.AssertPageIsAnyTuplePage[dbPagePtr]; localTSID _ DBStoragePage.TypeOfSlot[dbPagePtr, slotIndex]; AssertTupleTypeIsOK[dbPagePtr, localTSID]; DBStorageTuple.InvalidateMatchingTuples[x]; -- invalidate all x-like tuple objects DBStoragePage.FreeVec[dbPagePtr, slotIndex]; -- free tuple storage on page FOR i: CARDINAL IN [1 .. DBStoragePage.HighSlotIndexOfPage[dbPagePtr]] DO IF DBStoragePage.TypeOfSlot[dbPagePtr, i] = localTSID THEN GOTO FoundTupleFromSameTS; REPEAT FoundTupleFromSameTS => {}; --No changes to TSDict are necessary FINISHED => { RemoveTSDictEntry[dbPage, dbPagePtr, localTSID]; pageEmpty _ (TSDictBaseFromPagePtr[dbPagePtr].header.length = DBStoragePage.SizeOfNullTSDict) }; ENDLOOP; IF pageEmpty THEN { DBSegment.FreePage[DBSegment.SegmentIDFromDBPage[dbPage], dbPage, dbPageHdl]; } ELSE { IF DBStoragePage.TagOfPage[dbPagePtr] = DBStoragePage.Tuple THEN { MaybePutOnAllocList[dbPage, dbPagePtr, dbPageHdl] }; DBSegment.UnlockPage[dbPageHdl]; };--IF };--DestroyTuple AssertTupleTypeIsOK: PROC[p: LONG POINTER TO DBStoragePage.VecPage, type: CARDINAL] = INLINE { SELECT type FROM IN [1..DBStoragePage.MaxTuplesetPerPage] => {}; ENDCASE => ERROR DBCommon.InternalError; -- BadLocalTSID };--AssertTupleTypeIsOK RemoveTSDictEntry: PROC[ dbPage: DBPage, dbPagePtr: LONG POINTER TO DBStoragePage.VecPage, entry: CARDINAL] = TRUSTED { isUserPage: BOOLEAN _ SELECT DBStoragePage.TagOfPage[dbPagePtr] FROM DBStoragePage.Tuple => TRUE, DBStoragePage.SystemTuple => FALSE, ENDCASE => ERROR; tsDict: LONG POINTER TO DBStoragePage.TSDict _ TSDictBaseFromPagePtr[dbPagePtr]; tsID: TID; prevDBPage, nextDBPage: DBPage; IF isUserPage THEN { ts: TupleHandle; [tuplesetID: tsID, prev: prevDBPage, next: nextDBPage] _ tsDict.seq[entry]; ts _ DBStorageTuple.ConsTupleObject[tsID]; IF entry=1 AND tsDict.allocLink#NotOnList THEN { BypassPageOnAllocList[ts, dbPage, tsDict.allocLink]; tsDict.allocLink _ NotOnList; };--IF WriteTSDictField[prevDBPage, ts, next, nextDBPage]; WriteTSDictField[nextDBPage, ts, prev, prevDBPage]; };--IF isUserPage FOR i: CARDINAL IN [entry .. NEntries[tsDict]) DO tsDict.seq[i] _ tsDict.seq[i+1]; ENDLOOP; [] _ DBStoragePage.ModifyVec[dbPagePtr, DBStoragePage.TSDictSlotIndex, -SIZE[DBStoragePage.TSDictEntry], TRUE]; FOR i: CARDINAL IN [1 .. DBStoragePage.HighSlotIndexOfPage[dbPagePtr]] DO type: CARDINAL _ DBStoragePage.TypeOfSlot[dbPagePtr, i]; IF type IN [1..DBStoragePage.MaxTuplesetPerPage] AND type > entry THEN DBStoragePage.SetTypeOfSlot[dbPagePtr, i, type-1]; ENDLOOP; IF isUserPage THEN { DBStorageTuplesetScan.NoticeDeletion[dbPage, tsID, entry, nextDBPage]; };--IF isUserPage };--RemoveTSDictEntry TSPart: TYPE = {next, prev}; WriteTSDictField: PROC[ page: DBPage, ts: TupleHandle, part: TSPart, val: DBPage] = TRUSTED { cacheHint: DBCommon.CacheHandle; IF page = NullDBPage THEN { tsObjPtr: LONG POINTER TO DBStoragePage.TuplesetObject; [tsObjPtr, cacheHint] _ DBStoragePrivate.GetNWordBase[ts, DBStorageField.TuplesetFieldHandle[]]; DBSegment.WriteLockedPage[cacheHint]; SELECT part FROM next => tsObjPtr.searchList.next _ val; prev => tsObjPtr.searchList.prev _ val; ENDCASE => ERROR; } ELSE { pagePtr: LONG POINTER TO DBStoragePage.VecPage; tsDictEntry: LONG POINTER TO DBStoragePage.TSDictEntry; [cacheHint, pagePtr] _ DBSegment.WritePage[page, NIL]; DBStoragePage.AssertPageIsTuplePage[pagePtr]; tsDictEntry _ GetEntry[pagePtr, ts.tid]; SELECT part FROM next => tsDictEntry.next _ val; prev => tsDictEntry.prev _ val; ENDCASE => ERROR; };--IF DBSegment.UnlockPage[cacheHint]; };--WriteTSDictField BypassPageOnAllocList: PROC[ ts: TupleHandle, dbPageToRemove, dbPageReplacement: DBPage] = TRUSTED { tsObjPtr: LONG POINTER TO DBStoragePage.TuplesetObject; cacheHdl: DBCommon.CacheHandle; [tsObjPtr, cacheHdl] _ DBStoragePrivate.GetNWordBase[ts, DBStorageField.TuplesetFieldHandle[]]; IF tsObjPtr.allocList = dbPageToRemove THEN { DBSegment.WriteLockedPage[cacheHdl]; tsObjPtr.allocList _ dbPageReplacement; } ELSE { curPage: DBPage _ tsObjPtr.allocList; curPagePtr: LONG POINTER TO DBStoragePage.VecPage; tsDict: LONG POINTER TO DBStoragePage.TSDict; DO DBSegment.UnlockPage[cacheHdl]; --unlock page containing the pointer curPage IF curPage = NullDBPage OR curPage = NotOnList THEN DBCommon.InternalError; [cacheHdl, curPagePtr] _ DBSegment.ReadPage[curPage, NIL]; DBStoragePage.AssertPageIsTuplePage[curPagePtr]; tsDict _ TSDictBaseFromPagePtr[curPagePtr]; IF tsDict.allocLink = dbPageToRemove THEN { DBSegment.WriteLockedPage[cacheHdl]; tsDict.allocLink _ dbPageReplacement; EXIT; } ELSE curPage _ tsDict.allocLink; ENDLOOP };--IF DBSegment.UnlockPage[cacheHdl]; --unlock the page we modified };--BypassPageOnAllocList MaybePutOnAllocList: PROC[ dbPage: DBPage, dbPagePtr: LONG POINTER TO DBStoragePage.VecPage, dbPageHdl: DBCommon.CacheHandle] = TRUSTED { tsDict: LONG POINTER TO DBStoragePage.TSDict _ TSDictBaseFromPagePtr[dbPagePtr]; IF tsDict.allocLink = NotOnList AND DBStoragePage.WordsLeftOnPage[dbPagePtr] >= AllocThreshold THEN { tsObjPtr: LONG POINTER TO DBStoragePage.TuplesetObject; tsObjHdl: DBCommon.CacheHandle; { -- make tupleset object addressible ts: TupleHandle; IF tsDict.header.length = DBStoragePage.SizeOfNullTSDict THEN ERROR DBCommon.InternalError; --we expected the thing to be nonempty! ts _ DBStorageTuple.ConsTupleObject[tsDict.seq[1].tuplesetID]; [tsObjPtr,tsObjHdl] _ DBStoragePrivate.GetNWordBase[ts, DBStorageField.TuplesetFieldHandle[]]; }; DBSegment.WriteLockedPage[dbPageHdl]; tsDict.allocLink _ tsObjPtr.allocList; DBSegment.WriteLockedPage[tsObjHdl]; tsObjPtr.allocList _ dbPage; DBSegment.UnlockPage[tsObjHdl]; };--IF };--MaybePutOnAllocList FreeTupleHandle: PUBLIC PROC[x: TupleHandle] = { };--FreeTupleHandle END.--StorageImplC Created by MBrown on February 17, 1980 2:13 PM Changed by MBrown on February 25, 1980 5:18 PM Changed by MBrown on 28-Feb-80 22:12 Changed by MBrown on April 9, 1980 9:24 AM Changed by MBrown on April 9, 1980 3:43 PM Changed by MBrown on April 9, 1980 6:14 PM Changed by MBrown on April 10, 1980 10:15 AM Changed by MBrown on 11-Apr-80 11:07 Changed by MBrown on April 11, 1980 11:19 AM Changed by MBrown on April 11, 1980 11:54 AM Changed by MBrown on April 17, 1980 11:30 PM Changed by MBrown on April 21, 1980 10:09 PM Changed by MBrown on April 23, 1980 9:16 PM Changed by MBrown on June 8, 1980 8:46 PM Changed by MBrown on June 17, 1980 8:48 AM Changed by MBrown on June 24, 1980 11:23 AM Changed by MBrown on July 24, 1980 10:05 PM Changed by MBrown on July 31, 1980 3:39 PM Changed by MBrown on August 1, 1980 1:29 AM Changed by MBrown on August 25, 1980 1:45 PM Changed by MBrown on August 29, 1980 10:58 AM -the expected length. Changed by MBrown on September 22, 1980 5:14 PM Changed by MBrown on September 23, 1980 3:27 PM Changed by MBrown on September 26, 1980 3:57 PM Changed by Cattell on October 1, 1980 9:52 AM [10] Changed by MBrown on December 7, 1980 12:25 PM Changed by MBrown on February 27, 1981 5:08 PM Changed by MBrown on March 12, 1981 9:57 AM Changed by MBrown on 19-Jun-81 13:46:51 Changed by MBrown on 7-Aug-81 15:35:37 Changed by Willie-Sue on February 15, 1985 )|File: DBStorageImplA.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Last edited by: MBrown on December 16, 1982 2:45 pm Cattell on November 8, 1983 1:13 pm Willie-Sue, March 19, 1985 6:06:49 pm PST Widom, August 6, 1985 3:13:47 pm PDT Donahue, June 6, 1986 2:05:35 pm PDT This module exports part of DBStorage: tuple creation/destruction, tupleset scans. It also exports ops to DBStoragePage. It exports DBStorage.TupleObject in order to gain direct access to tid and cacheHint for reading; it deals through the DBStorageTuple interface for writing. It also exports DBStorage.SystemTuplesetObject in order to perform CreateSystemPageTuple. PROBLEMS, THINGS YET TO BE DONE: Unimplemented: CreateTuple with colocation. DestroyTuple will require work when long string values are implemented. if TRUE, perform lots of redundant internal checking. require this many free words on a page (when DONE) to create a tuple there. require this many free words on a page to place on alloc list. Types exported to DBStorage Error exported to DBCommon Other Useful Types Called from DBSegmentImpl for the initialization of a segment. Transaction interface Opens a transaction on the segment. Make sure that s is in the list All volatile state referring to nonexistent segments must now be discarded. Segment interface add a page to allocList, and set mustSucceed flag make page be first (and last) on allocList. link page into searchList of this tupleset DBStorageTuple.FreeTupleObject[ts]; allocList is nonempty; try to place the tuple on its first page first page of allocList isn't too full, so try allocation tuple won't fit on this page, so remove it from allocList. this is done AFTER tuple allocation so that if page gets full we take it off right away. here slotIndex is the index of the new tuple, on page dbPage, with unlocked cache handle dbPageHdl. TRUE iff another tuple from ts should NOT be placed on page. Guaranteed to get more elaborate, and change every time we change anything... Note: the 3 * SIZE[DBStorageGroup.GroupListEntry] is a crock to allocate some space to handle expected group head entries for the tuple. If there is insufficient space on a page, then it is impossible to allocate space for an additional head of group entry. This needs to be fixed, but until it does . . . TRUE iff another tuple from ts should NOT be placed on page. Guaranteed to get more elaborate... The same as CreateTuple except that when y = NIL the new tuple is always placed on an otherwise empty page in segment s. try to create the tuple here here if colocation was requested but couldn't be done. ignore the supplied value of s, and force new tuple to be in same segment as y. y=NIL, so use segment s (this should be used for relatively few tuples) here if no colocation here slotIndex is the index of the new tuple, on page dbPage, with locked cache handle dbPageHdl. p points to a writeable cache page; this proc turns it into a page for storing tuples. It creates a tupleset dictionary with an entry for tsID, and makes the page's tag be pageTag. The page's allocLink is NullDBPage for Tuple pages, NotOnList for SystemTuple pages. The page's searchList links are set to NotOnList, independent of page type. p points to a tuple page, and pValidHint is a valid cache hint for it; this proc attempts to create a new tuple with tuplesetID tsID and length wordsForTupleContents on this page. If this is possible without overflow, then slotIndex is the slot of the resulting tuple, and success is TRUE; otherwise slotIndex contains garbage and success is FALSE. We compute (an upper bound for) the number of words needed, and proceed only if they are available. This means that we write the page only when necessary, and never have to undo partial updates. (We may fail when the room is really there, too). Here we're sure to make it... Returns index of the TSDictEntry on page p that contains tsID as its tuplesetID field. Second result is TRUE iff entry was actually found; otherwise we return 1+number of entries in the TSDict, and FALSE. Called from TryAllocTuple, GetEntry, NextScanTupleset. Returns the length (in elements, not words) of tsDictPtr.seq Exported to DBStoragePage. Called internally from GetIndex. Returns ptr to the TSDictEntry on page p that contains tsID as its tuplesetID field. ERRORs InternalError if no such entry exists. Called from CreateTuple. Destroys tuple x, and frees x's TupleObject. x's group fields must have already been NILed out, since we don't even know where they are... To here, we've just been gathering useful information about the tuple we're destroying. With overflow pages and long strings, things are not quite so simple. You must first deallocate all vecs that depend on this one. Such vecs might live on other pages, which might become empty, ... Deleted page's last tuple from TS; this does not work with colocation of user tuples. Page is now empty. Its TSDict must be empty, hence there are no pointers to this page, and its lock count is 1. Free it back to its segment. (Someday maybe to TS page allocator). User tuple page is not empty, but it may have just gone below the 'full' threshold. If so, then it should be placed on an alloc list if it is not already on one. DBStorageTuple.FreeTupleObject[x]; This test should be made stronger by having it examine the TSDict itself. dbPage is a (system or user) tuple page, writeable-locked in cache, and dbPagePtr points to it. This proc deletes the entry-th entry in the TSDict on this page, and makes the rest of the world consistent with this. Extract the next and prev pages, and the TSID, from the entry we're deleting If page is on ts's allocList, remove it (it doesn't contain any tuples from ts). (Page might also be empty now, in which case alloc list ptr to it would soon "dangle"). Delete the page from the doubly-linked list of pages for this tupleset. DBStorageTuple.FreeTupleObject[ts]; Move higher entries down and shrink the vec by 1 entry (may now even have 0 entries) For each tuple vec on page whose local tsid is > entry, reduce by 1. Update active tupleset scans to account for changes to this page. If page=NullDBPage, then this writes the specified part of the tupleset object; otherwise it writes part of the ts dict entry on the given page for the given tupleset. Make the link of ts's allocList that points to dbPageToRemove point instead to dbPageReplacement. Called from: RemoveTSDictEntry. This looks pretty gross. A problem with secondary-storage data structures is that we can't put in header nodes to make things be uniform to the program; the header node would in this case burn up a page! So there is invariably a special case involving the list head. Another clumsy thing is the number of distinct variables that I require, and the number of distinct operations necessary, to maintain and follow a page pointer. This makes me want to minimize the number of these; hence this proc never looks at more than one page at a time, and the caller is required to do some of the work. ran off end of list without finding dbPageToRemove if page pointed to by dbPagePtr is not on an allocList and under threshold, put it on allocList do not change lock count of dbPageHdl. DBStorageTuple.FreeTupleObject[ts]; Deallocates the storage used by the TupleObject pointed to by x. The caller is responsible not to keep dangling references to this object. DBStorageTuple.FreeTupleObject[x]; Module History All coded but Create[User]Tuple, DestroyTuple; testing can proceed. Make NEntriesInTSDict PUBLIC, for use in StorageImplB. When y#NIL in CreateSystemPageTuple, ignore s and place tuple in same segment as y. Coded CreateTuple, with limitations (1) no colocation, (2) no delete tuple. Added check of page tag to CreateSystemPageTuple. Coded tupleset scan stuff. Cleaned up the TSDict searching stuff; that loop appears in only one place now. Procedure calls are not to be avoided for now. In the process of cleaning up the TSDict stuff I introduced a bug in TryAllocTuple: entry was declared twice, which prevented communication between the two sections. In CreateTuple, in the case that the tuple fits on the first page of searchList, dbPage was not being assigned any value. Next time, make sure that the code before a GOTO sets up the destination's invariants properly! Changed CreateThreshold from 3/4 of pagesize to 1/4. (This is what I intended all along, but the code didn't agree). Tupleset scan returned the same tuple over and over; changed FOR slotI IN [scan.slotIndex .. to FOR slotI IN [scan.slotIndex + 1 .. Added USING clauses. In TryAllocTuple, when extending the TSDict, I computed the pointer to the vec BEFORE extending the vec. But extending the vec is almost guaranteed to move it... Made FreeTupleHandle check for x#NIL before calling DeallocTupleHandle. Changed DBStoragePrivateA.TuplesetFieldHandle to DBStorageField.TuplesetFieldHandle. Moved tupleset scan stuff to StorageTuplesetScanImpl, and made TSDict procs public via DBStoragePage. Made SystemTuplesetObject an opaque type. Implemented DestroyTuple. This involved a near-total rewrite of CreateTuple. Alloc list scheme is now in, and we both insert and delete from search lists. The number of modules affected was 14. Changed PageFullP to use expectedNGroups information from tupleset object. Added SystemPageFullP. Bug in PageFullP: I had computed the difference of two cardinals, forgetting that the difference might be negative! Modified PageFullP to account for space used by LStrings. Var fields can still cause page overflow, but only if they start out smaller than the lengthHint and then get larger. In PageFullP, if tuple has longer group list than expected, then use the actual rather than In MaybePutOnAllocList, can't call NEntries since that raises a signal. In DestroyTuple, keep explicit flag that page is empty of tuples, to avoid problem with LStrings that aren't going away. In DestroyTuple, was trying to put system tuple on alloc list. Converted to use new DBException. Added DBStoragePage.FreeType to SELECT on slotType in PageFullP: was generating error. Added DBStats calls for CreateTuple, CreateSystemPageTuple, DestroyTuple. Pre-Pilot changes. Bug in DestroyTuple: decided page was empty when last system tuple of a particular type was deleted. Still does not handle colocation of user tuples. Pre-Pilot changes. Conversion to new TupleHandle representation. In DestroyTuple, if pageEmpty then call to NEntries raised signal. Fix is same as for the September 22, 1980 5:14 PM bug in MaybePutOnAllocList. made Cedar, added tioga formatting Ê|˜šœ™Jšœ Ïmœ1™<—Jšœ™Jšœ#™#Jšœ#™#Jšœ)™)™$Icode™$—˜šÏk ˜ Jšœžœ˜%Jšžœžœ ˜Jšœ ˜ J˜Jšœžœ˜+Jšœ ˜ J˜Jšœ ˜ Jšœžœ˜.Jšœžœ˜*Jšœžœ˜%Jšœžœ$˜™>J˜L˜ J˜F—Jšœ žœ žœ ˜=šžœžœžœ ž˜4Jš œ6žœžœžœžœ˜ZJ˜"Jšœ2˜2Jšžœ˜—J˜—Lšœ™šŸœž œžœžœ˜TJšœ'˜'—J™š ŸœžœžœJžœžœ˜wJšœ#™#Jšœ@˜@Jšœ™šžœAžœ žœž˜[Jšžœžœž˜)Jšžœ˜—JšœžœI˜^J˜J˜—š Ÿœžœžœ-žœ žœ˜dJšœ žœ žœžœ ˜'šžœ9žœžœž˜MJšžœžœžœ'˜8Jšžœ˜—J˜JšœBžœ˜HJ˜šžœ žœ˜JšœK™KJšœ3˜3Jšœ0˜0šžœ9žœžœž˜MJšœ1˜1Jšœ8˜8J˜#Jšžœžœžœ%˜6Jšž˜—šžœžœž˜Jš œ1žœ žœžœ žœ˜d——J˜—Lšœ™šŸ œžœžœ žœXžœ;žœžœžœ˜æJšœ|˜|Jšœ$˜$J˜J˜—š Ÿœžœžœ žœ žœžœ˜ƒJšœ žœžœžœ'˜\šžœžœžœ ž˜4Jšœ>˜>Jšž˜—J˜—š Ÿ œžœžœžœžœ˜IJ˜Jš œ žœžœžœ;žœ˜YJšœ žœ˜Jšœžœžœ˜&Jšœ žœžœžœ>˜WJ˜^J˜J˜ J˜šžœÏc˜Jšžœ žœžœ˜Dšžœ!žœ˜)Jšœ1™1J˜WJšœ& ˜A˜GJšœ˜—Jšœ+™+J˜B˜Jšœ*™*J˜[J˜/J˜=J˜=J˜/Jšœ#™#—J˜J˜ Jšœžœ˜—Jšœ ˜Jšœ?™?J˜J˜?Jšœ/˜/šžœ!ž˜'Jšœ9™9˜ZJ˜——Jšžœ žœ žœžœ˜>šžœ žœ žœ˜4Jšœ:™:JšœX™XJšœžœžœžœ9˜PJšžœžœžœ˜BJ˜LJ˜D—Jšœ ˜J˜ Jšžœ žœžœ˜—Jšžœ˜J˜JšœX™XJšœ ™ JšžœX˜^Jšœ  ˜—J˜šŸ œžœ žœžœžœ"žœžœžœžœžœžœ˜—šœ<™J˜%JšœH˜H˜J˜I—Jšžœ žœžœ˜Jšžœ˜—šž˜šœ ž˜JšœV™VJšœ ™ ˜J˜V—J˜ Jšžœ ˜—Jšžœ ˜ —Jšžœ˜Jšœ ˜—J˜šŸœžœ  žœžœžœžœ žœžœ˜zšœÕ™ÕJ˜—Jšœ žœ žœ˜&Jšœžœžœžœ˜-Jšœ,˜,JšœT˜Tšžœ žœ žœ,ž˜LJšžœ˜—Jšœ žœ<˜Mšœžœ ž˜&Jšœ"˜"Jšœ'˜'Jšžœžœ˜—J˜EJšœX˜XJšœ ˜—J˜šŸ œžœžœžœžœ@žœžœ˜ŠJš žœ  œžœ  œžœžœ˜@šœÒ™ÒJ˜—Jšœ žœ˜Jšœžœ˜šž ˜šž ˜!Jšœ žœ˜Jšœ žœ˜J˜'Jšžœ žœ*žœžœ ˜Mšžœ7žœ˜TJš œžœ žœžœžœ˜;Jšœ#žœžœ ˜5—Jšœ™J˜&šžœ žœ˜šœE˜EJšžœžœ˜'—Jšžœ žœ žœžœ˜;šžœ;˜CJšžœžœžœ#˜2J˜5——Jšœ ˜—Jšžœ ˜ šž ˜ Jšœ žœ˜Jšœ žœžœžœ˜2˜JšœO˜O—Jšžœ žœ žœžœ˜;Jšœ žœ(˜;JšœK˜Kšžœžœžœ ˜:J˜˜PJ˜——Jšžœ ˜Jšœ1˜1Jšžœ ˜ —Jšžœ ˜—šž˜Jšœ žœ žœ˜#Jšœ žœžœ˜,—Jšžœ˜Jšœ ˜—J˜šŸœžœžœžœžœžœžœžœžœžœžœ˜rJšœÍ™ÍJšœ6™6Jšœ žœžœžœ1˜KJšœ žœ˜)šžœžœžœž˜'Jšžœ(žœžœ ˜?—šž˜Jšœžœžœ˜"Jšžœžœ žœ˜&—Jšžœ˜Jšœ  ˜—J˜J˜šŸœžœžœ žœžœžœžœžœžœ˜ešœ<™žœ˜]JšœO™OJšœW™WJšœ ˜ šžœžœ˜Jšœ žœžœžœ˜7J˜`J˜%šžœž˜J˜'J˜'—Jšžœžœ˜—šžœ˜Jšœ žœžœžœ˜/Jšœ žœžœžœ˜7Jšœ1žœ˜6Jšœ-˜-J˜(šžœž˜J˜J˜—Jšžœžœ˜Jšœ ˜—J˜ Jšœ ˜—J˜šŸœžœ@žœ˜dJšœa™aJšœ™JšœÒ™ÒJšœ žœžœžœ˜7Jšœ˜J˜_šžœ%žœ˜-J˜O—šžœ˜J˜%Jšœ žœžœžœ˜2Jšœžœžœžœ˜-šž˜Jšœ  ,˜Lšžœžœžœžœ˜KJšœ2™2—Jšœ5žœ˜:Jšœ0˜0J˜+šžœ#žœ˜+J˜LJšžœ˜—Jšžœ˜ —Jšž˜Jšœ ˜—Jšœ  ˜=Jšœ ˜—J˜J˜š Ÿœžœžœžœžœ;žœ˜‰Jšœ†™†Jšœžœžœžœ9˜Pšžœž˜#šœ;žœ˜AJšœ žœžœžœ˜7Jšœ˜šœ #˜%J˜šžœ7ž˜=Jšžœ '˜E—J˜>J˜^Jšœ#™#—J˜J˜MJ˜BJ˜—Jšœ ˜—Jšœ ˜—J˜J˜šŸœžœžœ˜0Jšœ‹™‹Jšœ"™"Jšœ ˜—J˜J˜—Jšžœ ˜J˜Jšœ™J˜Jšœ.ž˜0J˜Jšœ.ž˜0JšœC™CJ˜J˜$Jšœ6™6J˜Jšœ)ž˜+JšœS™SJ˜Jšœ)ž˜+Jšœ[™[Jšœ"™"J˜Jšœ)ž˜+JšœW™WJšœC™CJ˜Jšœ+ž˜-JšœS™SJšœQ™QJ˜J˜$JšœP™PJšœS™SJšœ5™5J˜Jšœ+ž˜-JšœQ™QJšœ"™"J˜Jšœ+ž˜-Jšœ\™\Jšœ&™&J˜Jšœ+ž˜-Jšœ™J˜Jšœ+ž˜-JšœU™UJšœL™LJ˜Jšœ*ž˜,JšœG™GJ˜Jšœ(ž˜*JšœT™TJ˜Jšœ)ž˜+JšœV™VJšœ™J˜Jšœ*ž˜,Jšœ)™)J˜Jšœ*ž˜,JšœY™YJšœY™YJšœ™J˜Jšœ)ž˜+Jšœb™bJ˜Jšœ*ž˜,JšœU™UJšœ™J˜Jšœ+ž˜-JšœZ™ZJšœU™UJ˜Jšœ,ž˜.Jšœ[™[J˜J˜Jšœ.ž˜0JšœY™YJšœ[™[Jšœ ™ J˜Jšœ.ž˜0Jšœ>™>J˜Jšœ.ž˜0Jšœ!™!J˜Jšœ,žœ˜3JšœV™VJ˜Jšœ-ž˜/JšœI™IJ˜Jšœ-ž˜/Jšœ™J˜Jšœ*ž˜,JšœW™WJšœ>™>Jšœ™J˜J˜'Jšœ-™-J˜J˜&JšœV™VJšœ;™;J˜J˜*Jšœ"™"J˜—…—Vø™ð