-- 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 18, 1984 11:13 am


  DIRECTORY
    DBCache USING[CacheHandle],
    DBCommon USING[DBPage, NullDBPage, WordsPerPage],
    DBEnvironment USING[Error, InternalError, Failure],
    DBSegment USING[SegmentIDFromDBPage, UnlockPage, WriteLockedPage],
    DBStorage,
    DBStorageConcrete USING[SystemTuplesetObject, FieldObject, FieldType],
    DBStorageExtTuple USING[SizeOfNullTuple],
    DBStorageField USING[],
    DBStoragePrivate USING[GetNWordBase],
    DBStorageTID USING[TID, DecomposeTID],
    DBStorageTupleset USING[TuplesetObject],
    DBStorageTuple USING[TIDOfTuple];

DBStorageFieldImpl: PROGRAM
  IMPORTS
    DBEnvironment,
    DBSegment,
    DBStoragePrivate,
    DBStorageTID,
    DBStorageTuple
  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 = REF FieldObject;
  ListofFieldHandle: TYPE = LIST OF FieldHandle;

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

  -- Procs exported to DBStorageField

  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



  -- 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[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


  CreateFieldHandle: PUBLIC PROC RETURNS [FieldHandle] =
    -- Since the FieldObject type is opaque, this procedure is needed to allocate a FieldHandle,
    -- to be passed to ReadNWord when retrieving a FieldHandle stored in the data schema.
    {RETURN[NEW[DBStorageConcrete.FieldObject]]};



  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 ← NEW[DBStorageConcrete.FieldObject];
    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.Failure[$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.Error[NotImplemented]; -- Adding field to existing relation!
    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.

Changed by Cattell on November 4, 1983 2:21 am
-- Removed tuple object zone, just do vanilla NEWs for Cedar 5.0