/*
Licensed Materials - Property of IBM
DB2 Storage Engine Enablement
Copyright IBM Corporation 2007,2008
All rights reserved

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met: 
 (a) Redistributions of source code must retain this list of conditions, the
     copyright notice in section {d} below, and the disclaimer following this
     list of conditions. 
 (b) Redistributions in binary form must reproduce this list of conditions, the
     copyright notice in section (d) below, and the disclaimer following this
     list of conditions, in the documentation and/or other materials provided
     with the distribution. 
 (c) The name of IBM may not be used to endorse or promote products derived from
     this software without specific prior written permission. 
 (d) The text of the required copyright notice is: 
       Licensed Materials - Property of IBM
       DB2 Storage Engine Enablement 
       Copyright IBM Corporation 2007,2008 
       All rights reserved

THIS SOFTWARE IS PROVIDED BY IBM CORPORATION "AS IS" AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL IBM CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
OF SUCH DAMAGE.
*/



#include "db2i_ileBridge.h"
#include "my_dbug.h"
#include "db2i_global.h"
#include "db2i_charsetSupport.h"
#include "db2i_errors.h"


// static class member data
ILEpointer* db2i_ileBridge::functionSymbols;
db2i_ileBridge* db2i_ileBridge::globalBridge;
#ifndef DBUG_OFF
uint32 db2i_ileBridge::registeredPtrs;
#endif

pthread_key(IleParms*, THR_ILEPARMS);

static void ileParmsDtor(void* parmsToFree)
{
  if (parmsToFree)
  {
    free_aligned(parmsToFree);
    DBUG_PRINT("db2i_ileBridge", ("Freeing space for parms"));
  }
}


/**
  Convert a timestamp in ILE time format into a unix time_t
*/
static inline time_t convertILEtime(const ILE_time_t& input)
{
  tm temp;
  
  temp.tm_sec = input.Second;
  temp.tm_min = input.Minute;
  temp.tm_hour = input.Hour;
  temp.tm_mday = input.Day;
  temp.tm_mon = input.Month-1;
  temp.tm_year = input.Year - 1900;
  temp.tm_isdst = -1;
  
  return mktime(&temp);
}

/**
  Allocate and intialize a new bridge structure
*/ 
db2i_ileBridge* db2i_ileBridge::createNewBridge(CONNECTION_HANDLE connID)
{
  DBUG_PRINT("db2i_ileBridge::createNewBridge",("Building new bridge..."));
  db2i_ileBridge* newBridge = (db2i_ileBridge*)my_malloc(sizeof(db2i_ileBridge), MYF(MY_WME));

  if (unlikely(newBridge == NULL))
    return NULL;

  newBridge->stmtTxActive = false;
  newBridge->connErrText = NULL;
  newBridge->pendingLockedHandles.head = NULL;
  newBridge->cachedConnectionID = connID;
      
  return newBridge;
}


void db2i_ileBridge::destroyBridge(db2i_ileBridge* bridge)
{
  bridge->freeErrorStorage();
  my_free(bridge);
}


void db2i_ileBridge::destroyBridgeForThread(const THD* thd)
{
  void* thdData = *thd_ha_data(thd, ibmdb2i_hton);
  if (thdData != NULL)
  {
    destroyBridge((db2i_ileBridge*)thdData);
  }
}


void db2i_ileBridge::registerPtr(const void* ptr, ILEMemHandle* receiver)
{
  static const arg_type_t ileSignature[] = { ARG_MEMPTR, ARG_END };
  
  if (unlikely(ptr == NULL))
  {
    *receiver = 0;
    return;
  }
  
  struct ArgList
  {
    ILEarglist_base base;
    ILEpointer ptr;
  } *arguments;

  char argBuf[sizeof(ArgList)+15];
  arguments = (ArgList*)roundToQuadWordBdy(argBuf);

  arguments->ptr.s.addr = (address64_t)(ptr);

  _ILECALL(&functionSymbols[funcRegisterSpace], 
           &arguments->base,
           ileSignature, 
           RESULT_INT64);
  
#ifndef DBUG_OFF
  uint32 truncHandle = arguments->base.result.r_uint64;
  DBUG_PRINT("db2i_ileBridge::registerPtr",("Register 0x%p with handle %d", ptr, truncHandle));
  getBridgeForThread()->registeredPtrs++;
#endif

  *receiver = arguments->base.result.r_uint64;
  return;
}

void db2i_ileBridge::unregisterPtr(ILEMemHandle handle)
{
  static const arg_type_t ileSignature[] = { ARG_UINT64, ARG_END };

  if (unlikely(handle == NULL))
    return;
  
  struct ArgList
  {
    ILEarglist_base base;
    uint64 handle;
  } *arguments;

  char argBuf[sizeof(ArgList)+15];
  arguments = (ArgList*)roundToQuadWordBdy(argBuf);

  arguments->handle = (uint64)(handle);

  _ILECALL(&functionSymbols[funcUnregisterSpace], 
           &arguments->base,
           ileSignature, 
           RESULT_VOID);

#ifndef DBUG_OFF
  DBUG_PRINT("db2i_ileBridge::unregisterPtr",("Unregister handle %d", (uint32)handle));
  getBridgeForThread()->registeredPtrs--;
#endif  
}



/**
   Initialize the bridge component
   
   @details  Resolves srvpgm and function names of the APIs. If this fails, 
   the approrpiate operating system support (PTFs) is probably not installed.
   
   WARNING:
   Must be called before any other functions in this class are used!!!!
   Can only be called by a single thread! 
*/
int db2i_ileBridge::setup()
{
  static const char funcNames[db2i_ileBridge::funcListEnd][32] = 
  {
    {"QmyRegisterParameterSpaces"},
    {"QmyRegisterSpace"},
    {"QmyUnregisterSpace"},
    {"QmyProcessRequest"}
  };

  DBUG_ENTER("db2i_ileBridge::setup");

  int actmark = _ILELOAD("QSYS/QMYSE", ILELOAD_LIBOBJ);
  if ( actmark == -1 )
  {
    DBUG_PRINT("db2i_ileBridge::setup", ("srvpgm activation failed"));
    DBUG_RETURN(1);
  }

  functionSymbols = (ILEpointer*)malloc_aligned(sizeof(ILEpointer) * db2i_ileBridge::funcListEnd);
  
  for (int i = 0; i < db2i_ileBridge::funcListEnd; i++)
  {
    if (_ILESYM(&functionSymbols[i], actmark, funcNames[i]) == -1)
    {
      DBUG_PRINT("db2i_ileBridge::setup", 
                 ("resolve of %s failed", funcNames[i]));
      DBUG_RETURN(errno);
    }
  }
  
  pthread_key_create(&THR_ILEPARMS, &ileParmsDtor);
    
#ifndef DBUG_OFF
  registeredPtrs = 0;
#endif

  globalBridge = createNewBridge(0);
  
  DBUG_RETURN(0);
}

/**
  Cleanup any resources before shutting down plug-in
*/
void db2i_ileBridge::takedown()
{
  if (globalBridge)
    destroyBridge(globalBridge);
  free_aligned(functionSymbols);
}

/** 
  Call off to QmyProcessRequest to perform the API that the caller prepared
*/
inline int32 db2i_ileBridge::doIt()
{ 
  static const arg_type_t ileSignature[] = {ARG_END};
  
  struct ArgList
  {
    ILEarglist_base base;
  } *arguments;
  
  char argBuf[sizeof(ArgList)+15];
  arguments = (ArgList*)roundToQuadWordBdy(argBuf);
  
  _ILECALL(&functionSymbols[funcProcessRequest], 
           &arguments->base,
           ileSignature, 
           RESULT_INT32);
   
  return translateErrorCode(arguments->base.result.s_int32.r_int32);
}

/** 
  Call off to QmyProcessRequest to perform the API that the caller prepared and
  log any errors that may occur.
*/
inline int32 db2i_ileBridge::doItWithLog()
{
  int32 rc = doIt();

  if (unlikely(rc))
  {
    // Only report errors that we weren't expecting
    if (rc != tacitErrors[0] &&
        rc != tacitErrors[1] &&
        rc != QMY_ERR_END_OF_BLOCK)
      reportSystemAPIError(rc, (Qmy_Error_output_t*)parms()->outParms);
  }
  memset(tacitErrors, 0, sizeof(tacitErrors));

  return rc;
}


/**
  Interface to QMY_ALLOCATE_SHARE API
  
  See QMY_ALLOCATE_SHARE documentation for more information about 
  parameters and return codes.
*/
int32 db2i_ileBridge::allocateFileDefn(ILEMemHandle definitionSpace,
                                     ILEMemHandle handleSpace,
                                     uint16 fileCount,
                                     const char* schemaName,
                                     uint16 schemaNameLength,
                                     ILEMemHandle formatSpace,
                                     uint32 formatSpaceLen)
{
  DBUG_ASSERT(cachedStateIsCoherent());
  
  IleParms* parmBlock = parms();
  Qmy_MAOS0100 *input = (Qmy_MAOS0100*)&(parmBlock->inParms);
  memset(input, 0, sizeof(*input));
  
  input->Format = QMY_ALLOCATE_SHARE;
  input->ShrDefSpcHnd = definitionSpace;
  input->ShrHndSpcHnd = handleSpace;
  input->ShrDefCnt = fileCount;
  input->FmtSpcHnd = formatSpace;
  input->FmtSpcLen = formatSpaceLen;

  if (schemaNameLength > sizeof(input->SchNam))
  {
    // This should never happen!
    DBUG_ASSERT(0);
    return HA_ERR_GENERIC;
  }
  
  memcpy(input->SchNam, schemaName, schemaNameLength);  
  input->SchNamLen = schemaNameLength;
  
  input->CnnHnd = cachedConnectionID;

  int32 rc = doItWithLog();
  
  return rc;
}


/**
  Interface to QMY_ALLOCATE_INSTANCE API
  
  See QMY_ALLOCATE_INSTANCE documentation for more information about 
  parameters and return codes.
*/
int32 db2i_ileBridge::allocateFileInstance(FILE_HANDLE defnHandle,
                                           ILEMemHandle inuseSpace,
                                           FILE_HANDLE* instance)
{
  DBUG_ASSERT(cachedStateIsCoherent());
  
  IleParms* parmBlock = parms();
  Qmy_MAOI0100 *input = (Qmy_MAOI0100*)&(parmBlock->inParms);
  memset(input, 0, sizeof(*input));  
  
  input->Format = QMY_ALLOCATE_INSTANCE;
  input->ShrHnd = defnHandle;
  input->CnnHnd = cachedConnectionID;
  input->UseSpcHnd = inuseSpace;
  
  int32 rc = doItWithLog();

  if (likely(rc == 0))
  {
    Qmy_MAOI0100_output* output = (Qmy_MAOI0100_output*)parmBlock->outParms;  
    DBUG_ASSERT(instance);
    *instance = output->ObjHnd;
  }

  return rc;
}


/**
  Interface to QMY_DEALLOCATE_OBJECT API
  
  See QMY_DEALLOCATE_OBJECT documentation for more information about 
  parameters and return codes.
*/
int32 db2i_ileBridge::deallocateFile(FILE_HANDLE rfileHandle,
                                   bool postDropTable)
{
  IleParms* parmBlock = parms();
  Qmy_MDLC0100 *input = (Qmy_MDLC0100*)&(parmBlock->inParms);
  memset(input, 0, sizeof(*input));  
  
  input->Format = QMY_DEALLOCATE_OBJECT; 
  input->ObjHnd = rfileHandle;
  input->ObjDrp[0] = (postDropTable ? QMY_YES : QMY_NO);
  
  DBUG_PRINT("db2i_ileBridge::deallocateFile", ("Deallocating %d", (uint32)rfileHandle));
  
  int32 rc = doItWithLog();

  return rc;
}


/**
  Interface to QMY_OBJECT_INITIALIZATION API
  
  See QMY_OBJECT_INITIALIZATION documentation for more information about 
  parameters and return codes.
*/
int32 db2i_ileBridge::initFileForIO(FILE_HANDLE rfileHandle,
                                  char accessIntent,
                                  char commitLevel,
                                  uint16* inRecSize,
                                  uint16* inRecNullOffset,
                                  uint16* outRecSize,
                                  uint16* outRecNullOffset)
{
  DBUG_ASSERT(cachedStateIsCoherent());
  IleParms* parmBlock = parms();
  Qmy_MOIX0100 *input = (Qmy_MOIX0100*)&(parmBlock->inParms);
  memset(input, 0, sizeof(*input));  
  
  input->Format = QMY_OBJECT_INITIALIZATION;
  input->CmtLvl[0] = commitLevel;
  input->Intent[0] = accessIntent;
  input->ObjHnd = rfileHandle;
  input->CnnHnd = cachedConnectionID;

  int32 rc = doItWithLog();

  if (likely(rc == 0))
  {
    Qmy_MOIX0100_output* output = (Qmy_MOIX0100_output*)parmBlock->outParms; 
    *inRecSize = output->InNxtRowOff;
    *inRecNullOffset = output->InNullMapOff;
    *outRecSize = output->OutNxtRowOff;
    *outRecNullOffset = output->OutNullMapOff;
  }
  
  return rc;
}

    
/**
  Interface to QMY_READ_ROWS API for reading a row with a specific RRN.
  
  See QMY_READ_ROWS documentation for more information about 
  parameters and return codes.
*/
int32 db2i_ileBridge::readByRRN(FILE_HANDLE rfileHandle, 
                              ILEMemHandle buf,
                              uint32 inRRN,
                              char accessIntent,
                              char commitLevel)
{
  DBUG_ASSERT(cachedStateIsCoherent());
  IleParms* parmBlock = parms();
  Qmy_MRDX0100 *input = (Qmy_MRDX0100*)&(parmBlock->inParms);
  memset(input, 0, sizeof(*input));  

  input->Format = QMY_READ_ROWS;
  input->CmtLvl[0] = commitLevel;
  input->ObjHnd = rfileHandle;
  input->Intent[0] = accessIntent;
  input->OutSpcHnd = (uint64)buf;
  input->RelRowNbr = inRRN;
  input->CnnHnd = cachedConnectionID;
    
  int32 rc = doItWithLog();

  if (rc == QMY_ERR_END_OF_BLOCK)
  {
    rc = 0;
    DBUG_PRINT("db2i_ileBridge::readByRRN", ("End of block signalled"));
  }
  
  return rc;
}


/**
  Interface to QMY_WRITE_ROWS API.
  
  See QMY_WRITE_ROWS documentation for more information about 
  parameters and return codes.
*/
int32 db2i_ileBridge::writeRows(FILE_HANDLE rfileHandle, 
                              ILEMemHandle buf, 
                              char commitLevel,
                              int64* outIdVal,
                              bool* outIdGen,
                              uint32* dupKeyRRN,
                              char** dupKeyName,
                              uint32* dupKeyNameLen,
                              uint32* outIdIncrement)
{
  DBUG_ASSERT(cachedStateIsCoherent());
  IleParms* parmBlock = parms();
  Qmy_MWRT0100 *input = (Qmy_MWRT0100*)&(parmBlock->inParms);
  memset(input, 0, sizeof(*input));  

  input->Format = QMY_WRITE_ROWS;
  input->CmtLvl[0] = commitLevel;
  
  input->ObjHnd = rfileHandle;
  input->InSpcHnd = (uint64_t) buf;
  input->CnnHnd = cachedConnectionID;
  
  int32 rc = doItWithLog();

  Qmy_MWRT0100_output_t* output = (Qmy_MWRT0100_output_t*)parmBlock->outParms;  
  if (likely(rc == 0 || rc == HA_ERR_FOUND_DUPP_KEY))
  {
    DBUG_ASSERT(dupKeyRRN && dupKeyName && dupKeyNameLen && outIdGen && outIdIncrement && outIdVal);
    *dupKeyRRN = output->DupRRN;
    *dupKeyName = (char*)parmBlock->outParms + output->DupObjNamOff;
    *dupKeyNameLen = output->DupObjNamLen;
    *outIdGen = (output->NewIdGen[0] == QMY_YES ? TRUE : FALSE);
    if (*outIdGen == TRUE)
    {
      *outIdIncrement = output->IdIncrement;
      *outIdVal = output->NewIdVal;
    }
  }
  
  return rc;
  
}

/**
  Interface to QMY_EXECUTE_IMMEDIATE API.
  
  See QMY_EXECUTE_IMMEDIATE documentation for more information about 
  parameters and return codes.
*/
uint32 db2i_ileBridge::execSQL(const char* statement,
                             uint32 statementCount,
                             uint8  commitLevel,
                             bool autoCreateSchema,
                             bool dropSchema,
                             bool noCommit,
                             FILE_HANDLE fileHandle)

{
  IleParms* parmBlock = parms();
  Qmy_MSEI0100 *input = (Qmy_MSEI0100*)&(parmBlock->inParms);
  memset(input, 0, sizeof(*input));  

  input->Format = QMY_EXECUTE_IMMEDIATE;
  
  registerPtr(statement, &input->StmtsSpcHnd); 

  input->NbrStmts = statementCount;
  *(uint16*)(&input->StmtCCSID) = 850;
  input->AutoCrtSchema[0] = (autoCreateSchema == TRUE ? QMY_YES : QMY_NO);
  input->DropSchema[0] = (dropSchema == TRUE ? QMY_YES : QMY_NO); 
  input->CmtLvl[0] = commitLevel;
  if ((commitLevel == QMY_NONE && statementCount == 1) || noCommit)
  {
    input->CmtBefore[0] = QMY_NO;
    input->CmtAfter[0] = QMY_NO;
  }
  else
  {
    input->CmtBefore[0] = QMY_YES;
    input->CmtAfter[0] = QMY_YES;
  }
  input->CnnHnd = current_thd->thread_id;
  input->ObjHnd = fileHandle;

  int32 rc = doItWithLog();
  
  unregisterPtr(input->StmtsSpcHnd);
  
  return rc;
}

/**
  Interface to QMY_PREPARE_OPEN_CURSOR API.
  
  See QMY_PREPARE_OPEN_CURSOR documentation for more information about 
  parameters and return codes.
*/
int32 db2i_ileBridge::prepOpen(const char* statement,
                             FILE_HANDLE* rfileHandle,
                             uint32* recLength) 
{
  IleParms* parmBlock = parms();
  Qmy_MSPO0100 *input = (Qmy_MSPO0100*)&(parmBlock->inParms);
  memset(input, 0, sizeof(*input));  

  input->Format = QMY_PREPARE_OPEN_CURSOR;
  
  registerPtr(statement, &input->StmtsSpcHnd ); 
  *(uint16*)(&input->StmtCCSID) = 850;
  input->CnnHnd = current_thd->thread_id;

  int32 rc = doItWithLog();

  if (likely(rc == 0))
  {
    Qmy_MSPO0100_output* output = (Qmy_MSPO0100_output*)parmBlock->outParms;
    *rfileHandle = output->ObjHnd;
    *recLength = max(output->InNxtRowOff, output->OutNxtRowOff);    
  }

  
  unregisterPtr(input->StmtsSpcHnd);
  
  return rc;
}


/**
  Interface to QMY_DELETE_ROW API.
  
  See QMY_DELETE_ROW documentation for more information about 
  parameters and return codes.
*/
int32 db2i_ileBridge::deleteRow(FILE_HANDLE rfileHandle,
                              uint32 rrn)
{
  DBUG_ASSERT(cachedStateIsCoherent());
  IleParms* parmBlock = parms();
  Qmy_MDLT0100 *input = (Qmy_MDLT0100*)&(parmBlock->inParms);
  memset(input, 0, sizeof(*input));  
    
  input->Format = QMY_DELETE_ROW;  
  input->ObjHnd = rfileHandle;
  input->RelRowNbr = rrn;
  input->CnnHnd = cachedConnectionID;
            
  int32 rc = doItWithLog();

  return rc;
}


/**
  Interface to QMY_UPDATE_ROW API.
  
  See QMY_UPDATE_ROW documentation for more information about 
  parameters and return codes.
*/
int32 db2i_ileBridge::updateRow(FILE_HANDLE rfileHandle, 
                              uint32 rrn,
                              ILEMemHandle buf,
                              uint32* dupKeyRRN,
                              char** dupKeyName,
                              uint32* dupKeyNameLen)
{
  DBUG_ASSERT(cachedStateIsCoherent());
  IleParms* parmBlock = parms();
  Qmy_MUPD0100 *input = (Qmy_MUPD0100*)&(parmBlock->inParms);
  memset(input, 0, sizeof(*input));  
    
  input->Format = QMY_UPDATE_ROW;
  input->ObjHnd = rfileHandle;
  input->InSpcHnd = (uint64)buf;
  input->RelRowNbr = rrn;
  input->CnnHnd = cachedConnectionID;
            
  int32 rc = doItWithLog();
  
  if (rc == HA_ERR_FOUND_DUPP_KEY) 
  {
    Qmy_MUPD0100_output* output = (Qmy_MUPD0100_output*)parmBlock->outParms;
    DBUG_ASSERT(dupKeyRRN && dupKeyName && dupKeyNameLen);
    *dupKeyRRN = output->DupRRN;
    *dupKeyName = (char*)parmBlock->outParms + output->DupObjNamOff;
    *dupKeyNameLen = output->DupObjNamLen;
  }

  return rc;
}

/**
  Interface to QMY_DESCRIBE_RANGE API.
  
  See QMY_DESCRIBE_RANGE documentation for more information about 
  parameters and return codes.
*/
int32 db2i_ileBridge::recordsInRange(FILE_HANDLE defnHandle,
                                   ILEMemHandle inSpc,
                                   uint32 inKeyCnt,
                                   uint32 inLiteralCnt,
                                   uint32 inBoundsOff,
                                   uint32 inLitDefOff,
                                   uint32 inLiteralsOff,
                                   uint32 inCutoff,
                                   uint32 inSpcLen,
                                   uint16 inEndByte,
                                   uint64* outRecCnt,
                                   uint16* outRtnCode)
{
  DBUG_ASSERT(cachedStateIsCoherent());
  
  IleParms* parmBlock = parms();
  Qmy_MDRG0100 *input = (Qmy_MDRG0100*)&(parmBlock->inParms);
  memset(input, 0, sizeof(*input));  

  input->Format = QMY_DESCRIBE_RANGE;
  input->ShrHnd = defnHandle;
  input->SpcHnd = (uint64)inSpc;
  input->KeyCnt = inKeyCnt;
  input->LiteralCnt = inLiteralCnt;
  input->BoundsOff = inBoundsOff;
  input->LitDefOff = inLitDefOff;
  input->LiteralsOff = inLiteralsOff;
  input->Cutoff = inCutoff;
  input->SpcLen = inSpcLen;
  input->EndByte = inEndByte;
  input->CnnHnd = cachedConnectionID;

  int rc = doItWithLog();

  if (likely(rc == 0))
  {
    Qmy_MDRG0100_output* output = (Qmy_MDRG0100_output*)parmBlock->outParms;
    DBUG_ASSERT(outRecCnt && outRtnCode);
    *outRecCnt = output->RecCnt;
    *outRtnCode = output->RtnCode;
  }

  return rc;
}


/**
  Interface to QMY_RELEASE_ROW API.
  
  See QMY_RELEASE_ROW documentation for more information about 
  parameters and return codes.
*/
int32 db2i_ileBridge::rrlslck(FILE_HANDLE rfileHandle, char accessIntent)
{
  DBUG_ASSERT(cachedStateIsCoherent());
  
  IleParms* parmBlock = parms();
  Qmy_MRRX0100 *input = (Qmy_MRRX0100*)&(parmBlock->inParms);
  memset(input, 0, sizeof(*input));  
  
  input->Format = QMY_RELEASE_ROW;
  
  input->ObjHnd = rfileHandle;
  input->CnnHnd = cachedConnectionID;
  input->Intent[0] = accessIntent;
            
  int32 rc = doItWithLog();

  return rc;
}

/**
  Interface to QMY_LOCK_OBJECT API.
  
  See QMY_LOCK_OBJECT documentation for more information about 
  parameters and return codes.
*/
int32 db2i_ileBridge::lockObj(FILE_HANDLE defnHandle, 
                            uint64 lockVal, 
                            char lockAction, 
                            char lockType,
                            char lockTimeout)
{
  DBUG_ASSERT(cachedStateIsCoherent());
  IleParms* parmBlock = parms();
  Qmy_MOLX0100 *input = (Qmy_MOLX0100*)&(parmBlock->inParms);
  memset(input, 0, sizeof(*input));  
    
  input->Format = QMY_LOCK_OBJECT;
  input->ShrHnd = defnHandle;
  input->LckTimeoutVal = lockVal;
  input->Action[0] = lockAction;
  input->LckTyp[0] = lockType;
  input->LckTimeout[0] = lockTimeout;
  input->CnnHnd = cachedConnectionID;
        
  int32 rc = doItWithLog();

  return rc;
}

/**
  Interface to QMY_DESCRIBE_CONSTRAINTS API.
  
  See QMY_DESCRIBE_CONSTRAINTS documentation for more information about 
  parameters and return codes.
*/
int32 db2i_ileBridge::constraints(FILE_HANDLE defnHandle,
                                ILEMemHandle inSpc,
                                uint32 inSpcLen,
                                uint32* outLen,  
                                uint32* outCnt)     
{
  DBUG_ASSERT(cachedStateIsCoherent());
  IleParms* parmBlock = parms();
  Qmy_MDCT0100 *input = (Qmy_MDCT0100*)&(parmBlock->inParms);
  memset(input, 0, sizeof(*input));  

  input->Format = QMY_DESCRIBE_CONSTRAINTS;
  input->ShrHnd = defnHandle;
  input->CstSpcHnd = (uint64)inSpc;
  input->CstSpcLen = inSpcLen;
  input->CnnHnd = cachedConnectionID;

  int32 rc = doItWithLog();

  if (likely(rc == 0))
  {
    Qmy_MDCT0100_output* output = (Qmy_MDCT0100_output*)parmBlock->outParms;
    DBUG_ASSERT(outLen && outCnt);
    *outLen = output->NeededLen;
    *outCnt = output->CstCnt;
  }

  return rc;
}


/**
  Interface to QMY_REORGANIZE_TABLE API.
  
  See QMY_REORGANIZE_TABLE documentation for more information about 
  parameters and return codes.
*/
int32 db2i_ileBridge::optimizeTable(FILE_HANDLE defnHandle)
{
  DBUG_ASSERT(cachedStateIsCoherent());
  IleParms* parmBlock = parms();
  Qmy_MRGX0100 *input = (Qmy_MRGX0100*)&(parmBlock->inParms);
  memset(input, 0, sizeof(*input));  
  
  input->Format = QMY_REORGANIZE_TABLE;
  input->ShrHnd = defnHandle;
  input->CnnHnd = cachedConnectionID;
  
  int32 rc = doItWithLog();

  return rc;
}


/**
  Interface to QMY_PROCESS_COMMITMENT_CONTROL API.
  
  See QMY_PROCESS_COMMITMENT_CONTROL documentation for more information about 
  parameters and return codes.
*/
int32 db2i_ileBridge::commitmentControl(uint8 function)
{
  DBUG_ASSERT(cachedStateIsCoherent());
  IleParms* parmBlock = parms();
  Qmy_MCCX0100 *input = (Qmy_MCCX0100*)&(parmBlock->inParms);
  memset(input, 0, sizeof(*input));  
    
  input->Format = QMY_PROCESS_COMMITMENT_CONTROL;              
  input->Function[0] = function;
  input->CnnHnd = cachedConnectionID;
 
  int32 rc = doItWithLog();

  return rc;
}


/**
  Interface to QMY_PROCESS_SAVEPOINT API.
  
  See QMY_PROCESS_SAVEPOINT documentation for more information about parameters and 
  return codes.
*/
int32 db2i_ileBridge::savepoint(uint8 function,
                                const char* savepointName)
{
  DBUG_ASSERT(cachedStateIsCoherent());
  DBUG_PRINT("db2i_ileBridge::savepoint",("%d %s", (uint32)function, savepointName));
  
  IleParms* parmBlock = parms();
  Qmy_MSPX0100 *input = (Qmy_MSPX0100*)&(parmBlock->inParms);
  memset(input, 0, sizeof(*input));  

  char* savPtNam = (char*)(input+1);
  
  input->Format  = QMY_PROCESS_SAVEPOINT;

  if (strlen(savepointName) > MAX_DB2_SAVEPOINTNAME_LENGTH)
  {
    DBUG_ASSERT(0);
    return HA_ERR_GENERIC;
  }
  strcpy(savPtNam, savepointName);
            
  input->Function[0] = function;
  input->SavPtNamOff = savPtNam - (char*)(input);
  input->SavPtNamLen = strlen(savepointName);
  input->CnnHnd = cachedConnectionID;
 
  int32 rc = doItWithLog();
  
  return rc;
}

static ILEMemHandle traceSpcHandle;
/**
  Do initialization for  the QMY_* APIs.
  
  @parm aspName  The name of the relational database to use for all 
                 connections.
  
  @return  0 if successful; error otherwise
*/
int32 db2i_ileBridge::initILE(const char* aspName,
                              uint16* traceCtlPtr)
{
  // We forego the typical thread-based parms space because MySQL doesn't
  // allow us to clean it up before checking for memory leaks. As a result
  // we get a complaint about leaked memory on server shutdown.
  int32 rc;
  char inParms[db2i_ileBridge_MAX_INPARM_SIZE];
  char outParms[db2i_ileBridge_MAX_OUTPARM_SIZE];
  if (rc = registerParmSpace(inParms, outParms))
  {
    reportSystemAPIError(rc, NULL);
    return rc;
  }
  
  registerPtr(traceCtlPtr, &traceSpcHandle);
  
  struct ParmBlock
  {
    Qmy_MINI0100 parms;
  } *parmBlock = (ParmBlock*)inParms;
  
  memset(inParms, 0, sizeof(ParmBlock));
    
  parmBlock->parms.Format = QMY_INITIALIZATION;
      
  char paddedName[18];
  if (strlen(aspName) > sizeof(paddedName))
  {
    getErrTxt(DB2I_ERR_BAD_RDB_NAME);
    return DB2I_ERR_BAD_RDB_NAME;
  }
    
  memset(paddedName, ' ', sizeof(paddedName));
  memcpy(paddedName, aspName, strlen(aspName));      
  convToEbcdic(paddedName, parmBlock->parms.RDBName, strlen(paddedName));
  
  parmBlock->parms.RDBNamLen = strlen(paddedName);
  parmBlock->parms.TrcSpcHnd = traceSpcHandle;
            
  rc = doIt();

  if (rc)
  {
    reportSystemAPIError(rc, (Qmy_Error_output_t*)outParms);
  }
  
  return rc;  
}

/**
  Signal to the QMY_ APIs to perform any cleanup they need to do.
*/
int32 db2i_ileBridge::exitILE()
{
  IleParms* parmBlock = parms();
  Qmy_MCLN0100 *input = (Qmy_MCLN0100*)&(parmBlock->inParms);
  memset(input, 0, sizeof(*input));  
    
  input->Format = QMY_CLEANUP;
  
  int32 rc = doIt();

  if (rc)
  {
    reportSystemAPIError(rc, (Qmy_Error_output_t*)parmBlock->outParms);
  }
  
  unregisterPtr(traceSpcHandle);

  DBUG_PRINT("db2i_ileBridge::exitILE", ("Registered ptrs remaining: %d", registeredPtrs));
#ifndef DBUG_OFF
  if (registeredPtrs != 0)
    printf("Oh no! IBMDB2I left some pointers registered. Count was %d.\n", registeredPtrs);
#endif  
  
  // This is needed to prevent SAFE_MALLOC from complaining at process termination.
  my_pthread_setspecific_ptr(THR_ILEPARMS, NULL);
  free_aligned(parmBlock);
    
  return rc;
  
}


/**
  Designate the specified addresses as parameter passing buffers.
  
  @parm in  Input to the API will go here; format is defined by the individual API
  @parm out  Output from the API will be; format is defined by the individual API
  
  @return  0 if success; error otherwise
*/
int db2i_ileBridge::registerParmSpace(char* in, char* out)
{
  static const arg_type_t ileSignature[] = { ARG_MEMPTR, ARG_MEMPTR, ARG_END };

  struct ArgList
  {
    ILEarglist_base base;
    ILEpointer input;
    ILEpointer output;
  } *arguments;

  char argBuf[sizeof(ArgList)+15];
  arguments = (ArgList*)roundToQuadWordBdy(argBuf);

  arguments->input.s.addr = (address64_t)(in);
  arguments->output.s.addr = (address64_t)(out);

  _ILECALL(&functionSymbols[funcRegisterParameterSpaces], 
           &arguments->base,
           ileSignature, 
           RESULT_INT32);

  return arguments->base.result.s_int32.r_int32;
}


/**
  Interface to QMY_OBJECT_OVERRIDE API.
  
  See QMY_OBJECT_OVERRIDE documentation for more information about parameters and 
  return codes.
*/
int32 db2i_ileBridge::objectOverride(FILE_HANDLE rfileHandle,
                                  ILEMemHandle buf,
                                  uint32 recordWidth)
{
  DBUG_ASSERT(cachedStateIsCoherent());
  IleParms* parmBlock = parms();
  Qmy_MOOX0100 *input = (Qmy_MOOX0100*)&(parmBlock->inParms);
  memset(input, 0, sizeof(*input));  
  
  input->Format = QMY_OBJECT_OVERRIDE;  
  input->ObjHnd = rfileHandle;
  input->OutSpcHnd = (uint64)buf;
  input->NxtRowOff = recordWidth;
  input->CnnHnd = cachedConnectionID;

  int32 rc = doItWithLog();

  return rc;
}

/**
  Interface to QMY_DESCRIBE_OBJECT API for obtaining table stats.
  
  See QMY_DESCRIBE_OBJECT documentation for more information about parameters and 
  return codes.
*/
int32 db2i_ileBridge::retrieveTableInfo(FILE_HANDLE defnHandle,
                                      uint16 dataRequested,
                                      ha_statistics& stats,
                                      ILEMemHandle inSpc)
{
  DBUG_ASSERT(cachedStateIsCoherent());
  IleParms* parmBlock = parms();
  Qmy_MDSO0100 *input = (Qmy_MDSO0100*)&(parmBlock->inParms);
  memset(input, 0, sizeof(*input));  

  input->Format = QMY_DESCRIBE_OBJECT;  
  input->ShrHnd = defnHandle;
  input->CnnHnd = cachedConnectionID;
  
  if (dataRequested & objLength)
    input->RtnObjLen[0] = QMY_YES;
  if (dataRequested & rowCount)
    input->RtnRowCnt[0] = QMY_YES;
  if (dataRequested & deletedRowCount)
    input->RtnDltRowCnt[0] = QMY_YES;
  if (dataRequested & rowsPerKey)
  {
    input->RowKeyHnd = (uint64)inSpc;
    input->RtnRowKey[0] = QMY_YES;
  }
  if (dataRequested & meanRowLen)
    input->RtnMeanRowLen[0] = QMY_YES;
  if (dataRequested & lastModTime)
    input->RtnModTim[0] = QMY_YES;
  if (dataRequested & createTime)
    input->RtnCrtTim[0] = QMY_YES;
  if (dataRequested & ioCount)
    input->RtnEstIoCnt[0] = QMY_YES;
  
  int32 rc = doItWithLog();

  if (likely(rc == 0))
  {
    Qmy_MDSO0100_output* output = (Qmy_MDSO0100_output*)parmBlock->outParms; 
    if (dataRequested & objLength)
      stats.data_file_length = output->ObjLen;
    if (dataRequested & rowCount)
      stats.records= output->RowCnt;
    if (dataRequested & deletedRowCount)
      stats.deleted = output->DltRowCnt;    
    if (dataRequested & meanRowLen)
      stats.mean_rec_length = output->MeanRowLen;
    if (dataRequested & lastModTime)
      stats.update_time = convertILEtime(output->ModTim);
    if (dataRequested & createTime)
      stats.create_time = convertILEtime(output->CrtTim);
    if (dataRequested & ioCount)
      stats.data_file_length = output->EstIoCnt;     
  }
  
  return rc;
}

/**
  Interface to QMY_DESCRIBE_OBJECT API for finding index size.
  
  See QMY_DESCRIBE_OBJECT documentation for more information about parameters and 
  return codes.
*/
int32 db2i_ileBridge::retrieveIndexInfo(FILE_HANDLE defnHandle,
                                      uint64* outPageCnt)    
{
  DBUG_ASSERT(cachedStateIsCoherent());
  IleParms* parmBlock = parms();
  Qmy_MDSO0100 *input = (Qmy_MDSO0100*)&(parmBlock->inParms);
  memset(input, 0, sizeof(*input));  
  
  input->Format = QMY_DESCRIBE_OBJECT;
  input->ShrHnd = defnHandle;
  input->CnnHnd = cachedConnectionID;
  input->RtnPageCnt[0] = QMY_YES;
  
  int32 rc = doItWithLog();

  if (likely(rc == 0))
  {
    Qmy_MDSO0100_output* output = (Qmy_MDSO0100_output*)parmBlock->outParms;  
    *outPageCnt = output->PageCnt;
  }
  
  return rc;
}


/**
  Interface to QMY_CLOSE_CONNECTION API
  
  See QMY_CLOSE_CONNECTION documentation for more information about parameters and 
  return codes.
*/
int32 db2i_ileBridge::closeConnection(CONNECTION_HANDLE conn)
{
  IleParms* parmBlock = parms();
  Qmy_MCCN0100 *input = (Qmy_MCCN0100*)&(parmBlock->inParms);
  memset(input, 0, sizeof(*input));  
  
  input->Format = QMY_CLOSE_CONNECTION;
  input->CnnHnd = conn;
  
  int32 rc = doItWithLog();

  return rc;
}


/**
  Interface to QMY_INTERRUPT API
  
  See QMY_INTERRUPT documentation for more information about parameters and 
  return codes.
*/
int32 db2i_ileBridge::readInterrupt(FILE_HANDLE fileHandle)
{
  DBUG_ASSERT(cachedStateIsCoherent());
  IleParms* parmBlock = parms();
  Qmy_MINT0100 *input = (Qmy_MINT0100*)&(parmBlock->inParms);
  memset(input, 0, sizeof(*input));  
  
  input->Format = QMY_INTERRUPT;
  input->CnnHnd = cachedConnectionID;
  input->ObjHnd = fileHandle;
  
  int32 rc = doItWithLog();

  if (rc == QMY_ERR_END_OF_BLOCK)
  {
    rc = 0;
    DBUG_PRINT("db2i_ileBridge::readInterrupt", ("End of block signalled"));
  }

  return rc;   
}

/**
  Interface to QMY_READ_ROWS API
  
  See QMY_READ_ROWS documentation for more information about parameters and 
  return codes.
*/
int32 db2i_ileBridge::read(FILE_HANDLE rfileHandle, 
                         ILEMemHandle buf, 
                         char accessIntent,
                         char commitLevel,
                         char orientation, 
                         bool asyncRead,
                         ILEMemHandle rrn,
                         ILEMemHandle key,
                         uint32 keylen,
                         uint16 keyParts,
                         int pipeFD)
{
  DBUG_ASSERT(cachedStateIsCoherent());
  IleParms* parmBlock = parms();
  Qmy_MRDX0100 *input = (Qmy_MRDX0100*)&(parmBlock->inParms);
  memset(input, 0, sizeof(*input));  

  input->Format = QMY_READ_ROWS;
  input->CmtLvl[0] = commitLevel;
  
  input->ObjHnd = rfileHandle;
  input->Intent[0] = accessIntent;
  input->OutSpcHnd = (uint64)buf;
  input->OutRRNSpcHnd = (uint64)rrn;
  input->RtnData[0] = QMY_RETURN_DATA;
  
  if (key)
  {
    input->KeySpcHnd = (uint64)key;
    input->KeyColsLen = keylen;
    input->KeyColsNbr = keyParts;
  }

  input->Async[0] = (asyncRead ? QMY_YES : QMY_NO);
  input->PipeDesc = pipeFD;
  input->Orientation[0] = orientation;
  input->CnnHnd = cachedConnectionID;
          
  int32 rc = doItWithLog();

  // QMY_ERR_END_OF_BLOCK is informational only, so we ignore it.
  if (rc == QMY_ERR_END_OF_BLOCK)
  {
    rc = 0;
    DBUG_PRINT("db2i_ileBridge::read", ("End of block signalled"));
  }
  
  return rc;
}


/**
  Interface to QMY_QUIESCE_OBJECT API
  
  See QMY_QUIESCE_OBJECT documentation for more information about parameters and
  return codes.
*/
int32 db2i_ileBridge::quiesceFileInstance(FILE_HANDLE rfileHandle)
{
  IleParms* parmBlock = parms();
  Qmy_MQSC0100 *input = (Qmy_MQSC0100*)&(parmBlock->inParms);
  memset(input, 0, sizeof(*input));  
  
  input->Format = QMY_QUIESCE_OBJECT; 
  input->ObjHnd = rfileHandle;
    
  int32 rc = doItWithLog();

#ifndef DBUG_OFF  
  if (unlikely(rc))
  {
    DBUG_ASSERT(0);
  }
#endif
  
  return rc;
}
  
void db2i_ileBridge::PreservedHandleList::add(const char* newname, FILE_HANDLE newhandle, IBMDB2I_SHARE* share) 
{
  NameHandlePair *newPair = (NameHandlePair*)my_malloc(sizeof(NameHandlePair), MYF(MY_WME)); 

  newPair->next = head;
  head = newPair;

  strcpy(newPair->name, newname);
  newPair->handle = newhandle;
  newPair->share = share;
  DBUG_PRINT("db2i_ileBridge", ("Added handle %d for %s", uint32(newhandle), newname));
}
    

FILE_HANDLE db2i_ileBridge::PreservedHandleList::findAndRemove(const char* fileName, IBMDB2I_SHARE** share)
{
  NameHandlePair* current = head;
  NameHandlePair* prev = NULL;

  while (current)
  {
    NameHandlePair* next = current->next;
    if (strcmp(fileName, current->name) == 0)
    { 
      FILE_HANDLE tmp = current->handle;
      *share = current->share;
      if (prev)
        prev->next = next;
      if (current == head)
        head = next;
      my_free(current);
      DBUG_PRINT("db2i_ileBridge", ("Found handle %d for %s", uint32(tmp), fileName));
      return tmp;
    }
    prev = current;
    current = next;
  }

  return 0;
}


IleParms* db2i_ileBridge::initParmsForThread()
{
  
  IleParms*  p = (IleParms*)malloc_aligned(sizeof(IleParms));
  DBUG_ASSERT((uint64)(&(p->outParms))% 16 == 0); // Guarantee that outParms are aligned correctly

  if (likely(p))
  {
    int32 rc = registerParmSpace((p->inParms), (p->outParms));
    if (likely(rc == 0))
    {
      my_pthread_setspecific_ptr(THR_ILEPARMS, p);
      DBUG_PRINT("db2i_ileBridge", ("Inited space for parms"));
      return p;
    }
    else
      reportSystemAPIError(rc, NULL);
  }

  return NULL;
}