/*! 
  -----------------------------------------------------------------------------
 
  module: vcn83.cpp
 
  -----------------------------------------------------------------------------
 
  responsible:  BerndV
 
  special area: DBMServer
 
  description:  implementation module for special sql access
                - index handling
                - update statistics
  
  -----------------------------------------------------------------------------
 
    ========== licence begin  GPL
    Copyright (c) 1998-2005 SAP AG

    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
    of the License, 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
    ========== licence end

 
  -----------------------------------------------------------------------------
*/

/* 
  -----------------------------------------------------------------------------
  includes
  -----------------------------------------------------------------------------
*/
#include "hin110.h"

#include "hcn51.h"
#include "hcn80.h"
#include "hcn90.h"

#include "hcn83.h"

/* 
  -----------------------------------------------------------------------------
  specification private types and macros
  -----------------------------------------------------------------------------
*/

#define SQL_COMMANDLENGTH      1024

#define SQL_BADINDEXBYNAME     "SELECT * FROM SYSDD.BAD_INDEXES WHERE OWNER='%s' AND TABLENAME='%s' AND INDEXNAME='%s'"

#define SQL_INDEXBYNAME        "SELECT * FROM SYSDD.INDEXES WHERE OWNER='%s' AND TABLENAME='%s' AND INDEXNAME='%s' ORDER BY COLUMNNO ASC"
#define SQL_INDEXCOUNT         "SELECT COUNT(*) FROM SYSDD.INDEXES WHERE OWNER='%s' AND TABLENAME='%s' AND INDEXNAME='%s'"

#define SQL_DROPINDEXBYNAME    "DROP INDEX \"%s\" ON \"%s\".\"%s\""

#define SQL_CREATEINDEXBYNAME1 "CREATE %s INDEX \"%s\" ON \"%s\".\"%s\" (%s) "
#define SQL_CREATEINDEXBYNAME2 "\"%s\" %s,"

#define SQL_RESTART            "CREATE INDEX RESTART"

#define SQLF_TYPE              "TYPE"
#define SQLF_SORT              "SORT"
#define SQLF_COLUMNNAME        "COLUMNNAME"

#define SQL_UPDATESTAT         "UPDATE STAT %s"
#define SQL_UPDATESTAT_ALL     "UPDATE STAT *"

/* 
  -----------------------------------------------------------------------------
  specification private functions
  -----------------------------------------------------------------------------
*/
static tcn00_Error cn83_RecreateIndex
      ( const tsp00_DbNamec & pDBName,
        const char   * pScheme,
        const char   * pTable,
        const char   * pIndex,
        char          * replyData,
        int           * replyLen);

static tcn00_Error cn83_RestartIndex
      ( const tsp00_DbNamec & pDBName,
        char          * replyData,
        int           * replyLen);

static tcn00_Error cn83_UpdateStat
      ( const tsp00_DbNamec & pDBName,
        const char   * pSpec,
        char          * replyData,
        int           * replyLen );
        
/* 
  -----------------------------------------------------------------------------
  function:     cn83RecreateIndex
  -----------------------------------------------------------------------------
 */
tcn00_Error cn83RecreateIndex
      ( VControlDataT * vcontrol,
        CommandT      * command,
        char          * replyData,
        int           * replyLen,
        int             replyLenMax)
{
  tcn00_Error         nFuncReturn   = OK_CN00;

  char               szIndexSpec[PARAMETER_MAXLEN_CN90];
  char             * pScheme       = NULL;
  char             * pTable        = NULL;
  char             * pIndex        = NULL;

  // analyze parameters
  if (cn90GetToken(command->args, szIndexSpec, 1, PARAMETER_MAXLEN_CN90)) {
    pScheme = &szIndexSpec[0];
    pTable  = strstr(szIndexSpec, ".");
    if (pTable == NULL) {
      nFuncReturn =  cn90AnswerIError(replyData, replyLen, ERR_PARAM_CN00);
    } else {
      *pTable = CHAR_STRINGTERM_CN90;
      ++pTable;
      pIndex  = strstr(pTable, ".");
      if (pIndex == NULL) {
        nFuncReturn =  cn90AnswerIError(replyData, replyLen, ERR_PARAM_CN00);
      } else {
        *pIndex = CHAR_STRINGTERM_CN90;
        ++pIndex;
      } // end if
    } // end if

    if (nFuncReturn == OK_CN00) {
      cn90StripLeadingChar(cn90StripTrailingChar(pScheme, '"'), '"');
      cn90StripLeadingChar(cn90StripTrailingChar(pTable,  '"'), '"');
      cn90StripLeadingChar(cn90StripTrailingChar(pIndex,  '"'), '"');

      nFuncReturn = cn83_RecreateIndex (vcontrol->dbname, pScheme, pTable, pIndex, replyData, replyLen);

    } // end if

  } else {
    nFuncReturn = cn83_RestartIndex (vcontrol->dbname, replyData, replyLen);
  } // end if

  return nFuncReturn;
} // end cn83RecreateIndex

/* 
  -----------------------------------------------------------------------------
  private function:     cn83_RecreateIndex
  -----------------------------------------------------------------------------
 */
static tcn00_Error cn83_RecreateIndex
      ( const tsp00_DbNamec & pDBName,
        const char   * pScheme,
        const char   * pTable,
        const char   * pIndex,
        char          * replyData,
        int           * replyLen )
{
  FUNCTION_DBG_MCN00_1("cn83_RecreateIndex");

  tcn00_Error             nFuncReturn    = OK_CN00;

  tin01_sql_session       SQLSession;
  tin110_SQLResultTable   aTable(&SQLSession);
  tin110_SQLRecord        aRecord;
  
  char                   szCommand     [SQL_COMMANDLENGTH];
  char                   szDropCommand [SQL_COMMANDLENGTH];
  char                 * pCreateCommand = NULL;
  char                 * pColumns       = NULL;
  char                 * pCurrent       = NULL;

  char                   szType  [10]   = "";
  char                   szSort  [10]   = "";
  tsp00_KnlIdentifierc    szColumn;

  bool                    bNamed = false;
  tsp00_Int4              nColumns = 0;
    
  // connect as sysdba
  cn50DBMUser oDBMUser   (pDBName, cn50DBMUser::getColdUser  (pDBName));
  if (oDBMUser.lastEvent()) {
    teo200_EventList aEvent(oDBMUser.lastEvent(), FUNCTION_NAME_MCN00_1, ERR_DBAUNKNOWN_CN00_1);
    nFuncReturn = cn90AnswerEvent(replyData, replyLen, aEvent);
  } else {
    nFuncReturn = cn80ConnectSQL(oDBMUser, &SQLSession, replyData, replyLen);
  } // end if

  // check sysdd.bad_indexes for existence and named property
  if (nFuncReturn == OK_CN00) {
    sprintf(szCommand, SQL_BADINDEXBYNAME, pScheme, pTable, pIndex);
    aTable.execute(szCommand);

    if (aTable.sqlRc() == cin01_db_row_not_found) {
      // oops index not found
      teo200_EventList lEvtList(aTable.lastEvent(), FUNCTION_NAME_MCN00_1, ERR_SQL_CN00_1);
      nFuncReturn = STATIC_CAST(tcn00_Error, cn90AnswerEvent(replyData, replyLen, lEvtList));
    } else if (aTable.sqlRc() != cin01_db_ok) {
      // oops other sql error
      teo200_EventList lEvtList(aTable.lastEvent(), FUNCTION_NAME_MCN00_1, ERR_SQL_CN00_1);
      nFuncReturn = STATIC_CAST(tcn00_Error, cn90AnswerEvent(replyData, replyLen, lEvtList));
    } // end if

    // generate drop command
    if (nFuncReturn == OK_CN00) {
      sprintf(szDropCommand, SQL_DROPINDEXBYNAME, pIndex, pScheme, pTable);

      // generate create command
      // check field count
      sprintf(szCommand, SQL_INDEXCOUNT, pScheme, pTable, pIndex);
      aTable.execute(szCommand);
      if (aTable.sqlRc() == cin01_db_ok) {
        aRecord = aTable.record();
        nColumns = aRecord.fieldAsInt(1);
      } else {
        teo200_EventList lEvtList(aTable.lastEvent(), FUNCTION_NAME_MCN00_1, ERR_SQL_CN00_1);
        nFuncReturn = STATIC_CAST(tcn00_Error, cn90AnswerEvent(replyData, replyLen, lEvtList));
      } // end if

      // check sysdd.indexes
      if (nFuncReturn == OK_CN00) {
        sprintf(szCommand, SQL_INDEXBYNAME, pScheme, pTable, pIndex);
        aTable.execute(szCommand);

        if (aTable.sqlRc() == cin01_db_ok) {
          aRecord = aTable.record();

          // save type
          SAPDB_strcpy(szType, aRecord.fieldAsChar(SQLF_TYPE));

          // allocate memory for fieldlist
          pColumns  = new char [nColumns * (sizeof(tsp00_KnlIdentifier) + strlen(SQL_CREATEINDEXBYNAME2)) + 1];
          *pColumns = CHAR_STRINGTERM_CN90;
          pCurrent  = pColumns;

          // create fieldlist
          while (aTable.sqlRc() == cin01_db_ok) {
            SAPDB_strcpy(szSort,   aRecord.fieldAsChar(SQLF_SORT));
            SAPDB_strcpy(szColumn, aRecord.fieldAsChar(SQLF_COLUMNNAME));
            sprintf(pCurrent, SQL_CREATEINDEXBYNAME2, szColumn.asCharp(), szSort);
            pCurrent = pCurrent + strlen(pCurrent);
        
            aRecord = aTable.nextRecord();
          } // end while

          // create command
          if (aTable.sqlRc() == cin01_db_row_not_found) {
            --pCurrent;
            *pCurrent = CHAR_STRINGTERM_CN90;
            pCreateCommand = new char [SQL_COMMANDLENGTH + strlen(pColumns)];
            sprintf(pCreateCommand, SQL_CREATEINDEXBYNAME1, szType, pIndex, pScheme, pTable, pColumns);

            // free field list
            delete pColumns;
          } else {
            teo200_EventList lEvtList(aTable.lastEvent(), FUNCTION_NAME_MCN00_1, ERR_SQL_CN00_1);
            nFuncReturn = STATIC_CAST(tcn00_Error, cn90AnswerEvent(replyData, replyLen, lEvtList));
          } // end if

        } else {
          teo200_EventList lEvtList(aTable.lastEvent(), FUNCTION_NAME_MCN00_1, ERR_SQL_CN00_1);
          nFuncReturn = STATIC_CAST(tcn00_Error, cn90AnswerEvent(replyData, replyLen, lEvtList));
        } // end if

      } // end if
    } // end if

    // drop index
    if (nFuncReturn == OK_CN00) {
      nFuncReturn = cn80ExecuteSQL  ( &SQLSession,
                                      szDropCommand,
                                      replyData,
                                      SQL_COMMANDLENGTH,
                                      *replyLen );
    } // end if

    // create index
    if (nFuncReturn == OK_CN00) {
      nFuncReturn = cn80ExecuteSQL  ( &SQLSession,
                                      pCreateCommand,
                                      replyData,
                                      SQL_COMMANDLENGTH,
                                      *replyLen );
    } // end if

    // free command buffer
    if (pCreateCommand != NULL) {
      delete pCreateCommand;
    } // end if

    // release session with commit or not
    cn80ReleaseSQL  ( &SQLSession, nFuncReturn == OK_CN00);

  } // end if

  if (nFuncReturn == OK_CN00) {
    cn90AnswerOK(replyData, replyLen, NULL);
  } // end if

  return nFuncReturn;

} // end cn83_RecreateIndex

/* 
  -----------------------------------------------------------------------------
  private function:     cn83_RestartIndex
  -----------------------------------------------------------------------------
 */
static tcn00_Error cn83_RestartIndex
      ( const tsp00_DbNamec & pDBName,
        char          * replyData,
        int           * replyLen )
{
  FUNCTION_DBG_MCN00_1("cn83_RestartIndex");

  tcn00_Error             nFuncReturn    = OK_CN00;
  tin01_sql_session       SQLSession;

  cn50DBMUser oDBAUser   (pDBName, cn50DBMUser::getSYSDBA  (pDBName));
  if (oDBAUser.lastEvent()) {
    teo200_EventList aEvent(oDBAUser.lastEvent(), FUNCTION_NAME_MCN00_1, ERR_DBAUNKNOWN_CN00_1);
    nFuncReturn = cn90AnswerEvent(replyData, replyLen, aEvent);
  } else {
    nFuncReturn = cn80ConnectSQL(oDBAUser, &SQLSession, replyData, replyLen);

    // restart index
    if (nFuncReturn == OK_CN00) {
      nFuncReturn = cn80ExecuteSQL  ( &SQLSession,
                                      SQL_RESTART,
                                      replyData,
                                      SQL_COMMANDLENGTH,
                                      *replyLen );
      // release session with commit or not
      cn80ReleaseSQL  ( &SQLSession, nFuncReturn == OK_CN00);
    } // end if
  } // end if

  if (nFuncReturn == OK_CN00) {
    cn90AnswerOK(replyData, replyLen, NULL);
  } // end if

  return nFuncReturn;

} // end cn83_RestartIndex

/*
static tcn00_Error cn83_AnalyzeIndex
      ( const tsp00_DbNamec & pDBName,
        const char   * pScheme,
        const char   * pTable,
        const char   * pIndex,
        char          * replyData,
        int           * replyLen )
{
  FUNCTION_DBG_MCN00_1("cn83_AnalyzeIndex");

  tcn00_Error         nFuncReturn   = OK_CN00;
  tin01_sql_session   SQLSession;

  nFuncReturn = cn80ConnectSQL(pDBName, &SQLSession);

  if (nFuncReturn == OK_CN00) {

    tin110_SQLResultTable aTable(&SQLSession, "select * from indexes";

    if (aTable.sqlRc() == cin01_db_ok) {
      tin110_SQLRecord aRecord = aTable.record();
      int i;
      char  * pCurrent = replyData;

      // init OK Answer
      sprintf (pCurrent, "%s%s", ANSWER_OK_CN00, LINE_SEPSTRING_CN00);
      pCurrent = pCurrent + strlen(pCurrent);

      // print column names
      for (i = 0; i < aRecord.fields(); ++i) {
        sprintf (pCurrent, "%s;", aRecord.fieldName (i+1) );
        pCurrent = pCurrent + strlen(pCurrent);
      } // end if
      --pCurrent;

      sprintf (pCurrent, "%s", LINE_SEPSTRING_CN00 );
      pCurrent = pCurrent + strlen(pCurrent);

      // print records
      while (aTable.sqlRc() == cin01_db_ok) {
        for (i = 0; i < aRecord.fields(); ++i) {
          if (aRecord.isNull(i + 1)) {
            sprintf (pCurrent, "(null);";
          } else {
            sprintf (pCurrent, "%s;", aRecord.fieldAsChar (i+1, '\'') );
          } // end if
          pCurrent = pCurrent + strlen(pCurrent);
        } // end if
        --pCurrent;

        sprintf (pCurrent, "%s", LINE_SEPSTRING_CN00 );
        pCurrent = pCurrent + strlen(pCurrent);

        aRecord = aTable.nextRecord();
      } // end if

      *replyLen = strlen(replyData);

      if (aTable.sqlRc() != cin01_db_row_not_found && aTable.sqlRc() != cin01_db_ok) {
        teo200_EventList lEvtList(aTable.lastEvent(), FUNCTION_NAME_MCN00_1, ERR_SQL_CN00_1);
        nFuncReturn = STATIC_CAST(tcn00_Error, cn90AnswerEvent(replyData, replyLen, lEvtList));
      } // end if
    
    } else {
      teo200_EventList lEvtList(aTable.lastEvent(), FUNCTION_NAME_MCN00_1, ERR_SQL_CN00_1);
      nFuncReturn = STATIC_CAST(tcn00_Error, cn90AnswerEvent(replyData, replyLen, lEvtList));
    } // end if

    cn80ReleaseSQL(&SQLSession, false);
  } // end if

  return nFuncReturn;

} // end cn83_AnalyzeIndex
*/

/* 
  -----------------------------------------------------------------------------
  function:     cn83UpdateStat
  -----------------------------------------------------------------------------
 */
tcn00_Error cn83UpdateStat
      ( VControlDataT * vcontrol,
        CommandT      * command,
        char          * replyData,
        int           * replyLen,
        int             replyLenMax)
{
  tcn00_Error         nFuncReturn   = OK_CN00;

  if (nFuncReturn == OK_CN00) {
    nFuncReturn = cn83_UpdateStat(vcontrol->dbname, command->args, replyData, replyLen);
  }

  if (nFuncReturn == OK_CN00) {
    //cn90AnswerOK(replyData, replyLen, NULL);
  } // end if

  return nFuncReturn;
} // end cn83UpdateStat

/* 
  -----------------------------------------------------------------------------
  private function:     cn83_UpdateStat
  -----------------------------------------------------------------------------
 */
static tcn00_Error cn83_UpdateStat
      ( const tsp00_DbNamec & pDBName,
        const char   * pSpec,
        char          * replyData,
        int           * replyLen )
{
  char                   szCommand     [SQL_COMMANDLENGTH];

  tcn00_Error             nFuncReturn    = OK_CN00;
  tin01_sql_session       SQLSession;

  nFuncReturn = cn80ConnectSQL(pDBName, &SQLSession, replyData, replyLen);

  // update statistics
  if (nFuncReturn == OK_CN00) {
    if (strlen(pSpec) == 0) {
      sprintf(szCommand, SQL_UPDATESTAT_ALL);
    } else {
      sprintf(szCommand, SQL_UPDATESTAT, pSpec);
    }

    nFuncReturn = cn80ExecuteSQL  ( &SQLSession,
                                    szCommand,
                                    replyData,
                                    SQL_COMMANDLENGTH,
                                    *replyLen );

    // release session with commit or not
    cn80ReleaseSQL  ( &SQLSession, nFuncReturn == OK_CN00);
  } // end if

  if (nFuncReturn == OK_CN00) {
    cn90AnswerOK(replyData, replyLen, NULL);
  } // end if

  return nFuncReturn;
} // end cn83_UpdateStat
