-- File: DBStorageFieldImpl.mesa
-- Last edited by MBrown on 18-Jun-81 14:40:28
-- Last Edited by: Willie-Sue, January 10, 1983 2:50 pm
-- Last Edited by: Cattell, January 16, 1983 11:41 am


  DIRECTORY
    DBCache USING[CacheHandle],
    DBCommon USING[DBPage, NullDBPage, WordsPerPage],
    DBEnvironment USING[InternalError, Fatal],
    DBHeapStorage USING[Node --GetTheMDSZone--],
    DBSegment USING[SegmentIDFromDBPage, UnlockPage, WriteLockedPage],
    DBStorage USING[FieldDescriptor, FieldHandle, TuplesetHandle,
      FieldObjectSize, TuplesetObjectSize, IndexObjectSize,
      SystemTupleID, MaxSystemTupleID],
    DBStorageConcrete USING[SystemTuplesetObject, FieldObject, FieldType],
    DBStorageExtTuple USING[SizeOfNullTuple],
    DBStorageField USING[],
    DBStoragePrivate USING[GetNWordBase],
    DBStorageTID USING[TID, DecomposeTID],
    DBStorageTupleset USING[TuplesetObject],
    DBStorageTuple USING[TIDOfTuple],
    SafeStorage USING [GetSystemZone];

DBStorageFieldImpl: PROGRAM
  IMPORTS
    DBEnvironment,
    DBHeapStorage,
    DBSegment,
    DBStoragePrivate,
    DBStorageTID,
    DBStorageTuple,
    SafeStorage
  EXPORTS
    DBStorage,
    DBStorageField
  = BEGIN OPEN DBCommon, DBStorageTID;

  --  This module exports the following procedures of DBStorage:
  -- CreateTupleset, CreateSystemTupleset, DestroyTupleset, AddField, AddSystemField, DeleteField.
  --  This module exports all of DBStorageField.

  --  PROBLEMS, THINGS YET TO BE DONE:
  -- postponed: DestroyTupleset, DeleteField.
  -- AddField will only work on an empty tupleset; otherwise it SIGNALs BadOperation.


  -- Types exported to DBStorage

  FieldObject: PUBLIC TYPE = DBStorageConcrete.FieldObject;
  FieldHandle: TYPE = POINTER TO FieldObject;
  ListofFieldHandle: TYPE = DESCRIPTOR FOR ARRAY OF FieldHandle;

  SystemTuplesetObject: PUBLIC TYPE = DBStorageConcrete.SystemTuplesetObject;
  SystemTuplesetHandle: TYPE = REF SystemTuplesetObject;

--  z: MDSZone = DBHeapStorage.GetTheMDSZone[];
  systemTuplesetObjectZone: ZONE = SafeStorage.GetSystemZone[];

  -- Procs exported to DBStorageField

  CopyFieldObject: PUBLIC PROC[fh: FieldHandle] RETURNS[FieldHandle] = {
    result: FieldHandle ← Alloc[];
    result↑ ← fh↑;
    RETURN[result];
  };--CopyFieldObject

  FreeFieldObject: PUBLIC PROC[fh: FieldHandle] = {
    IF fh # NIL THEN Free[fh];
  };--FreeFieldObject

  tuplesetFieldHandle: FieldHandle;
  indexFieldHandle: FieldHandle;

  TuplesetFieldHandle: PUBLIC PROC RETURNS[FieldHandle] =  {
    RETURN[tuplesetFieldHandle]
  };--TuplesetFieldHandle

  IndexFieldHandle: PUBLIC PROC RETURNS[FieldHandle] =  {
    RETURN[indexFieldHandle]
  };--IndexFieldHandle

  CheckFieldHandleType: PUBLIC PROC[fh: FieldHandle, t: DBStorageConcrete.FieldType] = {
    -- Generates an ERROR if fh's fieldType is not t.
    IF fh.fieldType # t THEN
      ERROR DBEnvironment.InternalError; -- [Unknown];
  };--CheckFieldHandleType

  FieldOffset: PUBLIC PROC[fh: FieldHandle] RETURNS[CARDINAL] = {
    RETURN[fh.offset];
  };--FieldOffset

  NWordsInField: PUBLIC PROC[fh: FieldHandle] RETURNS[CARDINAL] = {
    RETURN[fh.nWords];
  };--NWordsInField

  GroupIDOfField: PUBLIC PROC[fh: FieldHandle] RETURNS[TID] = {
    IF fh.fieldType # Group THEN
      ERROR DBEnvironment.InternalError; -- [Unknown];
    RETURN[fh.groupID];
  };--GroupIDOfField


  -- grubby details of FieldObject storage management.  LOOPHOLEs below assume that a FieldObject
  --is large enough to hold a FieldHandle.

  freeFieldObjectList: FieldHandle ← NIL;

  Alloc: PROC[] RETURNS[FieldHandle] = --INLINE-- {
    result: FieldHandle;
    IF freeFieldObjectList#NIL THEN BEGIN
      result ← freeFieldObjectList;
      freeFieldObjectList ← LOOPHOLE[freeFieldObjectList,POINTER TO FieldHandle]↑;
      RETURN[result];  END
    ELSE
--      RETURN[z.NEW[FieldObject]];
		RETURN[DBHeapStorage.Node[SIZE[FieldObject]]];
  };--Alloc

  Free: PROC[f: FieldHandle] = INLINE {
      LOOPHOLE[f,POINTER TO FieldHandle]↑ ← freeFieldObjectList;  freeFieldObjectList ← f;
  };--Free

  -- Procs exported to DBStorage

  CreateTupleset: PUBLIC PROC[x: DBStorage.TuplesetHandle, expectedNGroups: CARDINAL] = {
    -- Creates a new tupleset whose tuples have no fields.  The parameter  x  is an
    --existing tuple whose first-created field is an NWord field, TuplesetObjectLength
    --words long.  Information describing the new tupleset is placed in this field, and
    --tuples in the new tupleset are placed in the same segment as tuple x .  Tuple x is
    --returned by a ReadTuplesetID call on any tuple in this tupleset.
    -- Note that the TuplesetHandle is used primarily by CreateTuple, but is also
    --modified by AddField/DeleteField.

    tsObjPtr: LONG POINTER TO DBStorageTupleset.TuplesetObject;
    cacheHint: DBCache.CacheHandle;
    dbPage: DBPage;
    [dbPage,] ← DecomposeTID[DBStorageTuple.TIDOfTuple[x]];
    [tsObjPtr, cacheHint] ← DBStoragePrivate.GetNWordBase[x, TuplesetFieldHandle[]];
    DBSegment.WriteLockedPage[cacheHint];
    tsObjPtr↑ ← [
      wordsForTupleFields: 0,
      nVarFields: 0,
      expectedNGroups: expectedNGroups,
      searchList: [tuplesetID: DBStorageTuple.TIDOfTuple[x],
                   next: NullDBPage, prev: NullDBPage],
      pageAllocator: [segmentID: DBSegment.SegmentIDFromDBPage[dbPage]] ];
    DBSegment.UnlockPage[cacheHint];
  };--CreateTupleset


  CreateSystemTupleset: PUBLIC PROC[x: DBStorage.SystemTupleID]
   RETURNS[SystemTuplesetHandle] = {
    --  Creates a new system tupleset (system tuplesets do not live in a single segment
    --and the pages containing their tuples are not chained together by the storage
    --level).
    IF x NOT IN [1..DBStorage.MaxSystemTupleID] THEN
      ERROR DBEnvironment.InternalError; -- [Unknown];
    RETURN[systemTuplesetObjectZone.NEW[SystemTuplesetObject ← [
      tuplesetID: x, wordsForTupleFields: 0]]];
  };--CreateSystemTupleset


  DestroyTupleset: PUBLIC PROC[x: DBStorage.TuplesetHandle] = {
    --  In effect this procedure performs DestroyTuple on every tuple of the set
    --described by x, and undoes whatever was done by the CreateTupleset call that
    --created the tupleset. Any further user of TuplesetHandle x is an error.
    --(DestroyTupleset has no effect on system tuplesets).
    ERROR;
  };--DestroyTupleset

  MaxWordsPerTuple: CARDINAL = 3*(DBCommon.WordsPerPage/4);
    -- If AddField would make a tuple type with more than this many words per tuple, BadOperation is
    --raised.


  MakeFieldHandle: PROC[y: DBStorage.FieldDescriptor, wordsForTupleFields: CARDINAL]
   RETURNS[--fh-- FieldHandle, --newWordsInTuple-- CARDINAL] = {
    -- Creates a new field handle, for a tupleset whose tuples contain wordsInTuple words, in fh
    --based on the description of the field in y.  Returns the new number of words in a tuple.
    --If this number of words is uncomfortably large (see MaxWordsPerTuple above), raise
    --BadOperation.
    -- Called from: AddField, AddSystemField, module init code.

    fh: FieldHandle ← Alloc[];
    fieldInfo: RECORD[code: DBStorageConcrete.FieldType, length: CARDINAL] ←
     WITH fd: y SELECT FROM
      OneWord => [OneWord, 1],
      TwoWord => [TwoWord, 2],
      NWord =>   [NWord,   fd.length],
      VarWord => [VarWord, 1 + fd.lengthHint],
      VarByte => [VarByte, 1 + (fd.lengthHint+1)/2],
      Group =>   [Group,   3*SIZE[TID]],
    ENDCASE => ERROR DBEnvironment.InternalError; -- [Unknown];
    fh.fieldType ← fieldInfo.code;
    fh.offset ← DBStorageExtTuple.SizeOfNullTuple + wordsForTupleFields;
    fh.nWords ← fieldInfo.length;
    WITH fd: y SELECT FROM
      Group =>
        fh.groupID ← DBStorageTuple.TIDOfTuple[fd.groupID]; 
    ENDCASE => fh.fill1 ← 0;
    IF wordsForTupleFields + fieldInfo.length > MaxWordsPerTuple THEN
      ERROR DBEnvironment.Fatal[TupleTooLong];
    RETURN[fh, wordsForTupleFields + fieldInfo.length];
  };--MakeFieldHandle

  AddField: PUBLIC PROC[x: DBStorage.TuplesetHandle, y: DBStorage.FieldDescriptor]
   RETURNS[FieldHandle] = {
    --  In effect this procedure modifies each tuple of set x, adding a new field of
    --type y. It returns a FieldHandle for the new field.

    fh: FieldHandle;
    tsObjPtr: LONG POINTER TO DBStorageTupleset.TuplesetObject;
    cacheHint: DBCache.CacheHandle;
    [tsObjPtr, cacheHint] ← DBStoragePrivate.GetNWordBase[x, TuplesetFieldHandle[]];
    -- For now, die if user tries to add a field after a tuple has been created.
    IF tsObjPtr.allocList#NullDBPage OR tsObjPtr.searchList.next#NullDBPage
      THEN ERROR DBEnvironment.InternalError; -- [AddFieldToExistingTupleset];
    DBSegment.WriteLockedPage[cacheHint];
    [fh, tsObjPtr.wordsForTupleFields] ← MakeFieldHandle[y, tsObjPtr.wordsForTupleFields];
    SELECT fh.fieldType FROM
      VarWord, VarByte => {tsObjPtr.nVarFields ← tsObjPtr.nVarFields + 1; };
    ENDCASE => {};
    DBSegment.UnlockPage[cacheHint];
    RETURN[fh];
  };--AddField


  AddSystemField: PUBLIC PROC[x: SystemTuplesetHandle, y: DBStorage.FieldDescriptor]
   RETURNS[FieldHandle] = {
    -- This procedure adds to x the length of y. It returns a FieldHandle for the new
    --field.
    fh: FieldHandle;
    [fh, x.wordsForTupleFields] ← MakeFieldHandle[y, x.wordsForTupleFields];
    RETURN[fh];
  };--AddSystemField


  DeleteField: PUBLIC PROC
   [x: DBStorage.TuplesetHandle, z: ListofFieldHandle, i: INTEGER]
   RETURNS[ListofFieldHandle] = {
    --  In effect this procedure modifies each tuple of set x, deleting the i-th field
    --in list z.  Parameter z describes all of the fields of tuples in x, and the result
    --gives new handles for the undeleted fields, in the same order (handles will change
    --due to deletion).
    --  Note: will the field handles be presented in order of creation?  this would
    --avoid a sort.  (This procedure will NOT be implemented in the first system).  
    ERROR;
  };--DeleteField

  -- mainline code (start trap on first TuplesetFieldHandle[] or IndexFieldHandle[] will do this
  --required initialization)

  IF DBStorage.FieldObjectSize < SIZE[DBStorageConcrete.FieldObject] OR
     DBStorage.TuplesetObjectSize < SIZE[DBStorageTupleset.TuplesetObject] THEN
    ERROR DBEnvironment.InternalError; -- [Unknown];
  [tuplesetFieldHandle,] ← MakeFieldHandle[[NWord[DBStorage.TuplesetObjectSize]], 0];
  [indexFieldHandle,] ← MakeFieldHandle[[NWord[DBStorage.IndexObjectSize]], 0];

END.--DBStorageFieldImpl


CHANGE LOG

Created by MBrown on 2-Feb-80 16:07
-- AllocVec, CompactPage, and related inlines {~1 to paper-sketch AllocVec, 3:15 to
--code, :10 to compile}.

Changed by MBrown on February 3, 1980  3:07 PM
-- CreateTupleset, CreateSystemTupleset, AddField, AddSystemField, FreeTupleHandle.

Changed by MBrown on February 10, 1980  11:41 PM
-- Read1Word, Read2Word, ReadNWord (except overflow page reads).

Changed by MBrown on February 16, 1980  12:15 AM
-- Major restructuring of storage impl.  Moved everything but tupleset creation/field
--creation out of this module.

Changed by MBrown on February 16, 1980  9:11 PM
-- Moved heap storage allocation to DBStorageHeap.

Changed by MBrown on February 16, 1980  11:04 PM
-- Invented MakeFieldHandle to reduce some duplication.

Changed by MBrown on 17-Feb-80 13:59
-- Eliminated use of @ from CreateTupleset.  Added checks for inconsistent sizes of
--objects, making the "true" size be the one in DBStorage.  (This can be larger than
--the actual size with no ill effect, but smaller is a no-no).  Changed length to
--wordsPerTuple in SystemTuplesetObject.

Changed by MBrown on February 25, 1980  7:45 PM
-- Added procs exported to DBStoragePrivateA (TuplesetFieldHandle, IndexFieldHandle).
--Made tupleset creation and field creation consistent with DBStoragePrivateB's TupleBody
--structure.

Changed by MBrown on April 9, 1980  10:11 AM
-- Made CreateTupleset convert the TSID to a DBPage before converting THAT to a segmentID.

Changed by MBrown on April 17, 1980  11:02 PM
-- Changed SystemTuplesetID to SystemTupleID.

Changed by MBrown on June 8, 1980  9:25 PM
-- Scattered minor changes in introducing FieldObject as an opaque type.  The module now contains
--only 2 LOOPHOLEs.  Changed MakeFieldHandle to do storage allocation for the field handle it
--produces.

Changed by MBrown on June 8, 1980  10:19 PM
-- Compiler insists on an exported version of DBStorage.ListofFieldHandle.

Changed by MBrown on June 10, 1980  8:39 PM
-- Renamed to be StorageFieldImpl from StorageImplA.

Changed by MBrown on June 24, 1980  11:26 AM
-- Made SystemTuplesetObject be opaque in DBStorage.

Changed by MBrown on July 31, 1980  3:11 PM
-- Added expectedNGroups to CreateTupleset.

Changed by MBrown on August 13, 1980  11:59 AM
-- DBStoragePrivateB.SizeOfNullTuple became DBStorageExtTuple.SizeOfNullTuple.

Changed by MBrown on August 25, 1980  1:37 PM
-- Added nVarFields to TuplesetObject.

Changed by MBrown on September 26, 1980  12:28 PM
-- Converted to new DBException.

Changed by MBrown on February 27, 1981  3:58 PM
-- Use zone for storage allocation.

Changed by MBrown on 18-Jun-81 14:47:11
-- Use counted zone for allocation of system tupleset objects.