/*
_____       _    _    Corso   Italia,  178
(_|__   .  (_   |_|_  56125           Pisa
(_|_) |)|(()_)()| |   tel.  +39  050 46380
  |   |               picosoft@picosoft.it

 Copyright (C) Picosoft s.r.l. 1995-2002

 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2, or (at your option)
 any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/
# include "database.h"
static char rcsid[] = "$Id: database.cpp 5.6 2001/04/19 09:44:43 picoSoft Exp Marco $";
static char rcsidh[] = database_h;

# include "relation.h"
# include "relview.h"
# include "perror.h"
# include "trace.h"
# ifdef MSDOS
# include <dos.h>
# include <process.h>
# include <io.h>
# ifdef WIN32
# define open(a,b,c) _lopen(a, OF_READWRITE|OF_SHARE_COMPAT)
# else
# define open(a,b,c) _lopen(a, READ_WRITE|OF_SHARE_COMPAT)
# endif
# define lseek       _llseek
# define read        _lread
# define write       _lwrite
# define close       _lclose 
# else
# include <unistd.h>
# endif
# include "dberror.h"
# include "sqlpars.h"
# include "constant.h"
# include "envirini.h"

extern "C" {
# include <string.h>
# ifdef MSDOS
# include <io.h>
# ifdef WIN32
# include <direct.h>
# else
# include <dos.h>
# endif
# include <ctype.h>
# define strcpy lstrcpy
# define strlen lstrlen
# define strcmp lstrcmp
# else
# include <unistd.h>
# include <ctype.h>
# include <fcntl.h>
# endif 
# include <errno.h>
# include <stdlib.h>
}
extern int f_trans;

# define MAXDIRNAME 4096
# define MAXTABNAME 32
# define DEFAULT_MAXOPENCACHE 10
extern "C" {
# include "picoisam.h"
extern PBool PTransaction;
};


# define DT_CRIPT 0
# define DT_QUAL  1
# define DT_OWNER 2
# define DT_NAME  3
# define DT_RW    4
# define DT_TYPE  5
# define DT_CKOPT 6
# define DT_REMAR 7
# define DT_WHERE 8

// !! Occhio che questi sono duplicati in relation.cpp !!
# define DF_CRIPT  0
# define DF_NAME   1
# define DF_CONUM  2
# define DF_CONAM  3
# define DF_DTYPE  4
# define DF_LENGT  5
# define DF_SCALE  6
# define DF_SIGN   7
# define DF_NULL   8
# define DF_INCR   9
# define DF_PROG  10
# define DF_REMAR 11

# define DV_NAME   0
# define DV_PROG   1
# define DV_LENG   2
# define DV_TEXT   3

static const char * relTables = "__TABLES";
static const char * relFields = "__COLUMNS";
static const char * relViews = "__VIEWS";

PCLASSID(DataBase)

DataBase::DataBase(Environment &e) :
env(e)
{
   NumAliasView = 0;
   RelIdDef     = 0;
   Flag         = 0;
   Nome = "";
   internalStatus = NOT_CONNECTED;
   StringsNotNull = PFalse;
   OemCharSet = PFalse;
   Autocommit = PTrue;
   InTransact = PFalse;
   MaxOpenCache = DEFAULT_MAXOPENCACHE;
   waitLock = 0;
   startDate = 6;
   AccessMode = SQL_MODE_READ_WRITE;
   QuietMode = 0;
   DatadictTables = 0;
   DatadictFields = 0;
   DatadictViews = 0;
   e.addDataBase(this);
}

void
DataBase::Clear()
{
   RelObject *ro;
   Relation *r;
   RelView *rv;
   SqlParser *s;
   OpenFiles *of;

   DelError();
   for (s = StmtList.GetFirst (); s; s = StmtList.GetCurrent ()) // s si autocancella dalla lista
      delete s;
   for (of = openFiles.GetFirst(); of; of = openFiles.DeleteCurrent()) {
      FileClose (of->rel, of->fsId);
      delete of;
   }
   if (DatadictTables) {
      delete DatadictTables;
      DatadictTables = 0;
   }
   if (DatadictFields) {
      delete DatadictFields;
      DatadictFields = 0;
   }
   if (DatadictViews) {
      delete DatadictViews;
      DatadictViews = 0;
   }
   /*
      StmtAttr *sa;      
      for (sa = Aux.GetFirst (); sa; sa = Aux.DeleteCurrent ())
      delete sa;
    */
   for (ro = ViewBases.GetFirst (); ro; ro = ViewBases.DeleteCurrent ())
      delete ro;
   for (ro = RelBases.GetFirst (); ro; ro = RelBases.DeleteCurrent ())
      delete ro;
   for (r = Relations.GetFirst (); r; r = Relations.DeleteCurrent ())
      delete r;
   for (rv = Views.GetFirst (); rv; rv = Views.DeleteCurrent ())
      delete rv;
   Commit();   
}

DataBase::~DataBase()
{                        
   PStringKeyObject *sp;
   PString *s;

   for (sp = Replace.GetFirst(); sp; sp = Replace.DeleteCurrent()) {
      delete sp->getValue();
      delete sp;
   }
   for (s = FilePrefix.GetFirst(); s; s = FilePrefix.DeleteCurrent())
      delete s;
   Clear();
   env.deleteDataBase(this);
}

PBool
DataBase::isStmt(SqlParser *hstmt)
{
   PBool Return = PFalse;
   SqlParser *s;

   for (s = StmtList.GetFirst (); s != 0; s = StmtList.GetNext ())
      if (s == hstmt) {
         Return = PTrue;
         break;
      }
   return Return;
}

SqlParser *
DataBase::getHstmtByCursorName(const PString &name)
{
   SqlParser *Return;

   for (Return=StmtList.GetFirst(); Return != 0; Return=StmtList.GetNext ())
      if (name == Return->getCursorName())
         break;
   return Return;
}

void
remSpacesToUpper(char *s, long len)
{
   if (s != 0) {
      while (len >= 0 && s[len] == ' ')
         s[len--] = 0;
      for ( ;len >= 0; len--)
         s[len] = toupper(s[len]);
   }
}

Error
DataBase::Connect (const char * dirPath)
{
   char * point;
   char ProfString[MAXDIRNAME + 1];
   char nomeTab[MAXTABNAME + 1]; 
   StmtAttr *sa;
   char * valChar;
   double valNum, type; 
   Error Return = SQL_SUCCESS;
   long len = 0; 
   EnvirIni odbcEnv;
   RelObject *ro;

# ifdef MSDOS
   unsigned newdrive, ndrive;
   if (dirPath[1] == ':') {
      newdrive = toupper(dirPath[0]) - 'A' + 1;
      if (newdrive <= 26)
# ifdef WIN32
         ndrive = _chdrive (newdrive);
# else
      _dos_setdrive (newdrive, &ndrive);
# endif
   }
# endif
   /*  release con datadict __TABLES e __COLUMNS  */
   if (odbcEnv.Read(Nome.gets(), "SuspensiveLock",
            (char *) ProfString, MAXDIRNAME) != 0) {
      if (ProfString[0] == 'Y')
         waitLock = ISWAIT;
   }
   if (odbcEnv.Read(Nome.gets(), "CharSet",
            (char *) ProfString, MAXDIRNAME) != 0) {
      char *pnt = ProfString;
      while (*pnt)
         *pnt++ = toupper(*pnt);
      if (!strcmp (ProfString, "OEM") || !strcmp (ProfString, "2"))
         OemCharSet = PTrue;
   }

   if (odbcEnv.Read(Nome.gets(), "MaxOpenCache",
           (char *) ProfString, MAXDIRNAME) != 0) {
      MaxOpenCache = atoi (ProfString);
   }

   FilePrefix.AddItem(new PString (dirPath));
   
   Return = CheckDatadict(dirPath);
   if (Return == SQL_SUCCESS || Return == SQL_SUCCESS_WITH_INFO) {
      Return = DatadictTables->Open();
      if (Return != SQL_SUCCESS) {
         PrintError (E_CONN_EXCEPT_001, "Error opening __TABLES", DatadictTables->getStatus());
         return Return; 
      }
      Return = DatadictTables->Start();
      if (Return == EENDFILE || Return == ENOREC)
         Return = SQL_SUCCESS;
      if (Return != SQL_SUCCESS) {
         PrintError (E_CONN_EXCEPT_001, "Error reading __TABLES", Return);
         return Return; 
      }
      while ((Return = DatadictTables->ReadNext(PFalse)) == SQL_SUCCESS) {
         *ProfString = 0;
         *nomeTab = 0;
         sa = DatadictTables->GetFieldAt(DT_QUAL);
         sa->ToChar(&valChar, len);
         strcpy(ProfString, valChar);
         if (len != SQL_NULL_DATA) { 
            if (ProfString[0] == '\\' || ProfString[0] == '/' ||
                ProfString[0] == '@' || ProfString[1] == ':') {
               for (point = ProfString + strlen(ProfString) - 1;
                    point >= ProfString && *point != '/' && *point != '\\'; point--)
                   if (*point == ' ')
                      *point = 0; 
               if (point >= ProfString){ 
                  *point = 0;
                  strcpy (nomeTab, point + 1);
               }
            } else {
               strcpy (nomeTab, ProfString);
               *ProfString = 0;
            }  
         } 
         sa = DatadictTables->GetFieldAt(DT_NAME);
         sa->ToChar(&valChar, len);
         remSpacesToUpper(valChar, sa->getAttr()->GetLen() - 1);
         if (*nomeTab == 0)
            strcpy(nomeTab, valChar);
         sa = DatadictTables->GetFieldAt(DT_TYPE);
         sa->ToDouble(type, len);
         if (type == L_VIEW) {
            ro = new ViewBase (valChar, *this);
            ViewBases.AddItem (ro);
         } else {
            ro = new RelBase (valChar, *this);
            RelBases.AddItem (ro);
            switch( (int)type) {
            case L_BST:
               ((RelBase *)ro)->setFileType(bst);
               break;
            case L_PISAM:
               ((RelBase *)ro)->setFileType(pIsam);
               break;
            } 
            ((RelBase *)ro)->setDirName(ProfString); 
            ((RelBase *)ro)->setFileName(nomeTab);
            sa = DatadictTables->GetFieldAt(DT_WHERE);
            sa->ToChar(&valChar, len);
            remSpacesToUpper(valChar, sa->getAttr()->GetLen() - 1);
            if (*valChar) {
               ((RelBase *)ro)->setRelCond (1);
               ((RelBase *)ro)->setRelCondWhere (valChar);
            }
         }
         sa = DatadictTables->GetFieldAt(DT_RW);
         sa->ToDouble(valNum, len);
         if ((int)valNum == 0)  
            ro->setReadOnly(PTrue); 
         else
            ro->setReadOnly(PFalse);
         sa = DatadictTables->GetFieldAt(DT_CKOPT);
         sa->ToChar(&valChar, len);
         ro->setCheckOption (valChar[0] == 'Y');
            
/*         
         Relation *rl = new Relation (*rb);
         Relations.AddItem (rl);
         Return = rl->ReadFields(DatadictFields);
         if (Return != SQL_SUCCESS)
            break;
*/            
      }
      DatadictTables->Close();
   }

   Return = LookError();

   return Return; 
}

PBool    
DataBase::BuildPath (PString &FilePath)
{
   PBool Return = PTrue;
   PString *s;
   
   if (FilePath.size() == 0)
      s = FilePrefix.GetFirst();
   else
      s = FilePrefix.GetNext();
   if (s != 0)  
      FilePath = *s;
   else
      Return = PFalse;
   return Return;
}

void
DataBase::SelfSetAttrTables(Relation *r)
{  
   Attribute *a;
   IndexPart ip;

   a = new Attribute ("DT_CRIPT", r);
   a->setType (T_LONG, 0, 0);
   // INTERNAL_TRAILING perche' deve essere diverso da UNSIGNED
   a->setSign (INTERNAL_TRAILING);
   r->getAllAttrs().AddItem (a);
   r->getVisAttrs().AddItem (a);

   a = new Attribute ("DT_QUAL", r);
   a->setType (T_CSTRING, 63, 0);
   ip.setNumber (1);
   ip.setPart (1);
   ip.setDups (PFalse);
   a->AddIndex (ip);
   r->getAllAttrs().AddItem (a);
   r->getVisAttrs().AddItem (a);

   a = new Attribute ("DT_OWNER", r);
   a->setType (T_CSTRING, 30, 0);
   r->getAllAttrs().AddItem (a);
   r->getVisAttrs().AddItem (a);

   a = new Attribute ("DT_NAME", r);
   a->setType (T_CSTRING, 30, 0);
   ip.setNumber (1);
   ip.setPart (2);
   ip.setDups (PFalse);
   a->AddIndex (ip);
   r->getAllAttrs().AddItem (a);
   r->getVisAttrs().AddItem (a);

   a = new Attribute ("DT_RW", r);
   a->setType (T_PACKED_ORDERED, 5, 0);
   a->setSign (UNSIGNED);
   r->getAllAttrs().AddItem (a);
   r->getVisAttrs().AddItem (a);

   a = new Attribute ("DT_TYPE", r);
   a->setType (T_PACKED_ORDERED, 5, 0);
   a->setSign (UNSIGNED);
   r->getAllAttrs().AddItem (a);
   r->getVisAttrs().AddItem (a);
   
   a = new Attribute ("DT_CKOPT", r);
   a->setType (T_CSTRING, 1, 0);
   r->getAllAttrs().AddItem (a);
   r->getVisAttrs().AddItem (a);
   
   a = new Attribute ("DT_REMAR", r);
   a->setType (T_CSTRING, 63, 0);
   r->getAllAttrs().AddItem (a);
   r->getVisAttrs().AddItem (a);
      
   a = new Attribute ("DT_WHERE", r);
   a->setType (T_CSTRING, 128, 0);
   r->getAllAttrs().AddItem (a);
   r->getVisAttrs().AddItem (a);
}

void
DataBase::SelfSetAttrFields(Relation *r)
{  
   Attribute *a;
   IndexPart ip, ip2;

   a = new Attribute ("DF_CRIPT", r);
   a->setType (T_LONG, 0, 0);
   // INTERNAL_TRAILING perche' deve essere diverso da UNSIGNED
   a->setSign (INTERNAL_TRAILING);
   r->getAllAttrs().AddItem (a);
   r->getVisAttrs().AddItem (a);

   a = new Attribute ("DF_NAME", r);
   a->setType (T_CSTRING, 30, 0);
   ip.setNumber (1);
   ip.setPart (1);
   ip.setDups (PFalse);
   a->AddIndex (ip);
   ip.setNumber (2);
   ip.setPart (1);
   ip.setDups (PFalse);
   a->AddIndex (ip);
   r->getAllAttrs().AddItem (a);
   r->getVisAttrs().AddItem (a);

   a = new Attribute ("DF_CONUM", r);
   a->setType (T_PACKED_ORDERED, 5, 0);
   a->setSign (UNSIGNED);
   ip.setNumber (1);
   ip.setPart (2);
   ip.setDups (PFalse);
   a->AddIndex (ip);
   r->getAllAttrs().AddItem (a);
   r->getVisAttrs().AddItem (a);

   a = new Attribute ("DF_CONAM", r);
   a->setType (T_CSTRING, 30, 0);
   ip.setNumber (2);
   ip.setPart (2);
   ip.setDups (PFalse);
   a->AddIndex (ip);
   r->getAllAttrs().AddItem (a);
   r->getVisAttrs().AddItem (a);

   a = new Attribute ("DF_DTYPE", r);
   a->setType (T_PACKED_ORDERED, 5, 0);
   a->setSign (UNSIGNED);
   r->getAllAttrs().AddItem (a);
   r->getVisAttrs().AddItem (a);

   a = new Attribute ("DF_LENGT", r);
   a->setType (T_PACKED_ORDERED, 5, 0);
   a->setSign (UNSIGNED);
   r->getAllAttrs().AddItem (a);
   r->getVisAttrs().AddItem (a);

   a = new Attribute ("DF_SCALE", r);
   a->setType (T_PACKED_ORDERED, 5, 0);
   a->setSign (UNSIGNED);
   r->getAllAttrs().AddItem (a);
   r->getVisAttrs().AddItem (a);

   a = new Attribute ("DF_SIGN", r);
   a->setType (T_PACKED_ORDERED, 5, 0);
   a->setSign (UNSIGNED);
   r->getAllAttrs().AddItem (a);
   r->getVisAttrs().AddItem (a);

   a = new Attribute ("DF_NULL", r);
   a->setType (T_PACKED_ORDERED, 5, 0);
   a->setSign (UNSIGNED);
   r->getAllAttrs().AddItem (a);
   r->getVisAttrs().AddItem (a);
   
   a = new Attribute ("DF_INCR", r);
   a->setType (T_PACKED_ORDERED, 5, 0);
   r->getAllAttrs().AddItem (a);
   r->getVisAttrs().AddItem (a);

   a = new Attribute ("DF_PROG", r);
   a->setType (T_PACKED_ORDERED, 17, 0);
   r->getAllAttrs().AddItem (a);
   r->getVisAttrs().AddItem (a);

   a = new Attribute ("DF_REMAR", r);
   a->setType (T_CSTRING, 63, 0);
   r->getAllAttrs().AddItem (a);
   r->getVisAttrs().AddItem (a);
}

void
DataBase::SelfSetAttrViews(Relation *r)
{  
   Attribute *a;
   IndexPart ip;


   a = new Attribute ("DV_NAME", r);
   a->setType (T_CSTRING, 30, 0);
   ip.setNumber (1);
   ip.setPart (1);
   ip.setDups (PFalse);
   a->AddIndex (ip);
   r->getAllAttrs().AddItem (a);
   r->getVisAttrs().AddItem (a);

   a = new Attribute ("DV_PROG", r);
   a->setType (T_PACKED_ORDERED, 5, 0);
   ip.setNumber (1);
   ip.setPart (2);
   ip.setDups (PFalse);
   a->AddIndex (ip);
   r->getAllAttrs().AddItem (a);
   r->getVisAttrs().AddItem (a);

   a = new Attribute ("DV_LENG", r);
   a->setType (T_PACKED_ORDERED, 5, 0);
   r->getAllAttrs().AddItem (a);
   r->getVisAttrs().AddItem (a);
      
   a = new Attribute ("DV_TEXT", r);
   a->setType (T_CSTRING, 219, 0);
   r->getAllAttrs().AddItem (a);
   r->getVisAttrs().AddItem (a);
}

Error
DataBase::CheckDatadict (const char * dirPath)
{  
   AttributeList OrderList;
   Error Return = SQL_SUCCESS;
   PString pathTables;
   PString pathFields;
   PString pathViews; 
   StmtAttr *sa;
   SearchIndex *i;
   RelBase *rbTables = new RelBase (relTables, *this);
   RelBase *rbFields = new RelBase (relFields, *this);
   RelBase *rbViews = new RelBase (relViews, *this);
   RelBases.AddItem (rbTables);
   RelBases.AddItem (rbFields);
   RelBases.AddItem (rbViews);

   rbTables->setDirName(dirPath);
   rbTables->setFileName(relTables);
   rbTables->setFileType(pIsam);
   rbTables->setReadOnly(PFalse);

   rbFields->setDirName(dirPath);
   rbFields->setFileName(relFields);
   rbFields->setFileType(pIsam);
   rbFields->setReadOnly(PFalse); 

   rbViews->setDirName(dirPath);
   rbViews->setFileName(relViews);
   rbViews->setFileType(pIsam);
   rbViews->setReadOnly(PFalse); 

   Relation *rTables = new Relation (*rbTables);
   Relation *rFields = new Relation (*rbFields);
   Relation *rViews = new Relation (*rbViews);
 
   SelfSetAttrTables(rTables);
   rTables->BufferCalc();

   SelfSetAttrFields(rFields);
   rFields->BufferCalc();

   SelfSetAttrViews(rViews);
   rViews->BufferCalc();
  
   DatadictTables = new StmtRel (*rTables);
   DatadictFields = new StmtRel (*rFields);
   DatadictViews = new StmtRel (*rViews);
   
   sa = DatadictTables->GetFieldAt(DT_QUAL);
   i = new SearchIndex(1);
   i->AddAttr (sa);
   i->initKey ();
   DatadictTables->AddIndex (i);
   DatadictTables->SetCurrIndex (i);
   
   sa = DatadictFields->GetFieldAt(DF_NAME); 
   i = new SearchIndex (1);
   i->AddAttr (sa);
   i->initKey ();
   DatadictFields->AddIndex (i);
   DatadictFields->SetCurrIndex (i);

   i = new SearchIndex (2);
   i->AddAttr (sa);
   sa = DatadictFields->GetFieldAt(DF_CONAM);
   i->AddAttr (sa); 
   i->initKey ();
   DatadictFields->AddIndex (i);

   sa = DatadictViews->GetFieldAt(DV_NAME); 
   i = new SearchIndex (1);
   i->AddAttr (sa);
   sa = DatadictViews->GetFieldAt(DV_PROG);
   i->AddAttr (sa);
   i->initKey ();
   DatadictViews->AddIndex (i);
   DatadictViews->SetCurrIndex (i);
   
   pathTables.prints("%s/%s.dat", dirPath, relTables);
   pathFields.prints("%s/%s.dat", dirPath, relFields);
   pathViews.prints("%s/%s.dat", dirPath, relViews);
 
   if (access (dirPath, 0) < 0) {
      PrintError (E_CONN_EXCEPT_001, 
                  PString ("Data dictionary directory not found: ")+dirPath);
      return SQL_ERROR;
   }
   if (access (pathTables.gets(), 0) < 0 ||
       access(pathFields.gets(), 0)  < 0) {

      Return = DatadictTables->Create ();
      if (Return != SQL_SUCCESS) {
         PrintError (E_CONN_EXCEPT_001, "__TABLES creation error",
                                                 DatadictTables->getStatus());
         return SQL_ERROR;
      } else {
         DatadictTables->Close();
         UnloadOpenCache();
      }
      Return = DatadictFields->Create ();
      if (Return != SQL_SUCCESS) {
         PrintError (E_CONN_EXCEPT_001, "__COLUMNS creation error",
                                                 DatadictTables->getStatus());
         return SQL_ERROR;
      } else {
         DatadictFields->Close();
         UnloadOpenCache();
      }
   }
   if (access (pathViews.gets(), 0) < 0) {
      Return = DatadictViews->Create ();
      if (Return != SQL_SUCCESS) {
         PrintError (E_CONN_EXCEPT_001, "__VIEWS creation error",
                                                 DatadictTables->getStatus());
         return SQL_ERROR;
      } else {
         DatadictViews->Close();
         UnloadOpenCache();
      }
   }
   Relations.AddItem (DatadictTables->getRel());
   Relations.AddItem (DatadictFields->getRel());
   Relations.AddItem (DatadictViews->getRel());
   return Return;
}

Error
DataBase::writeTable (const PString &name, double type, PBool checkOption)
{
   StmtAttr *sa;

   sa = DatadictTables->GetFieldAt (DT_CRIPT);
   sa->setNullBuffer();
   sa = DatadictTables->GetFieldAt (DT_QUAL);
   sa->setNullBuffer();
   sa = DatadictTables->GetFieldAt (DT_OWNER);
   sa->setNullBuffer();
   sa = DatadictTables->GetFieldAt (DT_NAME);
   sa->FromString(name.gets());
   sa = DatadictTables->GetFieldAt (DT_RW);
   sa->FromDouble(1.0);
   sa = DatadictTables->GetFieldAt (DT_TYPE);
   sa->FromDouble(type);
   sa = DatadictTables->GetFieldAt (DT_CKOPT);
   if (checkOption)
      sa->FromString("Y");
   else
      sa->FromString("N");
   sa = DatadictTables->GetFieldAt (DT_REMAR);
   sa->setNullBuffer();
   sa = DatadictTables->GetFieldAt (DT_WHERE);
   sa->setNullBuffer();
   return DatadictTables->Write();
}

Error
DataBase::CreateRelation (SqlParser *p, const PString &name, AttributeList &al)
{
   Error Return = SQL_SUCCESS;
   StmtAttr *sa;

   Attribute *a;
   PString msg;
   double conum = 10;
   int valType = 0;
   int valSign = 0;
   
   Return = DatadictFields->Open();
   if (Return != SQL_SUCCESS) {
      p->PrintError (E_GENERAL_ERROR_000,
                     "Cannot open __COLUMNS", DatadictFields->getStatus());
   } else if ((Return = DatadictTables->Open()) != SQL_SUCCESS) {
      p->PrintError (E_GENERAL_ERROR_000,
                     "Cannot open __TABLES", DatadictTables->getStatus());
   } else {
      for (a = al.GetFirst(), conum = 10; a && Return == SQL_SUCCESS;
           a = al.GetNext(),  conum += 10) {
         switch(a->GetType()) {
         default:
         case T_CSTRING:
            valType = L_CSTRING;
            break;
         case T_SHORT:
            valType = L_SHORT;
            break;
         case T_LONG:
            valType = L_LONG;
            break;
         case T_FLOAT:
            valType = L_FLOAT;
            break;
         case T_DOUBLE:
            valType = L_DOUBLE;
            break;
         case T_BLOB:
            valType = L_BLOB;
            break;
         case T_CLOB:
            valType = L_CLOB;
            break;
         case T_PACKED_ORDERED:
            if (a->getDateFmt() != 0) {
               if (strcmp(a->getDateFmt(), DATE_INTFMT) == 0)
                  valType = L_DATE;
               else if (strcmp(a->getDateFmt(), TIME_INTFMT) == 0)
                  valType = L_TIME;
               else
                  valType = L_TIMESTAMP;

            } else
               valType = L_PACKED_ORDERED;
            break;
         }
         sa = DatadictFields->GetFieldAt (DF_CRIPT);
         sa->FromDouble(0.0);
         sa = DatadictFields->GetFieldAt (DF_NAME);
         sa->FromString(name.gets());
         sa = DatadictFields->GetFieldAt (DF_CONUM);
         sa->FromDouble(conum);
         sa = DatadictFields->GetFieldAt (DF_CONAM);
         sa->FromString(a->getName().gets());
         sa = DatadictFields->GetFieldAt (DF_DTYPE);
         sa->FromDouble(valType);
         sa = DatadictFields->GetFieldAt (DF_LENGT);
         sa->FromDouble(a->GetLen());
         sa = DatadictFields->GetFieldAt (DF_SCALE);
         sa->FromDouble(a->GetDecimal());
         sa = DatadictFields->GetFieldAt (DF_SIGN);
         valSign = a->GetSign();
         switch (valSign) {
         case UNSIGNED:
            valSign = L_UNSIGNED;
            break;
         case INTERNAL_TRAILING:
            valSign = L_SIGNED;
            break;
         case  INTERNAL_LEADING:
            valSign = L_INT_LEAD;
            break;
         case  EXTERNAL_TRAILING:
            valSign = L_EXT_TRAIL;
            break;
         case EXTERNAL_LEADING:
            valSign = L_EXT_LEAD;
            break;
         default: 
            break;
         }  
         sa->FromDouble(valSign);
         sa = DatadictFields->GetFieldAt (DF_NULL);
         sa->FromDouble(0);
         sa = DatadictFields->GetFieldAt (DF_INCR);
         if (a->isSerial())
            sa->FromDouble(1);
         else
            sa->FromDouble(0);
         sa = DatadictFields->GetFieldAt (DF_PROG);
         sa->FromDouble(0);
         sa = DatadictFields->GetFieldAt (DF_REMAR);
         sa->setNull(0);
         if ((Return = DatadictFields->Write()) != SQL_SUCCESS)
             p->PrintError (E_GENERAL_ERROR_000, "Cannot write __COLUMNS", 
                                                 DatadictFields->getStatus());
      }
      if (Return == SQL_SUCCESS) {
         if ((Return = writeTable(name,(double)L_PISAM, PTrue)) != SQL_SUCCESS) {
            p->PrintError (E_GENERAL_ERROR_000, "Cannot write __TABLES", 
                                                DatadictTables->getStatus());
         } else {
            RelBase *rb = new RelBase (name.gets(), *this);
            rb->setFileName(name.gets());
            rb->setFileType(pIsam);
            rb->setReadOnly(PFalse);
            addRelBase (rb);
         }
      }
      DatadictTables->Close();
      DatadictFields->Close();
   }
   return Return == SQL_SUCCESS ? SQL_SUCCESS : SQL_ERROR;
}


Error
DataBase::CreateView (SqlParser *p, const PString &name,
                      const char *text, PBool checkOption)
{
   Error Return = SQL_SUCCESS;
   
   Return = DatadictViews->Open();
   if (Return != SQL_SUCCESS) {
      p->PrintError (E_GENERAL_ERROR_000,
                     "Cannot open __VIEWS", DatadictViews->getStatus());
   } else if ((Return = DatadictTables->Open()) != SQL_SUCCESS) {
      p->PrintError (E_GENERAL_ERROR_000,
                     "Cannot open __TABLES", DatadictTables->getStatus());
      
   } else if ((Return = writeTable(name, (double)L_VIEW, checkOption)) != SQL_SUCCESS) {
      p->PrintError (E_GENERAL_ERROR_000, "Cannot write __TABLES", 
                                                DatadictTables->getStatus());
   } else {
      StmtAttr *saName, *saProg, *saLeng, *saText;
      int cmdLen = strlen(text);
      int rowLen;
      double prog = 0;
      int offset;
      saName = DatadictViews->GetFieldAt (DV_NAME);
      saName->FromString(name.gets());
      saProg = DatadictViews->GetFieldAt (DV_PROG);
      saProg->FromDouble(prog);
      saLeng = DatadictViews->GetFieldAt (DV_LENG);
      saLeng->FromDouble(cmdLen);
      saText = DatadictViews->GetFieldAt (DV_TEXT);
      rowLen = saText->getAttr()->GetLen();
      saText->FromString (text);
      if ((Return = DatadictViews->Write()) != SQL_SUCCESS) {
         p->PrintError (E_GENERAL_ERROR_000, "Cannot write __VIEWS", 
                                                DatadictViews->getStatus());
      } else {
         for (offset = rowLen; offset < cmdLen; offset += rowLen) {
            saProg->FromDouble(++prog);
            saText->FromString (&text[offset]);
            if ((Return = DatadictViews->Write()) != SQL_SUCCESS) {
               p->PrintError (E_GENERAL_ERROR_000, "Cannot write __VIEWS", 
                                                  DatadictViews->getStatus());
               break;
            }
         }
      }
   }
   DatadictTables->Close();
   DatadictViews->Close();
   return Return;
}

Error
DataBase::DropRelation (SqlParser *p, Relation *rd)
{
   Relation *r;
   RelObject *ro;
   Error Return;

   SqlParser del(this);
   PString cmd = "delete from __TABLES where dt_qual is null and DT_NAME = '";
   cmd += rd->getName();
   cmd += "'";
   Return = del.Parse (cmd.gets(), SQL_NTS);
   if (Return != SQL_SUCCESS) {
      MoveStmtError (p, &del);
   } else {
      Return = del.Exec ();
      if (Return != SQL_SUCCESS) {
         MoveStmtError (p, &del);
      } else {
      // Visto che ho cancellato da SYSTAB, cancello il piu' possobile
      // anche in caso di errori
         cmd = "delete from __COLUMNS where DF_NAME = '";
         cmd += rd->getName();
         cmd += "'";
         Return = del.Parse (cmd.gets(), SQL_NTS);
         if (Return != SQL_SUCCESS) {
            MoveStmtError (p, &del);
         } else {
            Return = del.Exec ();
            if (Return != SQL_SUCCESS) {
               MoveStmtError (p, &del);
            }
         }
         for (ro = RelBases.GetFirst (); ro; ) {
            if (ro->isName(rd->getName())) {
               delete ro;
               ro = RelBases.DeleteCurrent ();
               break;
            } else {
               ro = RelBases.GetNext ();
            } 
         }
         for (r = Relations.GetFirst (); r; ) {
            if (r == rd) {
               delete r;
               r = Relations.DeleteCurrent ();
               break;
            } else {
               r = Relations.GetNext ();
            } 
         }
      }
   }
   return Return;
}

Error
DataBase::DropView (SqlParser *p, RelObject *rd)
{
   RelObject *ro;
   Error Return;

   SqlParser del(this);
   PString cmd = "delete from __TABLES where dt_qual is null and DT_NAME = '";
   cmd += rd->getName();
   cmd += "'";
   Return = del.Parse (cmd.gets(), SQL_NTS);
   if (Return != SQL_SUCCESS) {
      MoveStmtError (p, &del);
      return Return;
   } else if ((Return = del.Exec ()) != SQL_SUCCESS) {
      MoveStmtError (p, &del);
      return Return;
   }
   cmd = "delete from __VIEWS where DV_NAME = '";
   cmd += rd->getName();
   cmd += "'";
   Return = del.Parse (cmd.gets(), SQL_NTS);
   if (Return != SQL_SUCCESS) {
      MoveStmtError (p, &del);
   } else {
      Return = del.Exec ();
      if (Return != SQL_SUCCESS) {
         MoveStmtError (p, &del);
      } else {
         for (ro = ViewBases.GetFirst (); ro; ) {
            if (ro->isName(rd->getName())) {
               delete ro;
               ro = ViewBases.DeleteCurrent ();
               break;
            } else {
               ro = ViewBases.GetNext ();
            } 
         }
         for (ro = Views.GetFirst (); ro; ) {
            if (ro == rd) {
               delete ro;
               ro = Views.DeleteCurrent ();
               break;
            } else {
               ro = Views.GetNext ();
            } 
         }
      }
   }
   return Return;
}

Error
DataBase::RenameRelation (SqlParser *p, char *oldName, Relation *rd)
{
   Error Return;

   SqlParser ren(this);
   PString cmd = "update __tables set dt_name = '";
   cmd += rd->getName();
   cmd += "' where dt_qual is null and dt_name = '";
   cmd += oldName;
   cmd += "'";
   Return = ren.Parse (cmd.gets(), SQL_NTS);
   if (Return != SQL_SUCCESS) {
      MoveStmtError (p, &ren);
   } else {
      Return = ren.Exec ();
      if (Return != SQL_SUCCESS) {
         MoveStmtError (p, &ren);
      } else {
      // Visto che ho modificato SYSTAB, modifico il piu' possobile
      // anche in caso di errori
         cmd = "update  __columns set df_name = '";
         cmd += rd->getName();
         cmd += "' where df_name = '";
         cmd += oldName;
         cmd += "'";
         Return = ren.Parse (cmd.gets(), SQL_NTS);
         if (Return != SQL_SUCCESS) {
            MoveStmtError (p, &ren);
         } else {
            Return = ren.Exec ();
            if (Return != SQL_SUCCESS) {
               MoveStmtError (p, &ren);
            }
         }
      }
   }
   return Return;
}
 
void
DataBase::FileClose (Relation *r, accessType &fd) const
{
   switch (r->getFileType ()) {
   case sort:
      delete fd.id_sort;
      fd.id_sort = 0;
      break;
   case bst:
      close (fd.id_bst);
      fd.id_bst = -1;
      break;
   case pIsam:
      pIsclose (fd.id_isam);
      fd.id_isam = -1;
      break; 
   }
}
 
void
DataBase::FileUnlock (Relation *r, accessType fd) const
{
   switch (r->getFileType ()) {
   case sort:
      break;
   case bst:
      break;
   case pIsam:
      pIsrelease (fd.id_isam);
      break; 
   }
}

void
DataBase::UnloadOpenCache (Relation *r)
{ 
   OpenFiles *of;
   for (of = openFiles.GetFirst(); of; )
      if (of->rel == r) {
         FileClose (of->rel, of->fsId);
         delete of;
         of = openFiles.DeleteCurrent();
      } else {
         of = openFiles.GetNext();
      }
}

PBool
DataBase::UnloadOpenCache ()
{ 
   OpenFiles *of; 

   if (of = openFiles.GetFirst()) {
      openFiles.DeleteCurrent();
      FileClose (of->rel, of->fsId);
      delete of;
      return PTrue;
   } else
      return PFalse;
}

void
DataBase::Close (Relation *r, accessType &fd)
{
   OpenFiles *of;
   if (MaxOpenCache > 0 && r->getRelationType () == Standard && Autocommit) {
      if (openFiles.GetItemNum() >= MaxOpenCache) {
         of = openFiles.GetFirst();
         openFiles.DeleteCurrent();
         FileClose (of->rel, of->fsId);
      } else
         of = new OpenFiles;
      of->rel = r;
      of->fsId = fd;
      FileUnlock (of->rel, of->fsId);
      openFiles.AddItem (of);
   } else
      FileClose (r, fd);
}

PBool
DataBase::FileOpenExists (PString &FsDirName,
                          PString &FsDataName,
                          RelBase *r,
                          accessType *fd )
{ 
   PBool Return = PFalse;
   OpenFiles *of; 
    
   if (fd == 0)
      if (r->IsExistant())
         return PTrue;
      else if (r->IsNotExistant())
         return PFalse;

   if (r->getRelationType () == Standard)
      for (of = openFiles.GetFirst(); of; of = openFiles.GetNext())
         if (r == of->rel) {
            if (fd) {
               *fd = of->fsId;
               openFiles.DeleteCurrent();
               delete of;
            }
            return PTrue;
         }

   if (FsDirName.size() == 0) {
      while (!Return && BuildPath (FsDirName))
         Return = Exists (FsDirName, FsDataName, r, fd);
   } else
      Return = Exists (FsDirName, FsDataName, r, fd);

   if (r->IsA(Relation::Class))
      if (Return)
         r->SetExistant();
      else
         r->SetNotExistant();

   return Return;
}

PBool
DataBase::Exists (const PString &FsDirName,
                  PString &FsDataName,
                  RelBase *r,
                  accessType *fd)
{ 
   PBool Return = PTrue;
   accessType fsId;
   errno = 0;
   struct dictinfo dictInfo;
   
   switch (r->getFileType ()) {                       
   case sort:
      PError::InternalError ("Opening Sort !!");

      Return = PFalse;
      break;
   case bst:
      if (!r->hasOnePermission(REL_INSERT|REL_UPDATE|REL_DELETE))
# ifdef MSDOS
         fsId.id_bst = open ((FsDirName + "/" + FsDataName).gets(), O_RDONLY|O_BINARY, 0666);
# else
         fsId.id_bst = open ((FsDirName + "/" + FsDataName).gets(), O_RDONLY, 0666);
# endif
      else      
# ifdef MSDOS
         fsId.id_bst = open ((FsDirName + "/" + FsDataName).gets(), O_RDWR|O_BINARY, 0666);
# else
         fsId.id_bst = open ((FsDirName + "/" + FsDataName).gets(), O_RDWR, 0666);
# endif
      if (fsId.id_bst < 0)
         if (errno == EMFILE && UnloadOpenCache())
            Return = Exists (FsDirName, FsDataName, r, fd);
         else {
            Return = PFalse;
         }
      else {
         if (Return) {
            if ( fd != 0 )
               fd->id_bst = fsId.id_bst;
            else if (fsId.id_bst >= 0)
               close (fsId.id_bst);
         }
      } 
      break;
   case pIsam:
      if (!r->hasOnePermission(REL_INSERT|REL_UPDATE|REL_DELETE)) 
         fsId.id_isam = pIsopen ((FsDirName + "/" + FsDataName).gets(), 
                                ISINPUT|ISMANULOCK);
      else 
         fsId.id_isam = pIsopen ((FsDirName + "/" + FsDataName).gets(), 
                                ISINOUT|ISMANULOCK|ISTRANS);
      if (fsId.id_isam < 0)
         if ((pIsGetErrno()==ETOOMANY || errno==EMFILE) && UnloadOpenCache()){
            Return = Exists (FsDirName, FsDataName, r, fd);
            fsId.id_isam = fd->id_isam;
         } else if (pIsGetErrno() == EACCES && !r->IsReadOnly()) {
            r->setReadOnly (PTrue);
            Return = Exists (FsDirName, FsDataName, r, fd);
            fsId.id_isam = fd->id_isam;
         } else {
            Return = PFalse;
         }
      else if (r->IsNeverOpen()) {
         long BufferLen = 0;
         if (r->IsA(Relation::Class)) {
            BufferLen = ((Relation *) r)->GetBufferLen();   
            pIsindexinfo(fsId.id_isam, &dictInfo, 0);
            if (BufferLen && dictInfo.di_recsize > BufferLen) {
               Return = PFalse;
               pIsclose (fsId.id_isam);
               pIserrno = ERECCHANGED;
            }
         }
      }
      if (Return) {
         if ( fd != 0 ) 
            fd->id_isam = fsId.id_isam;
         else if (fsId.id_isam >= 0)
            pIsclose (fsId.id_isam);
      }
      break; 
   default:
      PError::InternalError ("Invalid type in exist");
      break;
   }
   return Return;
}

RelObject *
DataBase::GetRelObject(const PString &name)
{
   RelObject *Return = 0; 

   for (Return = Relations.GetFirst (); Return; Return = Relations.GetNext ())
      if (Return->isName(name))
         return Return;
   for (Return = RelBases.GetFirst (); Return; Return = RelBases.GetNext ())
      if (Return->isName(name))
         return Return;
   return Return;
}

Relation *
DataBase::GetRelation(const PString &name, SqlParser *p)
{
   Relation *r;
   RelObject *ro;
   Relation *Return = 0;
   Error error = SQL_SUCCESS;
   
   for (r = Relations.GetFirst (); r; r = Relations.GetNext ())
      if (r->isName(name))
         return r;
   for (ro = RelBases.GetFirst (); ro; ro = RelBases.GetNext ())
      if (ro->isName(name) && ro->IsA(RelBase::Class)) { 
         r = new Relation (*(RelBase *)ro);
         if (r->ReadFields(DatadictFields) == SQL_SUCCESS) {
            Relations.AddItem (r);
            Return = r;
         } else
            delete r;
         break;
      }
   if (error == SQL_SUCCESS && Return == 0 && p)
      p->PrintError (E_SYNTAX_ERROR_S02, name);
   return Return;
}

void
DataBase::RemoveRelation(Relation *rel)
{
   Relation *r;
   Relation *Return = 0;
   Error error = SQL_SUCCESS;
   
   for (r = Relations.GetFirst (); r; r = Relations.GetNext ())
      if (r == rel) {
         Relations.DeleteCurrent();
         delete rel;
         break;
      }
}

Error
DataBase::readView (ViewBase *vb, const PString &name)
{
   long blkLen;
   long namLen;
   char *text;
   int offset;
   int toCpy;
   StmtAttr *saText;
   StmtAttr  *sa;
   Error Return = SQL_ERROR;
   char * valChar;
   double valNum;
   long len;
   
   Return = DatadictViews->Open();
   if (Return != SQL_SUCCESS) {
      PrintError (E_CONN_EXCEPT_001, "Error opening __VIEWS", DatadictViews->getStatus());
      return Return; 
   }
   if (DatadictViews->getCurrIndex() != 0) {
      StartEnd *se;
      StartEndList *sel = DatadictViews->getCurrIndex()->AttrsAndCond.GetCurrent();
      se = sel->GetFirst();
      if (se->CondStart == 0) {   
         sa = DatadictViews->GetFieldAt(DV_NAME);
         se->AddCondToDelete (new Expression (0, sa), (toknum)'=',
                              new StmtAttr (*sa));
      }
      se->CondStart->getValue()->FromString (name.gets()); 
      se->CondEnd->getValue()->FromString (name.gets());
   }
   Return = DatadictViews->Start();
   if (Return == EENDFILE || Return == ENOREC)
      Return = SQL_SUCCESS;
   if (Return != SQL_SUCCESS) {
      PrintError (E_CONN_EXCEPT_001, "Error reading __VIEWS", Return);
   } else {
      if (DatadictViews->ReadNext(PFalse) == SQL_SUCCESS) {
         blkLen = DatadictViews->GetFieldAt(DV_TEXT)->getAttr()->GetLen();
         sa = DatadictViews->GetFieldAt(DV_NAME);
         sa->ToChar(&valChar, len);
         remSpacesToUpper(valChar, sa->getAttr()->GetLen() - 1);
         if (name == (const char *)valChar) {
            sa = DatadictViews->GetFieldAt(DV_PROG);
            sa->ToDouble(valNum, len);
            namLen = strlen (valChar);
            sa = DatadictViews->GetFieldAt(DV_LENG);
            sa->ToDouble(valNum, len);
            saText = DatadictViews->GetFieldAt(DV_TEXT);
            saText->ToChar(&valChar, len);
            if (valChar[0] == 'C') { // Statement completo
               toCpy = (int)valNum;
               text = new char[toCpy + 1];
               offset = 0;
            } else {
               toCpy = (int)valNum + namLen + 16;//"CREATE VIEW " " AS "
               text = new char[toCpy + 1];
               memcpy (text, "CREATE VIEW ", 12);
               offset = 12;
               memcpy (&text[offset], name.gets(), namLen);
               offset += namLen;
               memcpy (&text[offset], " AS ", 4);
               offset += 4;
            }
            text[toCpy] = 0;
            toCpy -= offset;
            memcpy (&text[offset], valChar, blkLen > toCpy ? toCpy : blkLen);
            for (offset += blkLen, toCpy -= blkLen; toCpy > 0; 
                 offset += blkLen, toCpy -= blkLen) {
               if ((Return = DatadictViews->ReadNext(PFalse)) == SQL_SUCCESS) {
                  saText->ToChar(&valChar, len);
                  memcpy (&text[offset], valChar, blkLen > toCpy ? toCpy : blkLen);
               } else {
                  PrintError (E_CONN_EXCEPT_001, "Error reading __VIEWS", Return);
                  delete text;
                  break; 
               }
            }
            vb->setRelCondWhere(text);
            delete text;
            Return = (Error) EENDFILE;
         }
      }
      DatadictViews->Close(); 
   }
   return Return;
}

RelView *
DataBase::GetRelView(const PString &name)
{
   RelView *Return = 0;
   
   for (Return = Views.GetFirst (); Return; Return = Views.GetNext ())
      if (Return->isName(name))
         break;
   if (!Return) {
      ViewBase *bv;
      for (bv = (ViewBase *) ViewBases.GetFirst (); bv;
           bv = (ViewBase *) ViewBases.GetNext ()) {
         if (bv->isName(name)) {
            ViewBases.DeleteCurrent();
            if (readView (bv, name) == EENDFILE) {
               SqlParser *cv = new SqlParser(this, 0);
               const PString &cmd = bv->getRelCondWhere();
               if (cv->Parse(cmd.gets(), cmd.size()) == SQL_SUCCESS) {
                  cv->doCreateView (bv->getCheckOption());
                  Return = GetRelView(name);
               }
               delete bv;
               delete cv;
               break;
            }
         }
      }
   }

   return Return;
}

void
DataBase::SetAutoCommit (PBool onoff)
{
  if (onoff != Autocommit) {
     Autocommit = onoff;
     if (onoff) {
        Commit();
     } else {
        InTransact = PFalse;
     }
  }        
}

Error
DataBase::Transact (SqlParser *stmt)
{
   Error Return = SQL_SUCCESS;
   if (!Autocommit && !InTransact) {
      if (PTransaction && pIsbegin() < 0) {
         if (stmt)
            stmt->PrintError (E_GENERAL_ERROR_000,"Transaction error " +
                                     ToPString(pIserrno));
         Return = SQL_ERROR;
      } else
         InTransact = PTrue;
   }
   return Return;
}

Error
DataBase::Commit ()
{
   Error Return = SQL_SUCCESS;
   if (!Autocommit && InTransact) {
      if (PTransaction && pIscommit() < 0) {
         PrintError (E_GENERAL_ERROR_000,"Transaction error " +
                      ToPString(pIserrno));
         Return = SQL_ERROR;
      }
      InTransact = PFalse;
   }
   return Return;
}

Error
DataBase::Rollback ()
{
   Error Return = SQL_SUCCESS;
   if (!Autocommit && InTransact) {
      if (PTransaction && pIsrollback() < 0) {
         PrintError (E_GENERAL_ERROR_000,"Transaction error " +
                      ToPString(pIserrno));
         Return = SQL_ERROR;
      }
      InTransact = PFalse;
   }
   return Return;
}
