/* Klaus Thiele (kth@oblib.teuto.de) - June '95 */
/*
INTERFACE TO
  mSQL
REVISION HISTORY
  07/10/95 API release 1.00
DESCRIPTION
  mSqlBase/mSqlCursor class implementation
*/

extern "C" {
#include <sql.h>
}
#include <sql_priv.h>
#include <errsql.h>

// =======================================================================
// mSqlCursor
// =======================================================================

unsigned long mSqlCursor::staticID = 0L;

// Constructor
mSqlCursor::mSqlCursor( char *dbase, int len, int sd ) {

   int slen = (len == 0 ? strlen(dbase) : len);

   database = new char[slen+1];
   strncpy( database, dbase, slen );
   database[slen] = '\0';
   upperStr( database );

   // change "DBASE/USER/PASSW" to "DBASE"
   char *tmp;
   if( (tmp = strchr((char *)database,'/')) != NULL )
      *tmp = '\0';

   sockDesc    = sd;

   statement   = NULL;
   fieldResult = NULL;
   dataResult  = NULL;
   fields      = NULL;

   numSelItems = 0;
   numBndVars  = 0;

   flag.compiled = FALSE;
   flag.executed = FALSE;
   flag.rsetMode = FALSE;
   flag.nullInd  = 0;

   for( int i = 0; i < MAX_BIND_VARS; i++ )
      binds[i] = NULL;

   myID = (unsigned long)this;
}


// Destructor
mSqlCursor::~mSqlCursor() {

   if( database ) {
      delete[] database;
      database = NULL;
   }
   if( statement ) {
      delete[] statement;
      statement = NULL;
   }

   sockDesc = 0;
   myID     = 0L;
   staticID = 0L;

   cleanUp();
}


// Execute
int mSqlCursor::Execute() {

int  mrc;

   if( !flag.compiled )
      return( lastRc = SQLEMSC );

   if( staticID != myID ) {
      if( msqlSelectDB( sockDesc, database ) < 0 ) {
         return( lastRc = DBAECOD );   // using msqlErrMsg
      }
      staticID = myID;
   }

   // clear resultset
   if( dataResult ) {
      msqlFreeResult( dataResult );
      dataResult = NULL;
   }

   
   // bind variables available? ...
   if( numBndVars > 0 ) {

      // ... yes, put the whole statement byte-by-byte to workstring,
      // scan for bind vars, and replace them with data values

      char *wrkStmt = new char[MAX_STMT_SIZE];
      memset(wrkStmt,'\0',MAX_STMT_SIZE);

      int i = 0;
      char bindvar[BINDVAR_LEN+1];
      int bndcnt = 0;
      char *a = statement;
      char *wrk;

      while( *a != '\0' ) {

         if( *a == ':' ) {

            a++;
            memset(bindvar,'\0',sizeof(bindvar));

            for( int j = 0; *a != ' ' && *a != ')' && *a != ',' && *a != '\0'; j++, a++ )
               bindvar[j] = *a;

            if( binds[bndcnt] ) {

               void *bndData = binds[bndcnt]->bnd_data;
               int   bndLen  = binds[bndcnt]->bnd_length;

               if( bndData == NULL ) {
                  delete[] wrkStmt;
                  return( lastRc = SQLE073 );
               }

               switch( binds[bndcnt]->bnd_typ ) {

                  int k;

                  case SQLPBUF: // buffer
                     // now a quick hack :-|
                     for( k = 0; k < msqlNumFields(fieldResult); k++ ) {
                        if( fields[k]->nr == bndcnt+1 ) {
                           wrk = new char[1024];
                           memset(wrk,' ',1024); wrk[1023] = '\0';
                           strncpy(wrk,(const char *)bndData,bndLen);
                           wrk[bndLen] = '\0';
                           switch( fields[k]->typ ) {
                              case INT_TYPE:
                              case REAL_TYPE:
                                 sprintf(&wrkStmt[i],"%s",wrk);
                                 break;
                              default:
                                 sprintf(&wrkStmt[i],"'%s'",wrk);
                                 break;
                           }
                           delete[] wrk;
                           i = strlen(wrkStmt);
                           break;
                        }
                     }
                     break;

                  case SQLPUCH: // unsigned char
                  case SQLPSCH: // char
                     sprintf(&wrkStmt[i],"'%c'",*(char *)bndData);
                     i = strlen(wrkStmt);
                     break;

                  case SQLPDAT: // date (string (zero terminated))
                  case SQLPTIM: // time (string (zero terminated))
                  case SQLPSTR: // string (zero terminated)
                     sprintf(&wrkStmt[i],"'%s'",(char *)bndData);
                     i = strlen(wrkStmt);
                     break;

                  case SQLPULO: // unsigned long
                     sprintf(&wrkStmt[i],"%lu",*(long *)bndData);
                     i = strlen(wrkStmt);
                     break;

                  case SQLPSLO: // long
                     sprintf(&wrkStmt[i],"%ld",*(long *)bndData);
                     i = strlen(wrkStmt);
                     break;

                  case SQLPUIN: // unsigned int
                     sprintf(&wrkStmt[i],"%u",*(int *)bndData);
                     i = strlen(wrkStmt);
                     break;

                  case SQLPSIN: // int
                     sprintf(&wrkStmt[i],"%d",*(int *)bndData);
                     i = strlen(wrkStmt);
                     break;

                  case SQLPFLT: // float
                     sprintf(&wrkStmt[i],"%f",*(float *)bndData);
                     i = strlen(wrkStmt);
                     break;

                  case SQLPDOU: // double
                     sprintf(&wrkStmt[i],"%f",*(double *)bndData);
                     i = strlen(wrkStmt);
                     break;

                  default:
                     delete[] wrkStmt;
                     //fprintf(stderr,"Unsupported Fieldtype (Execute)\nBindVar: %s/%d\n%s\n",
                     //        binds[bndcnt]->name, binds[bndcnt]->bnd_typ, statement);
                     return( lastRc = SQLEDTN );
               }

               bndcnt++;
            }

         } else
           wrkStmt[i++] = *a++;

      } // end while *a != '\0'

      if( bndcnt != numBndVars ) {
         delete[] wrkStmt;
         return( lastRc = SQLETMB );
      }

      mrc = msqlQuery( sockDesc, wrkStmt );
      delete[] wrkStmt;

   } else {
      // ...no bindvars, execute the original statement
      mrc = msqlQuery( sockDesc, statement );
   }

   if( mrc == 0 ) {
      dataResult = msqlStoreResult();
      lastRc = 0;
      flag.executed = TRUE;
   } else
      lastRc = SQLE058; // using msqlErrMsg

   return( lastRc );
}


// Fetch Row
int mSqlCursor::Fetch() {

m_row rows;
Fld   *fld;
void  *varData;
char  *colData;
int   varLen;
int   wrkLen;

   if( !flag.executed )
      return( lastRc = DBOENEX );

   if( !dataResult || (rows = msqlFetchRow( dataResult )) == NULL )
      return( lastRc = FETEEOF );

   lastRc = 0;

   for( int i = 0; i < msqlNumFields(fieldResult); i++ ) {

      if( fields[i]->nr != 0 ) {

         fld     = fields[i];
         colData = (char *)rows[fld->nr - 1];

         if( colData ) {

            varData = fld->var_data;
            varLen  = fld->var_length;

            errno  = 0;

            switch( fld->var_typ ) {

               case SQLPBUF: // buffer
                  if( strlen(colData) > varLen ) {
                     wrkLen = varLen;
                     if( fld->flags ) *fld->flags = FETRTRU;
                     lastRc = SQLESTS; // select buffer to small
                  } else wrkLen = strlen(colData);
                  memcpy(varData,colData,wrkLen);
                  if( fld->cvl ) *fld->cvl = (unsigned char)wrkLen; // problem? only one byte!
                  break;

               case SQLPDAT: // date (string (zero terminated))
               case SQLPTIM: // time (string (zero terminated))
               case SQLPSTR: // string (zero terminated)
                  if( strlen(colData) > varLen - 1 ) {
                     if( fld->flags ) *fld->flags = FETRTRU;
                     lastRc = SQLESTS; // select buffer to small
                     strncpy( (char *)varData, colData , varLen-1 );
                     if( fld->cvl ) *fld->cvl = (unsigned char)varLen-1;
                  } else {
                     strcpy( (char *)varData, colData );
                     if( fld->cvl ) *fld->cvl = (unsigned char)strlen(colData);
                  }
                  break;

               case SQLPULO: // unsigned long
                  if( varLen >= sizeof(long) ) {
                     *(unsigned long *)varData = strtoul(colData,NULL,10);
                     if( errno == ERANGE ) {
                        if( fld->flags ) *fld->flags = FETRNOF;
                        lastRc = SQLEDNN;
                     }
                     if( fld->cvl ) *fld->cvl = (unsigned char)sizeof(long);
                  } else {
                      if( fld->flags ) *fld->flags = FETRNOF;
                      if( fld->cvl ) *fld->cvl = (unsigned char)varLen;
                      lastRc = SQLENOF;
                  }
                  break;

               case SQLPSLO: // long
                  if( varLen >= sizeof(long) ) {
                     *(long *)varData = strtol(colData,NULL,10);
                     if( errno == ERANGE ) {
                        if( fld->flags ) *fld->flags = FETRNOF;
                        lastRc = SQLEDNN;
                     }
                     if( fld->cvl ) *fld->cvl = (unsigned char)sizeof(long);
                  } else {
                      if( fld->flags ) *fld->flags = FETRNOF;
                      if( fld->cvl ) *fld->cvl = (unsigned char)varLen;
                      lastRc = SQLENOF;
                  }
                  break;

               case SQLPUIN: // unsigned int
                  if( varLen >= sizeof(int) ) {
                     *(unsigned int *)varData = (unsigned int)strtol(colData,NULL,10);
                     if( errno == ERANGE ) {
                        if( fld->flags ) *fld->flags = FETRNOF;
                        lastRc = SQLEDNN;
                     }
                     if( fld->cvl ) *fld->cvl = (unsigned char)sizeof(int);
                  } else {
                      if( fld->flags ) *fld->flags = FETRNOF;
                      if( fld->cvl ) *fld->cvl = (unsigned char)varLen;
                      lastRc = SQLENOF;
                  }
                  break;

               case SQLPSIN: // int
                  if( varLen >= sizeof(int) ) {
                     *(int *)varData = (int)strtol(colData,NULL,10);
                     if( errno == ERANGE ) {
                        if( fld->flags ) *fld->flags = FETRNOF;
                        lastRc = SQLEDNN;
                     }
                     if( fld->cvl ) *fld->cvl = (unsigned char)sizeof(int);
                  } else {
                      if( fld->flags ) *fld->flags = FETRNOF;
                      if( fld->cvl ) *fld->cvl = (unsigned char)varLen;
                      lastRc = SQLENOF;
                  }
                  break;

               case SQLPFLT: // float
                  if( varLen >= sizeof(float) ) {
                     *(float *)varData = (float)strtod(colData,NULL);
                     if( errno == ERANGE ) {
                        if( fld->flags ) *fld->flags = FETRNOF;
                        lastRc = SQLEDNN;
                     }
                     if( fld->cvl ) *fld->cvl = (unsigned char)sizeof(float);
                  } else {
                      if( fld->flags ) *fld->flags = FETRNOF;
                      if( fld->cvl ) *fld->cvl = (unsigned char)varLen;
                      lastRc = SQLENOF;
                  }
                  break;

               case SQLPDOU: // double
                  if( varLen >= sizeof(double) ) {
                     *(double *)varData = strtod(colData,NULL);
                     if( errno == ERANGE ) {
                        if( fld->flags ) *fld->flags = FETRNOF;
                        lastRc = SQLEDNN;
                     }
                     if( fld->cvl ) *fld->cvl = (unsigned char)sizeof(double);
                  } else {
                      if( fld->flags ) *fld->flags = FETRNOF;
                      if( fld->cvl ) *fld->cvl = (unsigned char)varLen;
                      lastRc = SQLENOF;
                  }
                  break;

               case SQLPUCH: // unsigned char
               case SQLPSCH: // char
                  if( strlen(colData) > 1 ) {
                     if( fld->flags ) *fld->flags = FETRTRU;
                     lastRc = SQLESTS;
                  }
                  *(char *)varData = *colData;
                  if( fld->cvl ) *fld->cvl = 1;
                  break;

               default:
                  if( fld->flags ) *fld->flags = FETRDTN;
                  //fprintf(stderr,"Unsupported Fieldtype (Fetch)\nField: %d/%d - %s\n%s\n",
                  //        fld->nr, fld->var_typ, fld->name, statement);
                  lastRc = SQLEDTN;
                  break;
            }

         } else {
              if( flag.nullInd == 1 && fld->flags )
                 *fld->flags = FETRNUL;  // data is null
              if( fld->cvl )
                 *fld->cvl = (unsigned char)0;
         }

      }

   }

   return( lastRc );
}


// Set Select Buffer
int mSqlCursor::SetSelectBuf(int slc, int pdt, void *pbp, int pdl,
                             unsigned char *cvl, unsigned char *pfc) {

   if( !flag.compiled )
      return( lastRc = SQLEMSC );

   if( fields ) {

      for( int i = 0; i < msqlNumFields(fieldResult); i++ ) {

         if( fields[i]->nr == slc ) {
            fields[i]->var_data = pbp;
            fields[i]->var_length = pdl;
            fields[i]->var_typ = pdt;
            fields[i]->flags = pfc;
            fields[i]->cvl   = cvl;
            //printf("Set Field %s to Typ %d\n", fields[i]->name,fields[i]->var_typ);
            break;
         }

      }

   } else
      return( lastRc = SQLE065 );

   return( lastRc = 0 );
}


// Bind Data by Number
int mSqlCursor::BindNum(int nvar, void *vaddr, int vlen, int vart) {

char bndvar[BINDVAR_LEN+1];

   if( !flag.compiled )
      return( lastRc = SQLEMSC );

   for( int i = 0; i < MAX_BIND_VARS; i++ ) {

      if( binds[i] ) {

         sprintf( bndvar, "%d", nvar );

         if( strcmp(binds[i]->name,bndvar) == 0  ) {
            binds[i]->bnd_data = vaddr;
            binds[i]->bnd_length = vlen;
            binds[i]->bnd_typ = vart;
            return( lastRc = 0 );
         }

      }

   }

   return( lastRc = SQLEIVC );
}


// Bind Data by Name
int mSqlCursor::BindName(char *bvar, int len, void *vaddr, int vlen, int vart) {

char bndvar[BINDVAR_LEN+1];

   if( !flag.compiled )
      return( lastRc = SQLEMSC );

   int slen = (len == 0 ? strlen(bvar) : len);

   strncpy(bndvar,bvar,slen);
   bndvar[slen] = '\0';

   upperStr(bndvar);

   for( int i = 0; i < MAX_BIND_VARS; i++ ) {

      if( binds[i] && strcmp(binds[i]->name,bndvar) == 0  ) {
         binds[i]->bnd_data = vaddr;
         binds[i]->bnd_length = vlen;
         binds[i]->bnd_typ = vart;
         return( lastRc = 0 );
      }

   }

   return( lastRc = SQLEIVC );
}


// Compile Statement
int mSqlCursor::Compile( char *stmt, int len ) {

   // select database
   if( staticID != myID ) {
      if( msqlSelectDB( sockDesc, database ) < 0 )
         return( lastRc = DBAECOD );   // using msqlErrMsg
      staticID = myID;
   }

   int slen = (len == 0 ? strlen(stmt) : len);

   // get statement to internal buffer ...
   char *wrkStmt = new char[slen+1];
   strncpy( wrkStmt, stmt, slen );
   wrkStmt[slen] = '\0';

   // ... and make it upercase
   upperStr( wrkStmt );

   // new statement or previous statement different from new statement?
   if( !statement || strcmp(statement, wrkStmt) != 0 ) {

      // delete statement and use the new one
      if( statement ) delete[] statement;
      statement = wrkStmt;

      // free resultTables
      cleanUp();

      if (strstr(statement,"SELECT")) sqlcmd = SELECT;
      else if (strstr(statement,"INSERT")) sqlcmd = INSERT;
      else if (strstr(statement,"DELETE")) sqlcmd = DELETE;
      else if (strstr(statement,"UPDATE")) sqlcmd = UPDATE;
      else if (strstr(statement,"CREATE")) sqlcmd = CREATE;
      else if (strstr(statement,"DROP")) sqlcmd = DROP;
      else
         return( lastRc = PRSEISC );

      if( sqlcmd == CREATE || sqlcmd == DROP ) {
         flag.compiled = TRUE;
         return( lastRc = 0 );
      }

      // get table-name
      char *tmp;
      char table[NAME_LEN+1];

      if( (tmp=strstr(statement,"FROM ")) != NULL ||
          (tmp=strstr(statement,"INTO ")) != NULL ||
          (tmp=strstr(statement,"UPDATE ")) != NULL ) {

         while( *tmp++ != ' ' );
         for( int i = 0; *tmp != ' ' && *tmp != '('  && *tmp != '\0'; i++, tmp++ )
            table[i] = *tmp;
         table[i] = '\0';

      } else {

         if( sqlcmd == INSERT )
            lastRc = PRSEMIK;
         else
            if( sqlcmd == SELECT )
               lastRc = PRSEMFC;
            else
               lastRc = PRSEISC;

         return( lastRc );
      }

      // get field-list
      if( ((fieldResult = msqlListFields(sockDesc,table)) == NULL) ||
          (msqlNumFields(fieldResult) == 0) ) {
         if(fieldResult) msqlFreeResult(fieldResult);
         fieldResult = NULL;
         return( lastRc = TLKETND );   // using msqlErrMsg
      }

      fields = (Fld **)calloc( msqlNumFields(fieldResult) ,sizeof(Fld *) );
      m_field *dbField;

      for( int i = 0; i < msqlNumFields(fieldResult); i++ ) {

         dbField = msqlFetchField(fieldResult);

         fields[i] = new Fld;
         fields[i]->var_data = NULL;
         fields[i]->var_length = 0;
         strcpy(fields[i]->name, dbField->name);
         fields[i]->typ = dbField->type;
         fields[i]->length = dbField->length;
         fields[i]->nr = 0;
         fields[i]->flags = NULL;
         fields[i]->cvl   = NULL;

      }

      // get select variables
      char *a, *b;
      char colname[NAME_LEN+1];

      switch( sqlcmd ) {

         case SELECT: // "SELECT fld,fld,... FROM table WHERE ..."
            a = strstr(statement,"SELECT ");
            b = strstr(statement,"FROM ");
            while( *a++ != ' ' );      // skip keyword
            while( *a == ' ' ) a++;    // ptr to first colname
            break;

         case INSERT: // "INSERT INTO table (fld,fld,...) VALUES (...,...)"
            a = strstr(statement,"(");
            b = strstr(statement,") ");
            while( *a == '(' || *a == ' ' ) a++;
            break;

         case UPDATE: // "UPDATE table SET fld=val,fld=val,... WHERE ..."
            // special handling required
            a = strstr(statement,"SET ");
            if( a ) a += 4;
            b = strstr(statement," WHERE ");
            setUpdateVars( a, b );
            flag.compiled = TRUE;
            return( lastRc = 0 ); // grrr ..

         default:
            a = b = NULL;
            break;

      }

      if( a && b ) {

         memset(colname,'\0',sizeof(colname));
         int vcnt = 0;
         i = 0;

         while( a <= b ) {

            if(*a == ',' || *a == ' ' || *a == ')' || *a == '\0') {

               colname[i] = '\0';
               i = 0;

               vcnt++;
               numSelItems++; // increase number of select items

               for( int j = 0; j < msqlNumFields(fieldResult); j++ ) {

                  if( strcmp(colname,fields[j]->name) == 0 ) {
                     fields[j]->nr = vcnt;
                     break;
                  }

               }

               while( *a == ',' || *a == ' ' || *a == ')' ) a++; // skip to next variable

               continue;

            } else colname[i++] = *a;

            a++;
         }

      }

      // get bind variables
      char bindvar[BINDVAR_LEN+1];
      tmp = statement;

      for( i = 0; i < MAX_BIND_VARS; i++ ) {

         memset(bindvar,'\0',sizeof(bindvar));

         if( (tmp = strchr(tmp,':')) == NULL )
            break;

         binds[i] = new Bnd;
         tmp++;

         for( int j = 0; *tmp != ' ' && *tmp != ')' && *tmp != ',' && *tmp != '\0'; j++, tmp++ )
            bindvar[j] = *tmp;

         strcpy(binds[i]->name,bindvar);
         binds[i]->bnd_data = NULL;
         binds[i]->bnd_length = 0;
         binds[i]->bnd_typ = 0;

         numBndVars++; // increase number of bind variables

      }

   } else
      delete[] wrkStmt;

   flag.compiled = TRUE;

   return( lastRc = 0 );
}


// brrrr ...
int mSqlCursor::setUpdateVars( char *s, char *e ) {

char colname[NAME_LEN+1];
char bindvar[BINDVAR_LEN+1];
int  i, j;
int  colcnt = 0;
int  bndcnt = 0;

   while( *s == ' ' ) s++;

   while( *s != '\0' ) {

      if( e != NULL && s >= e ) break;

      memset(colname,'\0',sizeof(colname));
      i = 0;

      // get colname
      while( *s != '=' && *s != ' ' )
         colname[i++] = *s++;

      colcnt++;
      numSelItems++; // increase number of select items

      // column stuff
      for( int j = 0; j < msqlNumFields(fieldResult); j++ ) {

         if( strcmp(colname,fields[j]->name) == 0 ) {
            fields[j]->nr = colcnt;
            break;
         }

      }

      // point to next char != ' ' behind the equal sign ...
      if( (s = strchr(s,'=')) == NULL ) break;
      s++;
      while( *s == ' ' ) s++;

      // now a bind variable ??
      if( *s != ':' ) {
         // no, point behind next comma and continue with next colname
         if( (s = strchr(s,',')) == NULL ) break;
         s++;
         while( *s == ' ' || *s == '\0' ) s++;
         continue;
      }

      s++; // point behind colon
      memset(bindvar,'\0',sizeof(bindvar));

      for( j = 0; *s != ',' && *s != ' ' && *s != '\0'; j++, s++ )
         bindvar[j] = *s;

      binds[bndcnt] = new Bnd;
      strcpy(binds[bndcnt]->name,bindvar);
      binds[bndcnt]->bnd_data = NULL;
      binds[bndcnt]->bnd_length = 0;
      binds[bndcnt]->bnd_typ = 0;

      bndcnt++;
      numBndVars++; // increase number of bind variables

      // point to next column (or end)
      while( (*s == ' ' || *s == ',') && *s != '\0' ) s++;

   }

   // get bind variables from where clause
   if( e != NULL ) {

      e += 7;
      while( *e == ' ' ) e++;

         for( ; ; ) {

            memset(bindvar,'\0',sizeof(bindvar));

            if( (e = strchr(e,':')) == NULL )
               break;

            e++;

            for( j = 0; *e != ' ' && *e != ')' && *e != ',' && *e != '\0'; j++, e++ )
               bindvar[j] = *e;

            binds[bndcnt] = new Bnd;
            strcpy(binds[bndcnt]->name,bindvar);
            binds[bndcnt]->bnd_data = NULL;
            binds[bndcnt]->bnd_length = 0;
            binds[bndcnt]->bnd_typ = 0;

            bndcnt++;
            numBndVars++; // increase number of bind variables

         }

   }

   return( 0 );
}


// get # of select items
int mSqlCursor::getNumSelItems( SQLTNSI PTR nsi ) {

   if( !flag.compiled )
      return( lastRc = SQLEMSC );

   *nsi = (SQLTNSI)numSelItems;

   return( lastRc = 0 );
}


// get # of bind variables
int mSqlCursor::getNumBndVars( SQLTNBV PTR nbv ) {

   if( !flag.compiled )
      return( lastRc = SQLEMSC );

   *nbv = (SQLTNBV)numBndVars;

   return( lastRc = 0 );
}


// get # of fetched rows
int mSqlCursor::getNumRows( SQLTROW PTR rcountp ) {

   if( !flag.executed )
      return( lastRc = DBOENEX );

   if( !flag.rsetMode )
      return( lastRc = SQLERNA );

   if( dataResult )
      *rcountp = (SQLTROW)msqlNumRows(dataResult);
   else
      *rcountp = 0;

   return( lastRc = 0 );
}


// get Command Type
int mSqlCursor::getCmdType( SQLTCTY PTR cty ) {

   if( !flag.compiled )
      return( lastRc = SQLEMSC );

   switch( sqlcmd ) {
      case SELECT: *cty = SQLTSEL; break;
      case INSERT: *cty = SQLTINS; break;
      case UPDATE: *cty = SQLTUPD; break;
      case DELETE: *cty = SQLTDEL; break;
      case CREATE: *cty = SQLTCTB; break;
      case DROP:   *cty = SQLTDTB; break;
      default:                     break;
   }

   return( lastRc = 0 );
}


// Set Row Position
int mSqlCursor::SetRowPos( SQLTROW row ) {

   if( !flag.executed )
      return( lastRc = DBOENEX );

   if( !flag.rsetMode )
      return( lastRc = SQLERNA );

   if( row < 0 || row >= (SQLTROW)msqlNumRows(dataResult) )
      return( lastRc = SQLEIRN );

   msqlDataSeek(dataResult, (int)row);

   return( lastRc = 0 );
}


// describe Item in SELECT
int mSqlCursor::describeItem(SQLTSLC slc, SQLTDDT PTR edt, SQLTDDL PTR edl, SQLTCHP chp, SQLTCHL PTR chlp) {

   if( !flag.compiled )
      return( lastRc = SQLEMSC );

   if( fields ) {

      for( int i = 0; i < msqlNumFields(fieldResult); i++ ) {

         if( fields[i]->nr == slc ) {

            if(chp)  strncpy((char *)chp,fields[i]->name,strlen(fields[i]->name));
            if(chlp) *chlp = strlen(fields[i]->name);

            switch( fields[i]->typ ) {
               case INT_TYPE:
                  if(edt) *edt = SQLEINT;
                  if(edl) *edl = fields[i]->length;
                  break;
               case CHAR_TYPE:
                  if(edt) *edt = SQLECHR;
                  if(edl) *edl = fields[i]->length;
                  break;
               case REAL_TYPE:
                  if(edt) *edt = SQLEDOU;
                  if(edl) *edl = fields[i]->length;
                  break;
               default:
                  fprintf(stderr,"what kind of mSQL-Datatype is it?\n");
                  // maybe more here ..
            }
            return( lastRc = 0 );
         }

      }

   } else
      return( lastRc = SQLE065 );

   return( lastRc = SQLE071 );
}


// describe Item in SELECT
int mSqlCursor::describeItem(SQLTSLC slc, SQLTDDT PTR ddt, SQLTDDL PTR ddl, SQLTCHP chp, SQLTCHL PTR chlp, char *) {

   if( !flag.compiled )
      return( lastRc = SQLEMSC );

   if( fields ) {

      for( int i = 0; i < msqlNumFields(fieldResult); i++ ) {

         if( fields[i]->nr == slc ) {

            if(chp)  strncpy((char *)chp,fields[i]->name,strlen(fields[i]->name));
            if(chlp) *chlp = strlen(fields[i]->name);

            switch( fields[i]->typ ) {
               case CHAR_TYPE:
                  if(ddt) *ddt = SQLDCHR;
                  if(ddl) *ddl = fields[i]->length;
                  break;
               case INT_TYPE:
               case REAL_TYPE:
                  if(ddt) *ddt = SQLDNUM;
                  if(ddl) *ddl = 27;
                  break;
               default:
                  fprintf(stderr,"what kind of mSQL-Datatype is it?\n");
                  // maybe more here ..
            }
            return( lastRc = 0 );
         }

      }

   } else
      return( lastRc = SQLE065 );

   return( lastRc = SQLE071 );
}


// GET parameter
int mSqlCursor::GetParm( SQLTPTY parm, SQLTDAP p ) {

   switch( parm ) {

      case SQLPBRN:
         *p = SQLBSQB;       // "Brand of database"
         break;

      case SQLPNIE:
         *p = flag.nullInd;  // Null indicator
         break;
   }

   return( lastRc = 0 );
}


// SET parameter
int mSqlCursor::SetParm( SQLTPTY parm, SQLTDAP p ) {

   switch( parm ) {

      case SQLPNIE:
         flag.nullInd = *p;      // Null indicator
         break;

      case START_SRS:
         if( flag.executed )             // start result set mode only
            return( lastRc = DBOENEX );  // if the command not executed
         flag.rsetMode = TRUE;
         break;

      case END_SRS:
         flag.rsetMode = FALSE;  // end result set mode
         break;
   }

   return( lastRc = 0 );
}


// Clear Bind variables
int mSqlCursor::ClearBindVars() {

  for( int i = 0; i < MAX_BIND_VARS; i++ ) {
      if( binds[i] ) {
         binds[i]->bnd_data = NULL;
         binds[i]->bnd_length = 0;
         binds[i]->bnd_typ = 0;
      }
   }

   return( lastRc = 0 );
}


// List Tables
int mSqlCursor::ListTables( char *buffer, int length ) {

m_result *tbResult;
m_row    tbRow;
char     *buf_end;

   if( staticID != myID ) {
      if( msqlSelectDB( sockDesc, database ) < 0 ) {
         return( lastRc = DBAECOD );   // using msqlErrMsg
      }
      staticID = myID;
   }

   memset( buffer, '\0', (size_t)length );
   buf_end = buffer + length - 2;

   if( (tbResult = msqlListTables( sockDesc )) == NULL )
      return( lastRc = TLKETND );

   while( (tbRow = msqlFetchRow(tbResult)) != NULL ) {

      if( (char *)buffer + strlen((char *)tbRow[0]) > buf_end )
         break;

      strcpy( (char *)buffer, (char *)tbRow[0] );
      (char *)buffer += strlen((char *)buffer) + 1;

   }

   msqlFreeResult( tbResult );
   return( lastRc = 0 );
}


// cleanUp
void mSqlCursor::cleanUp() {

int i;

   for( i = 0; i < MAX_BIND_VARS; i++ ) {
      if( binds[i] ) delete binds[i];
      binds[i] = NULL;
   }

   if( fields ) {
      for( i = 0; i < msqlNumFields(fieldResult); i++ ) {
         if( fields[i] ) delete fields[i];
         fields[i] = NULL;
      }
      free( (void *)fields );
      fields = NULL;
   }

   if( fieldResult ) {
      msqlFreeResult( fieldResult );
      fieldResult = NULL;
   }

   if( dataResult ) {
      msqlFreeResult( dataResult );
      dataResult = NULL;
   }

   numSelItems = 0;
   numBndVars  = 0;

   flag.compiled = FALSE;
   flag.executed = FALSE;
}


// make string uppercase (without quoted parts)
void mSqlCursor::upperStr(char *s) {

int quote = 0;

   while(*s) {

      if( !quote )
         *s = toupper(*s);

      if( *s == '\'' )
         quote = ~quote;

      s++;

   }

}


// =======================================================================
// mSqlBase
// =======================================================================

// Constructor
mSqlBase::mSqlBase( ) {

int i;

   for(i = 0; i < MAX_CONN; i++) {
      connection[i].sockDesc = 0;
      connection[i].useCount = 0;
      connection[i].host     = NULL;
   }

   for(i = 0; i < MAX_CURSORS; i++)
      cursor[i] = NULL;

}


// Destructor
mSqlBase::~mSqlBase( ) {

   // Delete Cursors
   for( int i = 0; i < MAX_CURSORS; i++ )
      if( cursor[i] ) deleteCursor( i );

}


// Connect server
int mSqlBase::connectServer( char *host, int *rc ) {

char *h;
int  i;
int  sd;

   if( host == NULL ) h = "[this]";
   else h = host;

   // look for existing connection
   for( i = 0; i < MAX_CONN; i++ ) {
      if( connection[i].host != NULL && strcmp(connection[i].host, h) == 0 ) {
         connection[i].useCount++;
         *rc = 0;
         return( connection[i].sockDesc );
      }
   }

   // no existing connection found
   if( strcmp( h, "[this]" ) == 0 )
      sd = msqlConnect( NULL );
   else
      sd = msqlConnect( h );

   if( sd < 0  ){
      *rc = SQLECNC;
      return( -1 );   // msqlConnect failed
   }

   for( i = 0; i < MAX_CONN; i++ ) {

      if( connection[i].host == NULL ) {
         connection[i].host = new char[strlen(h)+1];
         strcpy( connection[i].host, h );
         connection[i].sockDesc = sd;
         connection[i].useCount = 1;
         return( sd );
      }

   }

   msqlClose( sd );

   *rc = SQLECCS;
   return( -1 );   // connection limit exeeded
}


// Disconnect server
int mSqlBase::disconnectServer( int sd ) {

   for( int i = 0; i < MAX_CONN; i++ ) {

      if( connection[i].sockDesc == sd ) {

         // der letzte macht die tuer zu
         if( --connection[i].useCount == 0 ) {
            msqlClose( sd );
            delete[] connection[i].host;
            connection[i].host     = NULL;
            connection[i].sockDesc = 0;
         }
         break;

      }

   }

   return( 0 );
}


// Create new cursor
int mSqlBase::createCursor( char *host, char *dbase, int len, int *rc ) {

int sd;
int cur;
int lrc;

   if( (sd = connectServer((char *)host,&lrc)) < 0 ) {
      *rc = lrc;
      return( -1 );
   }

   mSqlCursor *newCursor = new mSqlCursor( dbase, len, sd );

   if( (cur = add( newCursor )) < 0 ) {

      delete newCursor;
      disconnectServer( sd );
      *rc = SQLECCS;
      return( -1 );

   }

   *rc = 0;
   return( cur );
}


// Delete cursor
int mSqlBase::deleteCursor( int cur ) {

int sd;

   sd = cursor[cur]->getSockDesc();
   delete cursor[cur];
   cursor[cur] = (mSqlCursor *)NULL;

   disconnectServer( sd );

   return( 0 );
}


// Add new Cursor
int mSqlBase::add( mSqlCursor *element ) {

   for( int i = 0; i < MAX_CURSORS; i++ ) {

      if( cursor[i] == NULL ) {
         cursor[i] = element;
         return( i );
      }

   }

   return( -1 );        // no more cursors available
}

