<> <> <> <> <> <> <> <> <> DIRECTORY DBCommon, DBSegment, DBStorage USING[FieldHandle, FirstLast, MaxSystemTupleID, TupleHandle], DBStorageField USING[ CheckFieldHandleType, GroupIDOfField], DBStorageGroup USING[GroupList, GroupListFromTuple, ReadGroupField, WriteGroupField, WriteHeadField, CreateHeadEntry, DestroyHeadEntry], DBStorageConcrete USING[GroupScanObject], DBStorageGroupScan USING[], DBStoragePrivate USING[TupleFromTID], DBStorageTuple USING[ConsTupleObject, EqualTuple, IsValidTuple, InvalidateTuple, NullTupleHandle, PrintTupleObject], IO; DBStorageGroupScanImpl: CEDAR PROGRAM IMPORTS DBCommon, DBSegment, DBStorageField, DBStorageGroup, DBStoragePrivate, DBStorageTuple, IO EXPORTS DBStorage, DBStorageGroupScan = BEGIN OPEN IO, DBStorageGroup, DBStorageTuple; TID: TYPE = DBCommon.TID; NullTID: TID = DBCommon.NullTID; TupleObject: PUBLIC TYPE = DBCommon.TupleObject; TupleHandle: TYPE = REF TupleObject; <> GroupScanObject: PUBLIC TYPE = DBStorageConcrete.GroupScanObject; GroupScanHandle: TYPE = REF GroupScanObject; <> activeGroupScanList: GroupScanHandle; <<1st item on list is a permanent header node>> nullTupleHandle: TupleHandle; Init: PUBLIC PROC = { activeGroupScanList _ NEW[GroupScanObject _ [tidFieldHandle: NIL, headOfGroup: NIL]]; tempGroupScanHandle _ NEW[GroupScanObject _ [tidFieldHandle: NIL, headOfGroup: NIL]]; nullTupleHandle _ DBStorageTuple.NullTupleHandle[]; }; ReadTID: PUBLIC PROC[x: TupleHandle, f: DBStorage.FieldHandle] RETURNS[TupleHandle] = { <> RETURN[ReadGroupField[x, f, headTID]]; };--ReadTID WriteTID: PUBLIC PROC[x: TupleHandle, r: GroupScanHandle] = { <> <<>> <> f: DBStorage.FieldHandle = r.tidFieldHandle; currentValue: TupleHandle = ReadTID[x, f]; IF currentValue # NIL AND r.headOfGroup.tid = currentValue.tid THEN RETURN; -- No-op WriteTIDNil[x, f]; WriteGroupField[x, f, headTID, r.headOfGroup]; SELECT TRUE FROM (r.prevInScan=NIL) AND (r.nextInScan=NIL) => BEGIN --x is only tuple in group CreateHeadEntry[r.headOfGroup, f]; WriteHeadField[r.headOfGroup, f, firstTID, x]; WriteHeadField[r.headOfGroup, f, lastTID, x]; WriteGroupField[x, f, prevTID, nullTupleHandle]; WriteGroupField[x, f, nextTID, nullTupleHandle]; END; (r.prevInScan=NIL) AND (r.nextInScan#NIL) => BEGIN --x is at front of group WriteHeadField[r.headOfGroup, f, firstTID, x]; WriteGroupField[x, f, prevTID, nullTupleHandle]; WriteGroupField[x, f, nextTID, r.nextInScan]; WriteGroupField[r.nextInScan, f, prevTID, x]; END; (r.prevInScan#NIL) AND (r.nextInScan=NIL) => BEGIN --x is at rear of group WriteHeadField[r.headOfGroup, f, lastTID, x]; WriteGroupField[x, f, nextTID, nullTupleHandle]; WriteGroupField[x, f, prevTID, r.prevInScan]; WriteGroupField[r.prevInScan, f, nextTID, x]; END; (r.prevInScan#NIL) AND (r.nextInScan#NIL) => BEGIN --x is in middle of group WriteGroupField[x, f, nextTID, r.nextInScan]; WriteGroupField[r.nextInScan, f, prevTID, x]; WriteGroupField[x, f, prevTID, r.prevInScan]; WriteGroupField[r.prevInScan, f, nextTID, x]; END; ENDCASE => ERROR; BEGIN --fix up active scans that are in identical position to scan (others are unaffected) FOR p: GroupScanHandle _ activeGroupScanList.link, p.link UNTIL p = NIL DO IF (DBStorageField.GroupIDOfField[p.tidFieldHandle] # DBStorageField.GroupIDOfField[r.tidFieldHandle]) OR (~EqualTuple[p.headOfGroup,r.headOfGroup]) OR (~EqualTuple[p.prevInScan,r.prevInScan]) THEN LOOP; <

> p.prevInScan _ x; -- no need for explicit free here anymore ... ENDLOOP; END; };--WriteTID WriteTIDNil: PUBLIC PROC[x: TupleHandle, f: DBStorage.FieldHandle] = { <> head, prev, next: TupleHandle; head _ ReadGroupField[x, f, headTID]; IF head = NIL THEN RETURN; prev _ ReadGroupField[x, f, prevTID]; next _ ReadGroupField[x, f, nextTID]; WriteGroupField[x, f, headTID, nullTupleHandle]; WriteGroupField[x, f, prevTID, nullTupleHandle]; --redundant WriteGroupField[x, f, nextTID, nullTupleHandle]; --redundant SELECT TRUE FROM (prev=NIL) AND (next=NIL) => BEGIN --x was only tuple in group DestroyHeadEntry[head, f]; END; (prev=NIL) AND (next#NIL) => BEGIN --x was first tuple in group WriteGroupField[next, f, prevTID, nullTupleHandle]; WriteHeadField[head, f, firstTID, next]; END; (prev#NIL) AND (next=NIL) => BEGIN --x was last tuple in group WriteGroupField[prev, f, nextTID, nullTupleHandle]; WriteHeadField[head, f, lastTID, prev]; END; (prev#NIL) AND (next#NIL) => BEGIN --x was in middle of group WriteGroupField[prev, f, nextTID, next]; WriteGroupField[next, f, prevTID, prev]; END; ENDCASE => ERROR; <> BEGIN --fix up active scans that contain x (others are unaffected) FOR p: GroupScanHandle _ activeGroupScanList.link, p.link UNTIL p = NIL DO IF DBStorageField.GroupIDOfField[p.tidFieldHandle] # DBStorageField.GroupIDOfField[f] THEN LOOP; IF EqualTuple[p.prevInScan,x] THEN p.prevInScan _ prev; IF EqualTuple[p.nextInScan,x] THEN p.nextInScan _ next; ENDLOOP; END; <> };--WriteTIDNil tempGroupScanHandle: GroupScanHandle; SetTupleField: PUBLIC PROC[x: TupleHandle, f: DBStorage.FieldHandle, val: TupleHandle] = { DBStorageField.CheckFieldHandleType[f, Group]; tempGroupScanHandle.tidFieldHandle _ f; tempGroupScanHandle.headOfGroup _ val; tempGroupScanHandle.prevInScan _ tempGroupScanHandle.nextInScan _ NIL; SetGroupHandle[tempGroupScanHandle, Last]; WriteTID[x, tempGroupScanHandle] }; TupleForField: PUBLIC PROC[x: TupleHandle, f: DBStorage.FieldHandle] RETURNS[handle: TupleHandle] = { DBStorageField.CheckFieldHandleType[f, Group]; tempGroupScanHandle.tidFieldHandle _ f; tempGroupScanHandle.headOfGroup _ x; tempGroupScanHandle.prevInScan _ tempGroupScanHandle.nextInScan _ NIL; SetGroupHandle[tempGroupScanHandle, First]; handle _ NextInGroup[tempGroupScanHandle] }; <<>> GetGroupIDs: PUBLIC PROC[x: TupleHandle] RETURNS[LIST OF TupleHandle] = TRUSTED { <> groupList: LONG DESCRIPTOR FOR DBStorageGroup.GroupList; cacheHint: DBCommon.CacheHandle; result: LIST OF TupleHandle; [groupList,cacheHint] _ GroupListFromTuple[x]; IF LENGTH[groupList] = 0 THEN result _ NIL ELSE { newCons, lastCons: LIST OF TupleHandle; FOR i: CARDINAL IN [0..LENGTH[groupList]) DO newCons _ LIST[ IF groupList[i].groupID <= DBStorage.MaxSystemTupleID THEN DBStoragePrivate.TupleFromTID[groupList[i].groupID] ELSE DBStorageTuple.ConsTupleObject[tid: groupList[i].groupID, cacheHint: NIL]]; <> IF i = 0 THEN result _ newCons ELSE lastCons.rest _ newCons; lastCons _ newCons; ENDLOOP; };--IF DBSegment.UnlockPage[cacheHint]; RETURN[result]; };--GetGroupIDs SetGroupHandle: PROC[handle: GroupScanHandle, start: DBStorage.FirstLast] = TRUSTED { head: TupleHandle = handle.headOfGroup; field: DBStorage.FieldHandle = handle.tidFieldHandle; groupList: LONG DESCRIPTOR FOR DBStorageGroup.GroupList; cacheHint: DBCommon.CacheHandle; [groupList, cacheHint] _ DBStorageGroup.GroupListFromTuple[head]; IF LENGTH[groupList] # 0 THEN BEGIN FOR i: CARDINAL IN [0..LENGTH[groupList]) DO IF groupList[i].groupID = DBStorageField.GroupIDOfField[field] THEN GOTO FoundIt; REPEAT FoundIt => SELECT start FROM First => handle.nextInScan _ DBStorageTuple.ConsTupleObject[groupList[i].firstTID, NIL]; Last => handle.prevInScan _ DBStorageTuple.ConsTupleObject[groupList[i].lastTID, NIL]; ENDCASE => ERROR; FINISHED => NULL; -- this is ok, the scan is just empty ENDLOOP; END;--IF DBSegment.UnlockPage[cacheHint]; }; OpenScanGroup: PUBLIC PROC[x: TupleHandle, f: DBStorage.FieldHandle, start: DBStorage.FirstLast] RETURNS [result: GroupScanHandle] = TRUSTED { <> DBStorageField.CheckFieldHandleType[f, Group]; result _ Alloc[f, x]; result.link _ activeGroupScanList.link; activeGroupScanList.link _ result; SetGroupHandle[result, start] };--OpenScanGroup Alloc: PROC[f: DBStorage.FieldHandle, x: TupleHandle] RETURNS [GroupScanHandle] = INLINE { RETURN[NEW[GroupScanObject _ [ tidFieldHandle: f, headOfGroup: x, prevInScan: NIL, nextInScan: NIL, link: NIL]]] }; NextInGroup: PUBLIC PROC[r: GroupScanHandle] RETURNS[TupleHandle] = { <> result: TupleHandle _ NIL; IF r=NIL THEN RETURN[NIL]; -- convenience feature IF r.tidFieldHandle = NIL THEN ERROR DBCommon.InternalError; -- InvalidGroupScan IF r.nextInScan#NIL THEN { r.prevInScan _ result _ r.nextInScan; r.nextInScan _ DBStorageGroup.ReadGroupField[result, r.tidFieldHandle, nextTID]; };--IF RETURN[result]; };--NextInGroup PrevInGroup: PUBLIC PROC[r: GroupScanHandle] RETURNS[TupleHandle] = { <> <> <> result: TupleHandle _ NIL; IF r=NIL THEN RETURN[NIL]; -- convenience feature IF r.tidFieldHandle = NIL THEN ERROR DBCommon.InternalError; -- InvalidGroupScan IF r.prevInScan # NIL THEN { r.nextInScan _ result _ r.prevInScan; r.prevInScan _ DBStorageGroup.ReadGroupField[result, r.tidFieldHandle, prevTID]; };--IF RETURN[result]; };--PrevInGroup CloseScanGroup: PUBLIC PROC[r: GroupScanHandle] = { FOR scanPred: GroupScanHandle _ activeGroupScanList, scanPred.link UNTIL scanPred=NIL DO IF scanPred.link=r THEN { scanPred.link _ r.link; r.link _ NIL; FreeContents[r]; RETURN; }; REPEAT FINISHED => ERROR DBCommon.InternalError; -- [GroupScanNotFound]; ENDLOOP; };--CloseScanGroup CallAfterFinishTransaction: PUBLIC PROC [] = { rPrev: GroupScanHandle _ activeGroupScanList; UNTIL rPrev.link = NIL DO r: GroupScanHandle _ rPrev.link; IF NOT DBStorageTuple.IsValidTuple[r.headOfGroup] THEN { DBStorageTuple.InvalidateTuple[r.prevInScan]; DBStorageTuple.InvalidateTuple[r.nextInScan]; FreeContents[r]; rPrev.link _ r.link; r.link _ NIL; } ELSE rPrev _ r; ENDLOOP; }; FreeContents: PROC[r: GroupScanHandle] = { <> r.tidFieldHandle _ NIL; r.headOfGroup _ r.prevInScan _ r.nextInScan _ NIL; };--FreeContents CheckState: PUBLIC PROC[doPrinting: BOOLEAN] = { <> activeGroupScanListLen: CARDINAL _ 0; FOR r: GroupScanHandle _ activeGroupScanList.link, r.link UNTIL r=NIL DO activeGroupScanListLen _ activeGroupScanListLen + 1; DBStorageField.CheckFieldHandleType[r.tidFieldHandle, Group]; IF r.headOfGroup = NIL THEN ERROR; ENDLOOP; IF doPrinting THEN { DBCommon.GetDebugStream[].PutF["list of active group scans contains %d elements*n", card[activeGroupScanListLen]]; FOR r: GroupScanHandle _ activeGroupScanList, r.link UNTIL r=NIL DO PrintGroupScanObject[r]; ENDLOOP; DBCommon.GetDebugStream[].PutF["*n"]; };--IF };--CheckState PrintGroupScanObject: PROC[scan: GroupScanHandle] = TRUSTED { pscan: POINTER TO GroupScanHandle = @scan; debugStream: IO.STREAM_ DBCommon.GetDebugStream[]; debugStream.PutF["groupScanHdl: %12bB, tidFieldHdl: ", card[LOOPHOLE[pscan, CARDINAL]]]; <> debugStream.PutF["*n headOfGroup: "]; DBStorageTuple.PrintTupleObject[scan.headOfGroup]; debugStream.PutF["*n prevInScan: "]; DBStorageTuple.PrintTupleObject[scan.prevInScan]; debugStream.PutF["*n nextInScan: "]; DBStorageTuple.PrintTupleObject[scan.nextInScan]; debugStream.PutF["*n"]; };--PrintGroupScanObject END.--DBStorageGroupScanImpl CHANGE LOG Created by MBrown on June 17, 1980 1:00 PM <> Changed by MBrown on June 20, 1980 4:19 PM <> Changed by MBrown on July 23, 1980 10:10 PM <> Changed by MBrown on August 4, 1980 11:50 PM <> Changed by MBrown on August 20, 1980 9:15 PM <> Changed by MBrown on September 12, 1980 2:13 PM <> Changed by MBrown on September 14, 1980 11:27 PM <> <> Changed by MBrown on September 26, 1980 12:33 PM <> Changed by MBrown on November 12, 1980 4:09 PM <> <> <> Changed by MBrown on December 11, 1980 2:26 PM <> <> <> Changed by MBrown on December 20, 1980 6:04 PM <> Changed by MBrown on February 27, 1981 3:58 PM <> Changed by MBrown on 9-Jun-81 18:13:50 <> Changed by Willie-Sue on June 24, 1982 12:19 pm < IO>> Changed by Willie-Sue on June 30, 1982 4:49 pm < DBCommon.GetDebugStream[]>> Changed by MBrown on November 30, 1982 10:07 pm <> <<>> Changed by Willie-Sue on February 15, 1985 <>