/*
_____       _    _    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 "sqlpars.h"
static char rcsid[] = "$Id: sqlpars.cpp 5.10 2001/09/24 13:34:30 picoSoft Exp Marco $";
static char rcsidh[] = sqlpars_h;
                     
# include "sqltype.h"
# include "attribut.h"
# include "piteratr.h"
extern int Int2OdbcType (int t, SignType s, DateTime d);
extern "C" {
# include <string.h>
# include <fcntl.h>
# include <time.h>
# ifdef MSDOS
# include <windows.h>
# endif 
};
                    
static fix_token SqlWord[] = { 
{"ABS", ABS},
{"ACOS", ACOS},
{"ADD", ADD},
{"ALL", ALL},
{"ALTER", ALTER},
{"AND", AND},
{"AS", AS},
{"ASC", ASC},
{"ASCII", ASCII},
{"ASIN", ASIN },
{"ATAN", ATAN },
{"AVG", AVG},
{"BETWEEN", BETWEEN},
{"BLOB", BLOB_},
{"BY", BY},
{"CALL", CALL},
{"CEILING", CEILING},
{"CHAR", CHAR_},
{"CHECK", CHECK},
{"CLOB", CLOB},
{"COLUMN", COLUMN},
{"CONCAT", CONCAT},
{"CONVERT", CONVERT},
{"COS", COS},
{"COT", COT},
{"COUNT", COUNT},
{"CREATE", CREATE},
{"CURRENT", CURRENT},
{"CURRENT_TIMESTAMP", CURRENT_TIMESTAMP},
{"CURRENT_TIMESTAMP_28", CURRENT_TIMESTAMP_28},
{"DATE", DATE_},
{"DAYOFMONTH", DAYOFMONTH},
{"DEFAULT", DEFAULT},
{"DELETE", DELETE_},
{"DESC", DESC},
{"DISTINCT", DISTINCT},
{"DOUBLE", DOUBLE_},
{"DROP", DROP},
{"EXISTS", EXISTS},
{"EXP", EXP},
{"FILLER", FILLER},
{"FLOAT", FLOAT_},
{"FLOOR", FLOOR},
{"FOR", FOR},
{"FOREIGN", FOREIGN},
{"FROM", FROM},
{"FULL", FULL},
{"GRANT", GRANT},
{"GROUP", GROUP},
{"HAVING", HAVING},
{"HOUR", HOUR},
{"IN", IN_},
{"INDEX", INDEX},
{"INNER", INNER},
{"INSERT",INSERT},
{"INT", INT_},
{"INTEGER", INTEGER},
{"INTO", INTO},
{"IS", IS},
{"JOIN", JOIN},
{"KEY", KEY},
{"LCASE", LCASE},
{"LEFT", LEFT},
{"LENGTH", LENGTH},
{"LIKE", LIKE},
{"LIMIT", LIMIT},
{"LOCATE", LOCATE},
{"LOG", LOG},
{"LOG10", LOG10},
{"LONGVARCHAR", LONGVARCHAR},
{"LOWER", LOWER},
{"LTRIM", LTRIM},
{"MAX", MAX},
{"MILLISECOND", MILLISECOND},
{"MIN", MIN},
{"MINUTE", MINUTE},
{"MONTH", MONTH},
{"NOT", NOT},
{"NULL", NULL_},
{"NUMERIC", NUMERIC},
{"OF", OF},
{"OFFSET", OFFSET},
{"ON", ON},
{"OPTION", OPTION},
{"OR", OR},
{"ORDER", ORDER},
{"OUTER", OUTER},
{"PRIMARY", PRIMARY},
{"REAL", REAL},
{"REFERENCES", REFERENCES},
{"RENAME", RENAME},
{"REPLACE", REPLACE},
{"REVOKE", REVOKE},
{"RIGHT", RIGHT},
{"ROUND", ROUND},
{"RTRIM", RTRIM},
{"SECOND", SECOND},
{"SELECT", SELECT},
{"SERIAL", SERIAL},
{"SET", SET},
{"SIGN", SIGN},
{"SIN", SIN},
{"SMALLINT", SMALLINT},
{"SPACE", SPACE},
{"SQRT", SQRT},
{"SUBSTRING", SUBSTRING},
{"SUM", SUM},
{"TABLE", TABLE},
{"TAN", TAN},
{"TIME", TIME},
{"TIMESTAMP", TIMESTAMP},
{"TRUNCATE", TRUNCATE},
{"UCASE", UCASE},
{"UNION", UNION},
{"UNIQUE", UNIQUE},
{"UPDATE", UPDATE},
{"UPPER", UPPER},
{"VALUES", VALUES},
{"VARCHAR", VARCHAR},
{"VIEW", VIEW},
{"WHERE", WHERE},
{"WITH", WITH},
{"YEAR", YEAR}
};

PCLASSID(SqlParser)

SqlParser::SqlParser (DataBase *db, SqlParser *sqo, StmtRel *uv) :
WhereStack (this, (Expression::Method)(&SqlParser::GetTableName), PFalse),
HavingStack (this, (Expression::Method)(&SqlParser::GetViewName), PTrue),
LocalView(*db),
View(uv ? *uv : LocalView)
{
   DB = db;
   DB->StmtList.AddItem (this);
   Parsed = PFalse;
   forUpdate = PFalse;
   Command = (toknum)0;
   Processed = 0;
   GroupList = 0;
   MaxRows = 0;
   Limit = 0;
   Offset = 0;
   Fetched = 0;
   rowSetSize = 1;
   explicitJoin = "";
   GroupByView = 0;
   subQueryOf = sqo;
   insertQuery = 0;
   pUpdStmtRel = 0;
   currentTimestamp = 0;
   isSortDesc = PFalse;
   sqlCmd = 0;
   cursorName.prints ("C_%lX", (long) this);
}

SqlParser::~SqlParser()
{
   SqlParser *s;
   CField *cf;

   Clear ();
   Unbind();
   for (cf = Params.GetFirst(); cf != 0; cf = Params.DeleteCurrent())
      delete cf;
   nParams = 0;

   for (s = DB->StmtList.GetFirst (); s != 0;
        s = DB->StmtList.GetNext ())
      if (s == this) {
         DB->StmtList.DeleteCurrent ();
         break;
      }
}

void
SqlParser::Clear()
{
   StmtAttr *sa;
   StmtRel *sr;
   CField *cf;
   OrderItem *oi;
   Expression *ex;
   StmtRelView *sv;
   SqlParser *sp;
   
   ClearExec ();
   WhereStack.Clear();
   HavingStack.Clear();
   for (ex = Selected.GetFirst(); ex != 0; ex = Selected.DeleteCurrent())
      delete ex;
   for (sa = Fields.GetFirst(); sa != 0; sa = Fields.DeleteCurrent())
      ;
   for (sr = FromList.GetFirst(); sr != 0; sr = FromList.DeleteCurrent())
      delete sr;
   for (sv = ViewList.GetFirst(); sv != 0; sv = ViewList.DeleteCurrent())
      delete sv;
   for (oi = OrderList.GetFirst(); oi != 0; oi = OrderList.DeleteCurrent())
      ;
   isSortDesc = PFalse;
   if (GroupList != 0) {
      for (oi = GroupList->GetFirst(); oi != 0; oi = GroupList->DeleteCurrent())
         if (oi->ex)
            delete oi->ex;
      delete GroupList;
      GroupList = 0;
   }
   for (sa = Info.GetFirst(); sa; sa = Info.DeleteCurrent())
      delete sa;
   for (cf = Params.GetFirst(); cf != 0; cf = Params.GetNext())
      cf->UnBind ();
   LocalView.DeleteRel();
   if (GroupByView) {
      delete GroupByView;
      GroupByView = 0;
   }
   for (sp = unions.GetFirst(); sp; sp = unions.DeleteCurrent())
      delete sp;
   if (insertQuery) {
      delete insertQuery;
      insertQuery = 0;
   }
   pUpdStmtRel = 0;
   Parsed = PFalse;
   forUpdate = PFalse;
   Command = (toknum)0;
   explicitJoin = "";
   if (sqlCmd != 0) {
      delete sqlCmd;
      sqlCmd = 0;
   }
   Limit = 0;
   Offset = 0;
}

void
SqlParser::ClearExec()
{  
   StmtRel *sr;
   
   for (sr = FromList.GetFirst(); sr != 0; sr = FromList.GetNext ())
      sr->Close();
   if (View.getRel() != 0)
      View.Erase();

   DelError ();
   Processed = 0;
   Fetched = 0;
}

void
SqlParser::Unbind ()
{
   CField *cf;
   
   for (cf = BindFields.GetFirst(); cf != 0; cf = BindFields.DeleteCurrent())
      delete cf;
}
 
void
SqlParser::ResetParams ()
{
   CField *cf;
   
   for (cf = Params.GetFirst(); cf != 0; cf = Params.GetNext())
      cf->reset();
} 

CField *
SqlParser::NewParam ()
{
   CField *Return;
   
   if (subQueryOf) {
      Return = subQueryOf->NewParam();
   } else {
      while (nParams >= Params.GetItemNum ())
         Params.AddItem (new CField(*DB));
      Return = Params.GetAt(nParams++);
   }
   return Return;
}                                          

Error
SqlParser::Parse (char *cmd, long len)
{
   Token *t;
   Error Return = SQL_ERROR;

   Clear ();

   if (len >= 0)
      sqlCmd = new char[len + 1];
   else
      sqlCmd = new char[(len = strlen(cmd)) + 1];
   if (DB->IsIntl())
      DB->IntlToServer (cmd, sqlCmd, (int)len);
   else
      memcpy (sqlCmd, cmd, (int)len);
   sqlCmd[len] = 0;
   Lex l(sqlCmd);

   PutKeyToken (l);

   t = l.GetToken ();
   if (t && t->token == '(')
      t = l.GetToken ();
   if (t == 0) {
      PrintError (E_GENERAL_ERROR_090, "");
      return Return;
   }
   nParams = 0;
   switch (t->token) {
   case '{':
      t = l.GetToken ();
      if (t->token != CALL) {
         PrintError (E_SYNTAX_ERROR_000, "Invalid keyword <{" + t->word + ">" );
         break;
      } /* PASSTHRU */
   case CALL:
      PrintError (E_SYNTAX_ERROR_000, "Procedures not supported: <" + t->word + ">" );
      break;
   case ALTER:
      if ((t = l.GetToken ()) == 0) {
         PrintError (E_SYNTAX_ERROR_000,
              "Expected <TABLE>, found end statement");
      } else {
         if (t->token == TABLE) {
            Return = AlterTable (l);
         } else {
            PrintError (E_SYNTAX_ERROR_000,
                 "Expected <TABLE>, found <" + t->word + ">" );
         }
      }
      break;
   case GRANT:
   case REVOKE:
      PrintError (E_SYNTAX_ERROR_000, "Statement not supported: <" + t->word + ">" );
      break;
   case DROP:
      if ((t = l.GetToken ()) == 0) {
         PrintError (E_SYNTAX_ERROR_000,
              "Expected <TABLE> or <INDEX>, found end statement");
      } else {
         if (t->token == TABLE) {
            Command = DROP_TABLE;
            Return = DropTable (l);
         } else if (t->token == INDEX) {
            Command = DROP_INDEX;
            Return = DropIndex (l);
         } else if (t->token == VIEW) {
            Command = DROP_VIEW;
            Return = DropView (l);
         } else {
            PrintError (E_SYNTAX_ERROR_000,
                 "Expected <TABLE> or <INDEX>, found <" + t->word + ">" );
         }
      }
      break;
   case CREATE:
      Command = CREATE;
      Return = Create (l);
      break;
   case DELETE_:
      Command = DELETE_;
      Return = Delete (l);
      break;
   case INSERT:
      Command = INSERT;
      Return = Insert (l);
      break;
   case REPLACE:
      Command = REPLACE;
      Return = Insert (l);
      break;
   case SELECT:
      Command = SELECT;
      Return = Select (l);
      break;
   case UPDATE:
      Command = UPDATE;
      Return = Update (l);
      break;
   default:
      PrintError (E_SYNTAX_ERROR_000, "Invalid keyword <" + t->word + ">" );
      break;
   }
   if (Return == SQL_SUCCESS || Return == SQL_SUCCESS_WITH_INFO)
      Parsed = PTrue;
   return Return;
} 

inline PBool
IsField (const PString & table, const PString & name, StmtRel *sr)
{
   if (sr && (table.size() == 0 || table == sr->getName()))
      if (sr->getByName (name))
         return PTrue;
   return PFalse;

}

Error
SqlParser::BuildJoin (Lex &l, PString &cond, StmtRel *left, StmtRel *right)
{
   Error Return = SQL_SUCCESS;
   Token *t;
   PString table, name;
   int openPar = 0;
   
   do {
      t = l.GetToken();
      if (t) {
         switch (t->token) {
         case USER_NAME:
            table = "";
            name = t->word;
            cond += t->word;
            explicitJoin += t->word;
            if (t = l.GetToken()) {
               if (t->token == '.') {
                  cond += '.';
                  explicitJoin += '.';
                  table = name;
                  if (t = l.GetToken()) {
                     if (t->token == USER_NAME) {
                        cond += t->word;
                        explicitJoin += t->word;
                        name = t->word;
                     } else {
                        Return = SQL_ERROR;
                        PrintError (E_SYNTAX_ERROR_000, "Expected user name, found <" + t->word + ">");
                     }
                  } else {
                     Return = SQL_ERROR;
                     PrintError (E_SYNTAX_ERROR_000, "Expected user name, found end of statement");
                  }
               } else {
                  l.UngetToken ();
               }
            }
            break;
         case '=':
            cond += t->word;
            explicitJoin += (char)_OJ_;
            break;
         case AND:
            cond += " AND ";
            explicitJoin += " AND ";
            break;
         case '(':
            openPar++;
            break;
         case ')':
            if (openPar > 0)
               openPar--;
            else {
               l.UngetToken();
               t = 0;
            }
            break;
         default:
            if (t->token != '}' && t->token != ON &&
                t->token != WHERE &&
                t->token != GROUP &&
                t->token != ORDER &&
                t->token != SET &&
                t->token != UNION &&
                t->token != VALUES &&
                t->token != LIMIT &&
                t->token != OFFSET &&
                t->token != FOR) {
               PrintError (E_SYNTAX_ERROR_000, "Invalid token <" + t->word + "> in JOIN");
               Return = SQL_ERROR;
            }
            l.UngetToken();
            t = 0;
            break;
         }
      }
   } while (t);
   return Return;
}

Error
SqlParser::BuildWhere (Token *t, Lex &l)
{
   Error Return = SQL_SUCCESS;
   PString whereTab;
   StmtRelIterator sri(FromList);
   PString viewWheres;

   if (ViewList.GetItemNum() > 0) {
      StmtRelView *srv;
      for (srv = ViewList.GetFirst(); srv; ) {
         if (srv->getWhere().size() > 0) {
            viewWheres += "(";
            viewWheres += srv->getWhere();
            viewWheres += ")";
         }
         srv = ViewList.GetNext();
         if (srv && srv->getWhere().size() > 0 && viewWheres.size() > 0)
            viewWheres += " AND ";
      }
      if (viewWheres.size () > 0) {
         Lex l1(viewWheres.gets());
         PutKeyToken (l1);
         Return = WhereStack.Parse (l1, 0);
      }
   }
   if (Return == SQL_SUCCESS && t && t->token == WHERE) {
      if (ViewList.GetItemNum() > 0) {
         TokenList w1;
         substViewWhere (w1, l);
         Lex l1(w1, PTrue);
         Return = WhereStack.Parse (l1, 0);
         for (t = w1.GetFirst(); t; t = w1.DeleteCurrent())
            delete t;
      } else {
         Return = WhereStack.Parse (l, t);
      }
   } else
      l.UngetToken();

   if (Return == SQL_SUCCESS && explicitJoin.size () > 0) {
      if (whereTab != "")
         whereTab += " AND "; 
      whereTab += explicitJoin;
      for (sri.reset(); *sri != 0 && Return == SQL_SUCCESS; sri++) {
         if ((*sri)->join.size() > 0) {
            Lex j((*sri)->join.gets());
            PutKeyToken (j);
            (*sri)->joinCond = new Where (this,
                                      (Expression::Method)(&SqlParser::GetTableName), PFalse);
            Return = (*sri)->joinCond->Parse (j, 0);
         }
      }
   }

// O^O Da provare
   for (sri.reset(); *sri != 0; sri++) {
       if ((*sri)->getRelCond()) {
          if (whereTab != "")
             whereTab += " AND "; 
          whereTab += (*sri)->getRelCondWhere();
       } 
   }

// O^O Da provare
   if (Return == SQL_SUCCESS && whereTab.size() > 0) {
      Lex wt(whereTab.gets());
      PutKeyToken (wt);
      Return = WhereStack.Parse (wt, 0);
   } 
   
   return Return;
}

Error
SqlParser::verifyGroup (Expression *ex)
{
   Error Return = SQL_SUCCESS;
   StmtAttrIterator dbf(ex->getStmtAttr ());
 
   for ( ; *dbf; dbf++)
      if ((Return = verifyGroup (*dbf)) != SQL_SUCCESS)
         break;
   return Return;
}

Error
SqlParser::verifyGroup (StmtAttr *sa)
{
   Error Return = SQL_SUCCESS;
   OrderItem *oi;

   if (!sa->isAggregate ()) {
      for (oi = GroupList->GetFirst(); oi != 0; oi = GroupList->GetNext())
         if (oi->sa->getAttr()->getSignature() == sa->getAttr()->getSignature())
            break;
         if (oi == 0) {
            PrintError (E_SYNTAX_ERROR_000,
                        "<" +  sa->getName() + "> not part of aggregate function or grouping");
            Return = SQL_ERROR;
         }
      }
   return Return;
}

Error
SqlParser::BuildHaving (Token *t, Lex &lo)
{
   Error Return = SQL_SUCCESS;
   Condition *c;
   Expression *ex;

   TokenList w1;
   substViewWhere (w1, lo);
   Lex ln(w1, PTrue);
   Return = HavingStack.Parse (ln, 0);
   if (Return == SQL_SUCCESS )
      for (c = HavingStack.GetFirst (); c != 0; c = HavingStack.GetNext()) {
         if (!c->isAndOr()) {
            if (c->left) {
               if (c->left->getAggregate() != NULL_) {
                  const PString &tmp = c->left->getName();
                  Fields.AddItem (c->left->getAggrValue());
                  GroupByView->AddNewField (c->left->getValue(), &tmp);
                  ex = new Expression (this, View.AddNewInvis (c->left->getAggrValue()));
                  Selected.AddItem(c->left); //Selected cancella le proprie espressioni
                  c->left = ex;
               }
            }
            if (c->right) {
               if (c->right->getAggregate() != NULL_) {
                  const PString &tmp = c->right->getName();
                  Fields.AddItem (c->right->getAggrValue());
                  GroupByView->AddNewField (c->right->getValue(), &tmp);
                  ex = new Expression (this, View.AddNewInvis (c->right->getAggrValue()));
                  Selected.AddItem(c->right); //Selected cancella le proprie espressioni
                  c->right = ex;
               }
            }
         }
      }
   
   for (t = w1.GetFirst(); t; t = w1.DeleteCurrent())
      delete t;
   return Return;
}

Error
SqlParser::Select (Lex &l)
{
   Token *t = 0, *from = 0, *afterFrom = 0;
   Error Return = SQL_ERROR;
   PBool Distinct = PFalse;
   OrderItem *oi;
   Expression *ex;

   while ((t = l.GetToken()) != 0) {
      if (t->token == FROM) {
         from = t;
         Return = From (l, PTrue);
         afterFrom = l.GetToken();
         if (Return == SQL_SUCCESS && afterFrom
                                   && afterFrom->token != WHERE 
                                   && afterFrom->token != GROUP
                                   && afterFrom->token != ORDER
                                   && afterFrom->token != SET
                                   && afterFrom->token != UNION
                                   && afterFrom->token != VALUES
                                   && afterFrom->token != LIMIT
                                   && afterFrom->token != OFFSET
                                   && afterFrom->token != FOR) {
            PrintError (E_SYNTAX_ERROR_000, "Invalid token :" +  afterFrom->word);
            Return = SQL_ERROR;
         }
         break;
      }
   }
   if (Return != SQL_SUCCESS) {
      if (from == 0) {
         PrintError (E_SYNTAX_ERROR_000, "FROM keyword not found in SELECT statement" );
      }
      return Return;
   }

   l.Rewind();
   t = l.GetToken (); // SELECT or (;
   if (t->token == '(')
      t = l.GetToken (); // SELECT;
   t = l.GetToken (); // DISTINCT/ALL ?
   if (t) {
      if (t->token == DISTINCT || t->token == UNIQUE)
         Distinct = PTrue;
      else if (t->token == ALL)
         ;
      else
         l.UngetToken();
   }
   TokenList selFields;
   substViewSelect (selFields, l);
   Lex sel(selFields, PTrue);
   t = sel.GetToken ();
   if (t == 0) {
      Return = SQL_ERROR;
      PrintError (E_SYNTAX_ERROR_000, "Invalid SELECT list" );
      return Return;
   }
   if (t->token == (toknum)'*') { //TODO da togliere
      if ((t = sel.GetToken()) == from) {
         AddAllExpr ();
         sel.UngetToken ();
      } else {
         Return = SQL_ERROR;
         PrintError (E_SYNTAX_ERROR_000, "Invalid field list in SELECT statement" );
         return Return;
      }
   } else {
      sel.UngetToken ();
      do {
         ex = new Expression (*this, sel, (Expression::Method)(&SqlParser::GetTableName), PTrue);
         if (ex->isOk()) {
            Selected.AddItem(ex);
            Fields.AddItem (ex->getAggrValue());
            if (ex->getAggrValue()->isAggregate() && GroupList == 0)
               GroupList = new OrderItemCopyList;
            if (ex->hasDistField()) {
               StmtAttrIterator dbf(ex->getDistField ());
               for ( ; *dbf; dbf++) {
                  OrderItem oi; 
                  oi.desc = 0;
                  oi.sa = (*dbf);
                  GroupList->AddItem (oi);
               }
            }
            t = sel.GetToken ();
            if (t != 0 && t->token != (toknum) ',' && t != from) {
               Return = SQL_ERROR;
               PrintError (E_SYNTAX_ERROR_000, "Expected <,> found <" + t->word + ">");
               return Return;
            }
         } else {
            Return = SQL_ERROR;
            delete ex;
            return Return;
         }
      } while (t && t != from && Return == SQL_SUCCESS);
   }
   for (t = selFields.GetFirst(); t; t = selFields.DeleteCurrent())
      delete t;

   while ((t = l.GetToken ()) != 0 && t != afterFrom)
      ; 
   if (Return != SQL_ERROR) {
      if ((Return = BuildWhere(t, l)) != SQL_SUCCESS)
         return Return;
       else
         t = l.GetToken();
   } else
      return Return;
   if (&LocalView != &View) {
      StmtAttr *sa;
      for (ex = Selected.GetFirst(), sa = View.GetFirstField();
           ex && sa;
           ex = Selected.GetNext(), sa = View.GetNextField())
         if (ex->getValue()->getAttr()->IsNumeric() != sa->getAttr()->IsNumeric()) {
            PrintError (E_DATA_EXCEPT_018, "Columns in UNION have different type");
            return SQL_ERROR;
         }
      if (ex != 0 || sa != 0) {
         PrintError (E_DATA_EXCEPT_018, "Columns number in UNION are different");
         return SQL_ERROR;
      }
   } else {
      View.DeleteRel();

      View.SetRel();
      for (ex = Selected.GetFirst(); ex; ex = Selected.GetNext()) {
         const PString &tmp = ex->getName();
         View.AddNewField (ex->getAggrValue(), &tmp);
      }
   }

   if (t && t->token == FOR) {
      if ((Return = SelectForUpdate (t, l, Distinct)) != SQL_SUCCESS)
         return Return;
   } else {
      if ((Return = SelectNotForUpdate (t, l, Distinct)) != SQL_SUCCESS)
         return Return;
   }

   StmtRel *sr;   
   WeighIndex (WhereStack);
   if (explicitJoin.size() == 0)
      SortRelation (FromList);
   StmtRel *prev;
   
   for (prev = 0, sr = FromList.GetFirst(); sr != 0;
        prev = sr,sr = FromList.GetNext ()) {
      sr->ChooseIndex (*this);
   }
   if (&LocalView == &View)
      View.BufferStmtRelAlloc();
   if (OrderList.GetItemNum () > 0) {
      View.DefineOrderBy(OrderList, PTrue);
      if (GroupList == 0) {
         OrderItem *firstOi = OrderList.GetFirst ();
         for (IndexPart *ip = firstOi->sa->getAttr()->GetFirstIndex ();
              ip != 0;
              ip = firstOi->sa->getAttr()->GetNextIndex ()) {
            int part = 1;
            SearchIndex *si = new SearchIndex(ip->getNumber());
            sr = FromList.GetFirst();
            oi = OrderList.GetFirst ();
            isSortDesc = oi->desc;
            for ( ; oi != 0; oi = OrderList.GetNext()) {
               if (oi->desc == isSortDesc &&
                   oi->sa->getAttr()->getParent () == sr->getRel() &&
                   oi->sa->getAttr()->IsIndex (ip->getNumber(), part)) {
                  si->AddAttr (oi->sa);
                  part++;
               } else
                  break;
            }
            if (oi == 0) {
               if (sr->getCurrIndex() == 0) {
                  si->initKey ();
                  sr->AddIndex (si);
                  sr->setCurrIndex(si);
                  for (oi = OrderList.GetFirst () ; oi != 0; oi = OrderList.DeleteCurrent())
                     ;
               } else {
                  if (sr->getCurrIndex()->getIndexNum () == si->getIndexNum ())
                     for (oi = OrderList.GetFirst () ; oi != 0; oi = OrderList.DeleteCurrent())
                        ;
                  delete si;
               }
               break;
            } else {
               isSortDesc = PFalse;
               delete si;
            }
         }
      }
   }
   if (GroupList != 0 && GroupList->GetItemNum() > 0) {
      GroupByView->BufferStmtRelAlloc(); 
      GroupByView->DefineGroupBy(*GroupList, PTrue);
   } // else if (Distinct) { Da Togliere O^O
     //  View.DefineGroupBy(*GroupList, PFalse);
   // }
   /*
   if (GroupList != 0 && GroupList->GetItemNum() > 0) 
      View.DefineGroupBy(*GroupList, PTrue);
   else if (Distinct)
      View.DefineGroupBy(*GroupList, PFalse);
   */          
   return Return;
}

Error
SqlParser::SelectForUpdate (Token *t, Lex &l, PBool Distinct)
{
   Error Return = SQL_SUCCESS;

   if (t = l.GetToken()) {
      if (t->token != UPDATE) {
         PrintError (E_SYNTAX_ERROR_000, "Expected <UPDATE> found <" +  t->word + ">");
         Return = SQL_ERROR;
         return Return;
      }
   } else {
      PrintError (E_SYNTAX_ERROR_000, "Expected <UPDATE> found <end statement>");
      Return = SQL_ERROR;
      return Return;
   }
   if (Distinct) {
      PrintError (E_SYNTAX_ERROR_000, "DISTINCT not allowed in a FOR UPDATE select");
      Return = SQL_ERROR;
      return Return;
   }
   if (GroupList) {
      PrintError (E_SYNTAX_ERROR_000, "Function not allowed in a FOR UPDATE select");
      Return = SQL_ERROR;
      return Return;
   }
   if (FromList.GetItemNum() != 1) {
      PrintError (E_SYNTAX_ERROR_000, "Only 1 tables allowed in a FOR UPDATE select");
      Return = SQL_ERROR;
      return Return;
   }
   if (t = l.GetToken()) {
      if ((t->token != ';' && t->token != ')') || (t = l.GetToken())) {
         PrintError (E_SYNTAX_ERROR_000, "Unexpected <" + t->word + " after FOR UPDATE");
         Return = SQL_ERROR;
         return Return;
      }
   }
   forUpdate = PTrue;

   return Return;
}

Error
SqlParser::SelectNotForUpdate (Token *t, Lex &l, PBool Distinct)
{
   Error Return = SQL_SUCCESS;
   Expression *ex;
   Attribute *a;
   StmtAttr *sa;
   OrderItem *oi;
   
   if (Distinct ||
       (t && t->token == GROUP) ||
        GroupList && GroupList->GetItemNum() > 0) {
      GroupByView = new StmtRel (*DB);
      GroupByView->SetRel();
      for (ex = Selected.GetFirst(); ex; ex = Selected.GetNext()) {
         const PString &tmp = ex->getName();
         GroupByView->AddNewField (ex->getValue(), &tmp);
      }
   }

   if (t && t->token == GROUP) {
      if (GroupList == 0)
         GroupList = new OrderItemCopyList;
      Return = OrderGroupBy(l, *GroupList);
      t = l.GetToken();        
      if (Return != SQL_ERROR && t != 0) {
         if (t->token == HAVING) {
            if ((Return = BuildHaving (t, l)) != SQL_SUCCESS)
               return Return;
            t = l.GetToken();
         }
      }
   }

   if (GroupList != 0) {
      for (oi = GroupList->GetFirst (); oi != 0; oi = GroupList->GetNext()) {
         for (sa = Fields.GetFirst (); sa != 0; sa = Fields.GetNext ())
            if (oi->sa == sa)
               break;
         if (sa == 0) {
            Fields.AddItem (oi->sa);
            Selected.AddItem (new Expression (this, oi->sa));
            GroupByView->AddNewField (oi->sa, 0);
            View.AddNewInvis (oi->sa);
         }
      }
   }
   if (t && t->token == ')')
      t = l.GetToken();
   if (Return != SQL_ERROR && t && t->token == UNION) {
      SqlParser *sp;
      while (Return != SQL_ERROR && t && t->token == UNION) {
         sp = new SqlParser (DB, this, &View);
         TokenList tl;
         if (t = l.GetToken())
            if (t->token != ALL)
               l.UngetToken();
         while ((t = l.GetToken()) && t->token != UNION &&
                                   t->token != ORDER)
            tl.AddItem(t);
         Lex unSel(tl, PTrue);
         Return = sp->Select(unSel);
         unions.AddItem (sp);
         if (Return != SQL_SUCCESS)
            MoveStmtError (this, sp);
      }
      StmtAttr *sa;
      int cnt = 1;
      for (sa = View.GetFirstField(); sa; sa = View.GetNextField())
         sa->setName ("Union_" + ToPString (cnt++));
   }
   if (Return != SQL_ERROR && t) {
      if (t->token == ORDER)
         if((Return = OrderGroupBy(l, OrderList)) != SQL_SUCCESS)
            return Return;
         else
            t = l.GetToken();
      if (t && t->token == LIMIT)
         if ((t = l.GetToken()) && t->token == NUMBER) {
            Limit = atol (t->word.gets());
            t = l.GetToken();
         } else {
            PrintError (E_SYNTAX_ERROR_000, "Invalid LIMIT clause");
            return Return = SQL_ERROR;
         }
      if (t && t->token == OFFSET)
         if ((t = l.GetToken()) && t->token == NUMBER) {
            Offset = atol (t->word.gets());
            t = l.GetToken();
         } else {
            PrintError (E_SYNTAX_ERROR_000, "Invalid OFFSET clause");
            return Return = SQL_ERROR;
         }
   }

   if (GroupList != 0) {
      for (ex = Selected.GetFirst (); ex != 0; ex = Selected.GetNext())
         if (!ex->getAggrValue()->isAggregate ()) {
            if ((Return = verifyGroup (ex)) != SQL_SUCCESS)
               return Return;
         }
/*
      if (OrderList.GetItemNum() > 0)
         for (oi = OrderList.GetFirst (); oi != 0; oi = OrderList.GetNext()) {
            for (sa1 = View.GetFirstField(); sa1 != 0;
                 sa1 = View.GetNextField())
               if (sa1->getAttr()->getSignature() == oi->sa->getAttr()->getSignature())
                  break;
            if (sa1 == 0) {
               PrintError (E_SYNTAX_ERROR_000,
                           "<" +  oi->sa->getName() +
                           "> not part of aggregate function or grouping");
               Return = SQL_ERROR;
               return Return;
            }
         }
*/
   } else if (Distinct) {
      if (GroupList == 0) {
         OrderItem oi;
         GroupList = new OrderItemCopyList; 
         oi.desc = 0;
         for (sa = Fields.GetFirst (); sa != 0; sa = Fields.GetNext ()) {
            oi.sa = sa;
            GroupList->AddItem (oi);
         }
      }
   }
      
   for (oi = OrderList.GetFirst (); oi != 0; oi = OrderList.GetNext()) {
      for (sa = Fields.GetFirst (); sa != 0; sa = Fields.GetNext ())
         if (oi->sa == sa)
            break;
      if (sa == 0) {
         if (oi->ex) {
            if (oi->ex->getAggregate() != NULL_) {
               const PString &tmp = oi->ex->getName();
               Fields.AddItem (oi->ex->getAggrValue());
               GroupByView->AddNewField (oi->ex->getValue(), &tmp);
               a = View.getRel()->AddInvis (oi->sa);
               View.AddField (sa = new StmtAttr (*DB, *a, &View, 0));
               sa->setAggregate (oi->ex->getAggregate());
            } else {
               Fields.AddItem (oi->sa);
               a = View.getRel()->AddInvis (oi->sa);
               View.AddField (new StmtAttr (*DB, *a, &View, 0));
            }
            Selected.AddItem(oi->ex);
         } else {
            Fields.AddItem (oi->sa);
            Selected.AddItem (new Expression (this, oi->sa));
            a = View.getRel()->AddInvis (oi->sa);
            View.AddField (new StmtAttr (*DB, *a, &View, 0));
         }
      } else if (oi->ex)
         delete oi->ex;
   }
   return Return;
}

class Join : public PObject
{
public:
   StmtRel *sr; // StmtRel o StmtRelView
   Join *lJoin;
   Join *rJoin;
/* O^O inutilizzati */
   // PString cond;
   // toknum type;
/* O^O */

   Join () {
      lJoin = rJoin = 0;
      sr = 0;
      //type = NULL_;
   }
   ~Join() {
      if (rJoin)
         delete rJoin;
      if (lJoin)
         delete lJoin;
   }
   Error From (SqlParser *p, Lex &l);
   Error InOuter (SqlParser *p, toknum type, Lex &l);
   StmtRel *getLeft() {
      if (lJoin)
         return lJoin->getLeft();
      else
         return sr;
   }
   StmtRel *getRight() {
      if (rJoin)
         return rJoin->getRight();
      else
         return sr;
   }
   void loadStmtRelList (StmtRelList &l, StmtRelViewList &v) {
      if (lJoin)
         lJoin->loadStmtRelList (l, v);
      if (sr)
         l.AddItem ((StmtRel*) sr);
      if (rJoin)
         rJoin->loadStmtRelList (l, v);
   }
};

Error
Join::From (SqlParser *p, Lex &l)
{
   Token *t = 0;
   Error Return = SQL_SUCCESS;

   t = l.GetToken();
   if (t) {
      if (t->token == '(') {
         lJoin = new Join;
         if ((Return = lJoin->From (p, l)) != SQL_SUCCESS)
            return Return;
         if ((t = l.GetToken()) == 0 || t->token != ')') {
            p->PrintError (E_SYNTAX_ERROR_000, "Missing parenthesis <)>");
            return Return = SQL_ERROR;
         }
      } else if (t->token == USER_NAME) {
         sr = p->getRelation (t, l);
         if (sr == 0) {
            return Return = SQL_ERROR;
         }
      } else {
         p->PrintError (E_SYNTAX_ERROR_000, "Unexpected token<" + t->word + ">");
         return Return = SQL_ERROR;
      }
      if (Return == SQL_SUCCESS && (t = l.GetToken())) {
         if (t && t->token == INNER || t->token == LEFT || t->token == RIGHT)
            Return = InOuter (p, t->token, l);
         else if (t->token == FULL) {
            p->PrintError (E_INTERNAL_ERROR_001, "Full outer join is not supported" );
            return Return = SQL_ERROR;
         } else if (t)
            l.UngetToken();
      }
   }
   if (Return == SQL_SUCCESS && sr == 0 && lJoin == 0)
      p->PrintError (E_SYNTAX_ERROR_000, "Empty FROM/INTO list in statement" );

   return Return;
}


Error
Join::InOuter (SqlParser *p, toknum type, Lex &l)
{
   Token *t;
   StmtRel *srLeft, *srRight;

   if (type == LEFT || type == RIGHT) {
      if (t = l.GetToken()) {
         if (t->token != OUTER) {
            p->PrintError (E_SYNTAX_ERROR_000, "Expected <OUTER> found <" + t->word + ">");
            return SQL_ERROR;
         }
      } else {
         p->PrintError (E_SYNTAX_ERROR_000, "Expected <OUTER> found <end statement>");
         return SQL_ERROR;
      }
   }
   if (t = l.GetToken()) {
      if (t->token != JOIN) {
         p->PrintError (E_SYNTAX_ERROR_000, "Expected <JOIN> found <" + t->word + ">");
         return SQL_ERROR;
      }
   } else {
      p->PrintError (E_SYNTAX_ERROR_000, "Expected <JOIN> found <end statement>");
      return SQL_ERROR;
   }
   rJoin = new Join;
   if (rJoin->From (p, l) != SQL_SUCCESS)
      return SQL_ERROR;


   if (type == RIGHT)
      if (lJoin) {
         Join *swap = lJoin;
         lJoin = rJoin;
         rJoin = swap;
      } else {
         lJoin = rJoin;
         rJoin = 0;
      }
   if (lJoin)
      srLeft = lJoin->getRight();
   else
      srLeft = sr;
   if (rJoin)
      srRight = rJoin->getLeft();
   else
      srRight = sr;

   if (t = l.GetToken()) {
      if (t->token != ON) {
         p->PrintError (E_SYNTAX_ERROR_000, "Expected <ON> found <" + t->word + ">");
         return SQL_ERROR;
      } else {
         if (p->explicitJoin.size() > 0)
            p->explicitJoin += " AND (";
         else
            p->explicitJoin += "(";
            if (((StmtRel *)srRight)->join.size() > 0) {
               p->PrintError (E_SYNTAX_ERROR_000, "Malformed join");
               return SQL_ERROR;
            }
            if (p->BuildJoin(l, srRight->join, srLeft, srRight) != SQL_SUCCESS)
               return SQL_ERROR;
         p->explicitJoin += ")";
         if (type != INNER)
            srRight->leftJoin = PTrue;
      }
   } else {
      p->PrintError (E_SYNTAX_ERROR_000, "Expected <ON> found <end statement>");
      return SQL_ERROR;
   }
   return SQL_SUCCESS;
}


inline void
SqlParser::parseUserName (Token *t, Lex &l,
                          PString &field, PString &table, PString *alias)
{
   field = t->word;
   table = "";
   if (alias)
      *alias = "";
   if (t = l.GetToken()) {
      if (t->token == (toknum)'.') {
         if (t = l.GetToken()) {
            if (t->token == USER_NAME) {
               table = field;
               field = t->word;
            } else {
               t = l.UngetToken();
               t = l.UngetToken();
            }
         } else {
            t = l.UngetToken();
         }
      } else {
         t = l.UngetToken();
      }
   }
   if (alias && (t = l.GetToken())) {
      if (t->token == AS) {
         if (t = l.GetToken()) {
            if (t->token == USER_NAME) {
               *alias = t->word;
            } else {
               t = l.UngetToken();
               t = l.UngetToken();
            }
         } else {
            t = l.UngetToken();
         }
      } else if (t->token == USER_NAME) {
         *alias = t->word;
      } else {
         t = l.UngetToken();
      }
   }
}

void
SqlParser::substViewFrom (TokenList &from, Lex &l)
{
   Token *t;
   RelView *rv;
   PString tName, tAlias;

   do {
      while ((t = l.GetToken()) && t->token == '(') {
         from.AddItem (new Token (*t));
      }
      if (t) {
         if (t->token == USER_NAME) {
            tAlias = "";
            tName = t->word;
            if (t = l.GetToken()) {
               if (t->token == USER_NAME) {
                  tAlias = t->word;
               } else {
                  l.UngetToken();
               }
            }
            if (rv = DB->GetRelView (tName)) {
               for (TokenIterator ti(rv->getFrom()); *ti; ti++)
                  from.AddItem (new Token (**ti));
               if (tAlias.size() > 0)
                  ViewList.AddItem (new StmtRelView (*rv, tAlias));
               else
                  ViewList.AddItem (new StmtRelView (*rv));
            } else {
               from.AddItem (new Token (USER_NAME, tName));
               if (tAlias.size()) {
                  from.AddItem (new Token (USER_NAME, tAlias));
               }
            }
         }
         while ((t = l.GetToken()) && t->token != ',' && t->token != JOIN &&
                                      t->token != '}' && 
                                      t->token != '(' && 
                                      t->token != WHERE &&
                                      t->token != GROUP &&
                                      t->token != ORDER &&
                                      t->token != SET &&
                                      t->token != UNION &&
                                      t->token != VALUES &&
                                      t->token != LIMIT &&
                                      t->token != OFFSET &&
                                      t->token != FOR) {
            from.AddItem (new Token (*t));
            if (t->token == ON)
               substViewWhere (from, l);
         }
         if (t) {
            if (t->token == JOIN || t->token == ',') {
               from.AddItem (new Token (*t));
            } else {
               l.UngetToken();
            }
         }
      }
   } while (t && (t->token == ',' || t->token == JOIN));

   PString debug;
   for (t = from.GetFirst(); t; t = from.GetNext()) {
      debug += t->word;
      debug += ' ';
   }
}

AttrView *
SqlParser::getViewField (const PString &name, const PString &table, const PString &alias)
{
   AttrView *Return = 0;
   AttrViewKeyObject *avko = 0;
   StmtRelView *srv;
   for (srv = ViewList.GetFirst(); srv; srv = ViewList.GetNext())
      if (table.size() == 0 || table == srv->getName())
         if (avko = srv->getField(name)) {
            Return = avko->getValue();
            break;
         }
   return Return;
}

void
SqlParser::substViewSelect (TokenList &sel, Lex &l)
{
   Token *t;
   AttrView *av;
   StmtRel *sr;
   StmtAttr *sa;
   PString table, field, alias;

   t = l.GetToken();
   if (t && t->token == '*') {
      StmtRelView *srv = ViewList.GetFirst();
      PBool needComma = (srv != 0);
      for ( ; srv; ) {
         for (AttrViewKeyObjectIterator avkoi(srv->getFields()); *avkoi; ) {
            for (TokenIterator ti((*avkoi)->getValue()->getValue()); *ti; ti++) {
               sel.AddItem (new Token(**ti));
            }
            sel.AddItem (new Token(USER_NAME, (*avkoi)->getValue()->getName ()));
            avkoi++;
            if (*avkoi)
               sel.AddItem (new Token((toknum)',', ","));
         }
         srv = ViewList.GetNext();
         if (srv)
            sel.AddItem (new Token((toknum)',', ","));
      }
      for (sr = FromList.GetFirst (); sr ; sr = FromList.GetNext ()) {
         if (sr->getName()[0] == RIVCHAR[0])
            continue;
         for (sa = sr->GetFirstField (); sa; ) {
            if (needComma) {
               sel.AddItem (new Token((toknum)',', ","));
               needComma = PFalse;
            }
            sel.AddItem (new Token(USER_NAME, sr->getName ()));
            sel.AddItem (new Token((toknum)'.', "."));
            sel.AddItem (new Token(USER_NAME, sa->getName ()));
            sa = sr->GetNextField ();
            if (sa)
               sel.AddItem (new Token((toknum)',', ","));
         }
         needComma = PTrue;
      }
   } else {
      l.UngetToken();
      while ((t = l.GetToken()) && t->token != FROM
                                && t->token != ORDER
                                && t->token != GROUP
                                && t->token != UNION
                                && t->token != LIMIT
                                && t->token != OFFSET
                                && t->token != HAVING) {
         if (t->token == USER_NAME) {
            parseUserName (t, l, field, table, &alias);
            if (av = getViewField (field, table, alias)) {
               t = sel.GetLast();
               PBool isExpr = (t && t->token != ',') ? PTrue : PFalse;
               for (TokenIterator ti(av->getValue()); *ti; ti++)
                  sel.AddItem (new Token(**ti));
               if (alias.size() > 0)
                  sel.AddItem (new Token(USER_NAME, alias));
               else if (!isExpr) {
                  t = l.GetToken();
                  if (t == 0 || t->token == ',' || t->token == FROM)
                     sel.AddItem (new Token(USER_NAME, field));
                  l.UngetToken ();
               }
            } else {
               if (table.size() > 0) {
                  sel.AddItem (new Token(USER_NAME, table));
                  sel.AddItem (new Token((toknum)'.', "."));
                  sel.AddItem (new Token(USER_NAME, field));
               } else
                  sel.AddItem (new Token(USER_NAME, field));
               if (alias.size() > 0)
                  sel.AddItem (new Token(USER_NAME, alias));
            }
         } else {
            sel.AddItem (new Token (*t));
         }
      }
      if (t)
         l.UngetToken();
   }
/*
   PString debug;
   for (t = sel.GetFirst(); t; t = sel.GetNext()) {
      debug += t->word;
      debug += ' ';
   }
*/
}

void
SqlParser::substViewWhere (TokenList &sel, Lex &l)
{
   Token *t;
   AttrView *av;
   PString table, field, empty;

   while ((t = l.GetToken()) && t->token != ',' && t->token != JOIN &&
                                t->token != '}' &&
                                t->token != ON &&
                                t->token != WHERE &&
                                t->token != GROUP &&
                                t->token != ORDER &&
                                t->token != SET &&
                                t->token != UNION &&
                                t->token != VALUES &&
                                t->token != LIMIT &&
                                t->token != OFFSET &&
                                t->token != FOR) {
      if (t->token == USER_NAME) {
         parseUserName (t, l, field, table, 0);
         if (av = getViewField (field, table, empty)) {
            for (TokenIterator ti(av->getValue()); *ti; ti++)
                sel.AddItem (new Token(**ti));
         } else {
            if (table.size() > 0) {
               sel.AddItem (new Token(USER_NAME, table));
               sel.AddItem (new Token((toknum)'.', "."));
               sel.AddItem (new Token(USER_NAME, field));
            } else
               sel.AddItem (new Token(USER_NAME, field));
         }
      } else {
            sel.AddItem (new Token (*t));
      }
   }
   if (t)
      l.UngetToken();

   PString debug;
   for (t = sel.GetFirst(); t; t = sel.GetNext()) {
      debug += t->word;
      debug += ' ';
   }
}

Error
SqlParser::From (Lex &l, PBool viewAllowed)
{
   Token *t = 0;
   Error Return = SQL_SUCCESS;
   TokenList from;

   if (viewAllowed) {
      substViewFrom (from, l);
      Lex fromLex(from, PTrue);
      do { 
         Join j;
         if ((Return = j.From (this, fromLex)) != SQL_SUCCESS)
            break;
         else
            j.loadStmtRelList (FromList, ViewList);
         t = fromLex.GetToken();
      } while (t != 0 && t->token == ',');

      if (t)
         fromLex.UngetToken();
   } else {
      do { 
         Join j;
         if ((Return = j.From (this, l)) != SQL_SUCCESS)
            break;
         else
            j.loadStmtRelList (FromList, ViewList);
         t = l.GetToken();
      } while (t != 0 && t->token == ',');

      if (t)
         l.UngetToken();
   }
   if (Return == SQL_SUCCESS && FromList.GetCurrent() == 0)
      PrintError (E_SYNTAX_ERROR_000, "Empty FROM/INTO list in statement" );

   for (t = from.GetFirst(); t; t = from.DeleteCurrent())
      delete t;
   return Return;
}

Error
SqlParser::OrderGroupBy (Lex &lo, OrderItemCopyList &list)
{
   Token *t;
   Expression *ex;
   Error Return = SQL_SUCCESS;
   PString name, table, alias;
   OrderItem oi;

   if ((t = lo.GetToken()) != 0 && t->token == BY) {
      TokenList tl;
      substViewSelect (tl, lo);
      Lex ln(tl, PTrue);

      do {
         ex = new Expression (*this, ln, (Expression::Method)(&SqlParser::GetTableName), PTrue);
         if (ex->isOk()) {
            if (!ex->isDbExpr()) {
               if (ex->getAttr()->IsNumeric()) {
                  long idx;
                  long len;
                  ex->getValueUpdated()->ToLong (idx, len);
                  oi.sa = View.GetFieldAt (idx - 1);
                  if (oi.sa && !(oi.sa->getAttr()->GetVisibility() & ATTR_HIDDEN)) { // se esiste ed e' visibile
                     oi.ex = 0;
                  } else {
                     PrintError (E_SYNTAX_ERROR_S22, "Invalid ordinal number <" +
                                                      ToPString(idx) +
                                                     "> in ORDER/GROUP BY");
                     Return = SQL_ERROR;
                  }
               } else {
                  PrintError (E_SYNTAX_ERROR_S22, "Invalid expression in ORDER/GROUP BY");
                  Return = SQL_ERROR;
               }
            } else if (unions.GetItemNum() > 0) { 
               PrintError (E_SYNTAX_ERROR_000,
                          "Only ordinal numbers are allowed in UNION ORDER BY");
               Return = SQL_ERROR;
            } else {
               oi.sa = ex->getValue();
               oi.ex = ex;
            }
            t = ln.GetToken();
            oi.desc = PFalse;
            if (t != 0)
               if (t->token == ASC)
                  t = ln.GetToken();
               else if (t->token == DESC) {
                  oi.desc = PTrue; 
                  t = ln.GetToken();
               }
            list.AddItem (oi);
            if (t == 0 || t->token != ',') {
               if (t)
                  ln.UngetToken();
               break;
            }
         } else {
            Return = SQL_ERROR;
            delete ex;
            break;
         }
      } while (t && Return == SQL_SUCCESS);
/*
      while (Return == SQL_SUCCESS && (t = ln.GetToken()) != 0) {
         tn = t->token;
         switch (t->token) {
         case USER_NAME:
            sa = GetFieldByName (ln, t->word);
            if (sa == 0) { 
               PrintError (E_SYNTAX_ERROR_S22, "Unknown field <" + t->word +
                                               "> in ORDER/GROUP BY");
               Return = SQL_ERROR;
            } else if (unions.GetItemNum() > 0) { 
               PrintError (E_SYNTAX_ERROR_000,
                          "Only ordinal numbers are allowed in UNION ORDER BY");
               Return = SQL_ERROR;
            }
            else
               oi.sa = sa;
            break; 
         case NUMBER:
            sa = View.GetFieldAt (t->word.ToLong() - 1);
            if (sa && !(sa->getAttr()->GetVisibility() & ATTR_HIDDEN)) // se esiste ed e' visibile
               oi.sa = sa;
            else {
               PrintError (E_SYNTAX_ERROR_S22, "Unknown field <" + t->word +
                                               "> in ORDER/GROUP BY");
               Return = SQL_ERROR;
            }
            break;
         default:
            PrintError (E_SYNTAX_ERROR_000, "Invalid token <" + t->word + "> in ORDER/GROUP BY");
            Return = SQL_ERROR;
            break;
         }
         if (Return == SQL_SUCCESS)  {
            t = ln.GetToken();
            oi.desc = PFalse;
            if (t != 0)
               if (t->token == ASC)
                  t = ln.GetToken();
               else if (t->token == DESC) {
                  oi.desc = PTrue; 
                  t = ln.GetToken();
               }
            list.AddItem (oi);
            if (t == 0 || t->token != ',') {
               if (t)
                  ln.UngetToken();
               break;
            }
         }
      }
*/
      for (t = tl.GetFirst(); t; t = tl.DeleteCurrent())
         delete t;
   }
   if (Return == SQL_SUCCESS && list.GetCurrent() == 0) {
      PrintError (E_SYNTAX_ERROR_000, "Empty ORDER/GROUP BY list in SELECT statement" );
      Return = SQL_ERROR;
   }
   return Return;
}

StmtRel *
SqlParser::getRelation (Token *t, Lex &l)
{
   Relation *r = 0;
   StmtRel *Return = 0;


   if (r = DB->GetRelation (t->word, this)) {
      t = l.GetToken();
      if (t && t->token == USER_NAME)
         Return = new StmtRel (*r, t->word);
      else {
         if (t)
            l.UngetToken();
         Return = new StmtRel (*r);
      }
   } else {
      if (DB->GetRelView(t->word)) {
         DelError ();
         PrintError (E_SYNTAX_ERROR_000, "Operation not permitted on a view");
      }
   }
   return Return;
}

PBool
SqlParser::RedBefore (StmtRel *former, StmtRel *latter)
{
   if (subQueryOf && subQueryOf->RedBefore (former, latter)) {
      return PTrue;
   } else {      
      StmtRelIterator sri(FromList);
      for (sri.reset(); *sri; sri++)
         if (*sri == former)
            return PTrue;
         else if (*sri == latter)
            return PFalse;
   }
   return PFalse;
}

StmtAttr *
SqlParser::GetTableName (const PString & name, PString & table, StmtRel **sr, toknum dummy)
{
   StmtRelIterator sri(FromList);
   StmtAttr *Return;
   
   for (*sr = *sri; *sr != 0; *sr = *(++sri))
      if (table.size() == 0 && (*sr)->getName()[0] != RIVCHAR[0] ||
          table == (*sr)->getName())
         if (Return = (*sr)->getByName (name)) {
            table = (*sr)->getName();
            return Return;
         }
   if (subQueryOf)
      return subQueryOf->GetTableName (name, table, sr, dummy);
   else
      if (table.size() == 0)
         PrintError (E_SYNTAX_ERROR_S22, name);
      else
         PrintError (E_SYNTAX_ERROR_S22, table + "." + name);
   dummy;
   return 0;
} 

StmtAttr *
SqlParser::GetViewName (const PString & name, PString & table, StmtRel **sr, toknum bi)
{
   StmtAttr *Return = 0;
   
   if (bi == NULL_) {
      for (Return = View.GetFirstField (); Return != 0; Return = View.GetNextField ())
         if (Return = View.getByName (name))
            return Return;
      
      Return = GetTableName(name, table, sr);
      if (verifyGroup (Return) == SQL_SUCCESS) {
         Fields.AddItem (Return);
         GroupByView->AddNewField (Return, 0);
         Selected.AddItem(new Expression (this, Return));
         Return = View.AddNewInvis (Return);
      } else {
         Return = 0;
      }
   } else {
      Return = GetTableName(name, table, sr);
   }
   return Return;
}

Error
SqlParser::AddAllField ()
{
   StmtRel  *sr;
   StmtAttr *sa;
   Error Return = SQL_ERROR;

   if (Fields.GetCurrent () != 0) {
      PrintError (E_SYNTAX_ERROR_000, "Invalid field list");
   } else {
      Return = SQL_SUCCESS;
      StmtRelView *srv = ViewList.GetFirst();
                              
      if (srv) {
         StmtRel  *sr;
         PString table, name;
         for (AttrViewKeyObjectIterator avkoi(srv->getFields());
                                                         *avkoi; avkoi++) {
            TokenIterator ti((*avkoi)->getValue()->getValue());
            table = (*ti)->word;
            ti++;
            ti++;
            name = (*ti)->word;
            Fields.AddItem (GetTableName (name, table, &sr));
         }
      } else {
         for (sr = FromList.GetFirst (); sr != 0; sr = FromList.GetNext ())
            for (sa = sr->GetFirstField (); sa;
                 sa = sr->GetNextField ())
               Fields.AddItem (sa);
      }
   }
   return Return;
}

void
SqlParser::AddAllExpr () // TODO Da Togliere 
{
   StmtRel  *sr;
   StmtAttr *sa;
   StmtRelView *sv;

   for (sv = ViewList.GetFirst (); sv != 0; sv = ViewList.GetNext ())
      ;
   for (sr = FromList.GetFirst (); sr != 0; sr = FromList.GetNext ()) {
      if (sr->getName()[0] != RIVCHAR[0]) {
         for (sa = sr->GetFirstField (); sa; sa = sr->GetNextField ()) {
            Fields.AddItem (sa);
            Selected.AddItem (new Expression (this, sa));
         }
      }
   }
}

Error
SqlParser::AddField (PString &name, PString &table, PString &alias)
{                
   StmtAttr  *sa;
   StmtRel  *sr;
   Error Return = SQL_ERROR;
   StmtRelView *srv = ViewList.GetFirst();
                              
   if (srv) {
      AttrViewKeyObject *avko = srv->getField (name);
      if (avko != 0) {
         TokenIterator ti(avko->getValue()->getValue());
         table = (*ti)->word;
         ti++;
         ti++;
         name = (*ti)->word;
      }
   }
   if (sa = GetTableName (name, table, &sr)) {
      Fields.AddItem (sa);
      Return = SQL_SUCCESS;
   }
   
   return Return;
}

StmtAttr *
SqlParser::GetFieldByName (Lex &l, PString &fname)// Chiamata SOLO per ORDER/GROUP BY
{
   StmtRel *sr;
   StmtAttr *sa;
   PString tableName;
   PString fieldName;
   Token *t; 

   fieldName = fname;

   t = l.GetToken ();
   if (t != 0) {
      if (t->token == '.') {
         tableName = fieldName;
         t = l.GetToken();
         if (t == 0) {
            PrintError (E_SYNTAX_ERROR_000,
                       "Invalid field name near end");
            return 0;
         } else if (t->token != USER_NAME) {
            PrintError (E_SYNTAX_ERROR_000, "Expected <user name> found <" + t->word +">");
            return 0;
         }
         fieldName = t->word;
      } else
         l.UngetToken ();
   } 
   if (tableName.size() == 0) {
      for (sa = Fields.GetFirst(); sa != 0; sa = Fields.GetNext ())
         if (fieldName == sa->getName())
            return sa;
      for (Expression *ex = Selected.GetFirst(); ex; ex = Selected.GetNext()) 
         if (fieldName == ex->getName())
            return ex->getAggrValue();
   }
   if ((sa = GetTableName (fieldName, tableName, &sr)) != 0) {
      return sa;
   }
   
   return 0;
}

void
SqlParser::PutKeyToken (Lex &l)
{
   Token *t, *prevtoken = 0;

   l.Rewind();
   while ((t = l.GetToken()) != 0) {
      if (t->token == UOK_WORD) {
         t->word.toUpper();
         t->token = FindResKey (t->word.gets());
      } else if (t->token == '.') {
         if (prevtoken != 0 && prevtoken->token == NUMBER) {
            t = l.GetToken();
            if (t) {
               if (t->token == NUMBER) {
                  prevtoken->word += '.' + t->word;
                  l.DeleteToken ();
                  l.DeleteToken ();
               } else
                  l.UngetToken ();
            }
            t = prevtoken;
         }
      }
      prevtoken = t;
   }
   l.Rewind();
}

toknum
SqlParser::FindResKey (char * str)
{
   int i = ( sizeof (SqlWord) / sizeof (SqlWord[0]));
   int j = 0;
   int m = 0;
   int c;
   int ex = 0;

   for ( ex = 0, m = ( i - j ) / 2; m != ex; ex = m, m = j + (( i - j ) / 2)) {
      if ((c = strcmp (str, SqlWord[m].word)) == 0)
         return (SqlWord[m].token);
      else
         if (c > 0)
            j = m;
         else
            i = m;
   };

   return ( USER_NAME );
}

Error
SqlParser::ChecksBeforeExec ()
{
   Expression *ex;
   int dummy;

   for (ex = Selected.GetFirst(); ex; ex = Selected.GetNext())
      if (ex->verify(dummy, PFalse) != SQL_SUCCESS)
         return SQL_ERROR;
   if (WhereStack.verifyExpression() != SQL_SUCCESS)
      return SQL_ERROR;
   if (HavingStack.verifyExpression() != SQL_SUCCESS)
      return SQL_ERROR;
   return SQL_SUCCESS;
}

Error
SqlParser::Exec ()
{
   Error Return;
   CField *cf;
   int i;

   if (!Parsed) {
      PrintError (E_GENERAL_ERROR_007, "");
      return Return = SQL_ERROR;
   }

   for (i = 1, cf = Params.GetFirst (); cf != 0 && i <= nParams;
        i++  , cf = Params.GetNext ())
      if (cf->GetParamAttr().getAttr()->GetType() == T_UNKNOWN) {
         PrintError (E_DYN_SQL_009, "SQLBindParameter has not been called for parameter # " + ToPString(i));
         return Return = SQL_ERROR;
      } else if (cf->isDataAtExec()) {
         return Return = (Error) SQL_NEED_DATA;
      } else if ((Return = cf->FromExt (this)) != SQL_SUCCESS)
         return Return;
   if ((Return = ChecksBeforeExec ()) != SQL_SUCCESS)
      return Return;
   switch (Command) {
   case CREATE_TABLE:
      Return = ExecCreateTable();
      break;
   case DROP_TABLE:
      Return = ExecDropTable();
      break;
   case CREATE_INDEX:
   case CREATE_UNIQUE_INDEX:
      Return = ExecCreateIndex();
      break;
   case DROP_INDEX:
      Return = ExecDropIndex();
      break;
   case CREATE_VIEW:
      Return = ExecCreateView();
      break;
   case DROP_VIEW:
      Return = ExecDropView();
      break;
   case ALTER_TABLE_RENAME:
      Return = ExecAlterTableRename ();
      break;
   case ALTER_TABLE_DROP:
      Return = ExecAlterTableDrop ();
      break;
   case ALTER_TABLE_ADD:
      Return = ExecAlterTableAdd ();
      break;
   case DELETE_:
      if ((Return = DB->Transact(this)) == SQL_SUCCESS)
         Return = ExecDelete();
      break;
   case INSERT:
      if ((Return = DB->Transact(this)) == SQL_SUCCESS)
         Return = ExecInsert();
      break;
   case REPLACE:
      if ((Return = DB->Transact(this)) == SQL_SUCCESS)
         Return = ExecInsert();
      break;
   case SELECT:
      Return = ExecSelect();
      if (Offset > 0 && Return == SQL_SUCCESS) {
         for (unsigned long i = 0; i < Offset && Return == SQL_SUCCESS; i++)
            Return = Fetch();
      }
      break;
   case UPDATE:
      if ((Return = DB->Transact(this)) == SQL_SUCCESS)
         Return = ExecUpdate ();
      break;
   default:
      PrintError (E_INTERNAL_ERROR_001, "Unknown command");
      Return = SQL_ERROR;
      break;
   }
   return Return;
}

Error
SqlParser::ExecDelete ()
{
   Error Return; 
   StmtRel *sr = FromList.GetFirst();

   ClearExec ();
   if (pUpdStmtRel) {
      if (sr->ShareOpen (pUpdStmtRel) == 0) {
         if (!sr->getRel()->hasOnePermission(REL_DELETE))  {
            PrintError (E_INTEGRITY_VIOL_000,
                                    "No delete permission");
            Return = SQL_ERROR; 
         } else {
            Return = sr->DelCurrent();
            if (Return != SQL_SUCCESS) {
               PrintError (E_GENERAL_ERROR_000,
                                 "Cannot delete from table", sr->getStatus());
               Return = SQL_ERROR;
            }
         }
         sr->ShareClose();
      } else {
         PrintError(E_GENERAL_ERROR_000,"Binded cursor closed",sr->getStatus());
         Return = SQL_ERROR;
      }
   } else {
      sr->Open();
      sr->SortIndexes();
      sr->SetOrder (0x7FFF);
      Return = ProcessAll(1);
      if (Return == SQL_NO_DATA_FOUND)
         Return = SQL_SUCCESS;
      sr->Close();
   }
   return Return;
}

Error
SqlParser::ExecUpdate ()
{
   Error Return;
   StmtAttr *sa;
   Expression *ex;
   StmtRel *sr = FromList.GetFirst();
   PBool warnings = PFalse;

   for (sa = Fields.GetFirst(), ex = Selected.GetFirst(); sa;
        sa = Fields.GetNext(), ex = Selected.GetNext()) {
      if ((Return = sa->FromAttr(*(ex->getValueUpdated()))) == SQL_ERROR) {
         PrintError (E_DATA_EXCEPT_018, "Type mismatch in VALUES" );
         return Return;
      } else if (Return == SQL_SUCCESS_WITH_INFO) {
         PrintError (E_WARNING_004, "Data truncated column: " + sa->getName() );
         warnings = PTrue;
      }
   }
      
   ClearExec ();
   if (pUpdStmtRel) {
      if (sr->ShareOpen (pUpdStmtRel) == 0) {
         if (!sr->getRel()->hasOnePermission(REL_UPDATE))  {
            PrintError (E_INTEGRITY_VIOL_000,
                                    "No update permission");
            Return = SQL_ERROR; 
         } else {
            memcpy (sr->getBuffer(),pUpdStmtRel->getBuffer(), 
                    sr->getRel()->GetBufferLen());
            Return = UpdateBuffer(sr->getRel());
            if (Return == SQL_SUCCESS) {
               Return = sr->UpdCurrent();
               if (Return != SQL_SUCCESS) {
                  PrintError (E_GENERAL_ERROR_000,
                                    "Cannot update table", sr->getStatus());
                  Return = SQL_ERROR;
               }
            }
         }
         sr->ShareClose();
      } else {
         PrintError(E_GENERAL_ERROR_000,"Binded cursor closed",sr->getStatus());
         Return = SQL_ERROR;
      }
   } else {
      sr->Open();
      sr->SortIndexes();
      sr->SetOrder (0x7FFF);
      forUpdate = PTrue;

      Return = ProcessAll(1);
      if (Return == SQL_NO_DATA_FOUND)
         Return = SQL_SUCCESS;
      sr->Close();
   }
   if (warnings && Return == SQL_SUCCESS)
      Return = SQL_SUCCESS_WITH_INFO;
   return Return;
}

Error
SqlParser::ExecInsert ()
{
   Error Return = SQL_SUCCESS; 
   StmtRel *sr = FromList.GetFirst();
   PBool result;
 
   ClearExec ();
   sr->Open();
   if (!sr->getRel()->hasOnePermission(REL_INSERT))  {
      PrintError (E_INTEGRITY_VIOL_000, "No insert permission", Return); 
      Return = SQL_ERROR; 
   } else {
      if (insertQuery) {
         insertQuery->Command = SELECT;
         Return = insertQuery->ExecSelect ();
         if (Return != SQL_SUCCESS)
            MoveStmtError (this, insertQuery);
         else {
            Processed = 0;
            while ((Return = insertQuery->Fetch()) == SQL_SUCCESS) {
               InsertBuffer (Fields, insertQuery->Fields);
               if ((Return = WhereStack.Eval(sr, result)) == SQL_SUCCESS) {
                  if (result) {
                     if (Command == INSERT)
                        Return = sr->Write();
                     else
                        Return = sr->Replace();
                     if (Return != SQL_SUCCESS) {
                        if (sr->getStatus() == EDUPL)
                           PrintError (E_INTEGRITY_VIOL_000,
                                         "UNIQUE INDEX constraint violated");
                        else
                           PrintError (E_GENERAL_ERROR_000,
                                       "Error writing table", sr->getStatus());
                        Return = SQL_ERROR;
                        break;
                     } else
                        Processed++;
                  } else {
                     PrintError (E_INTEGRITY_VIOL_000,
                                 "WHERE clause violation");
                     Return = SQL_ERROR;
                     break;
                  }
               }
            }
         }
         if (Return == SQL_NO_DATA_FOUND)
            Return = SQL_SUCCESS;
      } else {
         InsertBuffer (sr->getRel());
         if ((Return = WhereStack.Eval(sr, result)) == SQL_SUCCESS) {
            if (result) {
               if (Command == INSERT)
                  Return = sr->Write();
               else
                  Return = sr->Replace();
               if (Return != SQL_SUCCESS) {
                  if (sr->getStatus() == EDUPL)
                     PrintError (E_INTEGRITY_VIOL_000,
                                 "UNIQUE INDEX constraint violated");
                  else
                     PrintError (E_GENERAL_ERROR_000,
                                 "Error writing table", sr->getStatus());
                  Return = SQL_ERROR;
               } else
                  Processed = 1;
            } else {
                  PrintError (E_INTEGRITY_VIOL_000,
                              "WHERE clause violation");
                  Return = SQL_ERROR;
            }
         }
      }
   }
   sr->Close();
   return Return;
}

Error
SqlParser::ExecSelect ()
{
   StmtRel *sr;
   Error Return;
   RelationList ordered;

   if (&View == &LocalView)
      ClearExec ();

   for (sr = FromList.GetFirst(); sr != 0; sr = FromList.GetNext ())
      if ((Return = sr->Open()) != SQL_SUCCESS) {
         PrintError (E_GENERAL_ERROR_000, "Error opening table", sr->getStatus());
         for (sr = FromList.GetPrevious();sr != 0;sr = FromList.GetPrevious ())
            sr->Close ();
         return Return;
      } else
         sr->SortIndexes();

   Direction = NEXT;

   if (GroupList != 0) {
      unsigned long count = 0;
      for (sr = FromList.GetFirst(); sr != 0; sr = FromList.GetNext())
         sr->setStatus(EENDFILE);
      sr = FromList.GetFirst();
      sr->Start();
      if ((Return = View.Create ()) == SQL_SUCCESS) {
         View.Clear();
         if (GroupList->GetItemNum() == 0) {
            while (Return == SQL_SUCCESS) {
               for (sr = FromList.GetFirst(); sr != 0; sr = FromList.GetNext())
                  sr->SetOrder (0x7FFF);
               FromList.GetFirst();
               Return = SingleProcess (1);
               if (Return == SQL_SUCCESS) {
                  count++;
                  View.FillBuffer(Selected, count);
               } else
                  break;
            }
            Return = SQL_SUCCESS;
            Return = View.Write ();
            View.Start ();
            View.SetOrder (0x7FFF);
         } else { // Group By O^O
            if ((Return = GroupByView->Create ()) == SQL_SUCCESS) {
               GroupByView->Clear();
               for ( ; ; ) {
                  for (sr = FromList.GetFirst(); sr != 0; sr = FromList.GetNext())
                     sr->SetOrder (0x7FFF);
                  FromList.GetFirst();
                  Return = SingleProcess (1);
                  if (Return == SQL_SUCCESS) {
                     GroupByView->FillBuffer(Selected, 0);
                     if ((Return = GroupByView->Write ()) != SQL_SUCCESS) {
                        break;
                     }
                  } else {
                     Return = SQL_SUCCESS;
                     break;
                  }
               }
               if (Return != SQL_SUCCESS ||
                   (Return = GroupByView->CalcAggregate (View)) != SQL_SUCCESS) {
                  PrintError (E_GENERAL_ERROR_001,"Problems on temporary file",
                              GroupByView->getStatus());
                  Return = SQL_ERROR;
               }
            } else {
               ClearExec ();
               PrintError (E_GENERAL_ERROR_001,"Temporary file creation failed",
                            GroupByView->getStatus());
               Return = SQL_ERROR;
            }
         }
      } else {
         ClearExec ();
         PrintError (E_GENERAL_ERROR_001, "Temporary file creation failed",
                     View.getStatus());
         Return = SQL_ERROR;
      }
   } else if (OrderList.GetCurrent() == 0 &&
              unions.GetItemNum() == 0 && &LocalView == &View) {
      for (sr = FromList.GetFirst(); sr != 0; sr = FromList.GetNext())
         sr->setStatus(EENDFILE);
      sr = FromList.GetFirst();
      if (isSortDesc)
         sr->PrevStart();
      else
         sr->Start();
   } else {
      if (&LocalView != &View || View.Create () == SQL_SUCCESS) {
         for (sr = FromList.GetFirst(); sr != 0; sr = FromList.GetNext())
            sr->SetOrder (0x7FFF);
         sr = FromList.GetFirst();
         Return = ProcessAll (1);
         if (Return == SQL_NO_DATA_FOUND) {
            Return = SQL_SUCCESS;
            if (unions.GetItemNum() > 0) {
               SqlParser *sp;
               for (sp = unions.GetFirst(); sp && Return == SQL_SUCCESS;
                    sp = unions.GetNext()) {
                  sp->Command = SELECT;
                  Return = sp->ExecSelect ();
               }
               if (Return != SQL_SUCCESS)
                  MoveStmtError (this, sp);
            }
            if (&LocalView == &View)
               View.Start ();
         } 
      } else {
         ClearExec ();
         PrintError (E_GENERAL_ERROR_001, "Temporary file creation failed",
                     View.getStatus());
         Return = SQL_ERROR;
      }
   }
   return Return;
}

Error
SqlParser::ExtendedFetch (int dir, UDWORD *pcrow,
                          UWORD *rgfRowStatus)
{
   Error Return = SQL_SUCCESS;
   unsigned long nFetch;
   
   for (nFetch = 0;
        nFetch < rowSetSize && Return != SQL_ERROR &&
                               Return != SQL_NO_DATA_FOUND; nFetch++) {
      switch (dir) {
      case SQL_FETCH_NEXT:
         Return = Fetch(NEXT, nFetch);
         break;
      case SQL_FETCH_PREV:
         Return = Fetch(PREV, nFetch);
         break;
      case SQL_FETCH_FIRST:
         Return = Fetch(FIRST, nFetch);
         dir = SQL_FETCH_NEXT;
         break;
      case SQL_FETCH_LAST:
         Return = Fetch(LAST, nFetch);
         dir = SQL_FETCH_PREV;
         break;
      default:
         PrintError (E_GENERAL_ERROR_106, "");
         Return = SQL_ERROR;
         break;
      }
      switch (Return) {
      case SQL_SUCCESS_WITH_INFO:
      case SQL_SUCCESS:
         rgfRowStatus[nFetch] = SQL_ROW_SUCCESS;
         break;
      case SQL_ERROR:
         rgfRowStatus[nFetch] = SQL_ROW_ERROR;
         nFetch--;
         break;
      case SQL_NO_DATA_FOUND:
         rgfRowStatus[nFetch] = SQL_ROW_NOROW;
         nFetch--;
         break;
      }
   }
   if (pcrow)
      *pcrow = nFetch;
   if (Return == SQL_NO_DATA_FOUND && nFetch > 0)
      Return = SQL_SUCCESS;
   return Return;
}

Error
SqlParser::Fetch (enum eDirection dir, unsigned long numElem)
{
   Error Return = SQL_SUCCESS;
   StmtRel * sr;
   Error rc;
   StmtAttr *sa;
   CField *cf;
   PBool HavingOk;

   currentTimestamp = 0;
   if (isSortDesc)
      switch (dir) {
      case NEXT:
         dir = PREV;
         break;
      case PREV:
         dir = NEXT;
         break;
      case FIRST:
         dir = LAST;
         break;
      case LAST:
         dir = FIRST;
         break;
      }
  
   if (MaxRows | Limit) {
      if (Limit == 0)
         Limit = MaxRows;

      switch (dir) {
      case NEXT:
         if (Fetched >= Limit)
            return Return = SQL_NO_DATA_FOUND;
         break;
      case PREV:
         if (Fetched <= 0)
            return Return = SQL_NO_DATA_FOUND;
         break;
      }
   }
   if (View.getRel() != 0) {
      if (View.isOpen())
         if (HavingStack.GetItemNum() > 0)
            do {
               if (dir == FIRST) {
                  View.StartNext();
                  dir = NEXT;
               } else if (dir == LAST) {
                  View.StartPrev();
                  dir = PREV;
               }
               if (dir == NEXT)
                  Return = View.ReadNext(forUpdate);
               else
                  Return = View.ReadPrev(forUpdate);
               if (Return == SQL_SUCCESS)
                   Return = HavingStack.Eval (&View, HavingOk);
            } while (! HavingOk && Return == SQL_SUCCESS);
         else {
            if (dir == FIRST) {
               View.StartNext();
               dir = NEXT;
            } else if (dir == LAST) {
               View.StartPrev();
               dir = PREV;
            }
            if (dir == NEXT)
               Return = View.ReadNext(forUpdate);
            else
               Return = View.ReadPrev(forUpdate);
         }
      else {
         sr = FromList.GetFirst();
         switch (dir) {
         case FIRST:
            sr->Start ();
            dir = NEXT;
            break;
         case LAST:
            sr->PrevStart ();
            dir = PREV;
            break;
         default:
            break;
         }
         for ( ; sr != 0; sr = FromList.GetNext())
            sr->SetOrder (0x7FFF);
         FromList.GetFirst();
         Return = SingleProcess (1, dir);
         if (Return == SQL_SUCCESS)
            View.FillBuffer(Selected);
      }
      if (Return == SQL_SUCCESS) {
         Fetched++;
         for (cf = BindFields.GetFirst (), sa = View.GetFirstField();
              cf != 0 && sa != 0 && Return != SQL_ERROR;
              cf = BindFields.GetNext (), sa = View.GetNextField() )
            if (cf->BufferExists ()) {
               cf->Bind (sa);
               rc = cf->ToExt (this, numElem);
               if (rc != SQL_SUCCESS)
                  Return = rc;
            }
      } else if (Return == EENDFILE || Return == ENOREC)
         Return = SQL_NO_DATA_FOUND;
      else if (Return == ELOCKED) {
         PrintError (E_WARNING_000, "Cannot lock row", Return);
         Return = SQL_ERROR;
      } else if (Return != SQL_ERROR) {  // Return = SQL_ERROR -> Errore gia segnalato
         PrintError (E_GENERAL_ERROR_000, "Error processing table", Return);
         Return = SQL_ERROR;
      } 
   } else {
      PrintError (E_GENERAL_ERROR_010, "No command executed");
      Return = SQL_ERROR;
   }
   return Return;
}

Error
SqlParser::GetColNum (long &num)
{
   Error Return = SQL_SUCCESS;
    
   if (&num) {
      if (View.getRel() != 0) {
         num = View.getRel()->GetFieldNum();
      } else
         num = 0;
   }

   return Return;
}

Error
SqlParser::GetRowCount (long &num)
{
   Error Return = SQL_SUCCESS;

   if (&num)
      num = Processed;

   return Return;
}


Error
SqlParser::GetNumParams (short &num)
{
   Error Return = SQL_SUCCESS;

   if (&num)
      num = nParams;

   return Return;
}

Error
SqlParser::GetColName (unsigned short icol, PString &rgbDesc)
{  
   Error Return = SQL_SUCCESS;
   StmtAttr *sa;

   if (View.getRel() != 0) {
      sa = View.GetFieldAt (icol - 1);
      if (sa == 0) {
         PrintError (E_SYNTAX_ERROR_S02, "");
         Return = SQL_ERROR;
      } else{
         rgbDesc = sa->getName();
         DB->IntlFromServer (sa->getName().gets(), rgbDesc.gets(),
                             (int) sa->getName().size());
      }
   } else {
      PrintError (E_GENERAL_ERROR_010, "No command executed");
      Return = SQL_ERROR;
   }

   return Return;
}

Error
SqlParser::GetLength (unsigned short icol, long &len)
{
   Attribute *a;
   Error Return = SQL_SUCCESS;
   
   if (&len) {
      if (View.getRel() != 0) {
         a = View.getRel()->GetFieldAt (icol - 1);
         if (a != 0)
            if (a->getDateType() == T_TIMESTAMP)
               len = sizeof(TIMESTAMP_STRUCT);
            else
               len = a->GetLen();
         else {
            PrintError (E_DYN_SQL_009, "");
            Return = SQL_ERROR;
         }
      } else {
         PrintError (E_GENERAL_ERROR_010, "No command executed");
         Return = SQL_ERROR;
      }
   }
   return Return;
}

Error
SqlParser::GetColLength (unsigned short icol, long &len)
{
   Error Return = SQL_SUCCESS;
   SignType s;
   DateTime d;
   
   if (&len) {
      GetColType (icol, len, s, d);
      switch (Int2OdbcType ((int)len, s, d)) {
      case  SQL_DATE:
      case  SQL_TIME:
      case  SQL_CHAR:
         Return = GetLength (icol, len);
         break;
      case SQL_SMALLINT:
         len = 2;
         break;
      case SQL_INTEGER:
         len = 4;
         break;
      case SQL_NUMERIC:
      case SQL_DECIMAL:
         Return = GetLength (icol, len);
         len += 2;
         break;
      case SQL_REAL:
         len = 4;
         break;
      case SQL_DOUBLE:
         len = 8;
         break;
      case SQL_TIMESTAMP:
         len = sizeof(TIMESTAMP_STRUCT);
         break;
      default:
         len = 0;
         break;
      }
   }
   return Return;
}

Error
SqlParser::GetColType (unsigned short icol, long &type,
                       SignType &s, DateTime &d)
{  
   Attribute *a;
   Error Return = SQL_SUCCESS;

   if (View.getRel() != 0) {
      a = View.getRel()->GetFieldAt (icol - 1);
      if (a != 0) {
         type = a->GetType();
         s = a->GetSign();
         d = a->getDateType();
      } else {
         PrintError (E_DYN_SQL_009, "");
         Return = SQL_ERROR;
      }
   } else {
      PrintError (E_GENERAL_ERROR_010, "No command executed");
      Return = SQL_ERROR;
   }
   return Return;
}

Error
SqlParser::GetUpdatable (unsigned short icol, long &upd)
{  
   Attribute *a;
   Error Return = SQL_ERROR;

   if (View.getRel() != 0) {
      a = View.getRel()->GetFieldAt (icol - 1);
      if (a != 0) {
         if (a->GetVisibility() == ATTR_ALL)
            upd = SQL_ATTR_WRITE;
         else
            upd = SQL_ATTR_READONLY;
         Return = SQL_SUCCESS;
      } else
         PrintError (E_DYN_SQL_009, "");
   } else
      PrintError (E_GENERAL_ERROR_010, "No command executed");
   return Return;
}

Error
SqlParser::GetIsChar (unsigned short icol, long &ischr)
{  
   Attribute *a;
   Error Return = SQL_SUCCESS;

   if (View.getRel() != 0) {
      a = View.getRel()->GetFieldAt (icol - 1);
      if (a != 0) {
         ischr = !(a->IsNumeric() || a->getDateFmt() != 0);
      } else {
         PrintError (E_DYN_SQL_009, "");
         Return = SQL_ERROR;
      }
   } else {
      PrintError (E_GENERAL_ERROR_010, "No command executed");
      Return = SQL_ERROR;
   }
   return Return;
}

Error
SqlParser::GetColPrecision (unsigned short icol, long &prc)
{
   Error Return = SQL_SUCCESS;
   SignType s;
   DateTime d;

   GetColType (icol, prc, s, d);
   switch (Int2OdbcType ((int)prc, s, d)) {
   case  SQL_DATE:
   case  SQL_TIME:
   case  SQL_CHAR:
      Return = GetLength (icol, prc);
      break;
   case SQL_SMALLINT:
      prc = 5;
      break;
   case SQL_INTEGER:
      prc = 10;
      break;
   case SQL_NUMERIC:
   case SQL_DECIMAL:
      Return = GetLength (icol, prc);
      break;
   case SQL_REAL:
      prc = 7;
      break;
   case SQL_DOUBLE:
      prc = 15;
      break;
   case SQL_TIMESTAMP:
      prc = TIMESTAMPPREC;
      break;
   case SQL_LONGVARCHAR:
   case SQL_LONGVARBINARY:
      prc = 0x7FFFFFFFL;
      break;
   default:
      prc = 0;
      break;
   }
   return Return;
}

Error
SqlParser::GetColScale (unsigned short icol, long &scale)
{   
   Attribute *a;
   Error Return = SQL_SUCCESS;

   if (View.getRel() != 0) {
      a = View.getRel()->GetFieldAt (icol - 1);
      if (a != 0)
         scale = a->GetDecimal();
      else {
         PrintError (E_DYN_SQL_009, "");
         Return = SQL_ERROR;
      }
   } else {
      PrintError (E_GENERAL_ERROR_010, "No command executed");
      Return = SQL_ERROR;
   }
   return Return;
}

Error
SqlParser::GetColSign (unsigned short icol, long &segno)
{  
   Attribute *a;
   Error Return = SQL_SUCCESS;

   if (View.getRel() != 0) {
      a = View.getRel()->GetFieldAt (icol - 1);
      if (a != 0)
         segno = a->GetSign();
      else {
         PrintError (E_DYN_SQL_009, "");
         Return = SQL_ERROR;
      }
   } else {
      PrintError (E_GENERAL_ERROR_010, "No command executed");
      Return = SQL_ERROR;
   }
   return Return;
}

Error
SqlParser::GetColDisplaySize (unsigned short icol, long &dispSize)
{
   Error Return = SQL_SUCCESS;
   SignType s;
   DateTime d;

   GetColType (icol, dispSize, s, d);
   switch (Int2OdbcType ((int)dispSize, s, d)) {
   case  SQL_DATE:
   case  SQL_TIME:
   case  SQL_CHAR:
      Return = GetLength (icol, dispSize);
      break;
   case SQL_SMALLINT:
      dispSize = 6;
      break;
   case SQL_INTEGER:
      dispSize = 11;
      break;
   case SQL_NUMERIC:
   case SQL_DECIMAL:
      Return = GetColPrecision (icol, dispSize);
      if (Return == SQL_SUCCESS)
         dispSize += 2;
      break;
   case SQL_REAL:
      dispSize = 18 + 6;
      break;
   case SQL_DOUBLE:
      dispSize = 36 + 6;
      break;
   case SQL_TIMESTAMP:
      dispSize = TIMESTAMPPREC;
      break;
   default:
      dispSize = 0;
      break;
   }
   return Return;
}

Error
SqlParser::Bind (unsigned short icol, AttrCType fCType, char *rgbValue, 
                 long cbValueMax, long *pcbValue)
{
   Error Return = SQL_SUCCESS;
   CField *cf;
   unsigned short bindsize = (unsigned short) BindFields.GetItemNum();
   
   if (icol == 0) {
      PrintError (E_GENERAL_ERROR_C00, "");
      return Return = SQL_ERROR;
   }
   if (rgbValue == 0) {
      if (bindsize >= icol) {
         cf = BindFields.GetAt (icol - 1);
         cf->UnBind();
         cf->SetType (SQL_C_UNKNOWN_TYPE, 0, 0, 0);
      }
   } else {
      if (bindsize < icol)
         for ( ; bindsize < icol; bindsize++)
            BindFields.AddItem (new CField(*DB));
      else
         BindFields.GetAt (icol - 1);
      cf = BindFields.GetCurrent ();
      // Controllare fCType !!
      cf->SetType (fCType, rgbValue, cbValueMax, pcbValue);
   }
 
   return Return;
}

Error
SqlParser::SetParam (int ipar, AttrCType fCType, int fSqlType,
                     long precision, char *rgbValue, long * pcbValue)
{
   Error Return = SQL_SUCCESS;
   CField *cf;
   int paramsize = (unsigned short) Params.GetItemNum();

   if (paramsize < ipar)
      for ( ; paramsize < ipar; paramsize++)
         Params.AddItem (new CField(*DB));
   else
      Params.GetAt (ipar - 1);
   cf = Params.GetCurrent ();
   
   cf->SetType (fCType, rgbValue, precision, pcbValue); 
   switch (fSqlType) {
   case SQL_CHAR:
   case SQL_VARCHAR:
   case SQL_LONGVARCHAR:
//    if (pcbValue != 0)
//       cf->SetParamAttr(T_ALPHA, precision,  0);
//    else
         cf->SetParamAttr(T_CSTRING, precision,  0);
      break;
   case SQL_BINARY:
   case SQL_VARBINARY:
   case SQL_LONGVARBINARY:
      cf->SetType (fCType, rgbValue, 10, pcbValue); 
      cf->SetParamAttr(T_BLOB, precision,  0);
      break;
   case SQL_NUMERIC:
   case SQL_DECIMAL:
   case SQL_REAL:
   case SQL_FLOAT:
   case SQL_DOUBLE:
   case SQL_INTEGER:
   case SQL_SMALLINT: 
      cf->SetParamAttr (T_DOUBLE, 0, 0);
      break;
   case SQL_TIMESTAMP:
      cf->SetTimeStamp();
      break;
   default:
      PrintError (E_GENERAL_ERROR_003, "");
      Return = SQL_ERROR;
      break;
   }
   return Return;
}

Error
SqlParser::DescribeParam (unsigned short ipar, short *fSqlType,
                          unsigned long *precision, short *scale, short *nullable)
{
   Error Return = SQL_SUCCESS;
   CField *cf;

   if (ipar < 1 || ipar > Params.GetItemNum()) {
      PrintError (E_DYN_SQL_009, "Parameter not existant");
      Return = SQL_ERROR;
   } else {
      CField *cf = Params.GetAt (ipar - 1);
      if (cf == 0) {
         PrintError (E_DYN_SQL_009, "Parameter not existant");
         Return = SQL_ERROR;
      } else {
         Attribute *a = cf->GetParamAttr().getAttr();
         if (a->GetType() == T_UNKNOWN) {
            a = getType(ipar);
            if (a) {
               *fSqlType=Int2OdbcType(a->GetType(),a->GetSign(),
                                                   a->getDateType());
               *precision = a->GetLen();
               *scale = a->GetDecimal();
            } else {
               *fSqlType = SQL_CHAR;
               *precision = 255;
               *scale = 0;
            }
         } else {
            *fSqlType=Int2OdbcType (a->GetType(),a->GetSign(),a->getDateType());
            *precision = a->GetLen();
            *scale = a->GetDecimal();
         }
         *nullable = SQL_NULLABLE_UNKNOWN;
      }
   }
   return Return;
}


Error
SqlParser::ParamData (void **dataPtr)
{
   Error Return;
   CField *cf;

   for (cf = Params.GetFirst (); cf != 0; cf = Params.GetNext ())
      if (cf->isDataAtExec()) {
         *dataPtr = cf->getBuffer();
         break;
      }
   if (cf == 0)
      Return = Exec();
   else
      Return = (Error) SQL_NEED_DATA;

   return Return;
}

Error
SqlParser::PutData (void *dataPtr, long len)
{
   Error Return = SQL_SUCCESS;
   CField *cf;

   for (cf = Params.GetFirst (); cf != 0; cf = Params.GetNext ()) {
      if (cf->isDataAtExec()) {
         cf->putData((char*)dataPtr, len);
         break;
      }
   }

   return Return;
}       

Error
SqlParser::GetData (unsigned short icol, AttrCType fCType, char *rgbValue,
                    long cbValueMax, long *pcbValue)
{
   Error Return = SQL_SUCCESS;
   StmtAttr *sa;

   if (View.getRel() != 0) {
      if ((sa = View.GetFieldAt (icol - 1)) != 0) {         
         CField cf(*DB);
         // Controllare fCType !!!         
         cf.SetType (fCType, rgbValue, cbValueMax, pcbValue);         
         cf.Bind(sa);
         Return = cf.ToExt (this);
         *pcbValue = cf.getByteRed();
      } else {
         PrintError (E_DYN_SQL_009, "");
         Return = SQL_ERROR;
      }
   } else {
      PrintError (E_GENERAL_ERROR_010, "No command executed");
      Return = SQL_ERROR;
   }
   return Return;
}

Error
SqlParser::ProcessAll (int order)
{
   Error Return;
   PBool result;
   StmtRel *rcurr = FromList.GetCurrent ();
   StmtRel *rnext = FromList.GetNext ();
   StmtRelView *srv = 0;
   Where viewWhere (this,
                    (Expression::Method)(&SqlParser::GetTableName), PFalse);
   
   if (Command == UPDATE && (srv = ViewList.GetFirst())
                         && srv->getRelView()->getCheckOption()) {
      const PString &viewWheres = srv->getWhere();
      if (viewWheres.size () > 0) {
         Lex l1(viewWheres.gets());
         PutKeyToken (l1);
         Return = viewWhere.Parse (l1, 0);
      } else
         srv = 0;
   } else
      srv = 0;

   rcurr->SetOrder(order++);
   Return = rcurr->Start();
   if (Return == SQL_SUCCESS) {
      while ((Return = rcurr->ReadNext(forUpdate, PFalse)) == SQL_SUCCESS) {
         if (rnext != 0) {
            if (rcurr->EvalJoin()) {
               rcurr->foundJoin = PTrue;
               if (WhereStack.Eval(rcurr, result) != SQL_SUCCESS) {
                  Return = SQL_ERROR;
                  break;
               }
               if (result) {
                  Return = ProcessAll (order);
                  if (Return != SQL_SUCCESS &&
                      Return != SQL_NO_DATA_FOUND)
                     break;
               }
            }
         } else {
            if (rcurr->EvalJoin()) {
               rcurr->foundJoin = PTrue;
               if (WhereStack.Eval(rcurr, result) != SQL_SUCCESS) {
                  Return = SQL_ERROR;
                  break;
               }
               if (result) {
                  switch (Command) {
                  case SELECT:
                     View.FillBuffer(Selected);
                     Return = View.Write();
                     if (Return != SQL_SUCCESS) {
                        PrintError (E_GENERAL_ERROR_000,
                                    "Cannot write sort table",View.getStatus());
                        Return = SQL_ERROR;
                     }
                     break;
                  case DELETE_:
                     if (!rcurr->getRel()->hasOnePermission(REL_DELETE))  {
                        PrintError (E_INTEGRITY_VIOL_000, "No delete permission"); 
                        Return = SQL_ERROR; 
                     } else {
                        Return = rcurr->DelCurrent();
                        if (Return != SQL_SUCCESS) {
                           PrintError (E_GENERAL_ERROR_000,
                                       "Cannot delete from table", rcurr->getStatus());
                           Return = SQL_ERROR;
                        }
                     }
                     break; 
                  case UPDATE:
                     if (!rcurr->getRel()->hasOnePermission(REL_UPDATE))  {
                        PrintError (E_INTEGRITY_VIOL_000,
                                    "No update permission");
                        Return = SQL_ERROR; 
                     } else {
                        Return = UpdateBuffer(rcurr->getRel());
                        if (srv)
                           Return = viewWhere.Eval(rcurr,result);
                        if (Return == SQL_SUCCESS) {
                           if (result) {
                              Return = rcurr->UpdCurrent();
                              if (Return != SQL_SUCCESS) {
                                 PrintError (E_GENERAL_ERROR_000,
                                    "Cannot update table", rcurr->getStatus());
                                 Return = SQL_ERROR;
                              }
                           } else {
                               PrintError (E_INTEGRITY_VIOL_000,
                                           "WHERE clause violation");
                               Return = SQL_ERROR;
                           }
                        }
                     }
                     break;
                  default:
                     Return = (Error)ERRNOINVALID;
                     break;
                  }
                  if (Return != 0)
                     break;
                  else
                     Processed++;
               }
            }
         }
      }
   }
   if (Return != SQL_ERROR && !rcurr->foundJoin && rcurr->leftJoin) {
      StmtRel *rlast;
      StmtRelIterator sri(FromList);
      for (sri.reset(); *sri; sri++)
         if (*sri == rcurr)
            break;
      (*sri)->foundJoin = PTrue;
      (*sri)->Clear();
      rlast = (*sri);
      for (sri++; *sri; sri++) {
         (*sri)->Clear();
         (*sri)->SetOrder (order++);
         rlast = (*sri);
      }
      if (WhereStack.Eval(rlast, result) == SQL_SUCCESS) {
         if (result) {
            View.FillBuffer(Selected);
            Return = View.Write();
            if (Return != SQL_SUCCESS) {
               PrintError (E_GENERAL_ERROR_000, "Cannot write sort table",
                           View.getStatus());
               Return = SQL_ERROR;
            } else {
               Processed++;
               Return = (Error)EENDFILE;
            }
         }
      } else {
         Return = SQL_ERROR;
      }
   }
   if (Return == EENDFILE || Return == ENOREC)
      Return = SQL_NO_DATA_FOUND;
   else if (Return != SQL_ERROR) {  // viene testato SQL_ERROR perche la funzione e ricorsiva
      PrintError (E_GENERAL_ERROR_000, "Error processing table", Return);
      Return = SQL_ERROR;
   }
   if (rnext != 0)
      FromList.GetPrevious();
   return Return;
}

Error
SqlParser::SingleProcess (int order, enum eDirection dir)
{
   Error Return;
   PBool result;
   StmtRel *rcurr = FromList.GetCurrent ();
   StmtRel *rnext = FromList.GetNext ();
   
   Return = (Error)rcurr->getStatus();
   rcurr->SetOrder(order);
   
   if (dir == NEXT) {
      if (Direction == dir) {
         if (order > 1 && rcurr->getStatus() != SQL_SUCCESS)
            Return = rcurr->Start();
      } else
         Return = SQL_SUCCESS;
   } else
      if (Direction == dir) {
         if (order > 1 && rcurr->getStatus() != SQL_SUCCESS)
            Return = rcurr->PrevStart();
      } else
         Return = SQL_SUCCESS; 
   Direction = dir;         
   order++;
   if (Return == SQL_SUCCESS) {
      for ( ; ; ) {
         if (rnext == 0) {
            if (Direction == NEXT)
               Return = rcurr->ReadNext(forUpdate);
            else
               Return = rcurr->ReadPrev(forUpdate);
         } else if (rnext->getStatus() != SQL_SUCCESS) {
            if (Direction == NEXT)
               Return = rcurr->ReadNext(forUpdate);
            else
               Return = rcurr->ReadPrev(forUpdate);
         }
         if (Return != SQL_SUCCESS)
            break;
         if (rnext != 0) {
            if (rcurr->EvalJoin()) {
               rcurr->foundJoin = PTrue;
               if (WhereStack.Eval(rcurr, result) != SQL_SUCCESS) {
                  Return = SQL_ERROR;
                  break;
               }
               if (result) {
                  Return = SingleProcess (order, Direction);
                  if (Return == SQL_SUCCESS)
                     break;
               }
            } 
         } else {
            if (rcurr->EvalJoin()) {
               rcurr->foundJoin = PTrue;
               if (WhereStack.Eval(rcurr, result) != SQL_SUCCESS) {
                  Return = SQL_ERROR;
                  break;
               }
               if (result) {
                  Processed++;
                  break;
               }
            }
         } 
      }
   }
   if (Return != SQL_ERROR && !rcurr->foundJoin && rcurr->leftJoin) {
      StmtRel *rlast;
      StmtRelIterator sri(FromList);
      for (sri.reset(); *sri; sri++)
         if (*sri == rcurr)
            break;
      (*sri)->foundJoin = PTrue;
      (*sri)->Clear();
      rlast = (*sri);
      for (sri++; *sri; sri++) {
         (*sri)->Clear();
         (*sri)->SetOrder (order++);
         rlast = (*sri);
      }
      if (WhereStack.Eval(rlast, result) == SQL_SUCCESS) {
         if (result) {
            Processed++;
            Return = SQL_SUCCESS;
         }
      } else {
         Return = SQL_ERROR;
      }
   }
   if (rnext != 0)
      FromList.GetPrevious();
   return Return;
}

Error
SqlParser::Insert (Lex &l)
{
   Token *t = 0;
   StmtRel   *sr;
   StmtAttr  *sa;
   Expression *ex;
   long nViews, nTabs;
   Error Return = SQL_ERROR;
   StmtRelView *srv = 0;

   if ((t = l.GetToken()) == 0 || t->token != INTO){
      PrintError (E_SYNTAX_ERROR_000,
                  "INTO keyword not found in INSERT/REPLACE statement" );
      return Return;
   } else if ((Return = From (l, PTrue)) != SQL_SUCCESS)
      return Return;

   nViews = ViewList.GetItemNum();
   nTabs = FromList.GetItemNum();

   if (nViews > 0) {
      srv = ViewList.GetFirst();
      if (nViews > 1) {
         PrintError (E_SYNTAX_ERROR_000,
                     "Only 1 VIEW is allowed in INSERT/REPLACE statement" );
         return Return;
      } else if (nTabs != 1) {
         PrintError (E_SYNTAX_ERROR_000,
            "Only VIEWS concerning 1 table are allowed in INSERT/REPLACE statement" );
         return Return;
      }
   } else if (nTabs != 1) {
      PrintError (E_SYNTAX_ERROR_000,
                  "Only 1 TABLE is allowed in INSERT/REPLACE statement" );
      return Return;
   }

   sr = FromList.GetFirst();
   sr->BufferStmtRelAlloc();
   sr->Clear();

   if ((t = l.GetToken()) == 0 || (t->token != '(' && 
                                   t->token != VALUES && t->token != SELECT)) {
      PrintError (E_SYNTAX_ERROR_000, "Invalid field list in INSERT/REPLACE statement" );
      return Return = SQL_ERROR;
   }

   if (t->token == '(') {

      PString fieldName, tableName, aliasName;

      while ((t = l.GetToken()) != 0 && t->token != ')') {
         switch (t->token) {
         case '*':
            if (((t = l.GetToken()) != 0 && t->token == ')')) {
               Return = AddAllField ();
               l.UngetToken ();
            } else {
               Return = SQL_ERROR;
               PrintError (E_SYNTAX_ERROR_000, "Invalid field list in INSERT/REPLACE statement" );
            }
            break;
         case USER_NAME:
            aliasName = "";
            tableName = "";
            fieldName = t->word;
            t = l.GetToken();
            if (t->token != ',') {
               if (t->token == '.') {
                  tableName = fieldName;
                  t = l.GetToken();
                  if (t->token == USER_NAME) {
                     fieldName = t->word;
                     t = l.GetToken();
                  } else {
                     Return = SQL_ERROR;
                     PrintError (E_SYNTAX_ERROR_000, "Invalid field list in INSERT/REPLACE statement" );
                     break;
                  }
               }
               if (t->token == USER_NAME) {
                  aliasName = t->word;
                  t = l.GetToken();
               }
            }
            Return = AddField (fieldName, tableName, aliasName);
            if (t && t->token != ',')
               l.UngetToken();
            break;
        default:
           Return = SQL_ERROR;
           PrintError (E_SYNTAX_ERROR_000, "Unexpected token <" + t->word + ">");
           return Return;
         }
      } 
      t = l.GetToken();
   } else if ((sa = Fields.GetFirst()) == 0)
      Return = AddAllField ();

   if (t == 0 || !(t->token == SELECT || t->token == VALUES)) {
      if (t)
         PrintError (E_SYNTAX_ERROR_000, "Expected <VALUES/SELECT> found <"+t->word+">");
      else
         PrintError (E_SYNTAX_ERROR_000, "Expected <VALUES/SELECT> found <end statement>");
   } else if (t->token == VALUES) {
      if ((t = l.GetToken()) == 0 || t->token != '(') {
         Return = SQL_ERROR;
         if (t)
            PrintError (E_SYNTAX_ERROR_000, "Expected <(> found <"+t->word+">");
         else
            PrintError (E_SYNTAX_ERROR_000, "Expected <(> found <end statement>");
         return Return;
      }

      sa = Fields.GetFirst();
      while ((t = l.GetToken()) != 0 && t->token != ')' && sa != 0)
         switch (t->token) {
         case NULL_:
            Return = sa->FromToken(*t);
            ex = new Expression (this, sa);
            Selected.AddItem(ex);
            sa = Fields.GetNext();
            break;
         case ',':
            break;
         default:
            l.UngetToken();
            ex = new Expression (*this, l,
                                 (Expression::Method)(&SqlParser::GetTableName), PFalse);
            if (ex->isOk()) {
               Selected.AddItem(ex);
            } else {
               Return = SQL_ERROR;
            }
            sa = Fields.GetNext();
            break;
         }
   } else {  // subquery
      insertQuery = new SqlParser (GetDB(), this);
      TokenList tl;
      l.UngetToken();
      while (t = l.GetToken())
         tl.AddItem(t);
      Lex inSel(tl, PTrue);
      Return = insertQuery->Select(inSel);
      if (Return != SQL_SUCCESS)
         MoveStmtError (this, insertQuery);
      else {
         StmtAttr *sa1, *sa2;
         for (sa1 = Fields.GetFirst(), sa2 = insertQuery->View.GetFirstField();
              sa1 && sa2;
              sa1 = Fields.GetNext(),  sa2 = insertQuery->View.GetNextField())
            if (sa1->getAttr()->IsNumeric() != sa2->getAttr()->IsNumeric()) {
               PrintError (E_CARD_VIOL_S01, "");
               Return = SQL_ERROR;
            }
         if (Return == SQL_SUCCESS && (sa1 != 0 || sa2 != 0)) {
            PrintError (E_CARD_VIOL_S01, "");
            Return = SQL_ERROR;
         }
      }
   }

   t = l.GetToken();
   if (srv != 0 && srv->getRelView()->getCheckOption() && Return != SQL_ERROR)
      Return = BuildWhere (t, l);
   if (t && t->token != ';')
      PrintError (E_SYNTAX_ERROR_000,
                 "Unexpected token <" + t->word + "> in INSERT/REPLACE statement");
   Return = LookError (0, (DataBase *) SQL_NULL_HDBC, this);
   return Return;
}

Error
SqlParser::Delete (Lex &l)
{
   Token *t = 0;
   StmtRel  *sr;
   long nViews, nTabs;
   Error Return = SQL_ERROR;
   
   if ((t = l.GetToken()) == 0 || t->token != FROM){
      PrintError (E_SYNTAX_ERROR_000,
                  "FROM keyword not found in DELETE statement" );
      return Return;
   } else if ((Return = From (l, PTrue)) != SQL_SUCCESS)
      return Return;

   nViews = ViewList.GetItemNum();
   nTabs = FromList.GetItemNum();

   if (nViews > 0) {
      if (nViews > 1) {
         PrintError (E_SYNTAX_ERROR_000,
                     "Only 1 VIEW is allowed in DELETE statement" );
         return Return;
      } else if (nTabs != 1) {
         PrintError (E_SYNTAX_ERROR_000,
            "Only VIEWS concerning 1 table are allowed in DELETE statement" );
         return Return;
      }
   } else if (nTabs != 1) {
      PrintError (E_SYNTAX_ERROR_000,
                  "Only 1 TABLE is allowed in DELETE statement" );
      return Return;
   }

   sr = FromList.GetFirst();
   sr->Clear();

   // if ((t = l.GetToken()) != 0 && t->token == WHERE)
   t = l.GetToken();
   Token *t1 = l.GetToken();
   if (t && t->token == WHERE && t1 && t1->token == CURRENT) {
      if ((t = l.GetToken()) && t->token == OF && (t = l.GetToken())) {
         SqlParser *pu;
         if (pu = DB->getHstmtByCursorName (t->word)) {
            if (!(pUpdStmtRel = pu->getFrom(sr->getName()))) {
               PrintError (E_SYNTAX_ERROR_000,
                     "Invalid cursor  <" + t->word + ">");
               Return = SQL_ERROR;
            }
         } else {
            PrintError (E_SYNTAX_ERROR_000,
                  "Unknown cursor name <" + t->word + ">");
            Return = SQL_ERROR;
         }
      } else {
         PrintError (E_SYNTAX_ERROR_000,
               "Invalid positioned in UPDATE statement");
            Return = SQL_ERROR;
         }
   } else {
      if (t1)
         l.UngetToken();

      Return = BuildWhere (t, l);
      if (Return == SQL_SUCCESS && t && (t = l.GetToken())) {
         PrintError (E_SYNTAX_ERROR_000,
                     "Unexpected token <" + t->word + "> in DELETE statement");
         return Return;
      }


      WeighIndex (WhereStack);
      sr->ChooseIndex (*this);
   }
   return Return;
}

StmtRel *
SqlParser::getFrom (const PString &name)
{
   StmtRelIterator sri(FromList);

   for (sri.reset(); *sri; sri++)
      if ((*sri)->getName() == name)
            break;
   return *sri;
}

Error
SqlParser::Update (Lex &l)
{
   Token *t = 0;
   StmtRel  *sr;
   StmtAttr *sa;
   Expression *ex;
   long nViews, nTabs;
   AttrViewKeyObject *avko;
   Error Return = SQL_ERROR;
   StmtRelView *srv = 0;

   if ((Return = From (l, PTrue)) != SQL_SUCCESS)
      return Return;

   nViews = ViewList.GetItemNum();
   nTabs = FromList.GetItemNum();

   if (nViews > 0) {
      srv = ViewList.GetFirst();
      if (nViews > 1) {
         PrintError (E_SYNTAX_ERROR_000,
                     "Only 1 VIEW is allowed in UPDATE statement" );
         return Return;
      } else if (nTabs != 1) {
         PrintError (E_SYNTAX_ERROR_000,
            "Only VIEWS concerning 1 table are allowed in UPDATE statement" );
         return Return;
      }
   } else if (nTabs != 1) {
      PrintError (E_SYNTAX_ERROR_000,
                  "Only 1 TABLE is allowed in UPDATE statement" );
      return Return;
   }

   sr = FromList.GetFirst();
   sr->BufferStmtRelAlloc();
   sr->Clear();

   if ((t = l.GetToken()) == 0 || t->token != SET) {
      PrintError (E_SYNTAX_ERROR_000, "SET keyword not found in UPDATE statement" );
      return Return;
   }
   PString fieldName, tableName, aliasName;

   while ((t = l.GetToken()) != 0 && t->token != WHERE &&
           Return == SQL_SUCCESS) {
      switch (t->token) {
      case ',':
         continue;
         break;
      case USER_NAME:
         aliasName = "";
         tableName = "";
         fieldName = t->word;
         t = l.GetToken();
         if (t->token != ',') {
            if (t->token == '.') {
               tableName = fieldName;
               t = l.GetToken();
               if (t->token == USER_NAME) {
                  fieldName = t->word;
                  t = l.GetToken();
               } else {
                  Return = SQL_ERROR;
                  PrintError (E_SYNTAX_ERROR_000, "Invalid field list in UPDATE statement" );
                  break;
               }
            }
            if (t->token == USER_NAME) {
               aliasName = t->word;
               t = l.GetToken();
            }
         }
         Return = AddField (fieldName, tableName, aliasName);
         if (Return == SQL_SUCCESS)
            sa = Fields.GetCurrent();
         else
            return Return;
         if (t && t->token != ',')
            l.UngetToken();
         break;
      default:
         Return = SQL_ERROR;
         PrintError (E_SYNTAX_ERROR_000, "Unknown token <" + t->word + "> in UPDATE statement");
         return Return;
         break;
      }
      if ((t = l.GetToken()) == 0 || t->token != '='){
         Return = SQL_ERROR;
         PrintError (E_SYNTAX_ERROR_000, "<=> after column-identifier not found in UPDATE statement" );
         return Return;
      }
      if ((t = l.GetToken()) != 0 && sa != 0)
         switch (t->token) {
         case NULL_:
            Return = sa->FromToken(*t);
            ex = new Expression (this, sa);
            Selected.AddItem(ex);
            break;
         default:
            l.UngetToken();
            if (srv) {
               TokenList exp;
               while ((t = l.GetToken()) != 0 && t->token != WHERE &&
                                                 t->token != (toknum) ',') {
                  if (t->token == USER_NAME && (avko = srv->getField(t->word))) {
                     for (TokenIterator ti(avko->getValue()->getValue()); *ti; ti++)
                         exp.AddItem (new Token(**ti));
                  } else {
                     exp.AddItem (new Token (*t));
                  }
               }
               if (t && t->token == WHERE)
                  l.UngetToken();
               
               Lex l1(exp, PTrue);
               ex = new Expression (*this, l1,
                     (Expression::Method)(&SqlParser::GetTableName), PFalse);
            } else {
               ex = new Expression (*this, l,
                                 (Expression::Method)(&SqlParser::GetTableName), PFalse);
            }
            if (ex->isOk()) {
               Selected.AddItem(ex);
            } else {
               Return = SQL_ERROR;
            }
            break;
         }
      else {
         Return = SQL_ERROR;
         PrintError (E_SYNTAX_ERROR_000, "not expression found after '=' in UPDATE statement" );
         return Return;
      }
   }
   if (Return != SQL_ERROR) {
      Token *t1 = l.GetToken();
      if (t && t->token == WHERE && t1 && t1->token == CURRENT) {
         if ((t = l.GetToken()) && t->token == OF &&
             (t = l.GetToken())) {
            SqlParser *pu;
            if (pu = DB->getHstmtByCursorName (t->word)) {
               if (!(pUpdStmtRel = pu->getFrom(sr->getName()))) {
                  PrintError (E_SYNTAX_ERROR_000,
                     "Invalid cursor  <" + t->word + ">");
                  Return = SQL_ERROR;
               }
            } else {
               PrintError (E_SYNTAX_ERROR_000,
                  "Unknown cursor name <" + t->word + ">");
               Return = SQL_ERROR;
            }
         } else {
            PrintError (E_SYNTAX_ERROR_000,
                  "Invalid positioned in UPDATE statement");
            Return = SQL_ERROR;
         }
      } else {
         if (t1)
            l.UngetToken();
         Return = BuildWhere (t, l);
         if (Return==SQL_SUCCESS && t && (t=l.GetToken()) && t->token!=';') {
            PrintError (E_SYNTAX_ERROR_000,
                  "Unexpected token <" + t->word + "> in UPDATE statement");
         } else {
            WeighIndex (WhereStack);
            sr->ChooseIndex (*this);
         }
      }
   }
   Return = LookError (0, (DataBase *) SQL_NULL_HDBC, this);
   return Return;
}

Error
SqlParser::UpdateBuffer (Relation *r)
{
   StmtAttrIterator aIint(Fields);
   ExpressionIterator eIext (Selected);

   for (eIext.reset(), aIint.reset(); *eIext != 0; eIext++, aIint++)
      (*aIint)->FromAttr (*(*eIext)->getValueUpdated());
   return SQL_SUCCESS;
}

Error
SqlParser::InsertBuffer (Relation *r)
{
   StmtAttrIterator aIint(Fields);
   ExpressionIterator eIext (Selected);

   for (eIext.reset(), aIint.reset(); *eIext != 0; eIext++, aIint++)
      if ((*aIint)->getAttr()->isSerial())
         (*aIint)->FromDouble(nextVal ((*aIint)));
      else
         (*aIint)->FromAttr (*(*eIext)->getValueUpdated());
   return SQL_SUCCESS;
}

Error
SqlParser::InsertBuffer (StmtAttrList &dst, StmtAttrList &src)
{
   PString Errore;
   Error Return = SQL_SUCCESS;
   StmtAttrIterator aIext(src);
   StmtAttrIterator aIint(dst);
   
   for (aIext.reset(), aIint.reset(); *aIext && *aIint; aIext++, aIint++) {
      if ((*aIint)->getAttr()->isSerial())
         (*aIint)->FromDouble(nextVal ((*aIint)));
      else
         (*aIint)->FromAttr (*(*aIext));
   }
   return Return;
}

Attribute *
SqlParser::getType (int n)
{
   StmtAttrIterator aIint(Fields);
   ExpressionIterator eIext (Selected);
   Attribute *Return;
   int cnt = 0;

   for (eIext.reset(), aIint.reset(); *eIext != 0; eIext++, aIint++) {
      Return = (*eIext)->getValue()->getAttr();
      if (Return->GetType() == T_UNKNOWN)
         cnt++;
      if (cnt == n) {
         Return = (*aIint)->getAttr();
         break;
      } else
         Return = 0;
   }
   return Return;
}

time_t
SqlParser::getCurrentTimestamp ()
{
   if (currentTimestamp == 0) {
      currentTimestamp = time(0);
   }
   return currentTimestamp;
}

double
SqlParser::nextVal(StmtAttr *sa)
{
   double Return = 0;
   char *colName = sa->getAttr()->getName().gets();
   char *tblName = sa->getAttr()->getParent()->getName().gets();
   int origWait = GetDB()->wait();

   GetDB()->setWait(ISWAIT);
   SqlParser getProgQuery(GetDB());
   PString where (" WHERE DF_NAME = '");
   where += tblName;
   where += "' AND DF_CONAM = '";
   where += colName;
   where += "'";
   PString query("SELECT DF_INCR, DF_PROG from __COLUMNS ");
   query += where;
   query += " FOR UPDATE";
   if (getProgQuery.Parse (query.gets(), query.size()) != SQL_SUCCESS ||
       getProgQuery.ExecSelect() != SQL_SUCCESS ||
       getProgQuery.Fetch() != SQL_SUCCESS) {
      MoveStmtError (this, &getProgQuery);
   } else {
/**/
      StmtRel *rcurr = getProgQuery.FromList.GetCurrent();
      StmtAttr *saIncr = rcurr->getByName ("DF_INCR");
      StmtAttr *saProg = rcurr->getByName ("DF_PROG");
      double prog = 0;
      long incr = 0;
      long dummy;
      saIncr->ToDouble(prog, dummy);
      saProg->ToLong(incr, dummy);;
      prog += incr;
      Return = prog;

      saProg->FromDouble (prog);
      rcurr->UpdCurrent();
      
/**/
/*
      StmtAttr *saIncr = getProgQuery.View.GetFirstField();
      StmtAttr *saProg = getProgQuery.View.GetNextField();
      long incr = 0;
      long prog = 0;
      long dummy;
      saIncr->ToLong(incr, dummy);
      saProg->ToLong(prog, dummy);;
      prog += incr;
      Return = prog;

      SqlParser update (GetDB());
      query.prints ("UPDATE __COLUMNS SET DF_PROG = %f ", Return);
      query += where;
      if (update.Parse (query.gets(), query.size()) != SQL_SUCCESS ||
          update.ExecUpdate() != SQL_SUCCESS) {
         MoveStmtError (this, &getProgQuery);
      }
*/
   }
   GetDB()->setWait(origWait);
   return Return;
}
