-- File: DBIndexPageImpl.mesa
-- Last edited by:
--   Suzuki:  December 16, 1980  5:15 PM
--   MBrown:  May 23, 1983 10:58 am
--   Cattell:   September 21, 1982 9:27 pm

  DIRECTORY
    DBCache,
    DBCommon,
    DBSegment USING[AllocPage, FreePage, ReadPage, UnlockPage, WriteLockedPage],
    DBStoragePagetags USING [BTree],
    DBIndex,
    DBIndexPage;

DBIndexPageImpl: PROGRAM
  IMPORTS
    DBSegment
  EXPORTS
    DBIndexPage
  = BEGIN
    
  Core: TYPE = DBIndex.Core;
  Page: TYPE = DBIndex.Page;
  PageObject: TYPE = DBIndex.PageObject;
  RealIndexHandle: TYPE = DBIndex.RealIndexHandle;

  -- global vars

  PageList: Page;
  TakeALookAtThis: SIGNAL = CODE;
  
  BadPage: PUBLIC SIGNAL = CODE;

  -- Public procs

  DestroyPageList: PUBLIC PROC =
    BEGIN
    p: Page;
    p ← PageList;
    DO
      IF NOT p.free THEN UnlockPage[p]; -- unlock page
      p.tree← NIL; p.cache← NIL; p.db← 0; p.pointer← NIL;  p.depth← 0; -- erase invalid data
      p ← p.front;
      IF p=PageList THEN EXIT;
      ENDLOOP;
    END;

  DestroyPage: PUBLIC PROC [
    segment: DBCommon.DBPage, p: Page, db: DBCommon.DBPage] =
    -- Deallocates the Page and DBPage; it should have a lock count of 1
    BEGIN
    DBSegment.FreePage[segment, db, p.cache];
    p.free ← TRUE;
    END;

  UnlockPage: PUBLIC PROC [p: Page] =
    BEGIN 
      CheckTag[p.pointer];
      DBSegment.UnlockPage[p.cache];  
      p.free ← TRUE;
    END;

  WriteAndUnlockPage: PUBLIC PROC [p: Page] =
    BEGIN 
      CheckTag[p.pointer];
      DBSegment.WriteLockedPage[p.cache];
      DBSegment.UnlockPage[p.cache];  
      p.free ← TRUE;
    END;
  
  WritePage: PUBLIC PROC [p: DBIndex.Page] =
    -- Mark page p as having been written, but keep locked.
    BEGIN 
      CheckTag[p.pointer];
      DBSegment.WriteLockedPage[p.cache];
    END;
    
  AllocatePage:  PROC RETURNS [Page] =
    -- Just allocates the DBIndex.Page, i.e. the HANDLE for the DBCommon.Page
    -- returns a "free" page; caller is responsible for marking it not free.
    BEGIN
    ret: Page;
    p: Page ← PageList;
    UNTIL p.free = TRUE DO
      p ← p.front;
      IF p=PageList THEN GOTO NoFreePage;
    REPEAT
      NoFreePage => {ret ← NEW[PageObject];
        PageList.front.back ← ret;
        ret↑ ← [NIL, DBCommon.NullDBPage, NIL, 0, NIL, TRUE, PageList.front, PageList];
        PageList.front ← ret;
        RETURN[ret]};
      FINISHED => {RETURN[p]}
    ENDLOOP;
    END;

  CreateEmptyPage: PUBLIC PROC [
    tree: RealIndexHandle, level: CARDINAL, s: DBCommon.DBPage]
    RETURNS [Page] =
    -- level = 1 if it is a leaf
    BEGIN
    cache: DBCache.CacheHandle;
    db: DBCommon.DBPage;
    h: Page;
    p: LONG POINTER TO Core;
    [db, cache, p] ← DBSegment.AllocPage[s];
    p.tag.pageTag ← DBStoragePagetags.BTree;
    p.left ← p.right ← DBCommon.NullDBPage;
    p.size ← 0;
    h ← AllocatePage[];
    h.tree ← tree;  h.db ← db;  h.cache ← cache;  h.depth ← level;  h.pointer ← p;
    h.free ← FALSE;
    RETURN[h];
    END;

  GetPage: PUBLIC PROC [
    tree: RealIndexHandle, db: DBCommon.DBPage, level: CARDINAL] RETURNS [Page] =
    BEGIN
    p: Page ← AllocatePage[];
    cache: DBCache.CacheHandle;
    pointer: LONG POINTER TO Core;
    [cache, pointer] ← DBSegment.ReadPage[db, p.cache];
    CheckTag[pointer];
    p.tree ← tree;  p.db ← db;  p.cache ← cache;  p.depth ← level;  p.pointer ← pointer;
    p.free ← FALSE;
    RETURN[p]
    END;

  CheckTag: PUBLIC PROC[p: LONG POINTER TO Core] = {
    IF p.tag.pageTag#DBStoragePagetags.BTree THEN SIGNAL BadPage};

-- Main part

  PageList ← NEW[PageObject];
  PageList.front ← PageList.back ← PageList;
  PageList.free ← TRUE;

  END.

Change Log:

Added to check page tag in CreateOldPage
	by Suzuki November 24, 1980  9:06 AM

Changed FreePage.  Now Unlocks the page
	by Suzuki November 24, 1980  1:33 PM

Changed AllocatePage.  Now initializes the page if it was obtained via AllocateHeapNode
	by MBrown December 13, 1980  12:41 AM

Allocate storage from heap storage mdsZone
	by MBrown February 27, 1981  6:18 PM

Convert to Cedar
	by Cattell 7-Jun-81 11:24:59

Fixed bug in DestroyPageList: it didn't deallocate the PageObjects, and later code was trying to treat the cache hints to non-existent storage as real!
	by Cattell June 24, 1982 11:18 am
	
Changed PageObjects from POINTERs to REFs.
	by Cattell August 6, 1982 5:10 pm
	
Eliminated import of heap storage allocator.
	by MBrown August 7, 1982 9:38 pm

Added BadPage signal.
	by Cattell August 25, 1982 5:21 pm
	
The following invariant of the "page" data structure was getting messed up: NOT p.free iff DBSegment.LockCount[p.db, p.cache] > 0.  The problem was that p.free was assigned FALSE, then a DBSegment call was made that can raise transaction aborted, then p.cache was assigned.  Fix is to have AllocatePage return a page with p.free = TRUE, then make DBSegment call.
	by MBrown May 23, 1983 10:58 am