/*
   Copyright (c) 2003-2007 MySQL AB
   Use is subject to license terms.

   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; version 2 of the License.

   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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
*/

#include <NdbOut.hpp>
#include <Ndb.hpp>
#include <NdbOperation.hpp>
#include <NdbIndexOperation.hpp>
#include <NdbIndexScanOperation.hpp>
#include "NdbApiSignal.hpp"
#include <NdbRecAttr.hpp>
#include "NdbUtil.hpp"
#include "API.hpp"
#include "NdbBlob.hpp"

void
Ndb::checkFailedNode()
{
  DBUG_ENTER("Ndb::checkFailedNode");
  Uint32 *the_release_ind= theImpl->the_release_ind;
  if (the_release_ind[0] == 0)
  {
    DBUG_VOID_RETURN;
  }
  Uint32 tNoOfDbNodes = theImpl->theNoOfDBnodes;
  Uint8 *theDBnodes= theImpl->theDBnodes;

  DBUG_PRINT("enter", ("theNoOfDBnodes: %d", tNoOfDbNodes));

  DBUG_ASSERT(tNoOfDbNodes < MAX_NDB_NODES);
  for (Uint32 i = 0; i < tNoOfDbNodes; i++){
    const NodeId node_id = theDBnodes[i];
    DBUG_PRINT("info", ("i: %d, node_id: %d", i, node_id));
    
    DBUG_ASSERT(node_id < MAX_NDB_NODES);    
    if (the_release_ind[node_id] == 1){

      /**
       * Release all connections in idle list (for node)
       */
      NdbTransaction * tNdbCon = theConnectionArray[node_id];
      theConnectionArray[node_id] = NULL;
      while (tNdbCon != NULL) {
        NdbTransaction* tempNdbCon = tNdbCon;
        tNdbCon = tNdbCon->next();
        releaseNdbCon(tempNdbCon);
      }
      the_release_ind[node_id] = 0;
    }
  }
  DBUG_VOID_RETURN;
}

/***************************************************************************
 * int createConIdleList(int aNrOfCon);
 * 
 * Return Value:   Return the number of created connection object 
 *                 if createConIdleList was succesful
 *                 Return -1: In all other case.  
 * Parameters:     aNrOfCon : Number of connections offered to the application.
 * Remark:         Create connection idlelist with NdbTransaction objects.
 ***************************************************************************/ 
int 
Ndb::createConIdleList(int aNrOfCon)
{
  if (theImpl->theConIdleList.fill(this, aNrOfCon))
  {
    return -1;
  }
  return aNrOfCon; 
}

/***************************************************************************
 * int createOpIdleList(int aNrOfOp);
 *
 * Return Value:   Return the number of created operation object if 
 *                 createOpIdleList was succesful.
 *                 Return -1: In all other case.
 * Parameters:     aNrOfOp:  Number of operations offered to the application. 
 * Remark:         Create  operation idlelist with NdbOperation objects..
 ***************************************************************************/ 
int 
Ndb::createOpIdleList(int aNrOfOp)
{ 
  if (theImpl->theOpIdleList.fill(this, aNrOfOp))
  {
    return -1;
  }
  return aNrOfOp; 
}

/***************************************************************************
 * NdbBranch* NdbBranch();
 *
 * Return Value:   Return a NdbBranch if the  getNdbBranch was successful.
 *                Return NULL : In all other case.  
 * Remark:         Get a NdbBranch from theBranchList and return the object .
 ***************************************************************************/ 
NdbBranch*
Ndb::getNdbBranch()
{
  return theImpl->theBranchList.seize(this);
}

/***************************************************************************
 * NdbCall* NdbCall();
 *
 * Return Value:   Return a NdbCall if the  getNdbCall was successful.
 *                Return NULL : In all other case.  
 * Remark:         Get a NdbCall from theCallList and return the object .
 ***************************************************************************/ 
NdbCall*
Ndb::getNdbCall()
{
  return theImpl->theCallList.seize(this);
}

/***************************************************************************
 * NdbTransaction* getNdbCon();
 *
 * Return Value:   Return a connection if the  getNdbCon was successful.
 *                Return NULL : In all other case.  
 * Remark:         Get a connection from theConIdleList and return the object .
 ***************************************************************************/ 
NdbTransaction*
Ndb::getNdbCon()
{
  NdbTransaction* tNdbCon = theImpl->theConIdleList.seize(this);
  tNdbCon->theMagicNumber = 0x37412619;
  return tNdbCon;
}

/***************************************************************************
 * NdbLabel* getNdbLabel();
 *
 * Return Value:   Return a NdbLabel if the  getNdbLabel was successful.
 *                 Return NULL : In all other case.  
 * Remark:         Get a NdbLabel from theLabelList and return the object .
 ***************************************************************************/ 
NdbLabel*
Ndb::getNdbLabel()
{
  return theImpl->theLabelList.seize(this);
}

/***************************************************************************
 * NdbScanReceiver* getNdbScanRec()
 * 
 * Return Value:  Return a NdbScanReceiver
 *                Return NULL : In all other case.  
 * Remark:        Get a NdbScanReceiver from theScanRecList and return the 
 *                object .
 ****************************************************************************/ 
NdbReceiver*
Ndb::getNdbScanRec()
{
  return theImpl->theScanList.seize(this);
}

/***************************************************************************
 * NdbSubroutine* getNdbSubroutine();
 *
 * Return Value: Return a NdbSubroutine if the  getNdbSubroutine was successful.
 *                Return NULL : In all other case.  
 * Remark:    Get a NdbSubroutine from theSubroutineList and return the object .
 ***************************************************************************/ 
NdbSubroutine*
Ndb::getNdbSubroutine()
{
  return theImpl->theSubroutineList.seize(this);
}

/***************************************************************************
NdbOperation* getOperation();

Return Value:   Return theOpList : if the  getOperation was succesful.
                Return NULL : In all other case.  
Remark:         Get an operation from theOpIdleList and return the object .
***************************************************************************/ 
NdbOperation*
Ndb::getOperation()
{
  return theImpl->theOpIdleList.seize(this);
}

/***************************************************************************
NdbScanOperation* getScanOperation();

Return Value:   Return theOpList : if the  getScanOperation was succesful.
                Return NULL : In all other case.  
Remark:         Get an operation from theScanOpIdleList and return the object .
***************************************************************************/ 
NdbIndexScanOperation*
Ndb::getScanOperation()
{
  return theImpl->theScanOpIdleList.seize(this);
}

/***************************************************************************
NdbIndexOperation* getIndexOperation();

Return Value:   Return theOpList : if the  getIndexOperation was succesful.
                Return NULL : In all other case.  
Remark:         Get an operation from theIndexOpIdleList and return the object .
***************************************************************************/ 
NdbIndexOperation*
Ndb::getIndexOperation()
{
  return theImpl->theIndexOpIdleList.seize(this);
}

/***************************************************************************
NdbRecAttr* getRecAttr();

Return Value:   Return a reference to a receive attribute object.
                Return NULL if it's not possible to get a receive attribute object.
***************************************************************************/
NdbRecAttr*
Ndb::getRecAttr()
{
  NdbRecAttr* tRecAttr = theImpl->theRecAttrIdleList.seize(this);
  if (tRecAttr != NULL) 
  {
    tRecAttr->init();
    return tRecAttr;
  }

  return NULL;
}

/***************************************************************************
NdbApiSignal* getSignal();

Return Value:   Return a reference to a signal object.
                Return NULL if not possible to get a signal object.
***************************************************************************/
NdbApiSignal*
Ndb::getSignal()
{
  return theImpl->theSignalIdleList.seize(this);
}

NdbBlob*
Ndb::getNdbBlob()
{
  NdbBlob* tBlob = theImpl->theNdbBlobIdleList.seize(this);
  if(tBlob)
  {
    tBlob->init();
  }
  return tBlob;
}

/***************************************************************************
void releaseNdbBranch(NdbBranch* aNdbBranch);

Parameters:     NdbBranch: The NdbBranch object.
Remark:         Add a NdbBranch object into the Branch idlelist.
***************************************************************************/
void
Ndb::releaseNdbBranch(NdbBranch* aNdbBranch)
{
  theImpl->theBranchList.release(aNdbBranch);
}

/***************************************************************************
void releaseNdbCall(NdbCall* aNdbCall);

Parameters:     NdbBranch: The NdbBranch object.
Remark:         Add a NdbBranch object into the Branch idlelist.
***************************************************************************/
void
Ndb::releaseNdbCall(NdbCall* aNdbCall)
{
  theImpl->theCallList.release(aNdbCall);
}

/***************************************************************************
void releaseNdbCon(NdbTransaction* aNdbCon);

Parameters:     aNdbCon: The NdbTransaction object.
Remark:         Add a Connection object into the signal idlelist.
***************************************************************************/
void
Ndb::releaseNdbCon(NdbTransaction* aNdbCon)
{
  aNdbCon->theMagicNumber = 0xFE11DD;
  theImpl->theConIdleList.release(aNdbCon);
}

/***************************************************************************
void releaseNdbLabel(NdbLabel* aNdbLabel);

Parameters:     NdbLabel: The NdbLabel object.
Remark:         Add a NdbLabel object into the Label idlelist.
***************************************************************************/
void
Ndb::releaseNdbLabel(NdbLabel* aNdbLabel)
{
  theImpl->theLabelList.release(aNdbLabel);
}

/***************************************************************************
void releaseNdbScanRec(NdbScanReceiver* aNdbScanRec);

Parameters:     aNdbScanRec: The NdbScanReceiver object.
Remark:         Add a NdbScanReceiver object into the Scan idlelist.
***************************************************************************/
void
Ndb::releaseNdbScanRec(NdbReceiver* aNdbScanRec)
{
  theImpl->theScanList.release(aNdbScanRec);
}

/***************************************************************************
void releaseNdbSubroutine(NdbSubroutine* aNdbSubroutine);

Parameters:     NdbSubroutine: The NdbSubroutine object.
Remark:         Add a NdbSubroutine object into theSubroutine idlelist.
***************************************************************************/
void
Ndb::releaseNdbSubroutine(NdbSubroutine* aNdbSubroutine)
{
  theImpl->theSubroutineList.release(aNdbSubroutine);
}

/***************************************************************************
void releaseOperation(NdbOperation* anOperation);

Parameters:     anOperation : The released NdbOperation object.
Remark:         Add a NdbOperation object into the signal idlelist.
***************************************************************************/
void
Ndb::releaseOperation(NdbOperation* anOperation)
{
  if(anOperation->m_tcReqGSN == GSN_TCKEYREQ){
    anOperation->theNdbCon = NULL;
    anOperation->theMagicNumber = 0xFE11D0;
    theImpl->theOpIdleList.release(anOperation);
  } else {
    assert(anOperation->m_tcReqGSN == GSN_TCINDXREQ);
    anOperation->theNdbCon = NULL;
    anOperation->theMagicNumber = 0xFE11D1;
    theImpl->theIndexOpIdleList.release((NdbIndexOperation*)anOperation);
  }
}

/***************************************************************************
void releaseScanOperation(NdbScanOperation* aScanOperation);

Parameters:     aScanOperation : The released NdbScanOperation object.
Remark:         Add a NdbScanOperation object into the signal idlelist.
***************************************************************************/
void
Ndb::releaseScanOperation(NdbIndexScanOperation* aScanOperation)
{
  DBUG_ENTER("Ndb::releaseScanOperation");
  DBUG_PRINT("enter", ("op: 0x%lx", (long) aScanOperation));
#ifdef ndb_release_check_dup
  { NdbIndexScanOperation* tOp = theScanOpIdleList;
    while (tOp != NULL) {
      assert(tOp != aScanOperation);
      tOp = (NdbIndexScanOperation*)tOp->theNext;
    }
  }
#endif
  aScanOperation->theNdbCon = NULL;
  aScanOperation->theMagicNumber = 0xFE11D2;
  theImpl->theScanOpIdleList.release(aScanOperation);
  DBUG_VOID_RETURN;
}

/***************************************************************************
void releaseRecAttr(NdbRecAttr* aRecAttr);

Parameters:     aRecAttr : The released NdbRecAttr object.
Remark:         Add a NdbRecAttr object into the RecAtt idlelist.
***************************************************************************/
void
Ndb::releaseRecAttr(NdbRecAttr* aRecAttr)
{
  aRecAttr->release();
  theImpl->theRecAttrIdleList.release(aRecAttr);
}

/***************************************************************************
void releaseSignal(NdbApiSignal* aSignal);

Parameters:     aSignal : The released NdbApiSignal object.
Remark:         Add a NdbApiSignal object into the signal idlelist.
***************************************************************************/
void
Ndb::releaseSignal(NdbApiSignal* aSignal)
{
#if defined VM_TRACE
  // Check that signal is not null
  assert(aSignal != NULL);
#if 0
  // Check that signal is not already in list
  NdbApiSignal* tmp = theSignalIdleList;
  while (tmp != NULL){
    assert(tmp != aSignal);
    tmp = tmp->next();
  }
#endif
#endif
#ifdef POORMANSPURIFY
  creleaseSignals++;
#endif
  theImpl->theSignalIdleList.release(aSignal);
}

void
Ndb::releaseSignals(Uint32 cnt, NdbApiSignal* head, NdbApiSignal* tail)
{
#ifdef POORMANSPURIFY
  creleaseSignals += cnt;
#endif
  theImpl->theSignalIdleList.release(cnt, head, tail);
}

void
Ndb::releaseSignalsInList(NdbApiSignal** pList){
  NdbApiSignal* tmp;
  while (*pList != NULL){
    tmp = *pList;
    *pList = (*pList)->next();
    releaseSignal(tmp);
  }
}

void
Ndb::releaseNdbBlob(NdbBlob* aBlob)
{
  aBlob->release();
  theImpl->theNdbBlobIdleList.release(aBlob);
}

/****************************************************************************
int releaseConnectToNdb(NdbTransaction* aConnectConnection);

Return Value:   -1 if error 
Parameters:     aConnectConnection : Seized schema connection to DBTC
Remark:         Release and disconnect from DBTC a connection and seize it to theConIdleList.
*****************************************************************************/
void
Ndb::releaseConnectToNdb(NdbTransaction* a_con)     
{
  DBUG_ENTER("Ndb::releaseConnectToNdb");
  NdbApiSignal          tSignal(theMyRef);
  int                   tConPtr;

// I need to close the connection irrespective of whether I
// manage to reach NDB or not.

  if (a_con == NULL)
    DBUG_VOID_RETURN;

  Uint32 node_id = a_con->getConnectedNodeId();
  Uint32 conn_seq = a_con->theNodeSequence;
  tSignal.setSignal(GSN_TCRELEASEREQ);
  tSignal.setData((tConPtr = a_con->getTC_ConnectPtr()), 1);
  tSignal.setData(theMyRef, 2);
  tSignal.setData(a_con->ptr2int(), 3); 
  a_con->Status(NdbTransaction::DisConnecting);
  a_con->theMagicNumber = 0x37412619;
  int ret_code = sendRecSignal(node_id,
                               WAIT_TC_RELEASE,
                               &tSignal,
                               conn_seq);
  if (ret_code == 0) {
    ;
  } else if (ret_code == -1) {
    TRACE_DEBUG("Time-out when TCRELEASE sent");
  } else if (ret_code == -2) {
    TRACE_DEBUG("Node failed when TCRELEASE sent");
  } else if (ret_code == -3) {
    TRACE_DEBUG("Send failed when TCRELEASE sent");
  } else if (ret_code == -4) {
    TRACE_DEBUG("Send buffer full when TCRELEASE sent");
  } else if (ret_code == -5) {
    TRACE_DEBUG("Node stopping when TCRELEASE sent");
  } else {
    ndbout << "Impossible return from sendRecSignal when TCRELEASE" << endl;
    abort();
  }//if
  releaseNdbCon(a_con);
  DBUG_VOID_RETURN;
}

template<class T>
static
Ndb::Free_list_usage*
update(Ndb::Free_list_usage* curr, 
       Ndb_free_list_t<T> & list, 
       const char * name)
{
  curr->m_name = name;
  curr->m_created = list.m_alloc_cnt;
  curr->m_free = list.m_free_cnt;
  curr->m_sizeof = sizeof(T);
  return curr;
}

Ndb::Free_list_usage*
Ndb::get_free_list_usage(Ndb::Free_list_usage* curr)
{
  if (curr == 0)
  {
    return 0;
  } 

  if(curr->m_name == 0)
  {
    update(curr, theImpl->theConIdleList, "NdbTransaction");
  }
  else if(!strcmp(curr->m_name, "NdbTransaction"))
  {
    update(curr, theImpl->theOpIdleList, "NdbOperation");
  }
  else if(!strcmp(curr->m_name, "NdbOperation"))
  {
    update(curr, theImpl->theScanOpIdleList, "NdbIndexScanOperation");
  }
  else if(!strcmp(curr->m_name, "NdbIndexScanOperation"))
  {
    update(curr, theImpl->theIndexOpIdleList, "NdbIndexOperation");
  }
  else if(!strcmp(curr->m_name, "NdbIndexOperation"))
  {
    update(curr, theImpl->theRecAttrIdleList, "NdbRecAttr");
  }
  else if(!strcmp(curr->m_name, "NdbRecAttr"))
  {
    update(curr, theImpl->theSignalIdleList, "NdbApiSignal");
  }
  else if(!strcmp(curr->m_name, "NdbApiSignal"))
  {
    update(curr, theImpl->theLabelList, "NdbLabel");
  }
  else if(!strcmp(curr->m_name, "NdbLabel"))
  {
    update(curr, theImpl->theBranchList, "NdbBranch");
  }
  else if(!strcmp(curr->m_name, "NdbBranch"))
  {
    update(curr, theImpl->theSubroutineList, "NdbSubroutine");
  }
  else if(!strcmp(curr->m_name, "NdbSubroutine"))
  {
    update(curr, theImpl->theCallList, "NdbCall");
  }
  else if(!strcmp(curr->m_name, "NdbCall"))
  {
    update(curr, theImpl->theNdbBlobIdleList, "NdbBlob");
  }
  else if(!strcmp(curr->m_name, "NdbBlob"))
  {
    update(curr, theImpl->theScanList, "NdbReceiver");
  }
  else if(!strcmp(curr->m_name, "NdbReceiver"))
  {
    return 0;
  }
  else
  {
    update(curr, theImpl->theConIdleList, "NdbTransaction");
  }

  return curr;
}

#define TI(T) \
 template Ndb::Free_list_usage* \
 update(Ndb::Free_list_usage*, Ndb_free_list_t<T> &, const char * name);\
 template struct Ndb_free_list_t<T>

TI(NdbBlob);
TI(NdbCall);
TI(NdbLabel);
TI(NdbBranch);
TI(NdbSubroutine);
TI(NdbApiSignal);
TI(NdbRecAttr);
TI(NdbOperation);
TI(NdbReceiver);
TI(NdbConnection);
TI(NdbIndexOperation);
TI(NdbIndexScanOperation);
