/*
_____       _    _    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.

*/
static char rcsid[] = "$Id: results.cpp 5.2 2001/09/24 13:36:03 picoSoft Exp Administrator $";
# include "sqlpars.h"
# include "dberror.h"
# include "trace.h"
# include "sqltype.h"

int
Int2OdbcType (int t, SignType s, DateTime d)
{
   short Return;
   switch (d) {
   case T_DATE:
      Return = SQL_DATE;
      break;
   case T_TIME:
      Return = SQL_TIME;
      break;
   case T_TIMESTAMP:
      Return = SQL_TIMESTAMP;
      break;
   case T_NODATE:
      switch (t) {
      case T_CSTRING:
         Return = SQL_CHAR;
         break;
// Attenzione sincronia con CField::ToExt in cfield.cpp
      case T_SHORT:
         if (s == UNSIGNED)
            Return = SQL_INTEGER;
         else
            Return = SQL_SMALLINT;
         break;
// Attenzione sincronia con CField::ToExt in cfield.cpp
      case T_LONG:
         if (s == UNSIGNED)
            Return = SQL_DOUBLE;
         else
            Return = SQL_INTEGER;
         break;
      case T_BLOB:
         Return = SQL_LONGVARBINARY;
         break;
      case T_CLOB:
         Return = SQL_LONGVARCHAR;
         break;
      case T_PACKED_ORDERED:
# ifdef NO_MS_ACCESS
         Return = SQL_DECIMAL;
# else
// I tipi COBOL hanno un default sempre DOUBLE per problemi di MS-ACCESS
// Questa parte deve essere in sincronia con CField::ToExt in cfield.cpp
         Return = SQL_DOUBLE;
# endif
         break;
      case T_FLOAT:
         Return = SQL_REAL;
         break;
      case T_DOUBLE:
         Return = SQL_DOUBLE;
         break;
      }
      break;
   }
   return Return;
}

const char *
Int2OdbcTypeStr (int t, SignType s, DateTime d)
{
   switch (d) {
   case T_DATE:
      return "DATE";
   case T_TIME:
      return "TIME";
   case T_TIMESTAMP:
      return "TIMESTAMP";
   }
   switch (t) {
   case T_CSTRING:
      return "CHAR";
   case T_SHORT:
      return "SMALLINT";
   case T_LONG:
      return "INTEGER";
   case T_BLOB:
      return "BLOB";
   case T_CLOB:
      return "CLOB";
   case T_PACKED_ORDERED:
      return "NUMERIC";
   case T_FLOAT:
      return "REAL";
   case T_DOUBLE:
      return "DOUBLE";
   }
   return "UNKNOWN";
}

//  -   -   -   -   -   -   -   -   -

inline Error
strNcpy (SQLHANDLE hstmt, char *dst, const char *src, int maxLen, short *realLen)
{
   int srcLen = strlen (src);
   if (srcLen < maxLen) {
      if (realLen)
         *realLen = srcLen;
      if (dst)
         strcpy(dst, src);
      return SQL_SUCCESS;
   } else {
      if (realLen)
         *realLen = maxLen - 1;
      if (dst) {
         strncpy(dst, src, maxLen - 1);
         dst[maxLen - 1] = 0;
      }
      PrintError ( 0, 0, (SqlParser *) hstmt, E_WARNING_004, "");
      return SQL_SUCCESS_WITH_INFO;
   }
}
extern "C" {
# ifdef MSDOS
# define strcpy lstrcpy
# define strncpy lstrcpyn
# endif

//  Returns result column descriptor information for a result set.

RETCODE SQL_API SQLColAttributes(
    HSTMT      hstmt,
    UWORD      icol,
    UWORD      fDescType,
    PTR        rgbDesc,
    SWORD      cbDescMax,
    SWORD FAR  *pcbDesc,
    SDWORD FAR *pfDesc)
{
   RETCODE Return = SQL_SUCCESS;
   PString rgbString;
   long ltemp = 0;
   SignType s;
   DateTime d;

   if (hstmt == 0 ||
       !((PObject *) hstmt)->IsA (SqlParser::Class))
      Return = SQL_INVALID_HANDLE;
   else {
      ((SqlParser *) hstmt)->DelError();
      if (pfDesc)
         *pfDesc = 0;
      if (pcbDesc)
         *pcbDesc = 0;
      if (rgbDesc && cbDescMax > 0)
         *((char *) rgbDesc) = 0;
      switch (fDescType) {
      case SQL_COLUMN_COUNT:
         if (pfDesc != 0)
            Return = ((SqlParser *) hstmt)->GetColNum (*pfDesc);
         break;
      case SQL_COLUMN_LENGTH:
         if (pfDesc != 0)
            Return = ((SqlParser *) hstmt)->GetColLength (icol, *pfDesc);
         break;
      case SQL_COLUMN_NAME:
            Return = ((SqlParser *) hstmt)->GetColName (icol, rgbString);
         break;
      case SQL_COLUMN_TYPE:
         if (pfDesc != 0) {
            Return = ((SqlParser *) hstmt)->GetColType (icol, *pfDesc, s, d);
            *pfDesc = (SDWORD) Int2OdbcType ((int)*pfDesc, s, d);
         }
         break;
      case SQL_COLUMN_PRECISION:
         if (pfDesc != 0)
            Return = ((SqlParser *) hstmt)->GetColPrecision (icol, *pfDesc);
         break;
      case SQL_COLUMN_SCALE:
         if (pfDesc != 0)
            Return = ((SqlParser *) hstmt)->GetColScale (icol, *pfDesc);
         break;
      case SQL_COLUMN_DISPLAY_SIZE:
         if (pfDesc != 0)
            Return = ((SqlParser *) hstmt)->GetColDisplaySize (icol, *pfDesc);
         break;
      case SQL_COLUMN_NULLABLE:
         if (pfDesc != 0)
            *pfDesc = SQL_NULLABLE;
         break;
      case SQL_COLUMN_UNSIGNED:
         if (pfDesc != 0) {
             Return = ((SqlParser *) hstmt)->GetColSign (icol, *pfDesc);
             if (Return == SQL_SUCCESS) {
                if (*pfDesc == UNSIGNED)
                   *pfDesc = PTrue;
                else
                   *pfDesc = PFalse;
             }
         }
         break;
      case SQL_COLUMN_MONEY:
         if (pfDesc != 0)
            *pfDesc = PFalse;
         break;
      case SQL_COLUMN_TYPE_NAME:
         Return = ((SqlParser *) hstmt)->GetColType (icol, ltemp, s, d);
         if (Return == SQL_SUCCESS) 
            rgbString = Int2OdbcTypeStr ((int)ltemp, s, d);
         break;
      case SQL_COLUMN_UPDATABLE:
         if (pfDesc != 0)
            Return = ((SqlParser *) hstmt)->GetUpdatable (icol,*pfDesc);
         break;
      case SQL_COLUMN_AUTO_INCREMENT:
         if (pfDesc != 0)
            *pfDesc = PFalse;
         break;
      case SQL_COLUMN_CASE_SENSITIVE:
         if (pfDesc != 0)
            Return = ((SqlParser *) hstmt)->GetIsChar (icol, *pfDesc);
         break;
      case SQL_COLUMN_SEARCHABLE:
         if (pfDesc != 0)
            *pfDesc = SQL_SEARCHABLE;
         break;
      case SQL_COLUMN_LABEL: // start 02.00
            Return = ((SqlParser *) hstmt)->GetColName (icol, rgbString);
         break;
      case SQL_COLUMN_OWNER_NAME:
      case SQL_COLUMN_QUALIFIER_NAME:
      case SQL_COLUMN_TABLE_NAME:
         break;
      default:  
         PrintError ( 0, 0, (SqlParser *) hstmt, E_GENERAL_ERROR_091,"");
         Return = SQL_ERROR;
         break;
      }
   }
   if (Return == SQL_SUCCESS && rgbString.size() > 0) {
      if (rgbString.size() < cbDescMax) {
         if (pcbDesc)
            *pcbDesc = (SWORD) rgbString.size();
         if (rgbDesc)
            strcpy(((char*)rgbDesc), rgbString.gets());
      } else {
         if (pcbDesc)
            *pcbDesc = cbDescMax - 1;
         if (rgbDesc) {
            strncpy(((char*)rgbDesc), rgbString.gets(), cbDescMax - 1);
            ((char*)rgbDesc)[cbDescMax - 1] = 0;
         }
         PrintError ( 0, 0, (SqlParser *) hstmt, E_WARNING_004, "");
         Return = SQL_SUCCESS_WITH_INFO;
      }
   }
   if (Trace.isSet)
      Trace.Write ("SQLColAttributes(0x%lx,%d,%d,'%s',%d, ... )=%d\n",
                       hstmt,icol,fDescType,rgbDesc,
                       cbDescMax,Return); 
   return Return;
}

//  This returns the number of columns associated with the database
//  attached to "hstmt".

RETCODE SQL_API SQLNumResultCols(
    HSTMT   hstmt,
    SWORD FAR *pccol)
{
   RETCODE Return = SQL_SUCCESS;
   SWORD pcbDesc;
   SDWORD pfDesc;
   PString errore;
   char rgbDesc[1];

   rgbDesc[0] = 0;


   Return = SQLColAttributes(hstmt, 0, SQL_COLUMN_COUNT, rgbDesc, 0,
                             &pcbDesc, &pfDesc);
   if (pccol)
      *pccol = (SWORD) pfDesc;
   
   if (Trace.isSet)
      if (pccol) 
         Trace.Write ("SQLNumResultCols(0x%lx,%d)=%d(SQLColAttributes)\n",
                      hstmt, *pccol , Return);
      else
         Trace.Write ("SQLNumResultCols(0x%lx,(null))=%d(SQLColAttributes)\n",
                      hstmt/*, *pccol */, Return); 
   return Return;
}

//  -   -   -   -   -   -   -   -   -

//  Return information about the database column the user wants
//  information about.

RETCODE SQL_API SQLDescribeCol(
    HSTMT   hstmt,
    UWORD   icol,
    UCHAR FAR *szColName,
    SWORD   cbColNameMax,
    SWORD FAR *pcbColName,
    SWORD FAR *pfSqlType,
    UDWORD FAR *pcbColDef,
    SWORD FAR *pibScale,
    SWORD FAR *pfNullable)
{
   RETCODE Return = SQL_SUCCESS;
   SWORD pcbDesc;
   SDWORD pfDesc;
   char rgbDesc[1];

   rgbDesc[0] = 0;
   if (szColName != 0) {
      Return = SQLColAttributes(hstmt, icol, SQL_COLUMN_NAME,
                                szColName, cbColNameMax,
                                &pcbDesc, &pfDesc);
      if (pcbColName)
         *pcbColName = pcbDesc;
   }
   if (Return == SQL_SUCCESS && pfSqlType != 0) {
      Return = SQLColAttributes(hstmt, icol, SQL_COLUMN_TYPE,
                                rgbDesc, 0,
                                &pcbDesc, &pfDesc);
      if (pfSqlType)
         *pfSqlType = (SWORD) pfDesc;
   }
   if (Return == SQL_SUCCESS && pcbColDef != 0) {
      Return = SQLColAttributes(hstmt, icol, SQL_COLUMN_PRECISION,
                                rgbDesc, 0,
                                &pcbDesc, &pfDesc);
      if (pcbColDef)
         *pcbColDef = pfDesc;
   }
   if (Return == SQL_SUCCESS && pibScale != 0) {
      Return = SQLColAttributes(hstmt, icol, SQL_COLUMN_SCALE,
                                rgbDesc, 0,
                                &pcbDesc, &pfDesc);
      if (pibScale)
         *pibScale = (SWORD) pfDesc;
   }
   if (Return == SQL_SUCCESS && pfNullable != 0) {
      Return = SQLColAttributes(hstmt, icol, SQL_COLUMN_NULLABLE,
                                rgbDesc, 0,
                                &pcbDesc, &pfDesc);
      if (pfNullable)
         *pfNullable = (SWORD) pfDesc;
   }
   if (Trace.isSet)
      Trace.Write ("SQLDescribeCol(0x%lx,%d,'%s',%d, ... )=%d(SQLColAttributes)\n",
                  hstmt,icol,
                  szColName,cbColNameMax,
                  Return); 
   return Return;
}

//  -   -   -   -   -   -   -   -   -

//  Associate a user-supplied buffer with a database column.

RETCODE SQL_API SQLBindCol(
    HSTMT   hstmt,
    UWORD   icol,
    SWORD   fCType,
    PTR rgbValue,
    SDWORD  cbValueMax,
    SDWORD FAR *pcbValue)
{
   RETCODE Return = SQL_SUCCESS;

   if (hstmt == 0 ||
       !((PObject *) hstmt)->IsA (SqlParser::Class))
      Return = SQL_INVALID_HANDLE;
   else {
      ((SqlParser *) hstmt)->DelError();
      Return = ((SqlParser *) hstmt)->Bind (icol, (AttrCType)fCType,
                                      (char *) rgbValue, cbValueMax, pcbValue);
   }
   if (Trace.isSet)
      Trace.Write ("SQLBindCol(0x%lx,%d,%d,'...',%ld,'...')=%d\n",
                       hstmt,icol,fCType,/* rgbValue, */
                       cbValueMax /*, *pcbValue */, Return); 
   return Return;
}

//  -   -   -   -   -   -   -   -   -

//  Returns data for bound columns in the current row ("hstmt->iCursor"),
//  advances the cursor.

RETCODE SQL_API SQLFetch(
    HSTMT   hstmt)
{
   RETCODE Return = SQL_SUCCESS;

   if (hstmt == 0 ||
       !((PObject *) hstmt)->IsA (SqlParser::Class))
      Return = SQL_INVALID_HANDLE;
   else {
      ((SqlParser *) hstmt)->DelError();
      Return = ((SqlParser *) hstmt)->Fetch ();
   }
   if (Trace.isSet)
      Trace.Write ("SQLFetch(0x%lx)=%d\n",
                       hstmt,Return);
   return Return;
}

//  Returns result data for a single column in the current row.

RETCODE SQL_API SQLGetData(
    HSTMT   hstmt,
    UWORD   icol,
    SWORD   fCType,
    PTR rgbValue,
    SDWORD  cbValueMax,
    SDWORD FAR *pcbValue)
{
   RETCODE Return = SQL_SUCCESS;

   if (hstmt == 0 ||
       !((PObject *) hstmt)->IsA (SqlParser::Class))
      Return = SQL_INVALID_HANDLE;
   else {
      ((SqlParser *) hstmt)->DelError();
      Return = ((SqlParser *) hstmt)->GetData (icol, (AttrCType)fCType,
                                      (char *) rgbValue, cbValueMax, pcbValue);
   }

   if (Trace.isSet)
      Trace.Write ("SQLGetData(0x%lx,%d,%d,'...',%ld,'...')=%d\n",
                       hstmt,icol,fCType,/* rgbValue, */
                       cbValueMax /*, *pcbValue */, Return);

   return Return;
}


//  This determines whether there are more results sets available for
//  the "hstmt".


RETCODE SQL_API SQLMoreResults(
    HSTMT   hstmt)
{
   RETCODE Return = SQL_NO_DATA_FOUND;
   if (Trace.isSet)
      Trace.Write ("SQLMoreResults(0x%lx)=%d\n",
                       hstmt,Return);
   return Return;
}

//  -   -   -   -   -   -   -   -   -

//  This returns the number of rows associated with the database
//  attached to "hstmt".

RETCODE SQL_API SQLRowCount(
    HSTMT   hstmt,
    SDWORD FAR *pcrow)
{
   RETCODE Return = SQL_SUCCESS;

   if (hstmt == 0 ||
       !((PObject *) hstmt)->IsA (SqlParser::Class))
      Return = SQL_INVALID_HANDLE;
   else {
      ((SqlParser *) hstmt)->DelError();
      if (pcrow)
         Return = ((SqlParser *) hstmt)->GetRowCount (*pcrow);
   }

   if (Trace.isSet)
      if (pcrow)
         Trace.Write ("SQLRowCount(0x%lx,%ld)=%d\n",
                       hstmt,*pcrow ,Return);
      else
         Trace.Write ("SQLRowCount(0x%lx,(null))=%d\n",
                       hstmt/*,*pcrow */ ,Return);
   return Return;
}

//  -   -   -   -   -   -   -   -   -

//  This positions the cursor within a block of data.
/*
RETCODE SQL_API SQLSetPos(
    HSTMT   hstmt,
    UWORD   irow,
# if ODBCVER >= 0x0201
    UWORD    fRefresh,
    UWORD    fLock)
# else
    PBool    fRefresh,
    PBool    fLock)
# endif
{
   RETCODE Return = SQL_SUCCESS;
   if (Trace.isSet)
      Trace.Write ("SQLSetPos(0x%lx,%d,%d,%d)=%d\n",
                       hstmt,irow,fRefresh,fLock,Return);
   return Return;
}
*/
//  -   -   -   -   -   -   -   -   -

//  This fetchs a block of data (rowset).

RETCODE SQL_API SQLExtendedFetch(
    HSTMT   hstmt,
    UWORD   fFetchType,
    SDWORD  irow,
    UDWORD FAR *pcrow,
    UWORD FAR *rgfRowStatus)
{
   RETCODE Return = SQL_SUCCESS;

   if (hstmt == 0 ||
       !((PObject *) hstmt)->IsA (SqlParser::Class))
      Return = SQL_INVALID_HANDLE;
   else {
      ((SqlParser *) hstmt)->DelError();
      Return = ((SqlParser *) hstmt)
               ->ExtendedFetch (fFetchType, pcrow, rgfRowStatus);
   }
   if (Trace.isSet)
      if (pcrow)
         Trace.Write ("SQLExtendedFetch(0x%lx,%d,%ld,...,%d)=%d\n",
                       hstmt,fFetchType,irow,*pcrow,Return);
      else                          
         Trace.Write ("SQLExtendedFetch(0x%lx,%d,(null),...,%d)=%d\n",
                       hstmt,fFetchType,irow,Return);
   return Return;
}

//  Returns the next SQL error information.

RETCODE SQL_API SQLError(
    HENV    henv,
    HDBC    hdbc,
    HSTMT   hstmt,
    UCHAR FAR *outSqlState,
    SDWORD FAR *outNativeError,
    UCHAR FAR *outErrorMsg,
    SWORD   cbErrorMsgMax,
    SWORD FAR *pcbErrorMsg)
{
     RETCODE Return;
     Return = GetError ((Environment *)henv,
                        (DataBase *)hdbc,
                        (SqlParser *)hstmt,
                        (char *) outSqlState,
                        *outNativeError,
                        (char *) outErrorMsg,
                        cbErrorMsgMax,
                        *pcbErrorMsg);
/*
     if (Return != SQL_NO_DATA_FOUND)
        printf ("SQLError>%s<+>%s<+>0x%x<+\n",(char *)outSqlState,
                                      (char *)outErrorMsg,
                                       outNativeError);
*/ 
   
   if (Trace.isSet) {
      Trace.Write ("SQLError(0x%lx,0x%lx,0x%lx,",henv,hdbc,hstmt);
      if (outSqlState != NULL) {
         Trace.Write ("'%s',", outSqlState);
      } else {
         Trace.Write ("'(null)',");
      }
      if (outNativeError != NULL) {
         Trace.Write ("%ld,",*outNativeError);
      } else {
         Trace.Write ("(null),");
      }
      if (outErrorMsg != NULL) {
         Trace.Write ("'%s',%d,", outErrorMsg,cbErrorMsgMax);
      } else {
         Trace.Write ("'(null)',%d,",cbErrorMsgMax);
      }
      if (outNativeError != NULL) {
         Trace.Write ("%d)=%d\n",*pcbErrorMsg, Return);
      } else {
         Trace.Write ("(null))=%d\n", Return);
      }
/*
   Trace.Write ("SQLError(0x%lx,0x%lx,0x%lx,'%s',%ld,'%s',%d,%d)=%d\n",
              henv,hdbc,hstmt,outSqlState,*outNativeError,outErrorMsg,
              cbErrorMsgMax,*pcbErrorMsg, Return);
*/
   }
   return Return;
}
};
