home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 22 gnu / 22-gnu.zip / db02_src.zip / dbobj.cc < prev    next >
C/C++ Source or Header  |  1993-11-05  |  26KB  |  1,023 lines

  1. /**************************************************************************
  2.  * Source Id :
  3.  *
  4.  * $Id: dbobj.cc,v 1.42 1993/11/04 14:59:41 kevinl Exp $
  5.  *-------------------------------------------------------------------------
  6.  * Project Notes :
  7.  *
  8.  *  Diamond Base
  9.  *  ============
  10.  *      A solid database implementation, spurred on by the continuing
  11.  *  Metal (Lead) Base saga.
  12.  *
  13.  *  Project Team :
  14.  *        A. Davison
  15.  *        K. Lentin
  16.  *        D. Platt
  17.  *
  18.  *    Project Commenced : 05-02-1993
  19.  *
  20.  *-------------------------------------------------------------------------
  21.  *  Module Notes :
  22.  *
  23.  *        dbObject    : Maintains information for a single database relation.
  24.  *
  25.  *
  26.  *  Original Author : Andy
  27.  *
  28.  *-------------------------------------------------------------------------
  29.  * Revision History:
  30.  *
  31.  * $Log: dbobj.cc,v $
  32. // Revision 1.42  1993/11/04  14:59:41  kevinl
  33. // Fixed record locking
  34. //
  35. // Revision 1.41  1993/11/03  10:04:46  kevinl
  36. // Added ichar and utils.{h,cc}
  37. //
  38. // Revision 1.40  1993/10/28  07:49:01  kevinl
  39. // Added diaGRel dbString/dbData support
  40. //
  41. // Revision 1.39  1993/10/18  11:36:57  kevinl
  42. // Added field names and setRecNum calls
  43. //
  44. // Revision 1.38  1993/10/07  13:22:17  darrenp
  45. // removed debugging info.
  46. //
  47. // Revision 1.37  1993/10/05  07:31:07  kevinl
  48. // Added in generation of info for dbObjData for generic relations (diaGRel)
  49. //
  50. // Revision 1.36  1993/09/26  06:40:07  kevinl
  51. // Added dbData support
  52. //
  53. // Revision 1.35  1993/08/12  05:31:50  darrenp
  54. // Zeroed theMemServer pointer, in case it doesn't get initialised
  55. //
  56. // Revision 1.34  1993/07/19  11:52:51  kevinl
  57. // Removed an extraneous return
  58. //
  59. // Revision 1.33  1993/07/13  04:45:25  kevinl
  60. // Added getData, putData, startData and endData for MULTI
  61. //
  62. // Revision 1.32  1993/07/01  13:26:30  kevinl
  63. // We now delete Strings
  64. // Pointers to uniques no longer returned. {has,get,set}Unqiue members
  65. //
  66. // Revision 1.31  1993/06/23  05:21:22  kevinl
  67. // Mallocs are now in angular brackets
  68. //
  69. // Revision 1.30  1993/06/20  13:42:44  kevinl
  70. // Fixed multiple mallocs
  71. // String support added
  72. //
  73. // Revision 1.29  1993/05/26  01:01:39  kevinl
  74. // MALLOC_H_MISSING
  75. //
  76. // Revision 1.28  1993/05/11  14:44:50  kevinl
  77. // Added version number output
  78. //
  79. // Revision 1.27  1993/05/06  04:00:19  kevinl
  80. // SASC define for malloc.h
  81. //
  82. // Revision 1.26  1993/05/03  23:34:09  kevinl
  83. // Removed err shadowing local var
  84. //
  85. // Revision 1.25  1993/05/03  01:34:48  kevinl
  86. // No more extern consts
  87. // Cosmetic changes for CC
  88. //
  89. // Revision 1.24  1993/05/01  14:37:59  kevinl
  90. // Got rid of ints
  91. //
  92. // Revision 1.23  1993/04/28  14:57:37  kevinl
  93. // Give records back when add fails
  94. //
  95. // Revision 1.22  1993/04/28  11:30:36  kevinl
  96. // write now checks for locks
  97. //
  98. // Revision 1.21  1993/04/27  07:15:24  kevinl
  99. // Added qWrite
  100. // Fixed qId bounds checks on all query functions
  101. //
  102. // Revision 1.20  1993/04/25  13:20:00  kevinl
  103. // Fixed up error code returns
  104. // Commetns
  105. //
  106. // Revision 1.19  1993/04/15  07:58:55  kevinl
  107. // Added del
  108. //
  109. // Revision 1.18  1993/04/15  04:49:23  kevinl
  110. // Fixed up include files
  111. //
  112. // Revision 1.17  1993/04/11  05:49:02  kevinl
  113. // Rationalised find/seek/peek methods etc
  114. //
  115. // Revision 1.16  1993/04/09  13:00:29  kevinl
  116. // Stats can be called from diaRel now.
  117. //
  118. // Revision 1.15  1993/04/08  15:40:00  kevinl
  119. // MAde sure unique fields are not touched when they don't exist
  120. //
  121. // Revision 1.14  1993/04/07  02:29:21  kevinl
  122. // Changed qEnd to reset qId to -1
  123. //
  124. // Revision 1.13  1993/04/06  05:48:23  kevinl
  125. // Range check in qEnd
  126. //
  127. // Revision 1.12  1993/04/05  07:50:21  kevinl
  128. // Fixed the rereading in add
  129. //
  130. // Revision 1.11  1993/04/04  23:58:11  kevinl
  131. // Handle unique fields
  132. // Reget record during add
  133. //
  134. // Revision 1.10  1993/04/01  11:28:40  kevinl
  135. // Range check on qBegin index field
  136. //
  137. // Revision 1.9  1993/04/01  11:22:03  kevinl
  138. // Set unique member to record number (till further notice)
  139. //
  140. // Revision 1.8  1993/04/01  04:23:18  kevinl
  141. // Fixed locking
  142. // Added qFind, fixed the rest
  143. //
  144. // Revision 1.7  1993/03/30  07:14:47  davison
  145. // Add initialisation of the usageCount.
  146. //
  147. // Revision 1.6  1993/03/29  08:05:47  darrenp
  148. // Nuked pathinfo
  149. // Fixed eof errors
  150. // Malloc lib
  151. //
  152. // Revision 1.5  1993/03/28  13:58:45  kevinl
  153. // Modified add to check for duplicates first.
  154. //
  155. // Revision 1.4  1993/03/28  10:32:33  davison
  156. // Modified for dbErr
  157. // ./
  158. //
  159. // Revision 1.3  1993/03/28  04:53:59  root
  160. // more error code standardization.
  161. //
  162. // Revision 1.2  1993/03/26  06:15:57  darrenp
  163. // standardised error codes.
  164. //
  165. // Revision 1.1  1993/03/25  22:29:25  davison
  166. // Initial revision
  167. //
  168.  **************************************************************************/
  169.  
  170. #include <stdio.h>
  171.  
  172. #if !defined(MALLOC_H_MISSING) && !defined(MALLOC_H_INCLUDED)
  173. extern "C" {
  174. #include <malloc.h>
  175. }
  176. #define MALLOC_H_INCLUDED
  177. #endif
  178.  
  179. #include <dbobj.h>
  180. #include <btree.h>
  181. #include <d_types.h>
  182.  
  183. char* dbObject::verStr(void)
  184. {
  185.     return "$Id: dbobj.cc,v 1.42 1993/11/04 14:59:41 kevinl Exp $";
  186. }
  187.  
  188. // Constructor. Creates a new dbObject
  189.  
  190. dbObject::dbObject(char* newpath, char* newprefix)
  191. {
  192.     int    dataOffset = 0;
  193.     theMemServer = 0;
  194.     long i,j,k;
  195.     
  196.     // Give recserver the database filename.
  197.     path=new char[strlen(newpath)+3];
  198.     prefix=new char[strlen(newprefix)+1];
  199.  
  200.     strcpy(path,newpath);
  201.     if(path[strlen(path)-1] != '/')
  202.          strcat(path,"/");
  203.  
  204.     strcpy(prefix,newprefix);
  205.  
  206.     char* dbName = new char[strlen(path) + strlen(prefix) + 5];
  207.     strcpy(dbName,path);
  208.     strcat(dbName,prefix);
  209.     strcat(dbName,".db");
  210.  
  211.     // Ok. Now lets extract the field and index info for future reference.
  212.     
  213. #    ifdef __BORLANDC__
  214.         ifstream dbFile(dbName,ios::binary);
  215. #    else
  216.         ifstream dbFile(dbName);
  217. #    endif
  218.  
  219.     checkStream(dbFile);    
  220.     dbFile.read((char*)&dataOffset,sizeof(int));
  221.     checkStream(dbFile);
  222.  
  223.     dbFile >> fldList >> idxList;
  224.     checkStream(dbFile);
  225.  
  226.     // Now create a recServer.
  227.  
  228.     theRecServer = new recServer(dbName,dataOffset);
  229.  
  230.     // Do we need a string server. Don't have an object to ask, so 
  231.     // scan the fields
  232.  
  233.     stringCount = 0;
  234.     theMemServer = 0;
  235.     fieldInfo *f = fldList.fields;
  236.     while (f)
  237.     {
  238.         if (f->fldType == D_STRING || f->fldType == D_DATA)
  239.         {
  240.             if (!theMemServer)
  241.             {
  242.                 char* memName = new char[strlen(path) + strlen(prefix) + 5];
  243.                 sprintf(memName,"%s%s.str", path, prefix);
  244.                 theMemServer = new memServer(memName);
  245.                 delete memName;
  246.             }
  247.             stringCount++;
  248.         }
  249.         f = f->nextFld;
  250.     }
  251.     
  252.     // And the list of bTrees.
  253.  
  254.     theBTreeList = new bTree *[idxList.numIndicies];
  255.     for(i=0;i<idxList.numIndicies;i++)
  256.     {
  257.         char* idxFile = new char[strlen(path) + strlen(prefix) + 5];
  258.         sprintf(idxFile,"%s%s.id%d",path,prefix,i);
  259.  
  260.         // Load the bTree for index i
  261.         theBTreeList[i] = new bTree(idxFile,i);
  262. //        theBTreeList[i]->dump();
  263. //        currentRecs[i] = NO_REC;
  264.         delete idxFile;
  265.     }
  266.  
  267.     // Finally clear out the query information;
  268.  
  269.     for(i=0;i<MAX_QUERY;i++)
  270.         queryInfo[i] = 0;
  271.     
  272.     // Nobody is using us
  273.     usageCount=0;
  274.     delete dbName;
  275.  
  276.     // OK, now reorganise all this into a form that can be used to
  277.     // understand the relation shape.
  278.  
  279.     // Now - construct the following tables:
  280.  
  281.     numIndices        =idxList.numIndicies;    // How many indices are there ?
  282.     numFields        =fldList.numFields;    // How many fields.
  283.  
  284.     fieldLength        = new long[numFields];    // How long each field is.
  285.     fieldOffset        = new long[numFields];    // Where each field is.
  286.     containsUniq    = new long[numIndices];    // If the index contains a uniq.
  287.     keyLength        = new long[numIndices];    // Length of the keys.
  288.     fieldType        = new char[numFields];    // What type each field is.
  289.     fieldName        = new char*[numFields];    // What they are called.
  290.  
  291.     indexedOnP        // Which fields are in each index. - packing order
  292.                        = (long (*)[MFII])new long[MFII * numIndices];
  293.  
  294.     indexedOnC        // Which fields are in each index. - comparison
  295.                        = (long (*)[MFII])new long[MFII * numIndices];
  296.  
  297.     // Memory ist allocated - now fill out the tables. This is
  298.     // considerably complicated by the fact that dsc reorders the
  299.     // fields to bypass alignment problems. Non char fields come
  300.     // out first, and then the char fields.
  301.  
  302.     // Iterate through the fields - on this pass, calculate the
  303.     // offsets of the non-char fields, the size of every field
  304.     // the offsets of the char fields relative to the start of the
  305.     // char section, and the field types.
  306.     // i is the field number,
  307.     // j is the offset into the non-strings.
  308.     // k is the offset into the strings.
  309.  
  310.     fieldInfo    *fPtr;
  311.     i = j = k = 0;
  312.     if (stringCount)
  313.     {
  314.         theLongs = new long[stringCount];
  315.         stringField = new long[stringCount];
  316.     }
  317.     else
  318.     {
  319.         theLongs = 0;
  320.         stringField = 0;
  321.     }
  322.     int s = 0;
  323.     for(fPtr=fldList.fields;fPtr;fPtr= fPtr->nextFld,i++) {
  324.         fieldLength[i] = fPtr->fldSize;
  325.  
  326.         // Strings get shuffled to the end of the fields
  327.         // as far as storage and structure layout goes.
  328.  
  329.         if (fPtr->fldType == D_CHAR || fPtr->fldType == D_ICHAR) {
  330.             fieldOffset[i] = k;
  331.             k+=fPtr->fldSize;
  332.         } else {
  333.             fieldOffset[i] = j;
  334.             j+=fPtr->fldSize;
  335.         }
  336.         if (fPtr->fldType == D_STRING || fPtr->fldType == D_DATA)
  337.         {
  338.             stringField[s] = i;
  339.             theLongs[s] = fieldOffset[i];
  340.             s++;
  341.         }
  342.         fieldType[i] = fPtr->fldType;
  343.         fieldName[i] = strdup(fPtr->fldName);
  344.     }
  345.     dataLength = j+k; // Cross fingers that compiler works this way.
  346.  
  347.     uniqOff = -1; // May be changed below.
  348.  
  349.     // Ok - now we have found out most of what we wanted to
  350.     // know - now  move the string stuff to the end.
  351.     for(i=0;i<numFields;i++) {
  352.         if (fieldType[i] == D_CHAR || fieldType[i] == D_ICHAR) {
  353.             fieldOffset[i] += j;
  354.         }
  355.         if (fieldType[i] == D_UNIQUE) {
  356.             // Icky pooh - set the unique field to -1.
  357.             // We don't do this anymore. It's in diagrel
  358.             // *(long *)((char *)theData+fieldOffset[i]) = -1;
  359.  
  360.             // Remember the offset of the unique field for
  361.             // future reference.
  362.             // uniqOffset = (long *)((char *)theData+fieldOffset[i]);
  363.             uniqOff = fieldOffset[i];
  364.         }
  365.     }
  366.  
  367.     // Traverse the index list to construct quick reference
  368.     // tables to do comparisons quickly.
  369.     // Calculate keyLength, containsUniq,
  370.     //
  371.     indexInfo    *iPtr;
  372.     i = 0;
  373.     for(iPtr = idxList.indicies;iPtr;iPtr=iPtr->nextIdx,i++) {
  374.         memcpy(indexedOnC[i],iPtr->idxFields,
  375.             MAX_FIELDS_IN_INDEX * sizeof(long));
  376.  
  377.         // Find out if this index contains a unique field.
  378.         containsUniq[i] = false;
  379.         // Tally key length
  380.         keyLength[i] = 0;
  381.  
  382.         // During this loop, also copy all the non-string indexed
  383.         // fields into the packing indexedOn array.
  384.         k = 0;
  385.         for(j=0;j<MAX_FIELDS_IN_INDEX;j++) {
  386.             if (iPtr->idxFields[j]==-1) break;
  387.             if (fieldType[iPtr->idxFields[j]]==D_UNIQUE) {
  388.                 containsUniq[i] = true;
  389.             }
  390.             keyLength[i] += fieldLength[iPtr->idxFields[j]];
  391.             if (fieldType[iPtr->idxFields[j]]!=D_CHAR || fieldType[iPtr->idxFields[j]]!=D_ICHAR) {
  392.                 indexedOnP[i][k++] = indexedOnC[i][j];
  393.             }
  394.         }
  395.         // Now finish copying across all the CHAR fields which
  396.         // need to migrate to the end of the packed key.
  397.         for(j=0;j<MAX_FIELDS_IN_INDEX;j++) {
  398.             if (iPtr->idxFields[j]==-1) break;
  399.             if (fieldType[iPtr->idxFields[j]]==D_CHAR || fieldType[iPtr->idxFields[j]]==D_ICHAR) {
  400.                 indexedOnP[i][k++] = indexedOnC[i][j];
  401.             }
  402.         }
  403.         // Put the -1 marker on the end.
  404.         if (j<MAX_FIELDS_IN_INDEX) {
  405.             indexedOnP[i][j] = -1;
  406.         }
  407.     }
  408. }
  409.  
  410. // A very neat, memory friendly destructor
  411.  
  412. dbObject::~dbObject()
  413. {
  414.     for(int i=0;i<idxList.numIndicies;i++)
  415.         delete theBTreeList[i];
  416.  
  417.     delete theBTreeList;
  418.     delete theRecServer;
  419.     delete theMemServer;
  420.     delete path;
  421.     delete prefix;
  422. }
  423.  
  424. // This is a multipurpose function.
  425. // It sets the record number in the current query, unlocks the current
  426. // query and then checks to see if any other queries already have this record
  427. // locked.
  428. dbError dbObject::checkRec(long qId, long recIdx, bool lockit)
  429. {
  430.     queryInfo[qId]->queryRec = recIdx;
  431.     queryInfo[qId]->unlock();
  432.  
  433.     for (int i=0; i < MAX_QUERY; i++)
  434.         if (queryInfo[i] && queryInfo[i]->locked() && queryInfo[i]->queryRec == recIdx)
  435.             return dbErr(db_reclocked);
  436.  
  437.     // It should be ok now to lock it.
  438.  
  439.     if (lockit)
  440.         queryInfo[qId]->lock();
  441.     else
  442.         queryInfo[qId]->unlock();
  443.  
  444.     return dbErr(db_ok);
  445. }
  446.  
  447. // Output the cache stats for the recServer and each bTree
  448.  
  449. void dbObject::stats(void)
  450. {
  451.     cout << "Record Server cache: ";
  452.     theRecServer->stats();
  453.  
  454.     for(int i=0;i<idxList.numIndicies;i++)
  455.     {
  456.         cout << "Index " << i << " cache: ";
  457.         theBTreeList[i]->stats();
  458.     }
  459. }
  460.  
  461. // add a new object into the dbObject. This verifies that the object is not
  462. // present in any bTree and if so proceeds to add it in.
  463. // We do NOT allow duplicates. Unique fields can be used to circumvent
  464. // this
  465.  
  466. dbError dbObject::add(object& theObject)
  467. {
  468.     long    newIdx;
  469.     bool    unq;
  470.     //long    uniq;
  471.     dbError err;
  472.  
  473.     unq = theObject.hasUnique();
  474.  
  475. #if 0
  476.     // We used to remember the unique id and set it to -1 to prevent bogus
  477.     // clashes in the inBTree calls. Now we don't do this anymore since we
  478.     // don't inBTree indexes with unique fields in them
  479.     if (unq)
  480.     {
  481.         uniq = *unq;
  482.         *unq = -1;
  483.     }
  484. #endif
  485.  
  486.     // Let's check the btrees to make sure it's not there already.
  487.     // If an index has a unique field, don't check it. This makes things
  488.     // much quicker (depending on the object concerned). It can't hurt.
  489.  
  490.     for(long i=0;i<idxList.numIndicies;i++)
  491.         if (!theObject.isUnique(i))
  492.             if (theBTreeList[i]->inBTree(theObject))
  493.             {
  494. #if 0
  495.                 // We don't save this anymore
  496.                 if (unq)
  497.                     *unq = uniq;
  498. #endif
  499.                 return dbErr(db_dup);
  500.             }
  501.  
  502.     // Ok. First get the recserver to insert the data into the database.
  503.  
  504.     err= theRecServer->newRec(newIdx);//Get the logical index of the new record.
  505.     if (err != db_ok)
  506.         return err;
  507.  
  508.     // If this object has a unique field, stick in the new value
  509.     if (unq)
  510.         theObject.setUnique(newIdx);
  511.  
  512.     // We must add to the memServer first so the long members are
  513.     // set correctly.
  514.  
  515.     if (theMemServer)
  516.     {
  517. #ifdef DEBUG
  518.         cout << "Adding to memServer now" << endl;
  519. #endif
  520.         for (int i=0; i < theObject.numStrings(); i++)
  521.         {
  522.             *theObject.getArray()[i].theLong=0;
  523.             dbError err = theMemServer->putString(*theObject.getArray()[i].theString, *theObject.getArray()[i].theLong);
  524.             if (err != db_ok)
  525.                 return err;
  526.         }
  527.     }
  528.  
  529.     // Where is the data to save?
  530.     theObject.getData();
  531.     void* thevoid = (void*)theObject;
  532.  
  533.     // Save it.
  534.     err= theRecServer->putRec(newIdx,thevoid);
  535.     theObject.setRecNum(newIdx);
  536.     theObject.endData();
  537.     if (err != db_ok)
  538.         return err;
  539.  
  540. #ifdef DEBUG
  541.     cout << "Adding to BTrees now..." << endl;
  542. #endif
  543.  
  544.     // Then tell all the indicies to install this object.
  545.  
  546.     for(i=0;i<idxList.numIndicies;i++)
  547.     {
  548.         //pKeyType fred = theObject.getKey(i);
  549.         //cout << *((long*)fred) << endl;
  550.         //delete fred;
  551.  
  552.         err = theBTreeList[i]->add(theObject,newIdx);
  553.         if (err != db_ok)
  554.         {
  555.             // Give the recServer its record back
  556.             theRecServer->delRec(newIdx);
  557.             return err;
  558.         }
  559.         // Since the btree insertion code can nuke the indexes,
  560.         // we reread the data
  561.         // Forget it, bTree::add is nice to us now.
  562. //        theRecServer->getRec(newIdx,thevoid);
  563.     }
  564.  
  565.     return dbErr(db_ok);
  566. }
  567.  
  568. // Now we can delete! We first check all the bTree's and if it's in ALL of
  569. // them, we delete the object from all of them!
  570.  
  571. dbError dbObject::del(object& theObject)
  572. {
  573.     long    delIdx;
  574.     dbError err;
  575.  
  576.     // First make sure that no query has this record locked
  577.  
  578.     // Let's check the btrees to make sure it's in all of them
  579.  
  580.     for(int i=0;i<idxList.numIndicies;i++)
  581.         if (!theBTreeList[i]->inBTree(theObject, delIdx))
  582.             return dbErr(db_nfound);
  583.         else if (!i)
  584.         {
  585.             // It is in the first BTree. Now let's see if anyone has it locked
  586.             for (int j=0; j < MAX_QUERY; j++)
  587.                 if (queryInfo[j] && queryInfo[j]->locked() && queryInfo[j]->queryRec == delIdx)
  588.                     return dbErr(db_reclocked);
  589.         }
  590.  
  591.     // First get rid of the strings
  592.  
  593.     if (theMemServer)
  594.     {
  595. #ifdef DEBUG
  596.         cout << "Nuking from memServer now" << endl;
  597. #endif
  598.         for (int i=0; i < theObject.numStrings(); i++)
  599.         {
  600.             dbError err = theMemServer->delString(*theObject.getArray()[i].theString, *theObject.getArray()[i].theLong);
  601.             if (err != db_ok)
  602.                 return err;
  603.         }
  604.     }
  605.  
  606.     // Ok. Now get the recserver to nuke the data from the database.
  607.  
  608.     err= theRecServer->delRec(delIdx);
  609.     if (err != db_ok)
  610.         return err;
  611.  
  612.     // Then tell all the indicies to delete this object.
  613.  
  614.     for(i=0;i<idxList.numIndicies;i++)
  615.     {
  616.         //pKeyType fred = theObject.getKey(i);
  617.         //cout << *((long*)fred) << endl;
  618.         //delete fred;
  619.  
  620.         err = theBTreeList[i]->del(theObject);
  621.         if (err != db_ok)
  622.             return err;
  623.     }
  624.  
  625.     return dbErr(db_ok);
  626. }
  627.  
  628. // qBegin:
  629. // Start up a query. This makes it possible to keep track of searches and
  630. // walks through the relation. The query has an index associated with it so it
  631. // knows which bTree to use for the query
  632.  
  633. dbError dbObject::qBegin(long idxId, long& qId)                               
  634. {                                                                             
  635.     if (idxId < 0 || idxId >= idxList.numIndicies)
  636.         return dbErr(db_range);
  637.  
  638.     dbError err;                                                       
  639.     // Tell the appropriate bTree to start a query
  640.     err = theBTreeList[idxId]->qBegin(qId);
  641.     switch (err)                                                              
  642.     {                                                                         
  643.         case db_ok:                                                           
  644.         {
  645.             // Find a vacant queryInfo.
  646.             for (int i=0;i<MAX_QUERY;i++)
  647.                 if (queryInfo[i] == 0) break;
  648.             if (i >= MAX_QUERY)
  649.             {
  650.                 // Nope, there is no space, end the query and get out
  651.                 theBTreeList[idxId]->qEnd(qId);
  652.                 return dbErr(db_toomany, "Creating database query");
  653.             }
  654.  
  655.             // Make a new queryInfo for the query
  656.             // qId is the number of the Btree query. Tell the dbQuery about
  657.             // it.
  658.             queryInfo[i] = new dbQueryInfo(qId, idxId);
  659.  
  660.             // Now we're finished with qId so we can set it to the dbQuery
  661.             // id.
  662.             qId = i;
  663.             return dbErr(db_ok);
  664.         }
  665.         case db_toomany:
  666.             return dbErr(db_toomany, "Creating btree query");
  667.         default:                                                              
  668.             return dbErr(db_err, "Strange error from btree");
  669.     }
  670. }                                                                             
  671.  
  672. // qSeekFirst:
  673. // This moves to the beginning of the relation according to the index
  674. // associated with the query concerned. NB after this call, the next call to
  675. // dbObject::qNext will return the first record of the relation.
  676.  
  677. dbError dbObject::qSeekFirst(long qId, object& theObject)
  678. {
  679.     long    recIdx;
  680.  
  681.     if (qId < 0 || qId > MAX_QUERY)
  682.         return dbErr(db_range);
  683.  
  684.     if (!queryInfo[qId])
  685.         return dbErr(db_noquery);
  686.  
  687. // Get the bTree to do a first
  688.     return theBTreeList[queryInfo[qId]->queryIdx]->
  689.             qFirst(queryInfo[qId]->queryId,theObject,recIdx);
  690. }
  691.  
  692. // qPeekNext:
  693. // Show us what the next call to dbObject::qNext will return without
  694. // altering the state of the relation
  695.  
  696. dbError dbObject::qPeekNext(long qId, object& theObject)
  697. {
  698.     dbError err;
  699.     long    recIdx;
  700.  
  701.     if (qId < 0 || qId > MAX_QUERY)
  702.         return dbErr(db_range);
  703.  
  704.     if (!queryInfo[qId])
  705.         return dbErr(db_noquery);
  706.  
  707.     err = theBTreeList[queryInfo[qId]->queryIdx]->
  708.             qPeekNext(queryInfo[qId]->queryId,theObject,recIdx);
  709.  
  710.     if (err == db_eof)
  711.     {
  712.         return err;
  713.     }
  714.     else if (err != db_ok)
  715.     {
  716.         return dbErr(db_err, "Fatal error from bTree::qPeekNext");
  717.     }
  718.  
  719.     // Now try to get the object from the record server... 
  720.  
  721.     theObject.startData();
  722.     theRecServer->getRec(recIdx, (void*)theObject);
  723.     theObject.setRecNum(recIdx);
  724.     theObject.putData();
  725.  
  726.     // And it's strings
  727.  
  728.     dbError err2 = getStrings(theObject);
  729.     if (err2 != db_ok)
  730.         return err2;
  731.  
  732.     // Remember the current record number and check for locks
  733.  
  734.     return checkRec(qId, recIdx);
  735. }
  736.  
  737. // qNext:
  738. // Return the next entry in the relation and advance the relation
  739. // If lock is true then we will attempt to lock the returned record
  740.  
  741. dbError dbObject::qNext(long qId, object& theObject, bool lock)
  742. {
  743.     dbError err;
  744.     long    recIdx;
  745.  
  746.     if (qId < 0 || qId > MAX_QUERY)
  747.         return dbErr(db_range);
  748.  
  749.     if (!queryInfo[qId])
  750.         return dbErr(db_noquery);
  751.  
  752.     err = theBTreeList[queryInfo[qId]->queryIdx]->
  753.             qNext(queryInfo[qId]->queryId,theObject,recIdx);
  754.  
  755.     if (err == db_eof)
  756.     {
  757.         return err;
  758.     }
  759.     else if (err != db_ok)
  760.     {
  761.         return dbErr(db_err, "Fatal error from bTree::qNext");
  762.     }
  763.  
  764.     // Now try to get the object from the record server...
  765.  
  766.     theObject.startData();
  767.     theRecServer->getRec(recIdx, (void*)theObject);
  768.     theObject.setRecNum(recIdx);
  769.     theObject.putData();
  770.  
  771.     // And it's strings
  772.  
  773.     dbError err2 = getStrings(theObject);
  774.     if (err2 != db_ok)
  775.         return err2;
  776.  
  777.     // Remember the current record number
  778.  
  779.     // bTree::qNext would return db_reclocked if
  780.     // the record was locked. Hence we can lock it
  781.     return checkRec(qId, recIdx, (bool)(err == db_ok && lock));
  782. }
  783.  
  784. // qPeekPrev:
  785. // like qPeekNext but different
  786.  
  787. dbError dbObject::qPeekPrev(long qId, object& theObject)
  788. {
  789.     dbError err;
  790.     long    recIdx;
  791.  
  792.     if (qId < 0 || qId > MAX_QUERY)
  793.         return dbErr(db_range);
  794.  
  795.     if (!queryInfo[qId])
  796.         return dbErr(db_noquery);
  797.  
  798.     err = theBTreeList[queryInfo[qId]->queryIdx]->
  799.             qPeekPrev(queryInfo[qId]->queryId,theObject,recIdx);
  800.  
  801.     if (err == db_eof)
  802.     {
  803.         return err;
  804.     }
  805.     else if (err != db_ok)
  806.     {
  807.         return dbErr(db_err, "Fatal error from bTree::qPeekPrev");
  808.     }
  809.  
  810.     // Now try to get the object from the record server... :-?
  811.  
  812.     theObject.startData();
  813.     theRecServer->getRec(recIdx, (void*)theObject);
  814.     theObject.setRecNum(recIdx);
  815.     theObject.putData();
  816.  
  817.     // And it's strings
  818.  
  819.     dbError err2 = getStrings(theObject);
  820.     if (err2 != db_ok)
  821.         return err2;
  822.  
  823.     // Remember the current record number
  824.  
  825.     return checkRec(qId, recIdx);
  826. }
  827.  
  828. // qPrev:
  829. // like qNext but different
  830.  
  831. dbError dbObject::qPrev(long qId, object& theObject, bool lock)
  832. {
  833.     dbError err;
  834.     long    recIdx;
  835.  
  836.     if (qId < 0 || qId > MAX_QUERY)
  837.         return dbErr(db_range);
  838.  
  839.     if (!queryInfo[qId])
  840.         return dbErr(db_noquery);
  841.  
  842.     err = theBTreeList[queryInfo[qId]->queryIdx]->
  843.             qPrev(queryInfo[qId]->queryId,theObject,recIdx);
  844.  
  845.     if (err == db_eof)
  846.     {
  847.         return db_eof;
  848.     }
  849.     else if (err != db_ok)
  850.     {
  851.         return dbErr(db_err, "Fatal error from bTree::qPrev");
  852.     }
  853.  
  854.     // Now try to get the object from the record server... :-?
  855.  
  856.     theObject.startData();
  857.     theRecServer->getRec(recIdx, (void*)theObject);
  858.     theObject.setRecNum(recIdx);
  859.     theObject.putData();
  860.  
  861.     // And it's strings
  862.  
  863.     dbError err2 = getStrings(theObject);
  864.     if (err2 != db_ok)
  865.         return err2;
  866.  
  867.     // Remember the current record number
  868.  
  869.     return checkRec(qId, recIdx, (bool)(err==db_ok && lock));
  870. }
  871.  
  872. // qSeekLast:
  873. // Like qSeekFirst but different
  874.  
  875. dbError dbObject::qSeekLast(long qId, object& theObject)
  876. {
  877.     long    recIdx;
  878.  
  879.     if (qId < 0 || qId > MAX_QUERY)
  880.         return dbErr(db_range);
  881.  
  882.     if (!queryInfo[qId])
  883.         return dbErr(db_noquery);
  884.  
  885.     return theBTreeList[queryInfo[qId]->queryIdx]->
  886.             qLast(queryInfo[qId]->queryId,theObject,recIdx);
  887. }
  888.  
  889. // qEnd:
  890. // We make sure the query is in use and then tell the bTree to lose it.
  891. // Then we lose our connection to it.
  892.  
  893. dbError dbObject::qEnd(long& qId)
  894. {
  895.     // Make sure the qId is a good one
  896.     if (qId < 0 || qId > MAX_QUERY)
  897.         return dbErr(db_range);
  898.     if (!queryInfo[qId])
  899.         return dbErr(db_noquery);
  900.  
  901.     // Tell the relevant bTree to end it's query
  902.     dbError err = theBTreeList[queryInfo[qId]->queryIdx]->qEnd(queryInfo[qId]->queryId);
  903.  
  904.     // Free our memory and reset the query to unused
  905.     delete queryInfo[qId];
  906.     queryInfo[qId] = 0;
  907.     qId = -1;
  908.     return err;
  909. }                      
  910.  
  911. // qSeek:
  912. // This searches the relation for a particular object. The next call to
  913. // dbObject::qNext will return this searched for object, or the one that will
  914. // follow it in the relation if it was not found
  915.  
  916. dbError dbObject::qSeek(long qId, object& theObject)
  917. {
  918.     long    recIdx;
  919.  
  920.     if (qId < 0 || qId > MAX_QUERY)
  921.         return dbErr(db_range);
  922.  
  923.     if (!queryInfo[qId])
  924.         return dbErr(db_noquery);
  925.  
  926.     return theBTreeList[queryInfo[qId]->queryIdx]->
  927.             qFind(queryInfo[qId]->queryId,theObject,recIdx);
  928. }
  929.  
  930. dbError dbObject::qWrite(long qId, object& theObject)
  931. {
  932.     // Validate the query
  933.     if (qId < 0 || qId > MAX_QUERY)
  934.         return dbErr(db_range);
  935.  
  936.     if (!queryInfo[qId])
  937.         return dbErr(db_noquery);
  938.  
  939.     // Count how many keys are already in the bTree's and make sure they
  940.     // refer to the same record number.
  941.     // NB We have to check unique fields here too, unlike in add.
  942.     long count = 0;
  943.     long recNum = -1;
  944.     long thisRec;
  945.     for (int i=0; i<idxList.numIndicies;i++)
  946.         if (theBTreeList[i]->inBTree(theObject, thisRec))
  947.         {
  948.             count++; // Got one more
  949.             if (recNum == -1)
  950.                 recNum = thisRec; // The first one
  951.             else
  952.                 // If we already have encountered this key and we do so
  953.                 // again now, but the record number is different, then the
  954.                 // write is not allowed.
  955.                 if (thisRec != recNum)
  956.                     return dbErr(db_dup);
  957.         }
  958.  
  959.     // If the record is not found at all we can't write, use add instead
  960.  
  961.     if (!count)
  962.         return dbErr(db_nfound);
  963.  
  964.     // If _all_ bTree's didn't match then we can't write
  965.  
  966.     if (count != idxList.numIndicies)
  967.         return dbErr(db_dup);
  968.  
  969.     // We are now assured that all the keys match to the same record
  970.     // number, which will be in recNum (or thisRec).
  971.  
  972.     // Let's check to see if someone has it locked....
  973.  
  974.     dbError err;
  975.     if ((err=checkRec(qId, thisRec)) != db_ok)
  976.         return err;
  977.  
  978.     // Save the strings back. Note that we leave the offsets along this
  979.     // time around as the string is assumed to be there already.
  980.  
  981.     if (theMemServer)
  982.     {
  983. #ifdef DEBUG
  984.         cout << "Writing to memServer now" << endl;
  985. #endif
  986.         for (int i=0; i < theObject.numStrings(); i++)
  987.         {
  988.             if ((err = theMemServer->putString(*theObject.getArray()[i].theString, *theObject.getArray()[i].theLong)) != db_ok)
  989.                 return err;
  990.         }
  991.     }
  992.  
  993.     // Let's tell the recServer to save the data then.
  994.  
  995.     theObject.getData();
  996.     err = theRecServer->putRec(thisRec, (void*)theObject);
  997.     theObject.endData();
  998.     return err;
  999. }
  1000.  
  1001. dbError dbObject::getStrings(object& theObject)
  1002. {
  1003.     if (theMemServer)
  1004.     {
  1005.         dbError err;
  1006. #ifdef DEBUG
  1007.         cout << "Reading from memServer now" << endl;
  1008. #endif
  1009.         for (int i=0; i < theObject.numStrings(); i++)
  1010.         {
  1011.             if ((err = theMemServer->getString(*theObject.getArray()[i].theString, *theObject.getArray()[i].theLong)) != db_ok)
  1012.             {
  1013. #ifdef DEBUG
  1014.                 cout << "memServer returned error " << err << endl;
  1015. #endif
  1016.                 return err;
  1017.             }
  1018.         }
  1019.     }
  1020.  
  1021.     return dbErr(db_ok);
  1022. }
  1023.