/*
    stmtrel.cpp

    Copyright (c) 1995, by picoSoft
    ALL RIGHTS RESERVED.

    Revision History:
    -----------------
*/

# include "stmtrel.h"
static char rcsid[] = "$Id: stmtrel.cpp 5.6 2001/01/10 10:51:43 picoSoft Exp Marco $";
static char rcsidh[] = stmtrel_h;
# include "database.h"
# include "relation.h"
# include "sqltype.h"
# include "perror.h"
# include "sort.h"
# include "trace.h" 
# include "express.h"
# include "sqlpars.h"
extern "C" {
# include <errno.h>
# include <string.h>
# include <ctype.h>
# include "isam.h"
# include "picoisam.h"
# ifdef MSDOS
# include <dos.h>
# include <process.h>
# include <io.h>
# define strcpy      lstrcpy
# else
# include <unistd.h>
# endif 
}  

PCLASSID(StmtRel)

void 
StmtRel::init(Relation *r)
{
   Buffer = (char *)0;
   bFile = 0;
   Rel = r;
   if (Rel && Rel->GetBufferLen()) {
      for (register Attribute *a = Rel->getAllAttrs().GetFirst();
           a;
           a = Rel->getAllAttrs().GetNext())
         AddField (new StmtAttr(DB, *a, this, (PString *)0));
      BufferStmtRelAlloc();
   }
   FsOpen = PFalse; 
   CurrIndex = 0;
   isStatus = ENOTOPEN;
   OrderBy = 0;
   GroupBy = 0;
   join = "";
   joinCond = 0;
   leftJoin = PFalse;
}

StmtRel::~StmtRel ()
{   
   Close ();
   
   if (Rel && Rel->getRelationType() == SortRel)
      Erase ();
   DeleteRel();
}

void
StmtRel::BufferStmtRelAlloc()
{
   StmtAttr *sa;
   if (Buffer == 0) {
      Rel->BufferCalc();
      if (Rel->GetBufferLen() >  0)
         Buffer = new char [Rel->GetBufferLen()];
         for (sa = Fields.GetFirst(); sa;
              sa = Fields.GetNext())
            sa->setBuffer();
      }
} 

void
StmtRel::DeleteRel()
{ 
   StmtAttr *sa; 
   SearchIndex *ix;
   Name = "";
   for (ix = Indexes.GetFirst(); ix != 0; ix = Indexes.DeleteCurrent())
      delete ix; 
   for (sa = Fields.GetFirst(); sa != 0; sa = Fields.DeleteCurrent())
      delete sa;
   if (Rel && Rel->getRelationType() == SortRel)
      delete Rel;
   if (Buffer != (char *)0)
      delete Buffer;
   if (OrderBy)
      delete OrderBy;
   if (GroupBy)
      delete GroupBy;
   if (joinCond)
      delete joinCond;
   if (bFile != 0)
      delete bFile;
   init(0);
}

// Da Cassare ? O^O (usata ancora in parscat e qui)
void
StmtRel::SetRel (StmtAttrList &sal, DataBase &d)
{
   Attribute *a;
   StmtAttr *saOld, *saNew;
   init(0);
   Rel = new Relation (sal, d);
   Name = Rel->getName();
   for (a = Rel->getVisAttrs().GetFirst(), saOld = sal.GetFirst();
        a != 0 && saOld != 0;
        a = Rel->getVisAttrs().GetNext(),  saOld = sal.GetNext()) {
      Fields.AddItem (saNew = new StmtAttr (DB, *a, this, (PString *)0));
      saNew->setAggregate (saOld->getAggregate());
   }
   if (a != 0 || saOld != 0)
      PError::InternalError ("Alignment problem in SetRel");
}

void
StmtRel::SetRel ()
{
   init(0);
   Rel = new Relation (DB);
   Name = Rel->getName();
}

StmtAttr *
StmtRel::AddNewField (StmtAttr *sa, const PString *name)
{
   StmtAttr *Return;
   Attribute *a = Rel->AddVisible (sa);

   Fields.AddItem (Return = new StmtAttr (DB, *a, this, name));
   if (sa->getBlob() != 0) {
      Return->setBlobField(new BlobField(*sa->getBlob()));
      setBlob();
   }
   Return->setAggregate (sa->getAggregate());
   return Return;
}

StmtAttr *
StmtRel::AddNewInvis (StmtAttr *sa)
{
   StmtAttr *Return;
   Attribute *a = Rel->AddInvis (sa);

   Fields.AddItem (Return = new StmtAttr (DB, *a, this, 0));
   Return->setAggregate (sa->getAggregate());
   return Return;
}

void
StmtRel::SortIndexes (void)
{
   SearchIndex *i;

   for (i = Indexes.GetFirst(); i != 0; i = Indexes.GetNext())
      i->SortSELL();
}

void
StmtRel::ChooseIndex (SqlParser &p)
{
   SearchIndex *i;
   int indexVal;
   int maxIndexVal = 1;
   StartEnd *se;
   StartEndList *sel;

   for (i = Indexes.GetFirst(); i != 0; i = Indexes.GetNext()) {
      indexVal = 0;
      if (i->isJoinIndex()) {
         for (sel = i->AttrsAndCond.GetFirst(); sel; sel = i->AttrsAndCond.GetNext()) {
            for (se = sel->GetFirst(); se; se = sel->GetNext())
               if (se->CondStart && se->CondStart->getAttr()->getParent() != 0
                                 && !p.RedBefore(se->CondStart->GetStmtParent(),this) ||
                   se->CondEnd   && se->CondEnd->getAttr()->getParent() != 0
                                 && !p.RedBefore(se->CondEnd->GetStmtParent(),this))
                  break;
            if (se == 0)
            indexVal += 0x400;
            else {
               se = sel->GetFirst();
               if (se->CondStart) {
                  if (se->CondStart->getAttr()->getParent() == 0)
                     indexVal += 0x100;
               }
               if (se->CondEnd) {
                  if (se->CondEnd->getAttr()->getParent() == 0)
                     indexVal += 0x100;
               }
               if (indexVal > 0)
                  i->CutJoinFields(sel);
            }
         }
      } else {
         indexVal += 0x200;
         sel = i->AttrsAndCond.GetFirst();
         for (se = sel->GetFirst(); se; se = sel->GetNext()) {
            if (se->CondStart && se->CondEnd)
               indexVal += se->Attr->getAttr()->getFldSize();
/* O^O
            else
               indexVal += se->Attr->getAttr()->getFldSize() / 2 + 1;
*/
         }
      }
      if (indexVal > maxIndexVal || 
          (indexVal == maxIndexVal && i->IsamKey.k_flags == ISNODUPS)) {
         CurrIndex = i;
         maxIndexVal = indexVal;
      }
   }
   if (Trace.isSet)
      if (CurrIndex != 0)
         Trace.Write ("--->Choose tab=%s, att=%s, num=%d, val=0x%x\n", Name.gets(),
               CurrIndex->getFirstAttr()->getAttr()->getName().gets(),
               CurrIndex->getIndexNum(), maxIndexVal);
      else
         Trace.Write ("--->Choose tab=%s, no index found\n", Name.gets());
}
 
void
StmtRel::DefineOrderGroup (OrderItemCopyList &o, PBool dups,
                          OrdCriteriaCopyList **occl)
{  
   StmtAttr *sa;
   OrdCriteria oc;
   OrderItem *oi;
   if (*occl)
      delete *occl;
   *occl = new OrdCriteriaCopyList;
      
   for (oi = o.GetFirst (); oi != 0; oi = o.GetNext())
      for (sa = Fields.GetFirst(); sa != 0; sa = Fields.GetNext())
         if (oi->sa->getAttr()->getSignature() == sa->getAttr()->getSignature()) {
            oc.type = sa->getAttr()->GetType();
            oc.isSigned = (sa->getAttr()->GetSign() != UNSIGNED);
            oc.offs = sa->getAttr()->getFldPos();
            oc.len = sa->getAttr()->getFldSize();
            oc.isDesc = oi->desc;
            oc.dateFmt = sa->getAttr()->getDateFmt();
            if (oi->sa->getAttr()->getParent())
               oc.coll = oi->sa->getAttr()->getParent()->getCollating();
            else
               oc.coll = (unsigned char *) 0;
            (*occl)->AddItem (oc);
         } 
}

Error
StmtRel::Open ()
{
   PString Errore;
   isStatus = 0;

   if (FsOpen == PTrue)
      return SQL_SUCCESS;
   if (Buffer == (char *)0)
      BufferStmtRelAlloc();

   if ( Rel->FileOpenExists( &FsId ) == PFalse ) { 
      switch (Rel->getFileType ()) {
      case sort:
         PError::InternalError ("Opening sort file");
         isStatus = EBADOPENMODE;
         break;
      case bst:
         isStatus = errno;
         break;
      case pIsam: 
         isStatus = pIsGetErrno();
         break;
      default:
         PError::InternalError ("Invalid file type in Open");
         isStatus = ERRNOINVALID; 
         break;
      }
   } else {
      FsOpen = PTrue;
      if (hasBlob()) {
         if (!Rel->hasOnePermission(REL_INSERT|REL_UPDATE|REL_DELETE))
            bFile->bopen (ISINPUT);
         else
            bFile->bopen (ISINOUT);
         if ((isStatus = bFile->getErrno()) != 0)
            FsOpen = PFalse;
         else
            setBlobFile();
      }
   }
   return (Error) isStatus;
}

void
StmtRel::Close (PBool unloadCache)
{
   if (FsOpen)
      Rel->getParent()->Close (Rel, FsId);
   FsOpen = PFalse;
   if (bFile != 0)
      bFile->bclose();
   if (unloadCache)
      Rel->getParent()->UnloadOpenCache (Rel);
}

Error
StmtRel::ShareOpen (StmtRel *sr)
{
   PString Errore;
   isStatus = 0;

   if (Buffer == (char *)0)
      BufferStmtRelAlloc();

   if (sr->FsOpen) { 
      FsOpen = PTrue;
      FsId = sr->FsId;
      if (sr->hasBlob()) {
         if (bFile)
            delete bFile;
         bFile = sr->bFile;
      }
   } else {
      isStatus = ENOTOPEN;
   }
   return (Error) isStatus;
}

void
StmtRel::ShareClose ()
{
   FsOpen = PFalse;
   bFile = 0;
}

Error
StmtRel::Create ()
{               
   SearchIndex noindx (0); 
   SearchIndex *idx; 
   isStatus = 0;

   if (Buffer == (char *)0)
      BufferStmtRelAlloc();

   errno = 0;
   switch (Rel->getFileType ()) {
   case sort:
      if (GroupBy)
         FsId.id_sort = new Sort (Rel->GetBufferLen(), *GroupBy);
      else if (OrderBy)
         FsId.id_sort = new Sort (Rel->GetBufferLen(), *OrderBy);
      else {
         GroupBy = new OrdCriteriaCopyList;
         FsId.id_sort = new Sort (Rel->GetBufferLen(), *GroupBy);
      } 
      break;
   case pIsam:
      if (Indexes.GetItemNum() > 0) {
         idx = Indexes.GetFirst();
         FsId.id_isam = pIsbuild ((Rel->getFsDirName() + "/" + Rel->getFsDataName()).gets(),
                                  Rel->GetBufferLen(), 
                                  &idx->IsamKey,
                                  ISEXCLLOCK|ISINOUT);
         isStatus = pIsGetErrno();
         if (isStatus == 0) {
            while ((idx = Indexes.GetNext()) != 0)
               AddIndexFile (*idx);
            isStatus = 0;
         }
      } else {
         FsId.id_isam = pIsbuild ((Rel->getFsDirName() + "/" + Rel->getFsDataName()).gets(),
                                   Rel->GetBufferLen(), 
                                   &noindx.IsamKey,
                                   ISEXCLLOCK|ISINOUT);
         isStatus = pIsGetErrno();
      }
      if (isStatus == 0 && hasBlob()) {
         Error rc;
         if (!Rel->hasOnePermission(REL_INSERT|REL_UPDATE|REL_DELETE))
            rc = bFile->bopen (ISINPUT);
         else
            rc = bFile->bopen (ISINOUT);
         isStatus = bFile->getErrno();
         setBlobFile();
      }
      break;
   default:
      PError::InternalError("Invalid file type in Create"); 
      isStatus = ERRNOINVALID;
      break;
   }
   if (isStatus == 0)
      FsOpen = PTrue;

   return (Error)isStatus;
}

Error
StmtRel::AddIndexFile (SearchIndex &idx)
{
   isStatus = 0;

   errno = 0;
   if (Rel->getFileType() == pIsam) {
      pIsaddindex (FsId.id_isam, &idx.IsamKey);
      if (pIsGetErrno() != 0)
         isStatus = pIsGetErrno();
   }
   else
      isStatus = ERRNOINVALID;

   return (Error)isStatus;
}

Error
StmtRel::Drop ()
{
   isStatus = ERRNOINVALID;
   errno = 0;
   if (Rel->getFileType() == pIsam) {
      Close(PTrue);
      pIserase ((Rel->getFsDirName() + "/" + Rel->getFsDataName()).gets());
      if (pIsGetErrno() != 0)
         isStatus = pIsGetErrno();
      else {
         isStatus = 0;
         if (hasBlob() && bFile->erase () == SQL_ERROR)
            isStatus = bFile->getErrno();
      }
   }
   Rel = (Relation *) 0;
   return (Error)isStatus;
}

Error
StmtRel::Rename (char *nName)
{
   isStatus = ERRNOINVALID;
   errno = 0;
   if (Rel->getFileType() == pIsam) {
      PString oldName = Rel->getFsDirName() + "/" + Rel->getFsDataName();
      PString newName = Rel->getFsDirName() + "/" + nName;
      Close(PTrue);
      pIsrename (oldName.gets(), newName.gets());
      if (pIsGetErrno() != 0) {
         isStatus = pIsGetErrno();
      } else {
         isStatus = 0;
         if (hasBlob() && bFile->rename (newName.gets()) == SQL_ERROR)
            isStatus = bFile->getErrno();
         if (isStatus == 0) {
            Rel->setName (nName);
            Rel->setFileName (nName);
         }
      }
   }
   return (Error)isStatus;
}

Error
StmtRel::StartNext ()
{
   SearchIndex noindx (0);
   StartEnd *se, *firstse = 0;
   StartEndList *sel; 
   isStatus = 0;
   foundJoin = PFalse;

   if (CurrIndex != 0) {
      int part = 0, indexNum = CurrIndex->getIndexNum();
      PBool firstMove = PFalse;
      sel = CurrIndex->AttrsAndCond.GetCurrent();
      for (firstse = se = sel->GetFirst(); se; se = sel->GetNext()) {
         part = se->Attr->getAttr()->GetIndexPart (indexNum);
         if (se->CondStart != 0) {
            if (firstMove == PFalse &&
                se->CondStart->getValueUpdated()->IndexCompare(se->Attr) <= 0)
               continue;
            se->Attr->FromAttr (*(se->CondStart->getValueUpdated()));
            firstMove = PTrue;
         } else if (se->OpStart == IS_NULL) {
            if (firstMove || se->Attr->isNull()) // Potrebbe essere un null logico!
               se->Attr->setNullBuffer();
         }
      }
      for (Attribute *a = Rel->getAllAttrs().GetFirst(); a;
                      a = Rel->getAllAttrs().GetNext())
         if (a->IsIndexNumber (indexNum) && a->GetIndexPart (indexNum) > part)
            memset (&Buffer[a->getFldPos()], '\0', a->getFldSize());
   }
   switch (Rel->getFileType()) {
   case sort:
      isStatus = FsId.id_sort->SeekStart();
      break;
   case bst:
      lseek (FsId.id_bst, 0L, SEEK_SET);
      break;
   case pIsam:
      if (CurrIndex) {
         if (pIsindexinfo (FsId.id_isam, &CurrIndex->IsamKey,
                               CurrIndex->getIndexNum()) == 0) {
            pIsstart (FsId.id_isam, &CurrIndex->IsamKey, 0, Buffer, ISGTEQ);
         } else {
            CurrIndex = 0;
            pIsstart (FsId.id_isam, &noindx.IsamKey, 0, Buffer, ISFIRST);
         }
      } else
         pIsstart (FsId.id_isam, &noindx.IsamKey, 0, Buffer, ISFIRST);
      isStatus = pIsGetErrno();
      break;
   default:
      PError::InternalError ("Invalid file type in Start"); 
      isStatus = ERRNOINVALID;
      break;
   }
   return (Error)isStatus;
}

Error
StmtRel::StartPrev ()
{
   SearchIndex noindx (0);
   StartEnd *se, *firstse = 0;
   StartEndList *sel; 
   isStatus = 0;
   foundJoin = PFalse;

   if (CurrIndex != 0) {
      int part = 0, indexNum = CurrIndex->getIndexNum();
      PBool firstMove = PFalse;
      sel = CurrIndex->AttrsAndCond.GetCurrent();
      for (firstse = se = sel->GetFirst(); se; se = sel->GetNext()) {
         part = se->Attr->getAttr()->GetIndexPart (indexNum);
         if (se->CondEnd != 0) {
            if (firstMove == PFalse &&
                se->CondEnd->getValueUpdated()->IndexCompare(se->Attr) >= 0)
               continue;
            se->Attr->FromAttr (*(se->CondEnd->getValueUpdated()));
            firstMove = PTrue;
         } else if (se->OpEnd == IS_NULL) {
            if (!se->Attr->isNull())
               firstMove = PTrue;
            se->Attr->setNullBuffer();
         }
      }
      for (Attribute *a = Rel->getAllAttrs().GetFirst(); a;
                      a = Rel->getAllAttrs().GetNext())
         if (a->IsIndexNumber (indexNum) && a->GetIndexPart (indexNum) > part)
            memset (&Buffer[a->getFldPos()], '\xFF', a->getFldSize());
   }
   switch (Rel->getFileType ()) {
   case sort:
      isStatus = FsId.id_sort->SeekStart();
      if (isStatus == 0)
         isStatus = FsId.id_sort->SeekEnd();
      break;
   case bst:
      lseek (FsId.id_bst, 0L, SEEK_END);
      break;
   case pIsam:
      if (CurrIndex) {
         if (pIsindexinfo (FsId.id_isam, &CurrIndex->IsamKey,
                                  CurrIndex->getIndexNum()) == 0) {
            pIsstart (FsId.id_isam, &CurrIndex->IsamKey, 0, Buffer, ISLTEQ);
         } else {
            CurrIndex = 0;
            pIsstart (FsId.id_isam, &noindx.IsamKey, 0, Buffer, ISLAST);
         }
      } else
         pIsstart (FsId.id_isam, &noindx.IsamKey, 0, Buffer, ISLAST);
      isStatus = pIsGetErrno();
      break;
   default:
      PError::InternalError ("Invalid file type in StartPrev"); 
      isStatus = ERRNOINVALID;
      break;
   }
   return (Error)isStatus;
}

Error
StmtRel::DelCurrent ()
{
   isStatus = 0;
      

   if (Buffer != (char *) 0) {
      switch (Rel->getFileType ()) {
      case pIsam:
         pIsdelcurr (FsId.id_isam);
         isStatus = pIsGetErrno();
         if (isStatus == 0 && hasBlob() && removeBlobs()==SQL_ERROR)
            isStatus = bFile->getErrno();
         break;
      default:
         isStatus = ERRNOINVALID;
         break;
      }
   } else 
      isStatus = ERRNOINVALID; 
   return (Error)isStatus;
}


Error
StmtRel::UpdCurrent ()
{
   isStatus = 0;
   if (Buffer != (char *) 0) {
      switch (Rel->getFileType ()) {
      case pIsam:
         if (hasBlob()) {
            if (rewriteBlobs() == SQL_SUCCESS) {
               pIsrewcurr (FsId.id_isam, Buffer);
               isStatus = pIsGetErrno();
            } else {
               isStatus = bFile->getErrno();
            }
         } else {
            pIsrewcurr (FsId.id_isam, Buffer);
            isStatus = pIsGetErrno();
         }
         break; 
      default:  
         isStatus = ERRNOINVALID;
         break;
      }
   } else 
      isStatus = ERRNOINVALID;  
   return (Error)isStatus;
}

Error
StmtRel::Write ()
{
   isStatus = 0;

   if (Buffer != (char *) 0) {
      switch (Rel->getFileType ()) {
      case sort:
         isStatus = FsId.id_sort->Write (Buffer);
         break;
      case bst:
         lseek (FsId.id_bst, 0L, SEEK_END);
         if (write (FsId.id_bst, Buffer, Rel->GetBufferLen()) < 1)
            isStatus = errno;
         break;
      case pIsam:
         if (hasBlob()) {
            if (writeBlobs() == SQL_SUCCESS) {
               pIswrite (FsId.id_isam, Buffer);
               isStatus = pIsGetErrno();
               if (isStatus != 0)
                  unwriteBlobs();
            } else {
               isStatus = bFile->getErrno();
            }
         } else {
            pIswrite (FsId.id_isam, Buffer);
            isStatus = pIsGetErrno();
         }
         break;
      default:
         isStatus = ERRNOINVALID;
         break;
      }
   } else 
      isStatus = ERRNOINVALID; 
   return (Error)isStatus;
}

Error
StmtRel::Replace ()
{
   isStatus = 0;

   if (Buffer != (char *) 0) {
      switch (Rel->getFileType ()) {
      case pIsam:
         if (hasBlob()) {
            if (writeBlobs() == SQL_SUCCESS) {
               pIsreplace (FsId.id_isam, Buffer);
               isStatus = pIsGetErrno();
               if (isStatus != 0)
                  unwriteBlobs();
            } else {
               isStatus = bFile->getErrno();
            }
         } else {
            pIsreplace (FsId.id_isam, Buffer);
            isStatus = pIsGetErrno();
         }
         break;
      default:
         isStatus = ERRNOINVALID;
         break;
      }
   } else 
      isStatus = ERRNOINVALID; 
   return (Error)isStatus;
}

Error
StmtRel::ReadNext (PBool lock, PBool readBlob)
{
   isStatus = 0;
   StartEndList *sel;
   
   errno = 0;

   for (StmtAttr *sa = Fields.GetFirst (); sa; sa = Fields.GetNext())
         sa->setNull (PFalse);
   if (Buffer != (char *) 0) {
      switch (Rel->getFileType ()) {
      case sort:
         isStatus = FsId.id_sort->Read (Buffer);
         if (readBlob && isStatus == 0 && hasBlob() &&
             readBlobs()==SQL_ERROR)
            isStatus = bFile->getErrno();
         break;
      case bst:
         if (read (FsId.id_bst, Buffer, Rel->GetBufferLen()) < 1)
            if (errno == 0)
               isStatus = EENDFILE;
            else
               isStatus = errno;
         break;
      case pIsam:
         if (lock) {
            pIsrelease (FsId.id_isam);
            pIsread (FsId.id_isam, Buffer, ISNEXT|ISLOCK|DB.wait());
         } else
            pIsread (FsId.id_isam, Buffer, ISNEXT);
         isStatus = pIsGetErrno();
         if (readBlob && isStatus == 0 && hasBlob() &&
             readBlobs()==SQL_ERROR)
            isStatus = bFile->getErrno();
         break;
      default:
         PError::InternalError ("Invalid type in ReadNext"); 
         isStatus = ERRNOINVALID;
         break;
      }
   } else 
      isStatus = ERRNOINVALID;
   
   if (isStatus == 0 && CurrIndex != 0) {
      if (!CurrIndex->IsTrueCondEnd()) {
         while ((sel = CurrIndex->AttrsAndCond.GetNext()) != 0) {
            if (CurrIndex->IsTrueCondEnd())
               if (CurrIndex->IsTrueCondStart())
                  break;
               else
                  if (StartNext() == SQL_SUCCESS) {
                     isStatus = ReadNext(lock, readBlob);
                     break;
                  }
            else
               continue;
         }
         if (sel == 0)
            isStatus = EENDFILE;
      }
   }
   return (Error)isStatus;
}

Error
StmtRel::ReadPrev (PBool lock)
{
   isStatus = 0;
   StartEndList *sel;
   
   errno = 0;

   for (StmtAttr *sa = Fields.GetFirst (); sa; sa = Fields.GetNext())
         sa->setNull (PFalse);
   if (Buffer != (char *) 0) {
      switch (Rel->getFileType ()) {
      case sort:
         isStatus = FsId.id_sort->ReadPrev (Buffer);
         break;
      case bst:
         if (lseek (FsId.id_bst, -Rel->GetBufferLen(), SEEK_CUR) < 0)
            isStatus = EENDFILE;
         else
            if (read (FsId.id_bst, Buffer, Rel->GetBufferLen()) < 1)
               if (errno == 0)
                  isStatus = EENDFILE;
               else
                  isStatus = errno;
         break;
      case pIsam:
         if (lock) {
            pIsrelease (FsId.id_isam);
            pIsread (FsId.id_isam, Buffer, ISPREV|ISLOCK|DB.wait());
         } else
            pIsread (FsId.id_isam, Buffer, ISPREV);
         isStatus = pIsGetErrno();
         if (isStatus == 0 && hasBlob() && readBlobs()==SQL_ERROR)
            isStatus = bFile->getErrno();
         break;
      default:
         PError::InternalError ("Invalid type in ReadPrev"); 
         isStatus = ERRNOINVALID;
         break;
      }
   } else 
      isStatus = ERRNOINVALID;
   
   if (isStatus == SQL_SUCCESS && CurrIndex != 0) {
      if (!CurrIndex->IsTrueCondStart()) {
         while ((sel = CurrIndex->AttrsAndCond.GetPrevious()) != 0) {
            if (CurrIndex->IsTrueCondStart())
               if (CurrIndex->IsTrueCondEnd())
                  break;
               else
                  if (StartPrev() == SQL_SUCCESS) {
                     isStatus = ReadPrev(lock);
                     break;
                  }
            else
               continue;
         }
         if (sel == 0)
            isStatus = EENDFILE;
      }
   }
   return (Error)isStatus;
}

Error
StmtRel::Erase ()
{
   isStatus = 0;
   PString FsPath =  Rel->getFsDirName() + "/" +
                     Rel->getFsDataName();
   if (FsOpen)
      Close (PTrue);
   switch (Rel->getFileType()) {
   case sort:
      break;
   case bst:
      errno = 0;
      unlink (FsPath.gets());
      if (errno != 0)
         isStatus = errno;
      break;
   case pIsam:
      pIserase (FsPath.gets());
      isStatus = pIsGetErrno();
      if (isStatus == 0 && hasBlob() && bFile->erase() == SQL_ERROR)
         isStatus = bFile->getErrno();
      break;
   default:
      PError::InternalError ("Invalid file type in Erase"); 
      isStatus = ERRNOINVALID;
      break;
   }
   return (Error)isStatus;
}

Error
StmtRel::GetCursorName (PString &cursorName)
{
   Error Return = SQL_SUCCESS;

   cursorName = Rel->getFsDataName ();
   return Return;
}

Error
StmtRel::CalcAggregate (StmtRel &v)
{                 
   isStatus = 0;
   long len = 0;
   long i;
   unsigned char *intBuffer;
   
   if (Buffer == (char *) 0 || v.Buffer == (char *) 0) {
      PError::InternalError ("Buffer == 0 in CalcAggregate");
      return (Error)(isStatus = ERRNOINVALID);
   }
   if (Rel->getFileType() != sort || v.Rel->getFileType() != sort) {
      PError::InternalError ("Relation != sort in CalcAggregate");
      return (Error)(isStatus = ERRNOINVALID);
   }
   intBuffer = new unsigned char[Rel->GetBufferLen()];
   Start ();
   if ((isStatus = ReadNext(PFalse)) == SQL_SUCCESS) {
      memcpy (intBuffer, Buffer, Rel->GetBufferLen());  
      v.FillBuffer (Fields, 1);
      i = 1;
      while ((isStatus = ReadNext(PFalse)) == SQL_SUCCESS) {
         if (FsId.id_sort->Compare (intBuffer, (unsigned char*)   Buffer) == 0)
            i++;                                                       
         else
            if (v.Write () != SQL_SUCCESS) {
               isStatus = v.getStatus();
               break;
            } else
               i = 1;
         memcpy (intBuffer, Buffer, Rel->GetBufferLen());
         v.FillBuffer (Fields, i);
      }
      if (isStatus == EENDFILE || isStatus == ENOREC) {
         v.Write ();
         isStatus = v.getStatus ();
         v.Start();
      }
   } else if (isStatus == EENDFILE || isStatus == ENOREC)
      isStatus = 0;
   delete intBuffer;
   return (Error)isStatus;
}

void
StmtRel::Clear ()
{
   StmtAttr *a;

   if (Buffer != (char *)0)
      for (a = Fields.GetFirst (); a != 0; a = Fields.GetNext())
         a->setNullBuffer ();
}

void
StmtRel::setHighValue ()
{
   StmtAttr *a;

   if (Buffer != (char *)0)
      for (a = Fields.GetFirst (); a != 0; a = Fields.GetNext())
         a->setHighValueBuffer ();
}

void
StmtRel::FillBuffer (StmtAttrList &al, unsigned long count)
{
   StmtAttr *aext;
   StmtAttr *aint;
   Relation *lastRel = 0;
    
   for (aint = Fields.GetFirst (), aext = al.GetFirst();
        aint != 0 && aext != 0;
        aint = Fields.GetNext (), aext = al.GetNext())  {
     if (count == 0 || !aint->isAggregate())
        aint->FromAttr (*aext);
     else
        aint->Aggregate (count, aext);
   }   
}

void
StmtRel::FillBuffer (ExpressionList &al, unsigned long count)
{
   Expression *aext;
   StmtAttr *aint;
   Relation *lastRel = 0;
    
   for (aint = Fields.GetFirst (), aext = al.GetFirst();
        aint != 0 && aext != 0;
        aint = Fields.GetNext (), aext = al.GetNext())  {
     if (count == 0)
        aint->FromAttr (*(aext->getValueUpdated()));
     else
        aint->FromAttr (*(aext->getAggrValueUpdated(count)));
      /*
     if (count == 0 || !aext->getValue()->IsBuiltIn())
        aint->FromAttr (*(aext->getValueUpdated()));
     else
        aext->getValueUpdated()->CalcBuiltIn(count, aint);
   */
   }   
}


StmtAttr *
StmtRel::getByName (const PString & s)
{
   StmtAttr *Return;
   char *pntS;
   char *pntA;

   for (Return = Fields.GetFirst(); Return;
        Return = Fields.GetNext()) {
      if (Return->getName().size() == s.size()) {
         for (pntA = Return->getName().gets(), pntS = s.gets();
              *pntA; pntS++, pntA++)
            if (*pntS != toupper(*pntA)) //s e' gia maiuscola per la FindResKey
               break;
         if (*pntA == 0)
            break;
      }
/* O^O
      if (Return->getAttr()->getName().size() == s.size()) {
         for (pntA = Return->getAttr()->getName().gets(), pntS = s.gets();
              *pntA; pntS++, pntA++)
            if (*pntS != toupper(*pntA)) //s e' gia maiuscola per la FindResKey
               break;
         if (*pntA == 0)
            break;
      }
*/
   }

   return Return;
}
 
short
StmtRel::getRelCond()
{
   if (Rel) 
      return Rel->getRelCond();
   else
      return 0; 
}
 
PString
StmtRel::getRelCondWhere()
{ 
   if (Rel) 
      return Rel->getRelCondWhere();
   else
      return ""; 
}

void
StmtRel::ReloadRelation(SqlParser *p)
{
   PString rName = Rel->getName();
   DB.RemoveRelation (Rel);
   Rel = DB.GetRelation(rName, p);
}

Error
StmtRel::writeBlobs ()
{
   Error Return = SQL_SUCCESS;
   StmtAttr *sa;
   BlobField *bf;

   for (sa = GetFirstField(); sa; sa = GetNextField())
      if ((bf = sa->getBlob()) != 0)
         if ((Return = bf->write ()) == SQL_ERROR)
            break;
         else
            sa->setBlobBlkNum(bf->getBlkNum());
   if (Return == SQL_ERROR) {
      int myErrno = bf->getErrno();
      unwriteBlobs ();
      bf->setErrno(myErrno);
   }
   return Return;
}

Error
StmtRel::unwriteBlobs ()
{
   Error Return = SQL_SUCCESS;
   StmtAttr *sa;
   BlobField *bf;
   unsigned long blkNum;

   for (sa = GetFirstField(); sa; sa = GetNextField())
      if ((bf = sa->getBlob()) != 0 && (blkNum = bf->getBlkNum()) != 0)
         if ((Return = bf->remove (blkNum)) == SQL_ERROR)
            break;
   return Return;
}

Error
StmtRel::readBlobs ()
{
   Error Return = SQL_SUCCESS;
   StmtAttr *sa;
   BlobField *bf;
   unsigned long blkNum;

   for (sa = GetFirstField(); sa; sa = GetNextField())
      if ((bf = sa->getBlob()) != 0 && (blkNum = sa->getBlobBlkNum()) != 0)
         if ((Return = bf->read (blkNum)) == SQL_ERROR)
            break;
   return Return;
}

Error
StmtRel::rewriteBlobs ()
{
   Error Return = SQL_SUCCESS;
   StmtAttr *sa;
   BlobField *bf;
   unsigned long blkNum;

   for (sa = GetFirstField(); sa; sa = GetNextField())
      if ((bf = sa->getBlob()) != 0) {
         blkNum = sa->getBlobBlkNum();
         if ((Return = bf->rewrite (blkNum)) == SQL_ERROR)
            break;
         else
            sa->setBlobBlkNum(bf->getBlkNum());
      }
   return Return;
}

Error
StmtRel::removeBlobs ()
{
   Error Return = SQL_SUCCESS;
   StmtAttr *sa;
   BlobField *bf;
   unsigned long blkNum;

   for (sa = GetFirstField(); sa; sa = GetNextField())
      if ((bf = sa->getBlob()) != 0) {
         blkNum = sa->getBlobBlkNum();
         if ((Return = bf->remove (blkNum)) == SQL_ERROR)
            break;
      }
   return Return;
}

void
StmtRel::setBlobFile ()
{
   Error Return = SQL_SUCCESS;
   StmtAttr *sa;
   BlobField *bf;

   for (sa = GetFirstField(); sa; sa = GetNextField())
      if ((bf = sa->getBlob()) != 0)
         bf->setFile (bFile);
}
