Main Page | Class List | File List | Class Members

/home/alex/STDB/stdb/src/stdb/stdb.cpp

00001 /***************************************************************************
00002                           stdb.cpp  -  Simple Tansactional Data Base
00003                              -------------------
00004     begin                :  16/mar/2004
00005     copyright            :  (C) 2004 by Andreu Moreno
00006     email                :  amoreno@euss.es
00007                             alexbc@auna.com
00008  ***************************************************************************/
00009 
00010 /***************************************************************************
00011  *                                                                         *
00012  *  This library is free software; you can redistribute it and/or
00013  *  modify it under the terms of the GNU Lesser General Public
00014  *  License as published by the Free Software Foundation; either
00015  *  version 2.1 of the License, or (at your option) any later version.
00016 
00017  *  This library is distributed in the hope that it will be useful,
00018  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00019  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00020  *  Lesser General Public License for more details.
00021 
00022  *  You should have received a copy of the GNU Lesser General Public
00023  *  License along with this library; if not, write to the Free Software
00024  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
00025  *                                                                         *
00026  ***************************************************************************/
00027 
00028 /*
00029  *
00030  * Features:
00031  *
00032  *  - Database (stored into DB file)
00033  *
00034  *  - Transaction support (transactions logged into LOG file)
00035  *
00036  *  - Operation:
00037  *
00038  *      -> Initially, database is created and contained in memory (faster)
00039  *      -> DB file: (cdb formatted) Contains a read only database
00040  *      -> LOG file:(rdbm log format) Adds the write feature to DB file
00041  *      -> Periodically the DB file is updated using the LOG file (Reorganize)
00042  * 
00043  * $Author: amoreno $
00044  * $Revision: 1.4 $
00045  * $Log: stdb.cpp,v $
00046  * Revision 1.4  2005/05/10 10:59:52  amoreno
00047  * Added comments, doxygen formatted and bug cleaning
00048  *
00049  * Revision 1.3  2004/06/14 16:25:34  amoreno
00050  * Remove cdb dependece
00051  *
00052  * Revision 1.2  2004/03/31 14:53:33  amoreno
00053  * stdb.cpp cleaning up
00054  *
00055  * Revision 1.1.1.1  2004/03/23 10:13:10  amoreno
00056  * Initial import.
00057  *
00058  *
00059  */
00060 
00061 #include <stdb.h>
00062 
00063 
00065 //  Constructor  /
00067 
00068 Tstdb::Tstdb(const char *TCDBName, const char *TCDBLogName, const int ParMaxLogSize, const int ParMaxTime){
00069     
00070     //  MaxLongLog
00071     MaxLogSize = ParMaxLogSize;
00072     
00073     //  MaxTime
00074     MaxTime = ParMaxTime * 24 * 60 * 60; //In seconds
00075     
00076     //  Save the path files
00077     db=strdup(TCDBName);
00078     log=strdup(TCDBLogName);
00079     
00080     //  Transfer database information from files to memory
00081     Initialize();
00082 }
00083 
00084 
00086 //Initialize
00088 
00089 void Tstdb::Initialize(void){
00090     
00091     //  To truncate the log file if it founds format errors
00092     int ReadBytesTran = 0;
00093     
00094     char dbtmp[100];
00095     sprintf(dbtmp, "%s.tmp", db); 
00096 
00097     //  Checking if TMP exists, because it means something went wrong
00098     //  during last Reorganize. If it exists, it renames it from TMP
00099     //  to DB
00100     ifstream fdtmp(dbtmp, ios::in);
00101     if(fdtmp)
00102         rename(dbtmp,db);
00103     fdtmp.close();
00104     
00105     //  Opening DB file in read-only mode. Now we're going to transfer
00106     //  DB entries saved into the file to a new DBMemory object.    
00107     
00108     ifstream cdbIn(db , ios::in); 
00109     if(!cdbIn){
00110         //  If DB file doesn't exist
00111         OpenCDBStatus=CDBNOFILE;
00112     }
00113     else{
00114         //  DB file do exist    
00115         OpenCDBStatus=CDBOK;
00116         
00117         //  Loops DB file to fill the map object
00118         //  It reads lines from DB file while we don't reach the end of file.
00119         string buffer;
00120         while (!cdbIn.eof()){
00121             bool final;
00122             TAction action;
00123             if (!Getcdbline(cdbIn, action, final)){             
00124                 //  Format error encountered, file corrupted
00125                 OpenCDBStatus=CDBCORRUPTION;    
00126                 break;
00127             }
00128             if(!final){
00129                 //  Introduce (key,data) into map object
00130                 DBMemory.insert(TValue(action.GetKey(),action.GetData()));
00131             }
00132         }
00133     }
00134     
00135     //  Let's transfer the log file into memory map
00136     ifstream logIn(log, ios::in);
00137     if (!logIn){
00138         //  LOG file doesn't exist
00139         OpenLogStatus=LogNOFILE;    
00140     }
00141     else{   
00142         //  LOG file do exist
00143         ReadBytes=0;    
00144         //  Loop LOG entries/lines to fill map object
00145         string buffer;
00146         while (!logIn.eof()){
00147             //  Read line
00148             bool commit, final;
00149             int size;
00150             TAction action;
00151             
00152             if (!Getlogline(logIn, action, commit, size, final)){
00153                 //  Format error
00154                 OpenLogStatus=LogCORRUPTION;
00155                 break;
00156             }
00157             if(final) continue;
00158             if(commit){
00159                 //  End of transaction
00160                 ReadBytes+=ReadBytesTran+2;
00161                 ReadBytesTran = 0;
00162                 //  Pop all Actions from stack
00163                 while(!RollBack.empty()){
00164                     RollBack.pop();
00165                 }
00166             }
00167             else{
00168                 //  Not the end of transaction
00169                 ReadBytesTran+=size;
00170                 
00171                 //  Search (key,data) into memory map
00172                 TDBMemory::iterator p = DBMemory.find(action.GetKey());
00173 
00174                 //  Difference between DELETE, UPDATE and INSERT and others
00175                 switch(action.GetFunction()){
00176                     case DELETE:                    
00177                         //  DELETE case
00178                         if (p != DBMemory.end()){
00179                             //  Found
00180                             //  Update the rollback with update
00181                             TAction action2(INSERT, action.GetKey(), p->second);
00182                             RollBack.push(action2);
00183                             //  Erase (key,data) from map object
00184                             DBMemory.erase(action.GetKey());
00185                         }
00186                         else{
00187                             //  Not found
00188                             //  Nothing to do here
00189                         }
00190                         break;
00191                     
00192                     case UPDATE:
00193                     case INSERT:
00194                         //  Distinguish between INSERT and UPDATE
00195                         if (p != DBMemory.end()){
00196                             //  Found
00197                             //  UPDATE case
00198                             //  Update the rollback with update
00199                             TAction action2(UPDATE, action.GetKey(), action.GetData());
00200                             RollBack.push(action2);
00201                             //  Update (key,data) into map object
00202                             DBMemory[action.GetKey()] = action.GetData();
00203                         }
00204                         else{
00205                             //  Not found
00206                             //  INSERT case
00207                             //  Update the rollback with delete
00208                             TAction action2(DELETE, action.GetKey(), "");
00209                             RollBack.push(action2);
00210                             //  Introduce (key,data) into map object
00211                             DBMemory.insert(TValue(action.GetKey(),action.GetData()));
00212                         }
00213                         break;
00214                 }
00215             }
00216         }
00217         //  If EOF and rollback stack is not empty -> LOG file corrupted
00218         if (OpenLogStatus == LogCORRUPTION){
00219             //  Rollback the current transaction
00220             Rollback();
00221             //  Truncate the log file
00222             truncate(log, ReadBytes);
00223         }
00224         else
00225             OpenLogStatus=LogOK;
00226     }   
00227 }
00228 
00229 
00231 //Get a database file line
00233 
00234 bool Tstdb::Getcdbline(ifstream &in, TAction &action, bool &final){
00235 
00236     //  "+" check
00237     char c;
00238     in.get(c);
00239     if (in.eof()) return false;
00240     if (c=='\n'){
00241         final=true;
00242         in.get(c);
00243         return true;
00244     }
00245     final=false;
00246     if(c!='+')  return false;
00247 
00248     int size;
00249     return Getline(in, action, size);
00250 }
00251 
00252 
00254 //  Get a log file line
00256 
00257 bool Tstdb::Getlogline(ifstream &in, TAction &action, bool &commit, int &size, bool &final){
00258 
00259     //  "C" check
00260     char c;
00261     in.get(c);
00262     if (in.eof()){
00263         //  End of file detection
00264         final = true;
00265         in.get(c);
00266         return true;
00267     }
00268     final = false;
00269     if(c==COMMIT){
00270         //  A Commit
00271         commit = true;
00272         
00273         //  Get '\n', end of line
00274         in.get(c);
00275         if(in.eof()) return false;  
00276         if(c!='\n')  return false;
00277         size = 2;
00278         return true;
00279     }
00280     else{
00281         //  Not a Commit
00282         commit = false;
00283         if(c!='+')  return false;
00284         return Getline(in, action, size);
00285     }
00286 }
00287 
00288 
00290 //  Get a generic line from the DB or the LOG file
00292 
00293 bool Tstdb::Getline(ifstream &in, TAction &action, int &size){
00294     
00295     //  Key long
00296     char buffer[BUFFSIZE],c;
00297     size=1;         //  Start with '+' character, counts one byte
00298     in.get(buffer, BUFFSIZE, ',');
00299     int lkey;
00300     lkey = atol(buffer);
00301     size+=strlen(buffer);
00302     in.get(c);
00303     if (in.eof())
00304         return false;
00305     if(c!=',')  
00306         return false;
00307     size+=1;
00308 
00309     //  Data long
00310 
00311     int ldata;
00312 
00313     in.get(buffer, BUFFSIZE, ':');
00314     if (in.eof()) 
00315         return false;
00316     if(buffer[0]=='-'){
00317         //  It's a deleted key
00318         in.get(buffer[1]);
00319         if (in.eof()) 
00320             return false;   
00321         if(buffer[1]=='1'){
00322             ldata=0;
00323             size+=2;
00324         }
00325         else{
00326             //  Format error
00327             return false;
00328         }
00329     }
00330     else{
00331         //  Usual key
00332         if (in.eof())
00333             return false;   
00334         ldata = atol(buffer);
00335         size+=strlen(buffer);
00336     }
00337     
00338     in.get(c);
00339     if (in.eof()) 
00340         return false;
00341     if(c!=':')  
00342         return false;
00343     size+=1;
00344     
00345     //  Get key
00346     in.read(buffer, lkey);
00347     if (in.eof()) 
00348         return false;
00349     string keys(buffer, buffer+lkey);
00350     size+=lkey;
00351 
00352     //  Get "->"
00353 
00354     in.get(c);
00355     if (in.eof()) return false;
00356     if(c!='-')  return false;
00357     in.get(c);
00358     if (in.eof()) return false;
00359     if(c!='>')  return false;
00360     size+=2;
00361     
00362     //  Get data
00363 
00364     if(ldata == 0){
00365         //  Deleted key
00366         TAction action1(DELETE,keys,"");
00367         action=action1;
00368     }
00369     else{
00370         //  Usual key
00371         in.read(buffer, ldata);
00372         string datas(buffer, buffer+ldata);
00373         TAction action1(INSERT,keys,datas);
00374         action=action1;
00375     }
00376     size+=ldata;
00377     
00378     //  Get '\n', end of line/entry
00379     in.get(c);
00380     if (in.eof()) 
00381         return false;   
00382     if(c!='\n')  
00383         return false;
00384     size+=1;
00385     return true;
00386 }
00387 
00388 
00390 //  Update a key/value pair to database   //
00392 
00393 bool Tstdb::Update(char *key,unsigned int lkey, char *data, unsigned int ldata){
00394     string keys(key,key+lkey);
00395     string datas(data,data+ldata);
00396     //  Search (key,data) into memory map
00397     TDBMemory::iterator p = DBMemory.find(keys);
00398     if (p != DBMemory.end()){
00399         //  Found key
00400         //  Update the rollback with update
00401         TAction action1(UPDATE, keys, p->second);
00402         RollBack.push(action1);
00403 
00404         //  Update the redolog with update
00405         TAction action2(UPDATE, keys, datas);
00406         RedoLog.push(action2);
00407         
00408         //  Update (key,data) into map object
00409         DBMemory[keys] = datas;
00410         
00411         return true;
00412     }
00413     else{
00414         //  Key not found
00415         //  Nothing to do
00416         return false;
00417     }
00418 }           
00419 
00420 
00422 //  Insert a key/value pair to  database
00424 
00425 bool Tstdb::Insert(char *key,unsigned int lkey, char *data, unsigned int ldata){
00426     string keys(key,key+lkey);
00427     string datas(data,data+ldata);
00428     //  Search (key,data) into memory map
00429     TDBMemory::iterator p = DBMemory.find(keys);
00430     if (p != DBMemory.end()){
00431         //  Key found,
00432         //  not possible to Insert 
00433         return false;
00434     }
00435     else{
00436         //  Not found
00437         //  Pushing a Delete to the RollBack stack
00438         TAction action1(DELETE, keys, "");
00439         RollBack.push(action1);
00440 
00441         //  Pushing an Insert to the RedoLog queue
00442         TAction action2(INSERT, keys, datas);
00443         RedoLog.push(action2);
00444         
00445         //  Inserting (key,data) into map object
00446         DBMemory.insert(TValue(keys,datas));
00447         return true;
00448     }
00449 }           
00450 
00451 
00453 //  Fetch the value for a given key from the database /
00455 
00456 bool Tstdb::Fetch(char *key,unsigned int lkey, const char **data, unsigned int *ldata){
00457     string keys(key,key+lkey);
00458     //  Search (key,data) into memory map
00459     TDBMemory::iterator p = DBMemory.find(keys);
00460     if (p != DBMemory.end()){       
00461         // Key has been found
00462         *ldata = p->second.length();
00463         *data = p->second.c_str();
00464         return true;
00465     }
00466     else{
00467         // Key has not been found
00468         return false;
00469     }
00470 }       
00471 
00472 
00474 //  Delete key/value pair from database //
00476 
00477 bool Tstdb::Delete(char *key,unsigned int lkey){
00478     string keys(key,key+lkey);
00479     //  Search (key,data) into memory map
00480     TDBMemory::iterator p = DBMemory.find(keys);
00481     if (p != DBMemory.end()){
00482         //  Key found
00483         //  Pushing an Insert to the RollBack stack
00484         TAction action1(INSERT, keys, p->second);
00485         RollBack.push(action1);
00486 
00487         //  Pushing a Delete to the RedoLog queue
00488         TAction action2(DELETE, keys, "");
00489         RedoLog.push(action2);
00490         
00491         //  Erasing (key,data) from map object
00492         DBMemory.erase(keys);
00493         
00494         return true;
00495     }
00496     else{
00497         //  Not found
00498         //  Nothing to do here
00499         return false;
00500     }
00501 }       
00502 
00503 
00505 //  Validate the transaction //
00507 
00508 bool Tstdb::Commit(void){
00509 
00510     //  Are there movements to log?
00511     if(RedoLog.empty()) 
00512         return true;
00513     
00514     //  Transfer the redolog 
00515     //  queue to LOG file
00516     ofstream logOut(log, ios::out|ios::app);
00517     while(!RedoLog.empty()){
00518         TAction &action = RedoLog.front();
00519         
00520         switch(action.GetFunction()){
00521             
00522             case INSERT:
00523             
00524             case UPDATE:{
00525                 //  Parser line out:
00526                 //  Output formatting
00527                 string buffer;
00528                 ParserLineOut(buffer, action);
00529                 //  Writting line into LOG file
00530                 logOut << buffer;
00531                 break;
00532             }
00533             case DELETE:{
00534                 //  Parser Delete line out:
00535                 //  Output formatting
00536                 string buffer;
00537                 ParserLineDeleteOut(buffer, action);
00538                 //  Writting line into log file
00539                 logOut << buffer;
00540                 break;
00541             }               
00542         }
00543         RedoLog.pop();
00544     }
00545     //  Intro COMMIT constant before
00546     //  closing LOG file
00547     logOut << COMMIT << endl;
00548 
00549     //  Flushing the buffer
00550     logOut.flush();
00551     
00552     //  Erase the RollBack stack
00553     //  Pop all Actions from stack
00554     while(!RollBack.empty())
00555         RollBack.pop();
00556 
00557     //  Verify if Reorganize() is necessary
00558     ReorganizeNecessary();
00559 
00560     return true;
00561 }   
00562 
00563 
00565 //  Parser line Out  /
00567 
00568 bool Tstdb::ParserLineOut(string &line, TAction &action){
00569     char buffer[100];
00570 
00571     
00572     //  Output format is defined here
00573     sprintf(buffer, "+%lu,%lu:", (unsigned long)action.GetKey().size(), 
00574                                     (unsigned long)action.GetData().size());
00575     char *d;
00576     d=buffer+strlen(buffer);
00577     memmove(d,action.GetKey().c_str(), action.GetKey().size()); 
00578     d+=action.GetKey().size();
00579     *(d++)='-'; 
00580     *(d++)='>';
00581     memmove(d,action.GetData().c_str(), action.GetData().size()); 
00582     d+=action.GetData().size();
00583     *(d++)='\n';
00584 
00585     string linetmp(buffer,d); 
00586     line = linetmp;
00587 
00588     return true;
00589 }
00590     
00591     
00593 //  Parser line Delete Out /
00595 
00596 bool Tstdb::ParserLineDeleteOut(string &line, TAction &action){
00597     char buffer[100];
00598     
00599     //  Output delete format defined here
00600     sprintf(buffer, "+%lu,-1:", (unsigned long) action.GetKey().size());
00601 
00602     char *d;
00603     d=buffer+strlen(buffer);
00604     memmove(d,action.GetKey().c_str(), action.GetKey().size()); 
00605     d+=action.GetKey().size();
00606     *(d++)='-'; 
00607     *(d++)='>';
00608     *(d++)='\n';
00609 
00610     string linermp(buffer,d);
00611     line = linermp;
00612 
00613     return true;
00614 }
00615 
00616 
00618 //  RollBack transaction /
00620 
00621 bool Tstdb::Rollback(void){
00622     //  Redo the last transaction, using the RollBack stack
00623     //  Transfer the RedoLog queue to LOG file
00624     while(!RollBack.empty()){
00625         TAction &action = RollBack.top();
00626         switch(action.GetFunction()){
00627             
00628             case INSERT:
00629                 //  Inserting (key,data) into map object
00630                 DBMemory.insert(TValue(action.GetKey(),action.GetData()));
00631                 break;
00632 
00633             case UPDATE:
00634                 //  Updating (key,data) into map object
00635                 DBMemory[action.GetKey()] = action.GetData();
00636                 break;
00637                 
00638             case DELETE:
00639                 //  Erasing (key,data) from map object
00640                 DBMemory.erase(action.GetKey());
00641                 break;
00642         }
00643         RollBack.pop();
00644     }
00645 
00646     //  Empty the RedoLog queue
00647     //  Pop all Actions from queue
00648     while(!RedoLog.empty())
00649         RedoLog.pop();
00650 
00651     return true;
00652 }           
00653 
00654 
00656 //  Reorganize /    
00658 
00659 bool Tstdb::Reorganize(){
00660     //  Rollback the current transaction if not finished
00661     Rollback();
00662 
00663     //  Rename the actual DB to DB.tmp
00664     char dbtmp[100];
00665     sprintf(dbtmp, "%s.tmp", db);
00666     rename(db, dbtmp);
00667     
00668     //  Checking if existing DB
00669     ofstream cdbOut(db);
00670     if (!cdbOut){
00671         return false;
00672     }
00673 
00674     //  Iterator at the beginning of
00675     //  the memory map 
00676     TDBMemory::iterator DBIter = DBMemory.begin();
00677 
00678     //  Loop: rebuilding the DB file
00679     while (DBIter != DBMemory.end()){
00680         TAction action(INSERT,DBIter->first, DBIter->second);
00681         //  Parser line out
00682         string buffer;
00683         ParserLineOut(buffer, action);
00684         cdbOut << buffer;
00685         DBIter++;
00686     }
00687         
00688     //  Closing DB file
00689     cdbOut << endl;
00690     
00691     //  Erasing the LOG file
00692     unlink(log);
00693 
00694     //  Erasing the TMP file
00695     unlink(dbtmp);
00696     
00697     return true;
00698 }
00699 
00700 
00702 //  Check if Reorganize is necessary    /
00704 
00705 bool Tstdb::ReorganizeNecessary(){
00706 
00707     struct stat file_cdb_stats;
00708     struct stat file_log_stats;
00709     
00710     if (stat(log, &file_log_stats)!=-1){
00711         //  LOG exists
00712         //  Verify max size
00713         if (file_log_stats.st_size > MaxLogSize) 
00714             return Reorganize();
00715 
00716         //  Verify max time cdb to log file
00717         if (stat(db, &file_cdb_stats)!=-1){
00718             if(difftime(file_log_stats.st_mtime, file_cdb_stats.st_mtime) > MaxTime) 
00719                 return Reorganize();
00720         }
00721     }
00722     return true;
00723 }
00724 
00725 
00727 //  Firstkey //
00729 
00730 bool Tstdb::Firstkey(const char **key,unsigned int *lkey, const char **data, unsigned int *ldata){
00731     //  Iterator at the beginning of the map
00732     index = DBMemory.begin();
00733 
00734     //  Verify if there are elements
00735     if (index == DBMemory.end()) 
00736         return false;
00737 
00738     *lkey = index->first.length();
00739     *key = index->first.c_str();
00740 
00741     *ldata = index->second.length();
00742     *data = index->second.c_str();
00743 
00744     index++;
00745         
00746     return true;
00747 }
00748 
00749 
00751 //  Nextkey  /
00753 
00754 bool Tstdb::Nextkey(const char **key,unsigned int *lkey, const char **data, unsigned int *ldata){
00755 
00756     //  Verify if not elements
00757     if (index == DBMemory.end()) 
00758         return false;
00759 
00760     *lkey = index->first.length();
00761     *key = index->first.c_str();
00762 
00763     *ldata = index->second.length();
00764     *data = index->second.c_str();
00765 
00766     index++;
00767 
00768     return true;
00769 }
00770 
00774 //
00775 //void Tstdb::Finalize(){
00776 //  delete db;
00777 //  delete log;
00778 //}
00779 
00780 
00782 //  Destructor /
00784 
00785 Tstdb::~Tstdb(){
00786 }

Generated on Thu May 12 17:58:08 2005 for SimpleTransactionalDatabase by  doxygen 1.4.0