/* Copyright (c) 2003, 2008, 2011, 2013, Oracle and/or its affiliates. All rights reserved.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License, version 2.0,
   as published by the Free Software Foundation.

   This program is also distributed with certain software (including
   but not limited to OpenSSL) that is licensed under separate terms,
   as designated in a particular file or component or in included license
   documentation.  The authors of MySQL hereby grant you an additional
   permission to link the program and your derivative works with the
   separately licensed software that they have included with MySQL.

   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, version 2.0, 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
*/

#define DBACC_C
#include "Dbacc.hpp"

#include <AttributeHeader.hpp>
#include <signaldata/AccFrag.hpp>
#include <signaldata/AccScan.hpp>
#include <signaldata/NextScan.hpp>
#include <signaldata/AccLock.hpp>
#include <signaldata/EventReport.hpp>
#include <signaldata/FsConf.hpp>
#include <signaldata/FsRef.hpp>
#include <signaldata/FsRemoveReq.hpp>
#include <signaldata/DropTab.hpp>
#include <signaldata/DumpStateOrd.hpp>
#include <signaldata/TuxMaint.hpp>
#include <signaldata/DbinfoScan.hpp>
#include <signaldata/TransIdAI.hpp>
#include <KeyDescriptor.hpp>
#include <signaldata/NodeStateSignalData.hpp>
#include <md5_hash.hpp>

#ifdef VM_TRACE
#define DEBUG(x) ndbout << "DBACC: "<< x << endl;
#else
#define DEBUG(x)
#endif

#ifdef ACC_SAFE_QUEUE
#define vlqrequire(x) do { if (unlikely(!(x))) {\
   dump_lock_queue(loPtr); \
   ndbrequire(false); } } while(0)
#else
#define vlqrequire(x) ndbrequire(x)
#define dump_lock_queue(x)
#endif


// primary key is stored in TUP
#include "../dbtup/Dbtup.hpp"
#include "../dblqh/Dblqh.hpp"

#define JAM_FILE_ID 345

// Index pages used by ACC instances, used by CMVMI to report index memory usage
extern Uint32 g_acc_pages_used[MAX_NDBMT_LQH_WORKERS];

// Signal entries and statement blocks
/* --------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------- */
/*                                                                                   */
/*       COMMON SIGNAL RECEPTION MODULE                                              */
/*                                                                                   */
/* --------------------------------------------------------------------------------- */

/* --------------------------------------------------------------------------------- */
/* ******************--------------------------------------------------------------- */
/* CONTINUEB                                       CONTINUE SIGNAL                   */
/* ******************------------------------------+                                 */
/*   SENDER: ACC,    LEVEL B       */
void Dbacc::execCONTINUEB(Signal* signal) 
{
  Uint32 tcase;

  jamEntry();
  tcase = signal->theData[0];
  tdata0 = signal->theData[1];
  tresult = 0;
  switch (tcase) {
  case ZINITIALISE_RECORDS:
    jam();
    initialiseRecordsLab(signal, signal->theData[3], signal->theData[4]);
    return;
    break;
  case ZREL_ROOT_FRAG:
    {
      jam();
      Uint32 tableId = signal->theData[1];
      releaseRootFragResources(signal, tableId);
      break;
    }
  case ZREL_FRAG:
    {
      jam();
      Uint32 fragIndex = signal->theData[1];
      releaseFragResources(signal, fragIndex);
      break;
    }
  case ZREL_DIR:
    {
      jam();
      releaseDirResources(signal);
      break;
    }

  default:
    ndbrequire(false);
    break;
  }//switch
  return;
}//Dbacc::execCONTINUEB()

/* ------------------------------------------------------------------------- */
/* ------------------------------------------------------------------------- */
/* ------------------------------------------------------------------------- */
/*                                                                           */
/*       END OF COMMON SIGNAL RECEPTION MODULE                               */
/*                                                                           */
/* ------------------------------------------------------------------------- */
/* ------------------------------------------------------------------------- */
/* ------------------------------------------------------------------------- */
/* ------------------------------------------------------------------------- */
/* ------------------------------------------------------------------------- */
/*                                                                           */
/*       SYSTEM RESTART MODULE                                               */
/*                                                                           */
/* ------------------------------------------------------------------------- */
/* ------------------------------------------------------------------------- */
/* ------------------------------------------------------------------------- */
void Dbacc::execNDB_STTOR(Signal* signal) 
{
  Uint32 tstartphase;
  Uint32 tStartType;

  jamEntry();
  cndbcntrRef = signal->theData[0];
  cmynodeid = signal->theData[1];
  tstartphase = signal->theData[2];
  tStartType = signal->theData[3];
  switch (tstartphase) {
  case ZSPH1:
    jam();
    ndbsttorryLab(signal);
    return;
    break;
  case ZSPH2:
    ndbsttorryLab(signal);
    return;
    break;
  case ZSPH3:
    break;
  case ZSPH6:
    jam();
    break;
  default:
    jam();
    /*empty*/;
    break;
  }//switch
  ndbsttorryLab(signal);
  return;
}//Dbacc::execNDB_STTOR()

/* ******************--------------------------------------------------------------- */
/* STTOR                                              START /  RESTART               */
/* ******************------------------------------+                                 */
/*   SENDER: ANY,    LEVEL B       */
void Dbacc::execSTTOR(Signal* signal) 
{
  jamEntry();
  Uint32 tstartphase = signal->theData[1];
  switch (tstartphase) {
  case 1:
    jam();
    ndbrequire((c_tup = (Dbtup*)globalData.getBlock(DBTUP, instance())) != 0);
    ndbrequire((c_lqh = (Dblqh*)globalData.getBlock(DBLQH, instance())) != 0);
    break;
  }
  tuserblockref = signal->theData[3];
  csignalkey = signal->theData[6];
  sttorrysignalLab(signal);
  return;
}//Dbacc::execSTTOR()

/* --------------------------------------------------------------------------------- */
/* ZSPH1                                                                             */
/* --------------------------------------------------------------------------------- */
void Dbacc::ndbrestart1Lab(Signal* signal) 
{
  cmynodeid = globalData.ownId;
  cownBlockref = calcInstanceBlockRef(DBACC);
  czero = 0;
  cminusOne = czero - 1;
  ctest = 0;
  return;
}//Dbacc::ndbrestart1Lab()

void Dbacc::initialiseRecordsLab(Signal* signal, Uint32 ref, Uint32 data) 
{
  switch (tdata0) {
  case 0:
    jam();
    initialiseTableRec(signal);
    break;
  case 1:
  case 2:
    break;
  case 3:
    jam();
    break;
  case 4:
    jam();
    break;
  case 5:
    jam();
    break;
  case 6:
    jam();
    initialiseFragRec(signal);
    break;
  case 7:
    jam();
    break;
  case 8:
    jam();
    initialiseOperationRec(signal);
    break;
  case 9:
    jam();
    initialisePageRec(signal);
    break;
  case 10:
    jam();
    break;
  case 11:
    jam();
    initialiseScanRec(signal);
    break;
  case 12:
    jam();

    {
      ReadConfigConf * conf = (ReadConfigConf*)signal->getDataPtrSend();
      conf->senderRef = reference();
      conf->senderData = data;
      sendSignal(ref, GSN_READ_CONFIG_CONF, signal, 
		 ReadConfigConf::SignalLength, JBB);
    }
    return;
    break;
  default:
    ndbrequire(false);
    break;
  }//switch

  signal->theData[0] = ZINITIALISE_RECORDS;
  signal->theData[1] = tdata0 + 1;
  signal->theData[2] = 0;
  signal->theData[3] = ref;
  signal->theData[4] = data;
  sendSignal(reference(), GSN_CONTINUEB, signal, 5, JBB);
  return;
}//Dbacc::initialiseRecordsLab()

/* *********************************<< */
/* NDB_STTORRY                         */
/* *********************************<< */
void Dbacc::ndbsttorryLab(Signal* signal) 
{
  signal->theData[0] = cownBlockref;
  sendSignal(cndbcntrRef, GSN_NDB_STTORRY, signal, 1, JBB);
  return;
}//Dbacc::ndbsttorryLab()

/* *********************************<< */
/* SIZEALT_REP         SIZE ALTERATION */
/* *********************************<< */
void Dbacc::execREAD_CONFIG_REQ(Signal* signal) 
{
  const ReadConfigReq * req = (ReadConfigReq*)signal->getDataPtr();
  Uint32 ref = req->senderRef;
  Uint32 senderData = req->senderData;
  ndbrequire(req->noOfParameters == 0);
  
  jamEntry();

  const ndb_mgm_configuration_iterator * p = 
    m_ctx.m_config.getOwnConfigIterator();
  ndbrequire(p != 0);
  
  ndbrequire(!ndb_mgm_get_int_parameter(p, CFG_ACC_FRAGMENT, &cfragmentsize));
  ndbrequire(!ndb_mgm_get_int_parameter(p, CFG_ACC_OP_RECS, &coprecsize));
  ndbrequire(!ndb_mgm_get_int_parameter(p, CFG_ACC_PAGE8, &cpagesize));
  ndbrequire(!ndb_mgm_get_int_parameter(p, CFG_ACC_TABLE, &ctablesize));
  ndbrequire(!ndb_mgm_get_int_parameter(p, CFG_ACC_SCAN, &cscanRecSize));
  initRecords();
  ndbrestart1Lab(signal);

  c_memusage_report_frequency = 0;
  ndb_mgm_get_int_parameter(p, CFG_DB_MEMREPORT_FREQUENCY, 
			    &c_memusage_report_frequency);
  
  tdata0 = 0;
  initialiseRecordsLab(signal, ref, senderData);
  return;
}//Dbacc::execSIZEALT_REP()

/* *********************************<< */
/* STTORRY                             */
/* *********************************<< */
void Dbacc::sttorrysignalLab(Signal* signal) 
{
  signal->theData[0] = csignalkey;
  signal->theData[1] = 3;
  /* BLOCK CATEGORY */
  signal->theData[2] = 2;
  /* SIGNAL VERSION NUMBER */
  signal->theData[3] = ZSPH1;
  signal->theData[4] = 255;
  BlockReference cntrRef = !isNdbMtLqh() ? NDBCNTR_REF : DBACC_REF;
  sendSignal(cntrRef, GSN_STTORRY, signal, 5, JBB);
  /* END OF START PHASES */
  return;
}//Dbacc::sttorrysignalLab()

/* --------------------------------------------------------------------------------- */
/* INITIALISE_FRAG_REC                                                               */
/*              INITIALATES THE FRAGMENT RECORDS.                                    */
/* --------------------------------------------------------------------------------- */
void Dbacc::initialiseFragRec(Signal* signal) 
{
  FragmentrecPtr regFragPtr;
  ndbrequire(cfragmentsize > 0);
  for (regFragPtr.i = 0; regFragPtr.i < cfragmentsize; regFragPtr.i++) {
    jam();
    refresh_watch_dog();
    ptrAss(regFragPtr, fragmentrec);
    initFragGeneral(regFragPtr);
    regFragPtr.p->nextfreefrag = regFragPtr.i + 1;
  }//for
  regFragPtr.i = cfragmentsize - 1;
  ptrAss(regFragPtr, fragmentrec);
  regFragPtr.p->nextfreefrag = RNIL;
  cfirstfreefrag = 0;
}//Dbacc::initialiseFragRec()

/* --------------------------------------------------------------------------------- */
/* INITIALISE_OPERATION_REC                                                          */
/*              INITIALATES THE OPERATION RECORDS.                                   */
/* --------------------------------------------------------------------------------- */
void Dbacc::initialiseOperationRec(Signal* signal) 
{
  ndbrequire(coprecsize > 0);
  for (operationRecPtr.i = 0; operationRecPtr.i < coprecsize; operationRecPtr.i++) {
    refresh_watch_dog();
    ptrAss(operationRecPtr, operationrec);
    operationRecPtr.p->m_op_bits = Operationrec::OP_INITIAL;
    operationRecPtr.p->nextOp = operationRecPtr.i + 1;
  }//for
  operationRecPtr.i = coprecsize - 1;
  ptrAss(operationRecPtr, operationrec);
  operationRecPtr.p->nextOp = RNIL;
  cfreeopRec = 0;
}//Dbacc::initialiseOperationRec()

/* --------------------------------------------------------------------------------- */
/* INITIALISE_PAGE_REC                                                               */
/*              INITIALATES THE PAGE RECORDS.                                        */
/* --------------------------------------------------------------------------------- */
void Dbacc::initialisePageRec(Signal* signal) 
{
  ndbrequire(cpagesize > 0);
  cnoOfAllocatedPages = 0;
  cnoOfAllocatedPagesMax = 0;
}//Dbacc::initialisePageRec()


/* --------------------------------------------------------------------------------- */
/* INITIALISE_ROOTFRAG_REC                                                           */
/*              INITIALATES THE ROOTFRAG  RECORDS.                                   */
/* --------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------- */
/* INITIALISE_SCAN_REC                                                               */
/*              INITIALATES THE QUE_SCAN RECORDS.                                    */
/* --------------------------------------------------------------------------------- */
void Dbacc::initialiseScanRec(Signal* signal) 
{
  ndbrequire(cscanRecSize > 0);
  for (scanPtr.i = 0; scanPtr.i < cscanRecSize; scanPtr.i++) {
    ptrAss(scanPtr, scanRec);
    scanPtr.p->scanNextfreerec = scanPtr.i + 1;
    scanPtr.p->scanState = ScanRec::SCAN_DISCONNECT;
  }//for
  scanPtr.i = cscanRecSize - 1;
  ptrAss(scanPtr, scanRec);
  scanPtr.p->scanNextfreerec = RNIL;
  cfirstFreeScanRec = 0;
}//Dbacc::initialiseScanRec()


/* --------------------------------------------------------------------------------- */
/* INITIALISE_TABLE_REC                                                              */
/*              INITIALATES THE TABLE RECORDS.                                       */
/* --------------------------------------------------------------------------------- */
void Dbacc::initialiseTableRec(Signal* signal) 
{
  ndbrequire(ctablesize > 0);
  for (tabptr.i = 0; tabptr.i < ctablesize; tabptr.i++) {
    refresh_watch_dog();
    ptrAss(tabptr, tabrec);
    for (Uint32 i = 0; i < NDB_ARRAY_SIZE(tabptr.p->fragholder); i++) {
      tabptr.p->fragholder[i] = RNIL;
      tabptr.p->fragptrholder[i] = RNIL;
    }//for
  }//for
}//Dbacc::initialiseTableRec()

/* --------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------- */
/*                                                                                   */
/*       END OF SYSTEM RESTART MODULE                                                */
/*                                                                                   */
/* --------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------- */
/*                                                                                   */
/*       ADD/DELETE FRAGMENT MODULE                                                  */
/*                                                                                   */
/* --------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------- */

// JONAS This methods "aer ett saall"
void Dbacc::execACCFRAGREQ(Signal* signal) 
{
  const AccFragReq * const req = (AccFragReq*)&signal->theData[0];
  jamEntry();
  if (ERROR_INSERTED(3001)) {
    jam();
    addFragRefuse(signal, 1);
    CLEAR_ERROR_INSERT_VALUE;
    return;
  }
  tabptr.i = req->tableId;
#ifndef VM_TRACE
  // config mismatch - do not crash if release compiled
  if (tabptr.i >= ctablesize) {
    jam();
    addFragRefuse(signal, 640);
    return;
  }
#endif
  ptrCheckGuard(tabptr, ctablesize, tabrec);
  ndbrequire((req->reqInfo & 0xF) == ZADDFRAG);
  ndbrequire(!getfragmentrec(signal, fragrecptr, req->fragId));
  if (cfirstfreefrag == RNIL) {
    jam();
    addFragRefuse(signal, ZFULL_FRAGRECORD_ERROR);
    return;
  }//if

  seizeFragrec(signal);
  initFragGeneral(fragrecptr);
  initFragAdd(signal, fragrecptr);

  if (!addfragtotab(signal, fragrecptr.i, req->fragId)) {
    jam();
    releaseFragRecord(signal, fragrecptr);
    addFragRefuse(signal, ZFULL_FRAGRECORD_ERROR);
    return;
  }//if
  seizePage(signal);
  if (tresult > ZLIMIT_OF_ERROR) {
    jam();
    addFragRefuse(signal, tresult);
    return;
  }//if
  if (!setPagePtr(fragrecptr.p->directory, 0, spPageptr.i))
  {
    jam();
    addFragRefuse(signal, ZDIR_RANGE_FULL_ERROR);
    return;
  }

  tipPageId = 0;
  inpPageptr = spPageptr;
  initPage(signal);

  Uint32 userPtr = req->userPtr;
  BlockReference retRef = req->userRef;
  fragrecptr.p->rootState = ACTIVEROOT;

  AccFragConf * const conf = (AccFragConf*)&signal->theData[0];
  conf->userPtr = userPtr;
  conf->rootFragPtr = RNIL;
  conf->fragId[0] = fragrecptr.p->fragmentid;
  conf->fragId[1] = RNIL;
  conf->fragPtr[0] = fragrecptr.i;
  conf->fragPtr[1] = RNIL;
  conf->rootHashCheck = fragrecptr.p->roothashcheck;
  sendSignal(retRef, GSN_ACCFRAGCONF, signal, AccFragConf::SignalLength, JBB);
}//Dbacc::execACCFRAGREQ()

void Dbacc::addFragRefuse(Signal* signal, Uint32 errorCode) 
{
  const AccFragReq * const req = (AccFragReq*)&signal->theData[0];  
  AccFragRef * const ref = (AccFragRef*)&signal->theData[0];  
  Uint32 userPtr = req->userPtr;
  BlockReference retRef = req->userRef;

  ref->userPtr = userPtr;
  ref->errorCode = errorCode;
  sendSignal(retRef, GSN_ACCFRAGREF, signal, AccFragRef::SignalLength, JBB);
  return;
}//Dbacc::addFragRefuseEarly()

void
Dbacc::execDROP_TAB_REQ(Signal* signal){
  jamEntry();
  DropTabReq* req = (DropTabReq*)signal->getDataPtr();

  TabrecPtr tabPtr;
  tabPtr.i = req->tableId;
  ptrCheckGuard(tabPtr, ctablesize, tabrec);
  
  tabPtr.p->tabUserRef = req->senderRef;
  tabPtr.p->tabUserPtr = req->senderData;
  tabPtr.p->tabUserGsn = GSN_DROP_TAB_REQ;

  signal->theData[0] = ZREL_ROOT_FRAG;
  signal->theData[1] = tabPtr.i;
  sendSignal(cownBlockref, GSN_CONTINUEB, signal, 2, JBB);
}

void
Dbacc::execDROP_FRAG_REQ(Signal* signal){
  jamEntry();
  DropFragReq* req = (DropFragReq*)signal->getDataPtr();

  TabrecPtr tabPtr;
  tabPtr.i = req->tableId;
  ptrCheckGuard(tabPtr, ctablesize, tabrec);

  tabPtr.p->tabUserRef = req->senderRef;
  tabPtr.p->tabUserPtr = req->senderData;
  tabPtr.p->tabUserGsn = GSN_DROP_FRAG_REQ;

  for (Uint32 i = 0; i < NDB_ARRAY_SIZE(tabPtr.p->fragholder); i++)
  {
    jam();
    if (tabPtr.p->fragholder[i] == req->fragId)
    {
      jam();
      tabPtr.p->fragholder[i] = RNIL;
      releaseFragResources(signal, tabPtr.p->fragptrholder[i]);
      return;
    }//if
  }//for
  
  releaseRootFragResources(signal, req->tableId);
}

void Dbacc::releaseRootFragResources(Signal* signal, Uint32 tableId)
{
  TabrecPtr tabPtr;
  tabPtr.i = tableId;
  ptrCheckGuard(tabPtr, ctablesize, tabrec);

  if (tabPtr.p->tabUserGsn == GSN_DROP_TAB_REQ)
  {
    jam();
    for (Uint32 i = 0; i < NDB_ARRAY_SIZE(tabPtr.p->fragholder); i++)
    {
      jam();
      if (tabPtr.p->fragholder[i] != RNIL)
      {
        jam();
        tabPtr.p->fragholder[i] = RNIL;
        releaseFragResources(signal, tabPtr.p->fragptrholder[i]);
        return;
      }
    }

    /**
     * Finished...
     */
    DropTabConf * const dropConf = (DropTabConf *)signal->getDataPtrSend();
    dropConf->senderRef = reference();
    dropConf->senderData = tabPtr.p->tabUserPtr;
    dropConf->tableId = tabPtr.i;
    sendSignal(tabPtr.p->tabUserRef, GSN_DROP_TAB_CONF,
               signal, DropTabConf::SignalLength, JBB);
  }
  else
  {
    ndbrequire(tabPtr.p->tabUserGsn == GSN_DROP_FRAG_REQ);

    DropFragConf * conf = (DropFragConf *)signal->getDataPtrSend();
    conf->senderRef = reference();
    conf->senderData = tabPtr.p->tabUserPtr;
    conf->tableId = tabPtr.i;
    sendSignal(tabPtr.p->tabUserRef, GSN_DROP_FRAG_CONF,
               signal, DropFragConf::SignalLength, JBB);
  }
  
  tabPtr.p->tabUserPtr = RNIL;
  tabPtr.p->tabUserRef = 0;
  tabPtr.p->tabUserGsn = 0;
}//Dbacc::releaseRootFragResources()

void Dbacc::releaseFragResources(Signal* signal, Uint32 fragIndex)
{
  jam();
  ndbassert(g_acc_pages_used[instance()] == cnoOfAllocatedPages);
  FragmentrecPtr regFragPtr;
  regFragPtr.i = fragIndex;
  ptrCheckGuard(regFragPtr, cfragmentsize, fragmentrec);
  verifyFragCorrect(regFragPtr);
  if (!regFragPtr.p->directory.isEmpty()) {
    jam();
    DynArr256::ReleaseIterator iter;
    DynArr256 dir(directoryPool, regFragPtr.p->directory);
    dir.init(iter);
    signal->theData[0] = ZREL_DIR;
    signal->theData[1] = regFragPtr.i;
    memcpy(&signal->theData[2], &iter, sizeof(iter));
    sendSignal(cownBlockref, GSN_CONTINUEB, signal, 2 + sizeof(iter) / 4, JBB);
  } else {
    jam();
    {
      LocalPage8List freelist(*this, cfreepages);
      cnoOfAllocatedPages -= regFragPtr.p->sparsepages.getCount();
      freelist.appendList(regFragPtr.p->sparsepages);
      cnoOfAllocatedPages -= regFragPtr.p->fullpages.getCount();
      freelist.appendList(regFragPtr.p->fullpages);
      ndbassert(freelist.count() + cnoOfAllocatedPages == cpageCount);
      g_acc_pages_used[instance()] = cnoOfAllocatedPages;
      if (cnoOfAllocatedPages < m_maxAllocPages)
        m_oom = false;
    }
    jam();
    Uint32 tab = regFragPtr.p->mytabptr;
    releaseFragRecord(signal, regFragPtr);
    signal->theData[0] = ZREL_ROOT_FRAG;
    signal->theData[1] = tab;
    sendSignal(cownBlockref, GSN_CONTINUEB, signal, 2, JBB);
  }//if
}//Dbacc::releaseFragResources()

void Dbacc::verifyFragCorrect(FragmentrecPtr regFragPtr)
{
  ndbrequire(regFragPtr.p->lockOwnersList == RNIL);
}//Dbacc::verifyFragCorrect()

void Dbacc::releaseDirResources(Signal* signal)
{
  jam();
  Uint32 fragIndex = signal->theData[1];

  DynArr256::ReleaseIterator iter;
  memcpy(&iter, &signal->theData[2], sizeof(iter));

  FragmentrecPtr regFragPtr;
  regFragPtr.i = fragIndex;
  ptrCheckGuard(regFragPtr, cfragmentsize, fragmentrec);
  verifyFragCorrect(regFragPtr);

  DynArr256::Head* directory;
  ndbrequire(signal->theData[0] == ZREL_DIR);
  directory = &regFragPtr.p->directory;

  DynArr256 dir(directoryPool, *directory);
  Uint32 ret;
  Uint32 pagei;
  /* TODO: find a good value for count
   * bigger value means quicker release of big index,
   * but longer time slice so less concurrent
   */
  int count = 10;
  while (count > 0 &&
         (ret = dir.release(iter, &pagei)) != 0)
  {
    jam();
    count--;
    if (ret == 1 && pagei != RNIL)
    {
      jam();
      rpPageptr.i = pagei;
      ptrCheckGuard(rpPageptr, cpagesize, page8);
      releasePage(signal);
    }
  }
  if (ret != 0)
  {
    jam();
    memcpy(&signal->theData[2], &iter, sizeof(iter));
    sendSignal(cownBlockref, GSN_CONTINUEB, signal, 2 + sizeof(iter) / 4, JBB);
  }
  else
  {
    jam();
    signal->theData[0] = ZREL_FRAG;
    sendSignal(cownBlockref, GSN_CONTINUEB, signal, 2, JBB);
  }
}//Dbacc::releaseDirResources()

void Dbacc::releaseFragRecord(Signal* signal, FragmentrecPtr regFragPtr) 
{
  regFragPtr.p->nextfreefrag = cfirstfreefrag;
  cfirstfreefrag = regFragPtr.i;
  initFragGeneral(regFragPtr);
  RSS_OP_FREE(cnoOfFreeFragrec);
}//Dbacc::releaseFragRecord()

/* -------------------------------------------------------------------------- */
/* ADDFRAGTOTAB                                                               */
/*       DESCRIPTION: PUTS A FRAGMENT ID AND A POINTER TO ITS RECORD INTO     */
/*                                TABLE ARRRAY OF THE TABLE RECORD.           */
/* -------------------------------------------------------------------------- */
bool Dbacc::addfragtotab(Signal* signal, Uint32 rootIndex, Uint32 fid) 
{
  for (Uint32 i = 0; i < NDB_ARRAY_SIZE(tabptr.p->fragholder); i++) {
    jam();
    if (tabptr.p->fragholder[i] == RNIL) {
      jam();
      tabptr.p->fragholder[i] = fid;
      tabptr.p->fragptrholder[i] = rootIndex;
      return true;
    }//if
  }//for
  return false;
}//Dbacc::addfragtotab()

/* --------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------- */
/*                                                                                   */
/*       END OF ADD/DELETE FRAGMENT MODULE                                           */
/*                                                                                   */
/* --------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------- */
/*                                                                                   */
/*       CONNECTION MODULE                                                           */
/*                                                                                   */
/* --------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------- */
/* ******************--------------------------------------------------------------- */
/* ACCSEIZEREQ                                           SEIZE REQ                   */
/*                                                    SENDER: LQH,    LEVEL B        */
/*          ENTER ACCSEIZEREQ WITH                                                   */
/*                    TUSERPTR ,                     CONECTION PTR OF LQH            */
/*                    TUSERBLOCKREF                  BLOCK REFERENCE OF LQH          */
/* ******************--------------------------------------------------------------- */
/* ******************--------------------------------------------------------------- */
/* ACCSEIZEREQ                                           SEIZE REQ                   */
/* ******************------------------------------+                                 */
/*   SENDER: LQH,    LEVEL B       */
void Dbacc::execACCSEIZEREQ(Signal* signal) 
{
  jamEntry();
  tuserptr = signal->theData[0];
  /* CONECTION PTR OF LQH            */
  tuserblockref = signal->theData[1];
  /* BLOCK REFERENCE OF LQH          */
  tresult = 0;
  if (cfreeopRec == RNIL) {
    jam();
    refaccConnectLab(signal);
    return;
  }//if
  seizeOpRec(signal);
  ptrGuard(operationRecPtr);
  operationRecPtr.p->userptr = tuserptr;
  operationRecPtr.p->userblockref = tuserblockref;
  operationRecPtr.p->m_op_bits = Operationrec::OP_INITIAL;
  /* ******************************< */
  /* ACCSEIZECONF                    */
  /* ******************************< */
  signal->theData[0] = tuserptr;
  signal->theData[1] = operationRecPtr.i;
  sendSignal(tuserblockref, GSN_ACCSEIZECONF, signal, 2, JBB);
  return;
}//Dbacc::execACCSEIZEREQ()

void Dbacc::refaccConnectLab(Signal* signal) 
{
  tresult = ZCONNECT_SIZE_ERROR;
  /* ******************************< */
  /* ACCSEIZEREF                     */
  /* ******************************< */
  signal->theData[0] = tuserptr;
  signal->theData[1] = tresult;
  sendSignal(tuserblockref, GSN_ACCSEIZEREF, signal, 2, JBB);
  return;
}//Dbacc::refaccConnectLab()

/* --------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------- */
/*                                                                                   */
/*       END OF CONNECTION MODULE                                                    */
/*                                                                                   */
/* --------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------- */
/*                                                                                   */
/*       EXECUTE OPERATION MODULE                                                    */
/*                                                                                   */
/* --------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------- */
/* INIT_OP_REC                                                                       */
/*           INFORMATION WHICH IS RECIEVED BY ACCKEYREQ WILL BE SAVED                */
/*           IN THE OPERATION RECORD.                                                */
/* --------------------------------------------------------------------------------- */
void Dbacc::initOpRec(Signal* signal) 
{
  register Uint32 Treqinfo;

  Treqinfo = signal->theData[2];

  operationRecPtr.p->hashValue = LHBits32(signal->theData[3]);
  operationRecPtr.p->tupkeylen = signal->theData[4];
  operationRecPtr.p->xfrmtupkeylen = signal->theData[4];
  operationRecPtr.p->transId1 = signal->theData[5];
  operationRecPtr.p->transId2 = signal->theData[6];

  Uint32 readFlag = (((Treqinfo >> 4) & 0x3) == 0);      // Only 1 if Read
  Uint32 dirtyFlag = (((Treqinfo >> 6) & 0x1) == 1);     // Only 1 if Dirty
  Uint32 dirtyReadFlag = readFlag & dirtyFlag;
  Uint32 operation = Treqinfo & 0xf;
  if (operation == ZREFRESH)
    operation = ZWRITE; /* Insert if !exist, otherwise lock */

  Uint32 opbits = 0;
  opbits |= operation;
  opbits |= ((Treqinfo >> 4) & 0x3) ? (Uint32) Operationrec::OP_LOCK_MODE : 0;
  opbits |= ((Treqinfo >> 4) & 0x3) ? (Uint32) Operationrec::OP_ACC_LOCK_MODE : 0;
  opbits |= (dirtyReadFlag) ? (Uint32) Operationrec::OP_DIRTY_READ : 0;
  if ((Treqinfo >> 31) & 0x1)
  {
    opbits |= Operationrec::OP_LOCK_REQ;            // TUX LOCK_REQ

    /**
     * A lock req has SCAN_OP, it can't delete a row,
     *   so OP_COMMIT_DELETE_CHECK is set like for SCAN
     *   see initScanOpRec
     */
    opbits |= Operationrec::OP_COMMIT_DELETE_CHECK;

    /**
     * TODO: Looking at it now, I think it would be more natural
     *       to treat it as a ZREAD...
     */
  }
  
  //operationRecPtr.p->nodeType = (Treqinfo >> 7) & 0x3;
  operationRecPtr.p->fid = fragrecptr.p->myfid;
  operationRecPtr.p->fragptr = fragrecptr.i;
  operationRecPtr.p->nextParallelQue = RNIL;
  operationRecPtr.p->prevParallelQue = RNIL;
  operationRecPtr.p->nextSerialQue = RNIL;
  operationRecPtr.p->prevSerialQue = RNIL;
  operationRecPtr.p->elementPage = RNIL;
  operationRecPtr.p->scanRecPtr = RNIL;
  operationRecPtr.p->m_op_bits = opbits;

  // bit to mark lock operation
  // undo log is not run via ACCKEYREQ

  if (operationRecPtr.p->tupkeylen == 0)
  {
    ndbassert(signal->getLength() == 9);
  }
}//Dbacc::initOpRec()

/* --------------------------------------------------------------------------------- */
/* SEND_ACCKEYCONF                                                                   */
/* --------------------------------------------------------------------------------- */
void Dbacc::sendAcckeyconf(Signal* signal) 
{
  signal->theData[0] = operationRecPtr.p->userptr;
  signal->theData[1] = operationRecPtr.p->m_op_bits & Operationrec::OP_MASK;
  signal->theData[2] = operationRecPtr.p->fid;
  signal->theData[3] = operationRecPtr.p->localdata[0];
  signal->theData[4] = operationRecPtr.p->localdata[1];
}//Dbacc::sendAcckeyconf()

void 
Dbacc::ACCKEY_error(Uint32 fromWhere)
{
  switch(fromWhere) {
  case 0:
    ndbrequire(false);
  case 1:
    ndbrequire(false);
  case 2:
    ndbrequire(false);
  case 3:
    ndbrequire(false);
  case 4:
    ndbrequire(false);
  case 5:
    ndbrequire(false);
  case 6:
    ndbrequire(false);
  case 7:
    ndbrequire(false);
  case 8:
    ndbrequire(false);
  case 9:
    ndbrequire(false);
  default:
    ndbrequire(false);
  }//switch
}//Dbacc::ACCKEY_error()

/* ******************--------------------------------------------------------------- */
/* ACCKEYREQ                                         REQUEST FOR INSERT, DELETE,     */
/*                                                   RERAD AND UPDATE, A TUPLE.      */
/*                                                   SENDER: LQH,    LEVEL B         */
/*  SIGNAL DATA:      OPERATION_REC_PTR,             CONNECTION PTR                  */
/*                    TABPTR,                        TABLE ID = TABLE RECORD POINTER */
/*                    TREQINFO,                                                      */
/*                    THASHVALUE,                    HASH VALUE OF THE TUP           */
/*                    TKEYLEN,                       LENGTH OF THE PRIMARY KEYS      */
/*                    TKEY1,                         PRIMARY KEY 1                   */
/*                    TKEY2,                         PRIMARY KEY 2                   */
/*                    TKEY3,                         PRIMARY KEY 3                   */
/*                    TKEY4,                         PRIMARY KEY 4                   */
/* ******************--------------------------------------------------------------- */
void Dbacc::execACCKEYREQ(Signal* signal) 
{
  jamEntry();
  operationRecPtr.i = signal->theData[0];   /* CONNECTION PTR */
  fragrecptr.i = signal->theData[1];        /* FRAGMENT RECORD POINTER         */
  if (!((operationRecPtr.i < coprecsize) ||
	(fragrecptr.i < cfragmentsize))) {
    ACCKEY_error(0);
    return;
  }//if
  ptrAss(operationRecPtr, operationrec);
  ptrAss(fragrecptr, fragmentrec);  

  ndbrequire(operationRecPtr.p->m_op_bits == Operationrec::OP_INITIAL);

  initOpRec(signal);
  // normalize key if any char attr
  if (operationRecPtr.p->tupkeylen && fragrecptr.p->hasCharAttr)
    xfrmKeyData(signal);

  /*---------------------------------------------------------------*/
  /*                                                               */
  /*       WE WILL USE THE HASH VALUE TO LOOK UP THE PROPER MEMORY */
  /*       PAGE AND MEMORY PAGE INDEX TO START THE SEARCH WITHIN.  */
  /*       WE REMEMBER THESE ADDRESS IF WE LATER NEED TO INSERT    */
  /*       THE ITEM AFTER NOT FINDING THE ITEM.                    */
  /*---------------------------------------------------------------*/
  OperationrecPtr lockOwnerPtr;
  const Uint32 found = getElement(signal, lockOwnerPtr);

  Uint32 opbits = operationRecPtr.p->m_op_bits;
  Uint32 op = opbits & Operationrec::OP_MASK;
  if (found == ZTRUE) 
  {
    switch (op) {
    case ZREAD:
    case ZUPDATE:
    case ZDELETE:
    case ZWRITE:
    case ZSCAN_OP:
      if (!lockOwnerPtr.p)
      {
	if(op == ZWRITE)
	{
	  jam();
	  opbits &= ~(Uint32)Operationrec::OP_MASK;
	  opbits |= (op = ZUPDATE);
	  operationRecPtr.p->m_op_bits = opbits; // store to get correct ACCKEYCONF
	}
	opbits |= Operationrec::OP_STATE_RUNNING;
	opbits |= Operationrec::OP_RUN_QUEUE;
        sendAcckeyconf(signal);
        if (! (opbits & Operationrec::OP_DIRTY_READ)) {
	  /*---------------------------------------------------------------*/
	  // It is not a dirty read. We proceed by locking and continue with
	  // the operation.
	  /*---------------------------------------------------------------*/
          Uint32 eh = gePageptr.p->word32[tgeElementptr];
          operationRecPtr.p->scanBits = ElementHeader::getScanBits(eh);
          operationRecPtr.p->reducedHashValue = ElementHeader::getReducedHashValue(eh);
          operationRecPtr.p->elementPage = gePageptr.i;
          operationRecPtr.p->elementContainer = tgeContainerptr;
          operationRecPtr.p->elementPointer = tgeElementptr;
          operationRecPtr.p->elementIsforward = tgeForward;

	  eh = ElementHeader::setLocked(operationRecPtr.i);
          dbgWord32(gePageptr, tgeElementptr, eh);
          gePageptr.p->word32[tgeElementptr] = eh;
	  
	  opbits |= Operationrec::OP_LOCK_OWNER;
	  insertLockOwnersList(signal, operationRecPtr);
        } else {
          jam();
	  /*---------------------------------------------------------------*/
	  // It is a dirty read. We do not lock anything. Set state to
	  // IDLE since no COMMIT call will come.
	  /*---------------------------------------------------------------*/
	  opbits = Operationrec::OP_EXECUTED_DIRTY_READ;
        }//if
	operationRecPtr.p->m_op_bits = opbits;
	return;
      } else {
        jam();
        accIsLockedLab(signal, lockOwnerPtr);
        return;
      }//if
      break;
    case ZINSERT:
      jam();
      insertExistElemLab(signal, lockOwnerPtr);
      return;
      break;
    default:
      ndbrequire(false);
      break;
    }//switch
  } else if (found == ZFALSE) {
    switch (op){
    case ZWRITE:
      opbits &= ~(Uint32)Operationrec::OP_MASK;
      opbits |= (op = ZINSERT);
    case ZINSERT:
      jam();
      opbits |= Operationrec::OP_INSERT_IS_DONE;
      opbits |= Operationrec::OP_STATE_RUNNING;
      opbits |= Operationrec::OP_RUN_QUEUE;
      operationRecPtr.p->m_op_bits = opbits;
      insertelementLab(signal);
      return;
      break;
    case ZREAD:
    case ZUPDATE:
    case ZDELETE:
    case ZSCAN_OP:
      jam();
      acckeyref1Lab(signal, ZREAD_ERROR);
      return;
      break;
    default:
      ndbrequire(false);
      break;
    }//switch
  } else {
    jam();
    acckeyref1Lab(signal, found);
    return;
  }//if
  return;
}//Dbacc::execACCKEYREQ()

void
Dbacc::execACCKEY_ORD(Signal* signal, Uint32 opPtrI)
{
  jamEntry();
  OperationrecPtr lastOp;
  lastOp.i = opPtrI;
  ptrCheckGuard(lastOp, coprecsize, operationrec);
  Uint32 opbits = lastOp.p->m_op_bits;
  Uint32 opstate = opbits & Operationrec::OP_STATE_MASK;
  
  if (likely(opbits == Operationrec::OP_EXECUTED_DIRTY_READ))
  {
    jam();
    lastOp.p->m_op_bits = Operationrec::OP_INITIAL;
    return;
  }
  else if (likely(opstate == Operationrec::OP_STATE_RUNNING))
  {
    opbits |= Operationrec::OP_STATE_EXECUTED;
    lastOp.p->m_op_bits = opbits;
    startNext(signal, lastOp);
    return;
  } 
  else
  {
  }

  ndbout_c("bits: %.8x state: %.8x", opbits, opstate);
  ndbrequire(false);
}

void
Dbacc::startNext(Signal* signal, OperationrecPtr lastOp) 
{
  jam();
  OperationrecPtr nextOp;
  OperationrecPtr loPtr;
  nextOp.i = lastOp.p->nextParallelQue;
  loPtr.i = lastOp.p->m_lock_owner_ptr_i;
  Uint32 opbits = lastOp.p->m_op_bits;
  
  if ((opbits & Operationrec::OP_STATE_MASK)!= Operationrec::OP_STATE_EXECUTED)
  {
    jam();
    return;
  }
  
  Uint32 nextbits;
  if (nextOp.i != RNIL)
  {
    jam();
    ptrCheckGuard(nextOp, coprecsize, operationrec);
    nextbits = nextOp.p->m_op_bits;
    goto checkop;
  }
  
  if ((opbits & Operationrec::OP_LOCK_OWNER) == 0)
  {
    jam();
    ptrCheckGuard(loPtr, coprecsize, operationrec);
  }
  else
  {
    jam();
    loPtr = lastOp;
  }
  
  nextOp.i = loPtr.p->nextSerialQue;
  ndbassert(loPtr.p->m_op_bits & Operationrec::OP_LOCK_OWNER);
  
  if (nextOp.i == RNIL)
  {
    jam();
    return;
  }
  
  /**
   * There is an op in serie queue...
   *   Check if it can run
   */
  ptrCheckGuard(nextOp, coprecsize, operationrec);  
  nextbits = nextOp.p->m_op_bits;
  
  {
    const bool same = nextOp.p->is_same_trans(lastOp.p);
    
    if (!same && ((opbits & Operationrec::OP_ACC_LOCK_MODE) ||
		  (nextbits & Operationrec::OP_LOCK_MODE)))
    {
      jam();
      /**
       * Not same transaction
       *  and either last had exclusive lock
       *          or next had exclusive lock
       */
      return;
    }
    
    /**
     * same trans and X-lock
     */
    if (same && (opbits & Operationrec::OP_ACC_LOCK_MODE))
    {
      jam();
      goto upgrade;
    }
  }

  /**
   * all shared lock...
   */
  if ((opbits & Operationrec::OP_ACC_LOCK_MODE) == 0 &&
      (nextbits & Operationrec::OP_LOCK_MODE) == 0)
  {
    jam();
    goto upgrade;
  }
  
  /**
   * There is a shared parallell queue & and exclusive op is first in queue
   */
  ndbassert((opbits & Operationrec::OP_ACC_LOCK_MODE) == 0 &&
	    (nextbits & Operationrec::OP_LOCK_MODE));
  
  /**
   * We must check if there are many transactions in parallel queue...
   */
  OperationrecPtr tmp;
  tmp= loPtr;
  while (tmp.i != RNIL)
  {
    ptrCheckGuard(tmp, coprecsize, operationrec);      
    if (!nextOp.p->is_same_trans(tmp.p))
    {
      jam();
      /**
       * parallel queue contained another transaction, dont let it run
       */
      return;
    }
    tmp.i = tmp.p->nextParallelQue;
  }
  
upgrade:
  /**
   * Move first op in serie queue to end of parallell queue
   */
  
  tmp.i = loPtr.p->nextSerialQue = nextOp.p->nextSerialQue;
  loPtr.p->m_lo_last_parallel_op_ptr_i = nextOp.i;
  nextOp.p->nextSerialQue = RNIL;
  nextOp.p->prevSerialQue = RNIL;
  nextOp.p->m_lock_owner_ptr_i = loPtr.i;
  nextOp.p->prevParallelQue = lastOp.i;
  lastOp.p->nextParallelQue = nextOp.i;
  
  if (tmp.i != RNIL)
  {
    jam();
    ptrCheckGuard(tmp, coprecsize, operationrec);      
    tmp.p->prevSerialQue = loPtr.i;
  }
  else
  {
    jam();
    loPtr.p->m_lo_last_serial_op_ptr_i = RNIL;
  }
  
  nextbits |= Operationrec::OP_RUN_QUEUE;
  
  /**
   * Currently no grouping of ops in serie queue
   */
  ndbrequire(nextOp.p->nextParallelQue == RNIL);
  
checkop:
  Uint32 errCode = 0;
  OperationrecPtr save = operationRecPtr;
  operationRecPtr = nextOp;
  
  Uint32 lastop = opbits & Operationrec::OP_MASK;
  Uint32 nextop = nextbits & Operationrec::OP_MASK;

  nextbits &= nextbits & ~(Uint32)Operationrec::OP_STATE_MASK;
  nextbits |= Operationrec::OP_STATE_RUNNING;

  /*
   * bug#19031389
   * Consider transactions such as read-0,read-1,read-2,delete-3.
   * Read-N commits come from TC while delete-3 commit comes from
   * backup replica.  In MT kernel delete-3 commit can come first.
   * Then at read-0 commit there is no ZDELETE left.  But all
   * ops in parallel queue have been marked OP_ELEMENT_DISAPPEARED.
   * So also check for that bit.
   */
  if (lastop == ZDELETE ||
      (lastOp.p->m_op_bits & Operationrec::OP_ELEMENT_DISAPPEARED))
  {
    jam();
    if (nextop != ZINSERT && nextop != ZWRITE)
    {
      errCode = ZREAD_ERROR;
      goto ref;
    }
    
    nextbits &= ~(Uint32)Operationrec::OP_MASK;
    nextbits &= ~(Uint32)Operationrec::OP_ELEMENT_DISAPPEARED;
    nextbits |= (nextop = ZINSERT);
    goto conf;
  }
  else if (nextop == ZINSERT)
  {
    jam();
    errCode = ZWRITE_ERROR;
    goto ref;
  }
  else if (nextop == ZWRITE)
  {
    jam();
    nextbits &= ~(Uint32)Operationrec::OP_MASK;
    nextbits |= (nextop = ZUPDATE);
    goto conf;
  }
  else
  {
    jam();
  }

conf:
  nextOp.p->m_op_bits = nextbits;
  nextOp.p->localdata[0] = lastOp.p->localdata[0];
  nextOp.p->localdata[1] = lastOp.p->localdata[1];
  
  if (nextop == ZSCAN_OP && (nextbits & Operationrec::OP_LOCK_REQ) == 0)
  {
    jam();
    takeOutScanLockQueue(nextOp.p->scanRecPtr);
    putReadyScanQueue(signal, nextOp.p->scanRecPtr);
  }
  else
  {
    jam();
    fragrecptr.i = nextOp.p->fragptr;
    ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
    
    sendAcckeyconf(signal);
    sendSignal(nextOp.p->userblockref, GSN_ACCKEYCONF, 
	       signal, 6, JBB);
  }
  
  operationRecPtr = save;
  return;
  
ref:
  nextOp.p->m_op_bits = nextbits;
  
  if (nextop == ZSCAN_OP && (nextbits & Operationrec::OP_LOCK_REQ) == 0)
  {
    jam();
    nextOp.p->m_op_bits |= Operationrec::OP_ELEMENT_DISAPPEARED;
    takeOutScanLockQueue(nextOp.p->scanRecPtr);
    putReadyScanQueue(signal, nextOp.p->scanRecPtr);
  }
  else
  {
    jam();
    signal->theData[0] = nextOp.p->userptr;
    signal->theData[1] = errCode;
    sendSignal(nextOp.p->userblockref, GSN_ACCKEYREF, signal, 
	       2, JBB);
  }    
  
  operationRecPtr = save;
  return;
}


#if 0
void
Dbacc::execACCKEY_REP_REF(Signal* signal, Uint32 opPtrI)
{
}
#endif

void
Dbacc::xfrmKeyData(Signal* signal)
{
  Uint32 table = fragrecptr.p->myTableId;
  Uint32 dst[MAX_KEY_SIZE_IN_WORDS * MAX_XFRM_MULTIPLY];
  Uint32 keyPartLen[MAX_ATTRIBUTES_IN_INDEX];
  Uint32* src = &signal->theData[7];
  Uint32 len = xfrm_key(table, src, dst, sizeof(dst) >> 2, keyPartLen);
  ndbrequire(len); // 0 means error
  memcpy(src, dst, len << 2);
  operationRecPtr.p->xfrmtupkeylen = len;
}

void 
Dbacc::accIsLockedLab(Signal* signal, OperationrecPtr lockOwnerPtr) 
{
  Uint32 bits = operationRecPtr.p->m_op_bits;
  validate_lock_queue(lockOwnerPtr);
  
  if ((bits & Operationrec::OP_DIRTY_READ) == 0){
    Uint32 return_result;
    if ((bits & Operationrec::OP_LOCK_MODE) == ZREADLOCK) {
      jam();
      return_result = placeReadInLockQueue(lockOwnerPtr);
    } else {
      jam();
      return_result = placeWriteInLockQueue(lockOwnerPtr);
    }//if
    if (return_result == ZPARALLEL_QUEUE) {
      jam();
      sendAcckeyconf(signal);
      return;
    } else if (return_result == ZSERIAL_QUEUE) {
      jam();
      signal->theData[0] = RNIL;
      return;
    } else {
      jam();
      acckeyref1Lab(signal, return_result);
      return;
    }//if
    ndbrequire(false);
  } 
  else 
  {
    if (! (lockOwnerPtr.p->m_op_bits & Operationrec::OP_ELEMENT_DISAPPEARED) &&
	! Local_key::isInvalid(lockOwnerPtr.p->localdata[0],
                               lockOwnerPtr.p->localdata[1]))
    {
      jam();
      /* ---------------------------------------------------------------
       * It is a dirty read. We do not lock anything. Set state to
       *IDLE since no COMMIT call will arrive.
       * ---------------------------------------------------------------*/
      sendAcckeyconf(signal);
      operationRecPtr.p->m_op_bits = Operationrec::OP_EXECUTED_DIRTY_READ;
      return;
    } 
    else 
    {
      jam();
      /*---------------------------------------------------------------*/
      // The tuple does not exist in the committed world currently.
      // Report read error.
      /*---------------------------------------------------------------*/
      acckeyref1Lab(signal, ZREAD_ERROR);
      return;
    }//if
  }//if
}//Dbacc::accIsLockedLab()

/* ------------------------------------------------------------------------ */
/*        I N S E R T      E X I S T      E L E M E N T                     */
/* ------------------------------------------------------------------------ */
void Dbacc::insertExistElemLab(Signal* signal, OperationrecPtr lockOwnerPtr) 
{
  if (!lockOwnerPtr.p)
  {
    jam();
    acckeyref1Lab(signal, ZWRITE_ERROR);/* THE ELEMENT ALREADY EXIST */
    return;
  }//if
  accIsLockedLab(signal, lockOwnerPtr);
}//Dbacc::insertExistElemLab()

/* --------------------------------------------------------------------------------- */
/* INSERTELEMENT                                                                     */
/* --------------------------------------------------------------------------------- */
void Dbacc::insertelementLab(Signal* signal)
{
  if (unlikely(m_oom))
  {
    jam();
    acckeyref1Lab(signal, ZPAGESIZE_ERROR);
    return;
  }
  if (unlikely(fragrecptr.p->dirRangeFull))
  {
    jam();
    acckeyref1Lab(signal, ZDIR_RANGE_FULL_ERROR);
    return;
  }
  if (fragrecptr.p->sparsepages.isEmpty())
  {
    jam();
    allocOverflowPage(signal);
    if (tresult > ZLIMIT_OF_ERROR) {
      jam();
      acckeyref1Lab(signal, tresult);
      return;
    }//if
  }//if
  ndbrequire(operationRecPtr.p->tupkeylen <= fragrecptr.p->keyLength);
  ndbassert(!(operationRecPtr.p->m_op_bits & Operationrec::OP_LOCK_REQ));
  Uint32 localKey = ~(Uint32)0;
  
  insertLockOwnersList(signal, operationRecPtr);

  operationRecPtr.p->scanBits = 0;	/* NOT ANY ACTIVE SCAN */
  operationRecPtr.p->reducedHashValue = fragrecptr.p->level.reduce(operationRecPtr.p->hashValue);
  tidrElemhead = ElementHeader::setLocked(operationRecPtr.i);
  idrPageptr = gdiPageptr;
  tidrPageindex = tgdiPageindex;
  tidrForward = ZTRUE;
  idrOperationRecPtr = operationRecPtr;
  clocalkey[0] = localKey;
  clocalkey[1] = localKey;
  operationRecPtr.p->localdata[0] = localKey;
  operationRecPtr.p->localdata[1] = localKey;
  /* ----------------------------------------------------------------------- */
  /* WE SET THE LOCAL KEY TO MINUS ONE TO INDICATE IT IS NOT YET VALID.      */
  /* ----------------------------------------------------------------------- */
  insertElement(signal);
  sendAcckeyconf(signal);
  return;
}//Dbacc::insertelementLab()


/* ------------------------------------------------------------------------ */
/* GET_NO_PARALLEL_TRANSACTION                                              */
/* ------------------------------------------------------------------------ */
Uint32
Dbacc::getNoParallelTransaction(const Operationrec * op) 
{
  OperationrecPtr tmp;
  
  tmp.i= op->nextParallelQue;
  Uint32 transId[2] = { op->transId1, op->transId2 };
  while (tmp.i != RNIL) 
  {
    jam();
    ptrCheckGuard(tmp, coprecsize, operationrec);
    if (tmp.p->transId1 == transId[0] && tmp.p->transId2 == transId[1])
      tmp.i = tmp.p->nextParallelQue;
    else
      return 2;
  }
  return 1;
}//Dbacc::getNoParallelTransaction()

#ifdef VM_TRACE
Uint32
Dbacc::getNoParallelTransactionFull(const Operationrec * op) 
{
  ConstPtr<Operationrec> tmp;
  
  tmp.p = op;
  while ((tmp.p->m_op_bits & Operationrec::OP_LOCK_OWNER) == 0)
  {
    tmp.i = tmp.p->prevParallelQue;
    if (tmp.i != RNIL)
    {
      ptrCheckGuard(tmp, coprecsize, operationrec);
    }
    else
    {
      break;
    }
  }    
  
  return getNoParallelTransaction(tmp.p);
}
#endif

#ifdef ACC_SAFE_QUEUE

Uint32
Dbacc::get_parallel_head(OperationrecPtr opPtr) 
{
  while ((opPtr.p->m_op_bits & Operationrec::OP_LOCK_OWNER) == 0 &&
	 opPtr.p->prevParallelQue != RNIL)
  {
    opPtr.i = opPtr.p->prevParallelQue;
    ptrCheckGuard(opPtr, coprecsize, operationrec);
  }    
  
  return opPtr.i;
}

bool
Dbacc::validate_lock_queue(OperationrecPtr opPtr)
{
  OperationrecPtr loPtr;
  loPtr.i = get_parallel_head(opPtr);
  ptrCheckGuard(loPtr, coprecsize, operationrec);
  
  while((loPtr.p->m_op_bits & Operationrec::OP_LOCK_OWNER) == 0 &&
	loPtr.p->prevSerialQue != RNIL)
  {
    loPtr.i = loPtr.p->prevSerialQue;
    ptrCheckGuard(loPtr, coprecsize, operationrec);
  }
  
  // Now we have lock owner...
  vlqrequire(loPtr.p->m_op_bits & Operationrec::OP_LOCK_OWNER);
  vlqrequire(loPtr.p->m_op_bits & Operationrec::OP_RUN_QUEUE);

  // 1 Validate page pointer
  {
    Page8Ptr pagePtr;
    pagePtr.i = loPtr.p->elementPage;
    ptrCheckGuard(pagePtr, cpagesize, page8);
    arrGuard(loPtr.p->elementPointer, 2048);
    Uint32 eh = pagePtr.p->word32[loPtr.p->elementPointer];
    vlqrequire(ElementHeader::getLocked(eh));
    vlqrequire(ElementHeader::getOpPtrI(eh) == loPtr.i);
  }

  // 2 Lock owner should always have same LOCK_MODE and ACC_LOCK_MODE
  if (loPtr.p->m_op_bits & Operationrec::OP_LOCK_MODE)
  {
    vlqrequire(loPtr.p->m_op_bits & Operationrec::OP_ACC_LOCK_MODE);
  }
  else
  {
    vlqrequire((loPtr.p->m_op_bits & Operationrec::OP_ACC_LOCK_MODE) == 0);
  }
  
  // 3 Lock owner should never be waiting...
  bool running = false;
  {
    Uint32 opstate = loPtr.p->m_op_bits & Operationrec::OP_STATE_MASK;
    if (opstate == Operationrec::OP_STATE_RUNNING)
      running = true;
    else
    {
      vlqrequire(opstate == Operationrec::OP_STATE_EXECUTED);
    }
  }
  
  // Validate parallel queue
  {
    bool many = false;
    bool orlockmode = loPtr.p->m_op_bits & Operationrec::OP_LOCK_MODE;
    OperationrecPtr lastP = loPtr;
    
    while (lastP.p->nextParallelQue != RNIL)
    {
      Uint32 prev = lastP.i;
      lastP.i = lastP.p->nextParallelQue;
      ptrCheckGuard(lastP, coprecsize, operationrec);
      
      vlqrequire(lastP.p->prevParallelQue == prev);

      Uint32 opbits = lastP.p->m_op_bits;
      many |= loPtr.p->is_same_trans(lastP.p) ? 0 : 1;
      orlockmode |= !!(opbits & Operationrec::OP_LOCK_MODE);
      
      vlqrequire(opbits & Operationrec::OP_RUN_QUEUE);
      vlqrequire((opbits & Operationrec::OP_LOCK_OWNER) == 0);
      
      Uint32 opstate = opbits & Operationrec::OP_STATE_MASK;
      if (running) 
      {
	// If I found a running operation, 
	// all following should be waiting
	vlqrequire(opstate == Operationrec::OP_STATE_WAITING);
      }
      else
      {
	if (opstate == Operationrec::OP_STATE_RUNNING)
	  running = true;
	else
	  vlqrequire(opstate == Operationrec::OP_STATE_EXECUTED);
      }
      
      if (lastP.p->m_op_bits & Operationrec::OP_LOCK_MODE)
      {
	vlqrequire(lastP.p->m_op_bits & Operationrec::OP_ACC_LOCK_MODE);
      }
      else
      {
	vlqrequire((lastP.p->m_op_bits && orlockmode) == orlockmode);
	vlqrequire((lastP.p->m_op_bits & Operationrec::OP_MASK) == ZREAD ||
		   (lastP.p->m_op_bits & Operationrec::OP_MASK) == ZSCAN_OP);
      }
      
      if (many)
      {
	vlqrequire(orlockmode == 0);
      }
    }
    
    if (lastP.i != loPtr.i)
    {
      vlqrequire(loPtr.p->m_lo_last_parallel_op_ptr_i == lastP.i);
      vlqrequire(lastP.p->m_lock_owner_ptr_i == loPtr.i);
    }
    else
    {
      vlqrequire(loPtr.p->m_lo_last_parallel_op_ptr_i == RNIL);
    }
  }
  
  // Validate serie queue  
  if (loPtr.p->nextSerialQue != RNIL)
  {
    Uint32 prev = loPtr.i;
    OperationrecPtr lastS;
    lastS.i = loPtr.p->nextSerialQue;
    while (true)
    {
      ptrCheckGuard(lastS, coprecsize, operationrec);      
      vlqrequire(lastS.p->prevSerialQue == prev);
      vlqrequire(getNoParallelTransaction(lastS.p) == 1);
      vlqrequire((lastS.p->m_op_bits & Operationrec::OP_LOCK_OWNER) == 0);
      vlqrequire((lastS.p->m_op_bits & Operationrec::OP_RUN_QUEUE) == 0);
      vlqrequire((lastS.p->m_op_bits & Operationrec::OP_STATE_MASK) == Operationrec::OP_STATE_WAITING);
      if (lastS.p->nextSerialQue == RNIL)
	break;
      prev = lastS.i;
      lastS.i = lastS.p->nextSerialQue;
    }
    
    vlqrequire(loPtr.p->m_lo_last_serial_op_ptr_i == lastS.i);
  }
  else
  {
    vlqrequire(loPtr.p->m_lo_last_serial_op_ptr_i == RNIL);
  }
  return true;
}

NdbOut&
operator<<(NdbOut & out, Dbacc::OperationrecPtr ptr)
{
  Uint32 opbits = ptr.p->m_op_bits;
  out << "[ " << dec << ptr.i 
      << " [ " << hex << ptr.p->transId1 
      << " " << hex << ptr.p->transId2 << "] "
      << " bits: H'" << hex << opbits << " ";
  
  bool read = false;
  switch(opbits & Dbacc::Operationrec::OP_MASK){
  case ZREAD: out << "READ "; read = true; break;
  case ZINSERT: out << "INSERT "; break;
  case ZUPDATE: out << "UPDATE "; break;
  case ZDELETE: out << "DELETE "; break;
  case ZWRITE: out << "WRITE "; break;
  case ZSCAN_OP: out << "SCAN "; read = true; break;
  default:
    out << "<Unknown: H'" 
	<< hex << (opbits & Dbacc::Operationrec::OP_MASK)
	<< "> ";
  }
  
  if (read)
  {
    if (opbits & Dbacc::Operationrec::OP_LOCK_MODE)
      out << "(X)";
    else
      out << "(S)";
    if (opbits & Dbacc::Operationrec::OP_ACC_LOCK_MODE)
      out << "(X)";
    else
      out << "(S)";
  }

  if (opbits)
  {
    out << "(RQ)";
  }
  
  switch(opbits & Dbacc::Operationrec::OP_STATE_MASK){
  case Dbacc::Operationrec::OP_STATE_WAITING:
    out << " WAITING "; break;
  case Dbacc::Operationrec::OP_STATE_RUNNING:
    out << " RUNNING "; break;
  case Dbacc::Operationrec::OP_STATE_EXECUTED:
    out << " EXECUTED "; break;
  case Dbacc::Operationrec::OP_STATE_IDLE:
    out << " IDLE "; break;
  default:
    out << " <Unknown: H'" 
	<< hex << (opbits & Dbacc::Operationrec::OP_STATE_MASK)
	<< "> ";
  }
  
/*
    OP_MASK                 = 0x000F // 4 bits for operation type
    ,OP_LOCK_MODE           = 0x0010 // 0 - shared lock, 1 = exclusive lock
    ,OP_ACC_LOCK_MODE       = 0x0020 // Or:de lock mode of all operation
                                     // before me
    ,OP_LOCK_OWNER          = 0x0040
    ,OP_DIRTY_READ          = 0x0080
    ,OP_LOCK_REQ            = 0x0100 // isAccLockReq
    ,OP_COMMIT_DELETE_CHECK = 0x0200
    ,OP_INSERT_IS_DONE      = 0x0400
    ,OP_ELEMENT_DISAPPEARED = 0x0800
    
    ,OP_STATE_MASK          = 0xF000
    ,OP_STATE_IDLE          = 0xF000
    ,OP_STATE_WAITING       = 0x0000
    ,OP_STATE_RUNNING       = 0x1000
    ,OP_STATE_EXECUTED      = 0x3000
  };
*/
  if (opbits & Dbacc::Operationrec::OP_LOCK_OWNER)
    out << "LO ";
  
  if (opbits & Dbacc::Operationrec::OP_DIRTY_READ)
    out << "DR ";
  
  if (opbits & Dbacc::Operationrec::OP_LOCK_REQ)
    out << "LOCK_REQ ";
  
  if (opbits & Dbacc::Operationrec::OP_COMMIT_DELETE_CHECK)
    out << "COMMIT_DELETE_CHECK ";

  if (opbits & Dbacc::Operationrec::OP_INSERT_IS_DONE)
    out << "INSERT_IS_DONE ";
  
  if (opbits & Dbacc::Operationrec::OP_ELEMENT_DISAPPEARED)
    out << "ELEMENT_DISAPPEARED ";
  
  if (opbits & Dbacc::Operationrec::OP_LOCK_OWNER)
  {
    out << "last_parallel: " << dec << ptr.p->m_lo_last_parallel_op_ptr_i << " ";
    out << "last_serial: " << dec << ptr.p->m_lo_last_serial_op_ptr_i << " ";
  }
  
  out << "]";
  return out;
}

void
Dbacc::dump_lock_queue(OperationrecPtr loPtr)
{
  if ((loPtr.p->m_op_bits & Operationrec::OP_LOCK_OWNER) == 0)
  {
    while ((loPtr.p->m_op_bits & Operationrec::OP_LOCK_OWNER) == 0 &&
	   loPtr.p->prevParallelQue != RNIL)
    {
      loPtr.i = loPtr.p->prevParallelQue;
      ptrCheckGuard(loPtr, coprecsize, operationrec);      
    }
    
    while ((loPtr.p->m_op_bits & Operationrec::OP_LOCK_OWNER) == 0 &&
	   loPtr.p->prevSerialQue != RNIL)
    {
      loPtr.i = loPtr.p->prevSerialQue;
      ptrCheckGuard(loPtr, coprecsize, operationrec);      
    }

    ndbassert(loPtr.p->m_op_bits & Operationrec::OP_LOCK_OWNER);
  }
  
  ndbout << "-- HEAD --" << endl;
  OperationrecPtr tmp = loPtr;
  while (tmp.i != RNIL)
  {
    ptrCheckGuard(tmp, coprecsize, operationrec);      
    ndbout << tmp << " ";
    tmp.i = tmp.p->nextParallelQue;
    
    if (tmp.i == loPtr.i)
    {
      ndbout << " <LOOP>";
      break;
    }
  }
  ndbout << endl;
  
  tmp.i = loPtr.p->nextSerialQue;
  while (tmp.i != RNIL)
  {
    ptrCheckGuard(tmp, coprecsize, operationrec);      
    OperationrecPtr tmp2 = tmp;
    
    if (tmp.i == loPtr.i)
    {
      ndbout << "<LOOP S>" << endl;
      break;
    }

    while (tmp2.i != RNIL)
    {
      ptrCheckGuard(tmp2, coprecsize, operationrec);
      ndbout << tmp2 << " ";
      tmp2.i = tmp2.p->nextParallelQue;

      if (tmp2.i == tmp.i)
      {
	ndbout << "<LOOP 3>";
	break;
      }
    }
    ndbout << endl;
    tmp.i = tmp.p->nextSerialQue;
  }
}
#endif

/* -------------------------------------------------------------------------
 * PLACE_WRITE_IN_LOCK_QUEUE
 *	INPUT:		OPERATION_REC_PTR OUR OPERATION POINTER
 *			QUE_OPER_PTR	  LOCK QUEUE OWNER OPERATION POINTER
 *			PWI_PAGEPTR       PAGE POINTER OF ELEMENT
 *			TPWI_ELEMENTPTR   ELEMENT POINTER OF ELEMENT
 *	OUTPUT		TRESULT =
 *			ZPARALLEL_QUEUE	  OPERATION PLACED IN PARALLEL QUEUE
 *					  OPERATION CAN PROCEED NOW.
 *			ZSERIAL_QUEUE	  OPERATION PLACED IN SERIAL QUEUE
 *			ERROR CODE	  OPERATION NEEDS ABORTING
 * ------------------------------------------------------------------------- */
Uint32 
Dbacc::placeWriteInLockQueue(OperationrecPtr lockOwnerPtr) 
{
  OperationrecPtr lastOpPtr;
  lastOpPtr.i = lockOwnerPtr.p->m_lo_last_parallel_op_ptr_i;
  Uint32 opbits = operationRecPtr.p->m_op_bits;
  
  if (lastOpPtr.i == RNIL)
  {
    lastOpPtr = lockOwnerPtr;
  }
  else
  {
    ptrCheckGuard(lastOpPtr, coprecsize, operationrec);
  }
  
  ndbassert(get_parallel_head(lastOpPtr) == lockOwnerPtr.i);

  Uint32 lastbits = lastOpPtr.p->m_op_bits;
  if (lastbits & Operationrec::OP_ACC_LOCK_MODE)
  {
    if(operationRecPtr.p->is_same_trans(lastOpPtr.p))
    {
      goto checkop;
    }
  }
  else
  {
    /**
     * We dont have an exclusive lock on operation and
     *   
     */
    jam();
    
    /**
     * Scan parallell queue to see if we are the only one
     */
    OperationrecPtr loopPtr = lockOwnerPtr;
    do
    {
      ptrCheckGuard(loopPtr, coprecsize, operationrec);
      if (!loopPtr.p->is_same_trans(operationRecPtr.p))
      {
	goto serial;
      }
      loopPtr.i = loopPtr.p->nextParallelQue;
    } while (loopPtr.i != RNIL);
    
    goto checkop;
  }
  
serial:
  jam();
  placeSerialQueue(lockOwnerPtr, operationRecPtr);

  validate_lock_queue(lockOwnerPtr);
  
  return ZSERIAL_QUEUE;
  
checkop:
  /* 
     WE ARE PERFORMING AN READ EXCLUSIVE, INSERT, UPDATE OR DELETE IN THE SAME
     TRANSACTION WHERE WE PREVIOUSLY HAVE EXECUTED AN OPERATION.
     Read-All, Update-All, Insert-All and Delete-Insert are allowed
     combinations.
     Delete-Read, Delete-Update and Delete-Delete are not an allowed
     combination and will result in tuple not found error.
  */
  Uint32 lstate = lastbits & Operationrec::OP_STATE_MASK;

  Uint32 retValue = ZSERIAL_QUEUE; // So that it gets blocked...
  if (lstate == Operationrec::OP_STATE_EXECUTED)
  {
    jam();

    /**
     * Since last operation has executed...we can now check operation types
     *   if not, we have to wait until it has executed 
     */
    Uint32 op = opbits & Operationrec::OP_MASK;
    Uint32 lop = lastbits & Operationrec::OP_MASK;
    if (op == ZINSERT && lop != ZDELETE)
    {
      jam();
      return ZWRITE_ERROR;
    }//if

    /**
     * NOTE. No checking op operation types, as one can read different save
     *       points...
     */
#if 0
    if (lop == ZDELETE && (op != ZINSERT && op != ZWRITE))
    {
      jam();
      return ZREAD_ERROR;
    }
#else
    if (lop == ZDELETE && (op == ZUPDATE && op == ZDELETE))
    {
      jam();
      return ZREAD_ERROR;
    }
#endif

    if(op == ZWRITE)
    {
      opbits &= ~(Uint32)Operationrec::OP_MASK;
      opbits |= (lop == ZDELETE) ? ZINSERT : ZUPDATE;
    }
    
    opbits |= Operationrec::OP_STATE_RUNNING;
    operationRecPtr.p->localdata[0] = lastOpPtr.p->localdata[0];
    operationRecPtr.p->localdata[1] = lastOpPtr.p->localdata[1];
    retValue = ZPARALLEL_QUEUE;
  }
  
  opbits |= Operationrec::OP_RUN_QUEUE;
  operationRecPtr.p->m_op_bits = opbits;
  operationRecPtr.p->prevParallelQue = lastOpPtr.i;
  operationRecPtr.p->m_lock_owner_ptr_i = lockOwnerPtr.i;
  lastOpPtr.p->nextParallelQue = operationRecPtr.i;
  lockOwnerPtr.p->m_lo_last_parallel_op_ptr_i = operationRecPtr.i;
  
  validate_lock_queue(lockOwnerPtr);
  
  return retValue;
}//Dbacc::placeWriteInLockQueue()

Uint32 
Dbacc::placeReadInLockQueue(OperationrecPtr lockOwnerPtr) 
{
  OperationrecPtr lastOpPtr;
  OperationrecPtr loopPtr = lockOwnerPtr;
  lastOpPtr.i = lockOwnerPtr.p->m_lo_last_parallel_op_ptr_i;
  Uint32 opbits = operationRecPtr.p->m_op_bits;

  if (lastOpPtr.i == RNIL)
  {
    lastOpPtr = lockOwnerPtr;
  }
  else
  {
    ptrCheckGuard(lastOpPtr, coprecsize, operationrec);
  }

  ndbassert(get_parallel_head(lastOpPtr) == lockOwnerPtr.i);
  
  /**
   * Last operation in parallell queue of lock owner is same trans
   *   and ACC_LOCK_MODE is exlusive, then we can proceed
   */
  Uint32 lastbits = lastOpPtr.p->m_op_bits;
  bool same = operationRecPtr.p->is_same_trans(lastOpPtr.p);
  if (same && (lastbits & Operationrec::OP_ACC_LOCK_MODE))
  {
    jam();
    opbits |= Operationrec::OP_LOCK_MODE; // Upgrade to X-lock
    goto checkop;
  }
  
  if ((lastbits & Operationrec::OP_ACC_LOCK_MODE) && !same)
  {
    jam();
    /**
     * Last op in serial queue had X-lock and was not our transaction...
     */
    goto serial;
  }

  if (lockOwnerPtr.p->nextSerialQue == RNIL)
  {
    jam();
    goto checkop;
  }

  /**
   * Scan parallell queue to see if we are already there...
   */
  do
  {
    ptrCheckGuard(loopPtr, coprecsize, operationrec);
    if (loopPtr.p->is_same_trans(operationRecPtr.p))
      goto checkop;
    loopPtr.i = loopPtr.p->nextParallelQue;
  } while (loopPtr.i != RNIL);

serial:
  placeSerialQueue(lockOwnerPtr, operationRecPtr);
  
  validate_lock_queue(lockOwnerPtr);
  
  return ZSERIAL_QUEUE;

checkop:
  Uint32 lstate = lastbits & Operationrec::OP_STATE_MASK;
  
  Uint32 retValue = ZSERIAL_QUEUE; // So that it gets blocked...
  if (lstate == Operationrec::OP_STATE_EXECUTED)
  {
    jam();
    
    /**
     * NOTE. No checking op operation types, as one can read different save
     *       points...
     */
    
#if 0
    /**
     * Since last operation has executed...we can now check operation types
     *   if not, we have to wait until it has executed 
     */
    if (lop == ZDELETE)
    {
      jam();
      return ZREAD_ERROR;
    }
#endif
    
    opbits |= Operationrec::OP_STATE_RUNNING;
    operationRecPtr.p->localdata[0] = lastOpPtr.p->localdata[0];
    operationRecPtr.p->localdata[1] = lastOpPtr.p->localdata[1];
    retValue = ZPARALLEL_QUEUE;
  }
  opbits |= (lastbits & Operationrec::OP_ACC_LOCK_MODE);
  opbits |= Operationrec::OP_RUN_QUEUE;
  operationRecPtr.p->m_op_bits = opbits;
  
  operationRecPtr.p->prevParallelQue = lastOpPtr.i;
  operationRecPtr.p->m_lock_owner_ptr_i = lockOwnerPtr.i;
  lastOpPtr.p->nextParallelQue = operationRecPtr.i;
  lockOwnerPtr.p->m_lo_last_parallel_op_ptr_i = operationRecPtr.i;
  
  validate_lock_queue(lockOwnerPtr);
  
  return retValue;
}//Dbacc::placeReadInLockQueue

void Dbacc::placeSerialQueue(OperationrecPtr lockOwnerPtr,
			     OperationrecPtr opPtr)
{
  OperationrecPtr lastOpPtr;
  lastOpPtr.i = lockOwnerPtr.p->m_lo_last_serial_op_ptr_i;

  if (lastOpPtr.i == RNIL)
  {
    // Lock owner is last...
    ndbrequire(lockOwnerPtr.p->nextSerialQue == RNIL);
    lastOpPtr = lockOwnerPtr;
  }
  else
  {
    ptrCheckGuard(lastOpPtr, coprecsize, operationrec);
  }
  
  operationRecPtr.p->prevSerialQue = lastOpPtr.i;
  lastOpPtr.p->nextSerialQue = opPtr.i;
  lockOwnerPtr.p->m_lo_last_serial_op_ptr_i = opPtr.i;
}

/* ------------------------------------------------------------------------- */
/* ACC KEYREQ END                                                            */
/* ------------------------------------------------------------------------- */
void Dbacc::acckeyref1Lab(Signal* signal, Uint32 result_code) 
{
  operationRecPtr.p->m_op_bits = Operationrec::OP_INITIAL;
  /* ************************<< */
  /* ACCKEYREF                  */
  /* ************************<< */
  signal->theData[0] = cminusOne;
  signal->theData[1] = result_code;
  return;
}//Dbacc::acckeyref1Lab()

/* ******************----------------------------------------------------- */
/* ACCMINUPDATE                                      UPDATE LOCAL KEY REQ  */
/*  DESCRIPTION: UPDATES LOCAL KEY OF AN ELEMENTS IN THE HASH TABLE        */
/*               THIS SIGNAL IS WAITED AFTER ANY INSERT REQ                */
/*          ENTER ACCMINUPDATE WITH         SENDER: LQH,    LEVEL B        */
/*                    OPERATION_REC_PTR,    OPERATION RECORD PTR           */
/*                    CLOCALKEY(0),         LOCAL KEY 1                    */
/*                    CLOCALKEY(1)          LOCAL KEY 2                    */
/* ******************----------------------------------------------------- */
void Dbacc::execACCMINUPDATE(Signal* signal) 
{
  Page8Ptr ulkPageidptr;
  Uint32 tulkLocalPtr;
  Uint32 tlocalkey1, tlocalkey2;

  jamEntry();
  operationRecPtr.i = signal->theData[0];
  tlocalkey1 = signal->theData[1];
  tlocalkey2 = signal->theData[2];
  Uint32 localref = Local_key::ref(tlocalkey1, tlocalkey2);
  ptrCheckGuard(operationRecPtr, coprecsize, operationrec);
  Uint32 opbits = operationRecPtr.p->m_op_bits;
  fragrecptr.i = operationRecPtr.p->fragptr;
  ulkPageidptr.i = operationRecPtr.p->elementPage;
  tulkLocalPtr = operationRecPtr.p->elementPointer + 
    operationRecPtr.p->elementIsforward;

  if ((opbits & Operationrec::OP_STATE_MASK) == Operationrec::OP_STATE_RUNNING)
  {
    ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
    ptrCheckGuard(ulkPageidptr, cpagesize, page8);
    dbgWord32(ulkPageidptr, tulkLocalPtr, tlocalkey1);
    arrGuard(tulkLocalPtr, 2048);
    operationRecPtr.p->localdata[0] = tlocalkey1;
    operationRecPtr.p->localdata[1] = tlocalkey2;
    if (likely(fragrecptr.p->localkeylen == 1))
    {
      ulkPageidptr.p->word32[tulkLocalPtr] = localref;
      return;
    }
    else if (fragrecptr.p->localkeylen == 2)
    {
      jam();
      ulkPageidptr.p->word32[tulkLocalPtr] = tlocalkey1;
      tulkLocalPtr = tulkLocalPtr + operationRecPtr.p->elementIsforward;
      dbgWord32(ulkPageidptr, tulkLocalPtr, tlocalkey2);
      arrGuard(tulkLocalPtr, 2048);
      ulkPageidptr.p->word32[tulkLocalPtr] = tlocalkey2;
      return;
    } else {
      jam();
    }//if
  }//if
  ndbrequire(false);
}//Dbacc::execACCMINUPDATE()

void
Dbacc::removerow(Uint32 opPtrI, const Local_key* key)
{
  jamEntry();
  operationRecPtr.i = opPtrI;
  ptrCheckGuard(operationRecPtr, coprecsize, operationrec);
  Uint32 opbits = operationRecPtr.p->m_op_bits;
  fragrecptr.i = operationRecPtr.p->fragptr;

  /* Mark element disappeared */
  opbits |= Operationrec::OP_ELEMENT_DISAPPEARED;
  opbits &= ~Uint32(Operationrec::OP_COMMIT_DELETE_CHECK);

  /**
   * This function is (currently?) only used when refreshTuple()
   *   inserts a record...and later wants to remove it
   *
   * Since this should not affect row-count...we change the optype to UPDATE
   *   execACC_COMMITREQ will be called in same timeslice as this change...
   */
  opbits &= ~Uint32(Operationrec::OP_MASK);
  opbits |= ZUPDATE;

  operationRecPtr.p->m_op_bits = opbits;

#ifdef VM_TRACE
  ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
  ndbrequire(operationRecPtr.p->localdata[0] == key->m_page_no);
  ndbrequire(operationRecPtr.p->localdata[1] == key->m_page_idx);
#endif
}//Dbacc::execACCMINUPDATE()

/* ******************--------------------------------------------------------------- */
/* ACC_COMMITREQ                                        COMMIT  TRANSACTION          */
/*                                                     SENDER: LQH,    LEVEL B       */
/*       INPUT:  OPERATION_REC_PTR ,                                                 */
/* ******************--------------------------------------------------------------- */
void Dbacc::execACC_COMMITREQ(Signal* signal) 
{
  Uint8 Toperation;
  jamEntry();
  Uint32 tmp = operationRecPtr.i = signal->theData[0];
  ptrCheckGuard(operationRecPtr, coprecsize, operationrec);
  void* ptr = operationRecPtr.p;
  Uint32 opbits = operationRecPtr.p->m_op_bits;
  fragrecptr.i = operationRecPtr.p->fragptr;
  ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
  Toperation = opbits & Operationrec::OP_MASK;
  commitOperation(signal);
  ndbassert(operationRecPtr.i == tmp);
  ndbassert(operationRecPtr.p == ptr);
  operationRecPtr.p->m_op_bits = Operationrec::OP_INITIAL;
  if((Toperation != ZREAD) &&
     (Toperation != ZSCAN_OP))
  {
    fragrecptr.p->m_commit_count++;
    if (Toperation != ZINSERT) {
      if (Toperation != ZDELETE) {
	return;
      } else {
	jam();
#ifdef ERROR_INSERT
        ndbrequire(fragrecptr.p->noOfElements > 0);
#else
        ndbassert(fragrecptr.p->noOfElements > 0);
#endif
	fragrecptr.p->noOfElements--;
	fragrecptr.p->slack += fragrecptr.p->elementLength;
	if (fragrecptr.p->slack > fragrecptr.p->slackCheck) { 
          /* TIME FOR JOIN BUCKETS PROCESS */
	  if (fragrecptr.p->expandCounter > 0) {
            if (!fragrecptr.p->expandOrShrinkQueued)
            {
	      jam();
	      signal->theData[0] = fragrecptr.i;
              fragrecptr.p->expandOrShrinkQueued = true;
              sendSignal(cownBlockref, GSN_SHRINKCHECK2, signal, 1, JBB);
	    }//if
	  }//if
	}//if
      }//if
    } else {
      jam();                                                /* EXPAND PROCESS HANDLING */
      fragrecptr.p->noOfElements++;
      fragrecptr.p->slack -= fragrecptr.p->elementLength;
      if (fragrecptr.p->slack < 0 && !fragrecptr.p->level.isFull())
      {
	/* IT MEANS THAT IF SLACK < ZERO */
        if (!fragrecptr.p->expandOrShrinkQueued)
        {
	  jam();
	  signal->theData[0] = fragrecptr.i;
          fragrecptr.p->expandOrShrinkQueued = true;
          sendSignal(cownBlockref, GSN_EXPANDCHECK2, signal, 1, JBB);
	}//if
      }//if
    }
  }
  return;
}//Dbacc::execACC_COMMITREQ()

/* ******************------------------------------------------------------- */
/* ACC ABORT REQ                   ABORT ALL OPERATION OF THE TRANSACTION    */
/* ******************------------------------------+                         */
/*   SENDER: LQH,    LEVEL B                                                 */
/* ******************------------------------------------------------------- */
/* ACC ABORT REQ                                         ABORT TRANSACTION   */
/* ******************------------------------------+                         */
/*   SENDER: LQH,    LEVEL B       */
void Dbacc::execACC_ABORTREQ(Signal* signal) 
{
  jamEntry();
  operationRecPtr.i = signal->theData[0];
  Uint32 sendConf = signal->theData[1];
  ptrCheckGuard(operationRecPtr, coprecsize, operationrec);
  fragrecptr.i = operationRecPtr.p->fragptr;
  Uint32 opbits = operationRecPtr.p->m_op_bits;
  Uint32 opstate = opbits & Operationrec::OP_STATE_MASK;
  tresult = 0;	/*                ZFALSE           */

  if (opbits == Operationrec::OP_EXECUTED_DIRTY_READ)
  {
    jam();
  }
  else if (opstate == Operationrec::OP_STATE_EXECUTED ||
	   opstate == Operationrec::OP_STATE_WAITING ||
	   opstate == Operationrec::OP_STATE_RUNNING)
  {
    jam();
    ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
    abortOperation(signal);
  }
  
  operationRecPtr.p->m_op_bits = Operationrec::OP_INITIAL;

  signal->theData[0] = operationRecPtr.p->userptr;
  signal->theData[1] = 0;
  switch(sendConf){
  case 0:
    return;
  case 2:
    if (opstate != Operationrec::OP_STATE_RUNNING)
    {
      return;
    }
  case 1:
    sendSignal(operationRecPtr.p->userblockref, GSN_ACC_ABORTCONF, 
	       signal, 1, JBB);
  }
  
  signal->theData[1] = RNIL;
}

/*
 * Lock or unlock tuple.
 */
void Dbacc::execACC_LOCKREQ(Signal* signal)
{
  jamEntry();
  AccLockReq* sig = (AccLockReq*)signal->getDataPtrSend();
  AccLockReq reqCopy = *sig;
  AccLockReq* const req = &reqCopy;
  Uint32 lockOp = (req->requestInfo & 0xFF);
  if (lockOp == AccLockReq::LockShared ||
      lockOp == AccLockReq::LockExclusive) {
    jam();
    // find table
    tabptr.i = req->tableId;
    ptrCheckGuard(tabptr, ctablesize, tabrec);
    // find fragment (TUX will know it)
    if (req->fragPtrI == RNIL) {
      for (Uint32 i = 0; i < NDB_ARRAY_SIZE(tabptr.p->fragholder); i++) {
        jam();
        if (tabptr.p->fragholder[i] == req->fragId){
	  jam();
	  req->fragPtrI = tabptr.p->fragptrholder[i];
	  break;
	}
      }
    }
    fragrecptr.i = req->fragPtrI;
    ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
    ndbrequire(req->fragId == fragrecptr.p->myfid);
    // caller must be explicit here
    ndbrequire(req->accOpPtr == RNIL);
    // seize operation to hold the lock
    if (cfreeopRec != RNIL) {
      jam();
      seizeOpRec(signal);
      // init as in ACCSEIZEREQ
      operationRecPtr.p->userptr = req->userPtr;
      operationRecPtr.p->userblockref = req->userRef;
      operationRecPtr.p->m_op_bits = Operationrec::OP_INITIAL;
      operationRecPtr.p->scanRecPtr = RNIL;
      // do read with lock via ACCKEYREQ
      Uint32 lockMode = (lockOp == AccLockReq::LockShared) ? 0 : 1;
      Uint32 opCode = ZSCAN_OP;
      signal->theData[0] = operationRecPtr.i;
      signal->theData[1] = fragrecptr.i;
      signal->theData[2] = opCode | (lockMode << 4) | (1u << 31);
      signal->theData[3] = req->hashValue;
      signal->theData[4] = 0;   // search local key
      signal->theData[5] = req->transId1;
      signal->theData[6] = req->transId2;
      // enter local key in place of PK
      signal->theData[7] = req->page_id;
      signal->theData[8] = req->page_idx;
      EXECUTE_DIRECT(DBACC, GSN_ACCKEYREQ, signal, 9);
      // translate the result
      if (signal->theData[0] < RNIL) {
        jam();
        req->returnCode = AccLockReq::Success;
        req->accOpPtr = operationRecPtr.i;
      } else if (signal->theData[0] == RNIL) {
        jam();
        req->returnCode = AccLockReq::IsBlocked;
        req->accOpPtr = operationRecPtr.i;
      } else {
        ndbrequire(signal->theData[0] == (UintR)-1);
        releaseOpRec(signal);
        req->returnCode = AccLockReq::Refused;
        req->accOpPtr = RNIL;
      }
    } else {
      jam();
      req->returnCode = AccLockReq::NoFreeOp;
    }
    *sig = *req;
    return;
  }
  if (lockOp == AccLockReq::Unlock) {
    jam();
    // do unlock via ACC_COMMITREQ (immediate)
    signal->theData[0] = req->accOpPtr;
    EXECUTE_DIRECT(DBACC, GSN_ACC_COMMITREQ, signal, 1);
    releaseOpRec(signal);
    req->returnCode = AccLockReq::Success;
    *sig = *req;
    return;
  }
  if (lockOp == AccLockReq::Abort) {
    jam();
    // do abort via ACC_ABORTREQ (immediate)
    signal->theData[0] = req->accOpPtr;
    signal->theData[1] = 0; // Dont send abort
    execACC_ABORTREQ(signal);
    releaseOpRec(signal);
    req->returnCode = AccLockReq::Success;
    *sig = *req;
    return;
  }
  if (lockOp == AccLockReq::AbortWithConf) {
    jam();
    // do abort via ACC_ABORTREQ (with conf signal)
    signal->theData[0] = req->accOpPtr;
    signal->theData[1] = 1; // send abort
    execACC_ABORTREQ(signal);
    releaseOpRec(signal);
    req->returnCode = AccLockReq::Success;
    *sig = *req;
    return;
  }
  ndbrequire(false);
}

/* --------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------- */
/*                                                                                   */
/*       END OF EXECUTE OPERATION MODULE                                             */
/*                                                                                   */
/* --------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------- */
/*                                                                                   */
/*       MODULE:         INSERT                                                      */
/*               THE FOLLOWING SUBROUTINES ARE ONLY USED BY INSERT_ELEMENT. THIS     */
/*               ROUTINE IS THE SOLE INTERFACE TO INSERT ELEMENTS INTO THE INDEX.    */
/*               CURRENT USERS ARE INSERT REQUESTS, EXPAND CONTAINER AND SHRINK      */
/*               CONTAINER.                                                          */
/*                                                                                   */
/*               THE FOLLOWING SUBROUTINES ARE INCLUDED IN THIS MODULE:              */
/*               INSERT_ELEMENT                                                      */
/*               INSERT_CONTAINER                                                    */
/*               ADDNEWCONTAINER                                                     */
/*               GETFREELIST                                                         */
/*               INCREASELISTCONT                                                    */
/*               SEIZE_LEFTLIST                                                      */
/*               SEIZE_RIGHTLIST                                                     */
/*                                                                                   */
/*               THESE ROUTINES ARE ONLY USED BY THIS MODULE AND BY NO ONE ELSE.     */
/*               ALSO THE ROUTINES MAKE NO USE OF ROUTINES IN OTHER MODULES.         */
/*               TAKE_REC_OUT_OF_FREE_OVERPAGE AND RELEASE_OVERFLOW_REC ARE          */
/*               EXCEPTIONS TO THIS RULE.                                            */
/*                                                                                   */
/*               THE ONLY SHORT-LIVED VARIABLES USED IN OTHER PARTS OF THE BLOCK ARE */
/*               THOSE DEFINED AS INPUT AND OUTPUT IN INSERT_ELEMENT                 */
/*               SHORT-LIVED VARIABLES INCLUDE TEMPORARY VARIABLES, COMMON VARIABLES */
/*               AND POINTER VARIABLES.                                              */
/*               THE ONLY EXCEPTION TO THIS RULE IS FRAGRECPTR WHICH POINTS TO THE   */
/*               FRAGMENT RECORD. THIS IS MORE LESS STATIC ALWAYS DURING A SIGNAL    */
/*               EXECUTION.                                                          */
/*                                                                                   */
/* --------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------- */
/* INSERT_ELEMENT                                                                    */
/*       INPUT:                                                                      */
/*               IDR_PAGEPTR (POINTER TO THE ACTIVE PAGE REC)                        */
/*               TIDR_PAGEINDEX (INDEX OF THE CONTAINER)                             */
/*               TIDR_FORWARD (DIRECTION FORWARD OR BACKWARD)                        */
/*               TIDR_ELEMHEAD (HEADER OF ELEMENT TO BE INSERTED                     */
/*               CIDR_KEYS(ARRAY OF TUPLE KEYS)                                      */
/*               CLOCALKEY(ARRAY OF LOCAL KEYS).                                     */
/*               FRAGRECPTR                                                          */
/*               IDR_OPERATION_REC_PTR                                               */
/*               TIDR_KEY_LEN                                                        */
/*                                                                                   */
/*       OUTPUT:                                                                     */
/*               TIDR_PAGEINDEX (PAGE INDEX OF INSERTED ELEMENT)                     */
/*               IDR_PAGEPTR    (PAGE POINTER OF INSERTED ELEMENT)                   */
/*               TIDR_FORWARD   (CONTAINER DIRECTION OF INSERTED ELEMENT)            */
/*               NONE                                                                */
/* --------------------------------------------------------------------------------- */
void Dbacc::insertElement(Signal* signal) 
{
  Page8Ptr inrNewPageptr;

  do {
    ContainerHeader containerhead;
    insertContainer(signal, containerhead);
    if (tidrResult != ZFALSE) {
      jam();
      return;
      /* INSERTION IS DONE, OR */
      /* AN ERROR IS DETECTED  */
    }//if
    if (containerhead.getNextEnd() != 0) {
      /* THE NEXT CONTAINER IS IN THE SAME PAGE */
      tidrPageindex = containerhead.getNextIndexNumber();      /* NEXT CONTAINER PAGE INDEX 7 BITS */
      if (containerhead.getNextEnd() == ZLEFT) {
        jam();
        tidrForward = ZTRUE;
      } else if (containerhead.getNextEnd() == ZRIGHT) {
        jam();
        tidrForward = cminusOne;
      } else {
        ndbrequire(false);
        return;
      }//if
      if (!containerhead.isNextOnSamePage()) {
        jam();     /* NEXT CONTAINER IS IN AN OVERFLOW PAGE */
        idrPageptr.i = idrPageptr.p->word32[tidrContainerptr + 1];
        ptrCheckGuard(idrPageptr, cpagesize, page8);
      }//if
      ndbrequire(tidrPageindex <= Container::MAX_CONTAINER_INDEX);
    } else {
      break;
    }//if
  } while (1);
  gflPageptr.p = idrPageptr.p;
  getfreelist(signal);
  if (tgflPageindex == Container::NO_CONTAINER_INDEX) {
    jam();
    /* NO FREE BUFFER IS FOUND */
    if (fragrecptr.p->sparsepages.isEmpty())
    {
      jam();
      allocOverflowPage(signal);
      ndbrequire(tresult <= ZLIMIT_OF_ERROR);
    }//if
    {
      LocalContainerPageList sparselist(*this, fragrecptr.p->sparsepages);
      sparselist.first(inrNewPageptr);
    }
    gflPageptr.p = inrNewPageptr.p;
    getfreelist(signal);
    ndbrequire(tgflPageindex != Container::NO_CONTAINER_INDEX);
    tancNext = 0;
  } else {
    jam();
    inrNewPageptr = idrPageptr;
    tancNext = 1;
  }//if
  tslPageindex = tgflPageindex;
  slPageptr = inrNewPageptr;
  Uint32 containerptr;
  if (tgflBufType == ZLEFT) {
    seizeLeftlist(signal);
    tidrForward = ZTRUE;
    containerptr = mul_ZBUF_SIZE(tgflPageindex) + ZHEAD_SIZE;
  } else {
    seizeRightlist(signal);
    tidrForward = cminusOne;
    containerptr = mul_ZBUF_SIZE(tgflPageindex) + ZHEAD_SIZE + ZBUF_SIZE - Container::HEADER_SIZE;
  }//if
  ContainerHeader containerhead;
  containerhead.initInUse();
  inrNewPageptr.p->word32[containerptr] = containerhead;
  tancPageindex = tgflPageindex;
  tancPagei = inrNewPageptr.i;
  tancBufType = tgflBufType;
  tancContainerptr = tidrContainerptr;
  ancPageptr.p = idrPageptr.p;
  addnewcontainer(signal);

  idrPageptr = inrNewPageptr;
  tidrPageindex = tgflPageindex;
  insertContainer(signal, containerhead);
  ndbrequire(tidrResult == ZTRUE);
}//Dbacc::insertElement()

/* --------------------------------------------------------------------------------- */
/* INSERT_CONTAINER                                                                  */
/*           INPUT:                                                                  */
/*               IDR_PAGEPTR (POINTER TO THE ACTIVE PAGE REC)                        */
/*               TIDR_PAGEINDEX (INDEX OF THE CONTAINER)                             */
/*               TIDR_FORWARD (DIRECTION FORWARD OR BACKWARD)                        */
/*               TIDR_ELEMHEAD (HEADER OF ELEMENT TO BE INSERTED                     */
/*               CKEYS(ARRAY OF TUPLE KEYS)                                          */
/*               CLOCALKEY(ARRAY 0F LOCAL KEYS).                                     */
/*               TIDR_KEY_LEN                                                        */
/*               FRAGRECPTR                                                          */
/*               IDR_OPERATION_REC_PTR                                               */
/*           OUTPUT:                                                                 */
/*               TIDR_RESULT (ZTRUE FOR SUCCESS AND ZFALSE OTHERWISE)                */
/*               containerhead (HEADER OF CONTAINER)                            */
/*               TIDR_CONTAINERPTR (POINTER TO CONTAINER HEADER)                     */
/*                                                                                   */
/*           DESCRIPTION:                                                            */
/*               THE FREE AREA OF THE CONTAINER WILL BE CALCULATED. IF IT IS         */
/*               LARGER THAN OR EQUAL THE ELEMENT LENGTH. THE ELEMENT WILL BE        */
/*               INSERT IN THE CONTAINER AND CONTAINER HEAD WILL BE UPDATED.         */
/*               THIS ROUTINE ALWAYS DEALS WITH ONLY ONE CONTAINER AND DO NEVER      */
/*               START ANYTHING OUTSIDE OF THIS CONTAINER.                           */
/*                                                                                   */
/*       SHORT FORM: IDR                                                             */
/* --------------------------------------------------------------------------------- */
void Dbacc::insertContainer(Signal* signal, ContainerHeader& containerhead)
{
  Uint32 tidrContainerlen;
  Uint32 tidrConfreelen;
  Uint32 tidrNextSide;
  Uint32 tidrNextConLen;
  Uint32 tidrIndex;
  Uint32 tidrInputIndex;
  Uint32 guard26;

  tidrResult = ZFALSE;
  tidrContainerptr = mul_ZBUF_SIZE(tidrPageindex) + ZHEAD_SIZE;
  /* --------------------------------------------------------------------------------- */
  /*       CALCULATE THE POINTER TO THE ELEMENT TO BE INSERTED AND THE POINTER TO THE  */
  /*       CONTAINER HEADER OF THE OTHER SIDE OF THE BUFFER.                           */
  /* --------------------------------------------------------------------------------- */
  if (tidrForward == ZTRUE) {
    jam();
    tidrNextSide = tidrContainerptr + (ZBUF_SIZE - Container::HEADER_SIZE);
    arrGuard(tidrNextSide + 1, 2048);
    containerhead = idrPageptr.p->word32[tidrContainerptr];
    tidrContainerlen = containerhead.getLength();
    tidrIndex = tidrContainerptr + tidrContainerlen;
  } else {
    jam();
    tidrNextSide = tidrContainerptr;
    tidrContainerptr = tidrContainerptr + (ZBUF_SIZE - Container::HEADER_SIZE);
    arrGuard(tidrContainerptr + 1, 2048);
    containerhead = idrPageptr.p->word32[tidrContainerptr];
    tidrContainerlen = containerhead.getLength();
    tidrIndex = (tidrContainerptr - tidrContainerlen) + (Container::HEADER_SIZE - 1);
  }//if
  if (tidrContainerlen > (ZBUF_SIZE - 3)) {
    return;
  }//if
  tidrConfreelen = ZBUF_SIZE - tidrContainerlen;
  /* --------------------------------------------------------------------------------- */
  /*       WE CALCULATE THE TOTAL LENGTH THE CONTAINER CAN EXPAND TO                   */
  /*       THIS INCLUDES THE OTHER SIDE OF THE BUFFER IF POSSIBLE TO EXPAND THERE.     */
  /* --------------------------------------------------------------------------------- */
  if (!containerhead.isUsingBothEnds()) {
    jam();
    /* --------------------------------------------------------------------------------- */
    /*       WE HAVE NOT EXPANDED TO THE ENTIRE BUFFER YET. WE CAN THUS READ THE OTHER   */
    /*       SIDE'S CONTAINER HEADER TO READ HIS LENGTH.                                 */
    /* --------------------------------------------------------------------------------- */
    tidrNextConLen = idrPageptr.p->word32[tidrNextSide] >> 26;
    tidrConfreelen = tidrConfreelen - tidrNextConLen;
    if (tidrConfreelen > ZBUF_SIZE) {
      ndbrequire(false);
      /* --------------------------------------------------------------------------------- */
      /*       THE BUFFERS ARE PLACED ON TOP OF EACH OTHER. THIS SHOULD NEVER OCCUR.       */
      /* --------------------------------------------------------------------------------- */
      return;
    }//if
  } else {
    jam();
    tidrNextConLen = 1;	/* INDICATE OTHER SIDE IS NOT PART OF FREE LIST */
  }//if
  if (tidrConfreelen < fragrecptr.p->elementLength) {
    jam();
    /* --------------------------------------------------------------------------------- */
    /*       THE CONTAINER COULD NOT BE EXPANDED TO FIT THE NEW ELEMENT. WE HAVE TO      */
    /*       RETURN AND FIND A NEW CONTAINER TO INSERT IT INTO.                          */
    /* --------------------------------------------------------------------------------- */
    return;
  }//if
  tidrContainerlen = tidrContainerlen + fragrecptr.p->elementLength;
  if (tidrNextConLen == 0) {
    /* EACH SIDE OF THE BUFFER WHICH BELONG TO A FREE */
    /* LIST, HAS ZERO AS LENGTH. */
    if (tidrContainerlen > Container::UP_LIMIT) {
      ContainerHeader conthead = idrPageptr.p->word32[tidrContainerptr];
      conthead.setUsingBothEnds();
      dbgWord32(idrPageptr, tidrContainerptr, conthead);
      idrPageptr.p->word32[tidrContainerptr] = conthead;
      tslPageindex = tidrPageindex;
      slPageptr = idrPageptr;
      if (tidrForward == ZTRUE) {
        jam();
        seizeRightlist(signal);	/* REMOVE THE RIGHT SIDE OF THE BUFFER FROM THE LIST */
      } else {
        jam();
	/* OF THE FREE CONTAINERS */
        seizeLeftlist(signal);	/* REMOVE THE LEFT SIDE OF THE BUFFER FROM THE LIST */
      }//if
    }//if
  }//if
  /* OF THE FREE CONTAINERS */
  /* --------------------------------------------------------------------------------- */
  /*       WE HAVE NOW FOUND A FREE SPOT IN THE CURRENT CONTAINER. WE INSERT THE       */
  /*       ELEMENT HERE. THE ELEMENT CONTAINS A HEADER, A LOCAL KEY AND A TUPLE KEY.   */
  /*       BEFORE INSERTING THE ELEMENT WE WILL UPDATE THE OPERATION RECORD WITH THE   */
  /*       DATA CONCERNING WHERE WE INSERTED THE ELEMENT. THIS MAKES IT EASY TO FIND   */
  /*       THIS INFORMATION WHEN WE RETURN TO UPDATE THE LOCAL KEY OR RETURN TO COMMIT */
  /*       OR ABORT THE INSERT. IF NO OPERATION RECORD EXIST IT MEANS THAT WE ARE      */
  /*       PERFORMING THIS AS A PART OF THE EXPAND OR SHRINK PROCESS.                  */
  /* --------------------------------------------------------------------------------- */
  if (idrOperationRecPtr.i != RNIL) {
    jam();
    idrOperationRecPtr.p->elementIsforward = tidrForward;
    idrOperationRecPtr.p->elementPage = idrPageptr.i;
    idrOperationRecPtr.p->elementContainer = tidrContainerptr;
    idrOperationRecPtr.p->elementPointer = tidrIndex;
  }//if
  /* --------------------------------------------------------------------------------- */
  /*       WE CHOOSE TO UNDO LOG INSERTS BY WRITING THE BEFORE VALUE TO THE UNDO LOG.  */
  /*       WE COULD ALSO HAVE DONE THIS BY WRITING THIS BEFORE VALUE WHEN DELETING     */
  /*       ELEMENTS. WE CHOOSE TO PUT IT HERE SINCE WE THEREBY ENSURE THAT WE ALWAYS   */
  /*       UNDO LOG ALL WRITES TO PAGE MEMORY. IT SHOULD BE EASIER TO MAINTAIN SUCH A  */
  /*       STRUCTURE. IT IS RATHER DIFFICULT TO MAINTAIN A LOGICAL STRUCTURE WHERE     */
  /*       DELETES ARE INSERTS AND INSERTS ARE PURELY DELETES.                         */
  /* --------------------------------------------------------------------------------- */
  dbgWord32(idrPageptr, tidrIndex, tidrElemhead);
  idrPageptr.p->word32[tidrIndex] = tidrElemhead;	/* INSERTS THE HEAD OF THE ELEMENT */
  tidrIndex += tidrForward;
  guard26 = fragrecptr.p->localkeylen - 1;
  arrGuard(guard26, 2);
  for (tidrInputIndex = 0; tidrInputIndex <= guard26; tidrInputIndex++) {
    dbgWord32(idrPageptr, tidrIndex, clocalkey[tidrInputIndex]);
    arrGuard(tidrIndex, 2048);
    idrPageptr.p->word32[tidrIndex] = clocalkey[tidrInputIndex];	/* INSERTS LOCALKEY */
    tidrIndex += tidrForward;
  }//for
  ContainerHeader conthead = idrPageptr.p->word32[tidrContainerptr];
  conthead.setLength(tidrContainerlen);
  dbgWord32(idrPageptr, tidrContainerptr, conthead);
  idrPageptr.p->word32[tidrContainerptr] = conthead;
  tidrResult = ZTRUE;
}//Dbacc::insertContainer()

/* --------------------------------------------------------------------------------- */
/* ADDNEWCONTAINER                                                                   */
/*       INPUT:                                                                      */
/*               TANC_CONTAINERPTR                                                   */
/*               ANC_PAGEPTR                                                         */
/*               TANC_NEXT                                                           */
/*               TANC_PAGEINDEX                                                      */
/*               TANC_BUF_TYPE                                                       */
/*               TANC_PAGEI                                                         */
/*       OUTPUT:                                                                     */
/*               NONE                                                                */
/*                                                                                   */
/* --------------------------------------------------------------------------------- */
void Dbacc::addnewcontainer(Signal* signal) 
{
  /* --------------------------------------------------------------------------------- */
  /*       KEEP LENGTH INFORMATION IN BIT 26-31.                                       */
  /*       SET BIT 9  INDICATING IF NEXT BUFFER IN THE SAME PAGE USING TANC_NEXT.      */
  /*       SET TYPE OF NEXT CONTAINER IN BIT 7-8.                                      */
  /*       SET PAGE INDEX OF NEXT CONTAINER IN BIT 0-6.                                */
  /*       KEEP INDICATOR OF OWNING OTHER SIDE OF BUFFER IN BIT 10.                    */
  /* --------------------------------------------------------------------------------- */
  ContainerHeader containerhead(ancPageptr.p->word32[tancContainerptr]);
  containerhead.setNext(tancBufType, tancPageindex, tancNext);
  dbgWord32(ancPageptr, tancContainerptr, containerhead);
  ancPageptr.p->word32[tancContainerptr] = containerhead;	/* HEAD OF THE CONTAINER IS UPDATED */
  dbgWord32(ancPageptr, tancContainerptr + 1, tancPagei);
  ancPageptr.p->word32[tancContainerptr + 1] = tancPagei;
}//Dbacc::addnewcontainer()

/* --------------------------------------------------------------------------------- */
/* GETFREELIST                                                                       */
/*         INPUT:                                                                    */
/*               GFL_PAGEPTR (POINTER TO A PAGE RECORD).                             */
/*         OUTPUT:                                                                   */
/*                TGFL_PAGEINDEX(POINTER TO A FREE BUFFER IN THE FREEPAGE), AND      */
/*                TGFL_BUF_TYPE( TYPE OF THE FREE BUFFER).                           */
/*         DESCRIPTION: SEARCHS IN THE FREE LIST OF THE FREE BUFFER IN THE PAGE HEAD */
/*                     (WORD32(1)),AND RETURN ADDRESS OF A FREE BUFFER OR NIL.       */
/*                     THE FREE BUFFER CAN BE A RIGHT CONTAINER OR A LEFT ONE        */
/*                     THE KIND OF THE CONTAINER IS NOTED BY TGFL_BUF_TYPE.          */
/* --------------------------------------------------------------------------------- */
void Dbacc::getfreelist(Signal* signal) 
{
  Uint32 tgflTmp;

  tgflTmp = gflPageptr.p->word32[ZPOS_EMPTY_LIST];
  tgflPageindex = (tgflTmp >> 7) & 0x7f;	/* LEFT FREE LIST */
  tgflBufType = ZLEFT;
  if (tgflPageindex == Container::NO_CONTAINER_INDEX) {
    jam();
    tgflPageindex = tgflTmp & 0x7f;	/* RIGHT FREE LIST */
    tgflBufType = ZRIGHT;
  }//if
  ndbrequire((tgflPageindex <= Container::MAX_CONTAINER_INDEX) || (tgflPageindex == Container::NO_CONTAINER_INDEX));
}//Dbacc::getfreelist()

/* --------------------------------------------------------------------------------- */
/* INCREASELISTCONT                                                                  */
/*       INPUT:                                                                      */
/*               ILC_PAGEPTR     PAGE POINTER TO INCREASE NUMBER OF CONTAINERS IN    */
/*           A CONTAINER OF AN OVERFLOW PAGE (FREEPAGEPTR) IS ALLOCATED, NR OF       */
/*           ALLOCATED CONTAINER HAVE TO BE INCRESE BY ONE .                         */
/*           IF THE NUMBER OF ALLOCATED CONTAINERS IS ABOVE THE FREE LIMIT WE WILL   */
/*           REMOVE THE PAGE FROM THE FREE LIST.                                     */
/* --------------------------------------------------------------------------------- */
void Dbacc::increaselistcont(Signal* signal) 
{
  dbgWord32(ilcPageptr, ZPOS_ALLOC_CONTAINERS, ilcPageptr.p->word32[ZPOS_ALLOC_CONTAINERS] + 1);
  ilcPageptr.p->word32[ZPOS_ALLOC_CONTAINERS] = ilcPageptr.p->word32[ZPOS_ALLOC_CONTAINERS] + 1;
  // A sparse page just got full
  if (ilcPageptr.p->word32[ZPOS_ALLOC_CONTAINERS] == ZFREE_LIMIT + 1) {
    // Check that it is an overflow page
    if (((ilcPageptr.p->word32[ZPOS_EMPTY_LIST] >> ZPOS_PAGE_TYPE_BIT) & 3) == 1)
    {
      jam();
      LocalContainerPageList sparselist(*this, fragrecptr.p->sparsepages);
      LocalContainerPageList fulllist(*this, fragrecptr.p->fullpages);
      sparselist.remove(ilcPageptr);
      fulllist.addLast(ilcPageptr);
    }//if
  }//if
}//Dbacc::increaselistcont()

/* --------------------------------------------------------------------------------- */
/* SEIZE_LEFTLIST                                                                    */
/*       INPUT:                                                                      */
/*               TSL_PAGEINDEX           PAGE INDEX OF CONTAINER TO SEIZE            */
/*               SL_PAGEPTR              PAGE POINTER OF CONTAINER TO SEIZE          */
/*               TSL_UPDATE_HEADER       SHOULD WE UPDATE THE CONTAINER HEADER       */
/*                                                                                   */
/*       OUTPUT:                                                                     */
/*               NONE                                                                */
/*         DESCRIPTION: THE BUFFER NOTED BY TSL_PAGEINDEX WILL BE REMOVED FROM THE   */
/*                      LIST OF LEFT FREE CONTAINER, IN THE HEADER OF THE PAGE       */
/*                      (FREEPAGEPTR). PREVIOUS AND NEXT BUFFER OF REMOVED BUFFER    */
/*                      WILL BE UPDATED.                                             */
/* --------------------------------------------------------------------------------- */
void Dbacc::seizeLeftlist(Signal* signal) 
{
  Uint32 tsllTmp1;
  Uint32 tsllHeadIndex;
  Uint32 tsllTmp;

  tsllHeadIndex = mul_ZBUF_SIZE(tslPageindex) + ZHEAD_SIZE;
  arrGuard(tsllHeadIndex + 1, 2048);
  tslNextfree = slPageptr.p->word32[tsllHeadIndex];
  tslPrevfree = slPageptr.p->word32[tsllHeadIndex + 1];
  if (tslPrevfree == Container::NO_CONTAINER_INDEX) {
    jam();
    /* UPDATE FREE LIST OF LEFT CONTAINER IN PAGE HEAD */
    tsllTmp1 = slPageptr.p->word32[ZPOS_EMPTY_LIST];
    tsllTmp = tsllTmp1 & 0x7f;
    tsllTmp1 = (tsllTmp1 >> 14) << 14;
    tsllTmp1 = (tsllTmp1 | (tslNextfree << 7)) | tsllTmp;
    dbgWord32(slPageptr, ZPOS_EMPTY_LIST, tsllTmp1);
    slPageptr.p->word32[ZPOS_EMPTY_LIST] = tsllTmp1;
  } else {
    ndbrequire(tslPrevfree <= Container::MAX_CONTAINER_INDEX);
    jam();
    tsllTmp = mul_ZBUF_SIZE(tslPrevfree) + ZHEAD_SIZE;
    dbgWord32(slPageptr, tsllTmp, tslNextfree);
    slPageptr.p->word32[tsllTmp] = tslNextfree;
  }//if
  if (tslNextfree <= Container::MAX_CONTAINER_INDEX) {
    jam();
    tsllTmp = mul_ZBUF_SIZE(tslNextfree) + ZHEAD_SIZE + 1;
    dbgWord32(slPageptr, tsllTmp, tslPrevfree);
    slPageptr.p->word32[tsllTmp] = tslPrevfree;
  } else {
    ndbrequire(tslNextfree == Container::NO_CONTAINER_INDEX);
    jam();
  }//if
  ilcPageptr = slPageptr;
  increaselistcont(signal);
}//Dbacc::seizeLeftlist()

/* --------------------------------------------------------------------------------- */
/* SEIZE_RIGHTLIST                                                                   */
/*         DESCRIPTION: THE BUFFER NOTED BY TSL_PAGEINDEX WILL BE REMOVED FROM THE   */
/*                      LIST OF RIGHT FREE CONTAINER, IN THE HEADER OF THE PAGE      */
/*                      (SL_PAGEPTR). PREVIOUS AND NEXT BUFFER OF REMOVED BUFFER     */
/*                      WILL BE UPDATED.                                             */
/* --------------------------------------------------------------------------------- */
void Dbacc::seizeRightlist(Signal* signal) 
{
  Uint32 tsrlHeadIndex;
  Uint32 tsrlTmp;

  tsrlHeadIndex = mul_ZBUF_SIZE(tslPageindex) + ((ZHEAD_SIZE + ZBUF_SIZE) - Container::HEADER_SIZE);
  arrGuard(tsrlHeadIndex + 1, 2048);
  tslNextfree = slPageptr.p->word32[tsrlHeadIndex];
  tslPrevfree = slPageptr.p->word32[tsrlHeadIndex + 1];
  if (tslPrevfree == Container::NO_CONTAINER_INDEX) {
    jam();
    tsrlTmp = slPageptr.p->word32[ZPOS_EMPTY_LIST];
    dbgWord32(slPageptr, ZPOS_EMPTY_LIST, ((tsrlTmp >> 7) << 7) | tslNextfree);
    slPageptr.p->word32[ZPOS_EMPTY_LIST] = ((tsrlTmp >> 7) << 7) | tslNextfree;
  } else {
    ndbrequire(tslPrevfree <= Container::MAX_CONTAINER_INDEX);
    jam();
    tsrlTmp = mul_ZBUF_SIZE(tslPrevfree) + ((ZHEAD_SIZE + ZBUF_SIZE) - Container::HEADER_SIZE);
    dbgWord32(slPageptr, tsrlTmp, tslNextfree);
    slPageptr.p->word32[tsrlTmp] = tslNextfree;
  }//if
  if (tslNextfree <= Container::MAX_CONTAINER_INDEX) {
    jam();
    tsrlTmp = mul_ZBUF_SIZE(tslNextfree) + ((ZHEAD_SIZE + ZBUF_SIZE) - (Container::HEADER_SIZE - 1));
    dbgWord32(slPageptr, tsrlTmp, tslPrevfree);
    slPageptr.p->word32[tsrlTmp] = tslPrevfree;
  } else {
    ndbrequire(tslNextfree == Container::NO_CONTAINER_INDEX);
    jam();
  }//if
  ilcPageptr = slPageptr;
  increaselistcont(signal);
}//Dbacc::seizeRightlist()

/* --------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------- */
/*                                                                                   */
/*       END OF INSERT_ELEMENT MODULE                                                */
/*                                                                                   */
/* --------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------- */
/*                                                                                   */
/*       MODULE:         GET_ELEMENT                                                 */
/*               THE FOLLOWING SUBROUTINES ARE ONLY USED BY GET_ELEMENT AND          */
/*               GETDIRINDEX. THIS ROUTINE IS THE SOLE INTERFACE TO GET ELEMENTS     */
/*               FROM THE INDEX. CURRENT USERS ARE ALL REQUESTS AND EXECUTE UNDO LOG */
/*                                                                                   */
/*               THE FOLLOWING SUBROUTINES ARE INCLUDED IN THIS MODULE:              */
/*               GET_ELEMENT                                                         */
/*               GET_DIRINDEX                                                        */
/*               SEARCH_LONG_KEY                                                     */
/*                                                                                   */
/*               THESE ROUTINES ARE ONLY USED BY THIS MODULE AND BY NO ONE ELSE.     */
/*               ALSO THE ROUTINES MAKE NO USE OF ROUTINES IN OTHER MODULES.         */
/*               THE ONLY SHORT-LIVED VARIABLES USED IN OTHER PARTS OF THE BLOCK ARE */
/*               THOSE DEFINED AS INPUT AND OUTPUT IN GET_ELEMENT AND GETDIRINDEX    */
/*               SHORT-LIVED VARIABLES INCLUDE TEMPORARY VARIABLES, COMMON VARIABLES */
/*               AND POINTER VARIABLES.                                              */
/*               THE ONLY EXCEPTION TO THIS RULE IS FRAGRECPTR WHICH POINTS TO THE   */
/*               FRAGMENT RECORD. THIS IS MORE LESS STATIC ALWAYS DURING A SIGNAL    */
/*               EXECUTION.                                                          */
/*                                                                                   */
/* --------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------- */
/* GETDIRINDEX                                                                       */
/*       SUPPORT ROUTINE FOR INSERT ELEMENT, GET ELEMENT AND COMMITDELETE            */
/*         INPUT:FRAGRECPTR ( POINTER TO THE ACTIVE FRAGMENT REC)                    */
/*               OPERATION_REC_PTR  (POINTER TO THE OPERATION REC).                  */
/*                                                                                   */
/*         OUTPUT:GDI_PAGEPTR ( POINTER TO THE PAGE OF THE ELEMENT)                  */
/*                TGDI_PAGEINDEX ( INDEX OF THE ELEMENT IN THE PAGE).                */
/*                                                                                   */
/*         DESCRIPTION: CHECK THE HASH VALUE OF THE OPERATION REC AND CALCULATE THE  */
/*                     THE ADDRESS OF THE ELEMENT IN THE HASH TABLE,(GDI_PAGEPTR,    */
/*                     TGDI_PAGEINDEX) ACCORDING TO LH3.                             */
/* --------------------------------------------------------------------------------- */
Uint32 Dbacc::getPagePtr(DynArr256::Head& directory, Uint32 index)
{
  DynArr256 dir(directoryPool, directory);
  Uint32* ptr = dir.get(index);
  return *ptr;
}

bool Dbacc::setPagePtr(DynArr256::Head& directory, Uint32 index, Uint32 ptri)
{
  DynArr256 dir(directoryPool, directory);
  Uint32* ptr = dir.set(index);
  if (ptr == NULL) return false;
  *ptr = ptri;
  return true;
}

Uint32 Dbacc::unsetPagePtr(DynArr256::Head& directory, Uint32 index)
{
  DynArr256 dir(directoryPool, directory);
  Uint32* ptr = dir.get(index);
  Uint32 ptri = *ptr;
  *ptr = RNIL;
  return ptri;
}

void Dbacc::getdirindex(Signal* signal) 
{
  Uint32 tgdiAddress;

  tgdiAddress = fragrecptr.p->level.getBucketNumber(operationRecPtr.p->hashValue);
  tgdiPageindex = fragrecptr.p->getPageIndex(tgdiAddress);
  gdiPageptr.i = getPagePtr(fragrecptr.p->directory, fragrecptr.p->getPageNumber(tgdiAddress));
  ptrCheckGuard(gdiPageptr, cpagesize, page8);
}//Dbacc::getdirindex()

Uint32
Dbacc::readTablePk(Uint32 localkey1, Uint32 localkey2,
                   Uint32 eh, Ptr<Operationrec> opPtr)
{
  int ret;
  Uint32 tableId = fragrecptr.p->myTableId;
  Uint32 fragId = fragrecptr.p->myfid;
  bool xfrm = fragrecptr.p->hasCharAttr;

#ifdef VM_TRACE
  memset(ckeys, 0x1f, (fragrecptr.p->keyLength * MAX_XFRM_MULTIPLY) << 2);
#endif
  
  if (likely(! Local_key::isInvalid(localkey1, localkey2)))
  {
    ret = c_tup->accReadPk(tableId, fragId, localkey1, localkey2,
			   ckeys, true);
  }
  else
  {
    ndbrequire(ElementHeader::getLocked(eh));
    if (unlikely((opPtr.p->m_op_bits & Operationrec::OP_MASK) == ZSCAN_OP))
    {
      dump_lock_queue(opPtr);
      ndbrequire(opPtr.p->nextParallelQue == RNIL);
      ndbrequire(opPtr.p->m_op_bits & Operationrec::OP_ELEMENT_DISAPPEARED);
      ndbrequire(opPtr.p->m_op_bits & Operationrec::OP_COMMIT_DELETE_CHECK);
      ndbrequire((opPtr.p->m_op_bits & Operationrec::OP_STATE_MASK) == Operationrec::OP_STATE_RUNNING);
      return 0;
    }
    ret = c_lqh->readPrimaryKeys(opPtr.p->userptr, ckeys, xfrm);
  }
  jamEntry();
  ndbrequire(ret >= 0);
  return ret;
}

/* --------------------------------------------------------------------------------- */
/* GET_ELEMENT                                                                       */
/*        INPUT:                                                                     */
/*               OPERATION_REC_PTR                                                   */
/*               FRAGRECPTR                                                          */
/*        OUTPUT:                                                                    */
/*               TGE_RESULT      RESULT SUCCESS = ZTRUE OTHERWISE ZFALSE             */
/*               TGE_LOCKED      LOCK INFORMATION IF SUCCESSFUL RESULT               */
/*               GE_PAGEPTR      PAGE POINTER OF FOUND ELEMENT                       */
/*               TGE_CONTAINERPTR CONTAINER INDEX OF FOUND ELEMENT                   */
/*               TGE_ELEMENTPTR  ELEMENT INDEX OF FOUND ELEMENT                      */
/*               TGE_FORWARD     DIRECTION OF CONTAINER WHERE ELEMENT FOUND          */
/*                                                                                   */
/*        DESCRIPTION: THE SUBROUTIN GOES THROUGH ALL CONTAINERS OF THE ACTIVE       */
/*                     BUCKET, AND SERCH FOR ELEMENT.THE PRIMARY KEYS WHICH IS SAVED */
/*                     IN THE OPERATION REC ARE THE CHECK ITEMS IN THE SEARCHING.    */
/* --------------------------------------------------------------------------------- */

#if __ia64 == 1
#if __INTEL_COMPILER == 810
int ndb_acc_ia64_icc810_dummy_var = 0;
void ndb_acc_ia64_icc810_dummy_func()
{
  ndb_acc_ia64_icc810_dummy_var++;
}
#endif
#endif

Uint32
Dbacc::getElement(Signal* signal, OperationrecPtr& lockOwnerPtr) 
{
  Uint32 errcode;
  Uint32 tgeElementHeader;
  Uint32 tgeElemStep;
  Uint32 tgePageindex;
  Uint32 tgeNextptrtype;
  register Uint32 tgeRemLen;
  register Uint32 TelemLen = fragrecptr.p->elementLength;
  register Uint32* Tkeydata = (Uint32*)&signal->theData[7];
  const Uint32 localkeylen = fragrecptr.p->localkeylen;
  Uint32 bucket_number = fragrecptr.p->level.getBucketNumber(operationRecPtr.p->hashValue);

  getdirindex(signal);
  tgePageindex = tgdiPageindex;
  gePageptr = gdiPageptr;
  /*
   * The value seached is
   * - table key for ACCKEYREQ, stored in TUP
   * - local key (1 word) for ACC_LOCKREQ and UNDO, stored in ACC
   */
  const bool searchLocalKey = operationRecPtr.p->tupkeylen == 0;

  ndbrequire(TelemLen == ZELEM_HEAD_SIZE + localkeylen);
  tgeNextptrtype = ZLEFT;

  do {
    tgeContainerptr = mul_ZBUF_SIZE(tgePageindex);
    if (tgeNextptrtype == ZLEFT) {
      jam();
      tgeContainerptr = tgeContainerptr + ZHEAD_SIZE;
      tgeElementptr = tgeContainerptr + Container::HEADER_SIZE;
      tgeElemStep = TelemLen;
      tgeForward = 1;
      if (unlikely(tgeContainerptr >= 2048)) 
      { 
	errcode = 4;
	goto error;
      }
      tgeRemLen = ContainerHeader(gePageptr.p->word32[tgeContainerptr]).getLength();
      if (unlikely(((tgeContainerptr + tgeRemLen - 1) >= 2048)))
      { 
	errcode = 5;
	goto error;
      }
    } else if (tgeNextptrtype == ZRIGHT) {
      jam();
      tgeContainerptr = tgeContainerptr + ((ZHEAD_SIZE + ZBUF_SIZE) - Container::HEADER_SIZE);
      tgeElementptr = tgeContainerptr - 1;
      tgeElemStep = 0 - TelemLen;
      tgeForward = (Uint32)-1;
      if (unlikely(tgeContainerptr >= 2048)) 
      { 
	errcode = 4;
	goto error;
      }
      tgeRemLen = ContainerHeader(gePageptr.p->word32[tgeContainerptr]).getLength();
      if (unlikely((tgeContainerptr - tgeRemLen) >= 2048)) 
      { 
	errcode = 5;
	goto error;
      }
    } else {
      errcode = 6;
      goto error;
    }//if
    if (tgeRemLen >= Container::HEADER_SIZE + TelemLen) {
      if (unlikely(tgeRemLen > ZBUF_SIZE)) 
      {
	errcode = 7;
	goto error;
      }//if
      /* ------------------------------------------------------------------- */
      // There is at least one element in this container. 
      // Check if it is the element searched for.
      /* ------------------------------------------------------------------- */
      do {
        bool possible_match;
        tgeElementHeader = gePageptr.p->word32[tgeElementptr];
        tgeRemLen = tgeRemLen - TelemLen;
	Uint32 localkey1, localkey2;
	lockOwnerPtr.i = RNIL;
	lockOwnerPtr.p = NULL;
        LHBits16 reducedHashValue;
        if (ElementHeader::getLocked(tgeElementHeader)) {
          jam();
	  lockOwnerPtr.i = ElementHeader::getOpPtrI(tgeElementHeader);
          ptrCheckGuard(lockOwnerPtr, coprecsize, operationrec);
          possible_match = lockOwnerPtr.p->hashValue.match(operationRecPtr.p->hashValue);
          reducedHashValue = lockOwnerPtr.p->reducedHashValue;
	  localkey1 = lockOwnerPtr.p->localdata[0];
	  localkey2 = lockOwnerPtr.p->localdata[1];
        } else {
          jam();
          reducedHashValue = ElementHeader::getReducedHashValue(tgeElementHeader);
          Uint32 pos = tgeElementptr + tgeForward;
          localkey1 = gePageptr.p->word32[pos];
          if (likely(localkeylen == 1))
          {
            localkey2 = Local_key::ref2page_idx(localkey1);
            localkey1 = Local_key::ref2page_id(localkey1);
          }
          else
          {
            localkey2 = gePageptr.p->word32[pos + tgeForward];
          }
          possible_match = true;
        }
        if (possible_match &&
            operationRecPtr.p->hashValue.match(fragrecptr.p->level.enlarge(reducedHashValue, bucket_number)))
        {
          jam();
          bool found;
          if (! searchLocalKey) 
	  {
            Uint32 len = readTablePk(localkey1, localkey2, tgeElementHeader,
				     lockOwnerPtr);
            found = (len == operationRecPtr.p->xfrmtupkeylen) &&
	      (memcmp(Tkeydata, ckeys, len << 2) == 0);
          } else {
            jam();
            found = (localkey1 == Tkeydata[0] && localkey2 == Tkeydata[1]);
          }
          if (found) 
	  {
            jam();
            operationRecPtr.p->localdata[0] = localkey1;
            operationRecPtr.p->localdata[1] = localkey2;
            return ZTRUE;
          }
        }
        if (tgeRemLen <= Container::HEADER_SIZE) {
          break;
        }
        tgeElementptr = tgeElementptr + tgeElemStep;
      } while (true);
    }//if
    if (unlikely(tgeRemLen != Container::HEADER_SIZE))
    {
      errcode = 8;
      goto error;
    }//if
    ContainerHeader containerhead = gePageptr.p->word32[tgeContainerptr];
    tgeNextptrtype = containerhead.getNextEnd();
    if (tgeNextptrtype == 0) {
      jam();
      return ZFALSE;	/* NO MORE CONTAINER */
    }//if
    tgePageindex = containerhead.getNextIndexNumber();	/* NEXT CONTAINER PAGE INDEX 7 BITS */
    if (unlikely(tgePageindex > Container::NO_CONTAINER_INDEX))
    {
      errcode = 9;
      goto error;
    }//if
    if (!containerhead.isNextOnSamePage()) {
      jam();
      gePageptr.i = gePageptr.p->word32[tgeContainerptr + 1];	/* NEXT PAGE ID */
      ptrCheckGuard(gePageptr, cpagesize, page8);
    }//if
  } while (1);

  return ZFALSE;

error:
  ACCKEY_error(errcode);
  return ~0;
}//Dbacc::getElement()

/* ------------------------------------------------------------------------- */
/* ------------------------------------------------------------------------- */
/* ------------------------------------------------------------------------- */
/*                                                                           */
/*       END OF GET_ELEMENT MODULE                                           */
/*                                                                           */
/* ------------------------------------------------------------------------- */
/* ------------------------------------------------------------------------- */
/* ------------------------------------------------------------------------- */
/* ------------------------------------------------------------------------- */
/* ------------------------------------------------------------------------- */
/*                                                                           */
/*       MODULE:         DELETE                                              */
/*                                                                           */
/* ------------------------------------------------------------------------- */
/* ------------------------------------------------------------------------- */
/* ------------------------------------------------------------------------- */
/* COMMITDELETE                                                              */
/*         INPUT: OPERATION_REC_PTR, PTR TO AN OPERATION RECORD.             */
/*                FRAGRECPTR, PTR TO A FRAGMENT RECORD                       */
/*                                                                           */
/*         OUTPUT:                                                           */
/*                NONE                                                       */
/*         DESCRIPTION: DELETE OPERATIONS WILL BE COMPLETED AT THE 
 *         COMMIT OF TRANSACTION. THIS SUBROUTINE SEARCHS FOR ELEMENT AND 
 *         DELETES IT. IT DOES SO BY REPLACING IT WITH THE LAST 
 *         ELEMENT IN THE BUCKET. IF THE DELETED ELEMENT IS ALSO THE LAST 
 *         ELEMENT THEN IT IS ONLY NECESSARY TO REMOVE THE ELEMENT
 * ------------------------------------------------------------------------- */
void
Dbacc::report_dealloc(Signal* signal, const Operationrec* opPtrP)
{
  Uint32 localKey1 = opPtrP->localdata[0];
  Uint32 localKey2 = opPtrP->localdata[1];
  Uint32 opbits = opPtrP->m_op_bits;
  Uint32 userptr= opPtrP->userptr;
  Uint32 scanInd = 
    ((opbits & Operationrec::OP_MASK) == ZSCAN_OP) || 
    (opbits & Operationrec::OP_LOCK_REQ);
  
  if (! Local_key::isInvalid(localKey1, localKey2))
  {
    signal->theData[0] = fragrecptr.p->myfid;
    signal->theData[1] = fragrecptr.p->myTableId;
    signal->theData[2] = localKey1;
    signal->theData[3] = localKey2;
    signal->theData[4] = userptr;
    signal->theData[5] = scanInd;
    EXECUTE_DIRECT(DBLQH, GSN_TUP_DEALLOCREQ, signal, 6);
    jamEntry();
  }
}

void Dbacc::commitdelete(Signal* signal)
{
  jam();
  report_dealloc(signal, operationRecPtr.p);
  
  getdirindex(signal);
  tlastPageindex = tgdiPageindex;
  lastPageptr.i = gdiPageptr.i;
  lastPageptr.p = gdiPageptr.p;
  tlastForward = ZTRUE;
  tlastContainerptr = mul_ZBUF_SIZE(tlastPageindex);
  tlastContainerptr = tlastContainerptr + ZHEAD_SIZE;
  arrGuard(tlastContainerptr, 2048);
  ContainerHeader containerhead(lastPageptr.p->word32[tlastContainerptr]);
  tlastContainerlen = containerhead.getLength();
  lastPrevpageptr.i = RNIL;
  ptrNull(lastPrevpageptr);
  tlastPrevconptr = 0;
  getLastAndRemove(signal, containerhead);

  delPageptr.i = operationRecPtr.p->elementPage;
  ptrCheckGuard(delPageptr, cpagesize, page8);
  tdelElementptr = operationRecPtr.p->elementPointer;
  /* --------------------------------------------------------------------------------- */
  // Here we have to take extreme care since we do not want locks to end up after the
  // log execution. Thus it is necessary to put back the element in unlocked shape.
  // We thus update the element header to ensure we log an unlocked element. We do not
  // need to restore it later since it is deleted immediately anyway.
  /* --------------------------------------------------------------------------------- */
  const Uint32 eh = ElementHeader::setUnlocked(0, LHBits16());
  delPageptr.p->word32[tdelElementptr] = eh;
  if (operationRecPtr.p->elementPage == lastPageptr.i) {
    if (operationRecPtr.p->elementPointer == tlastElementptr) {
      jam();
      /* --------------------------------------------------------------------------------- */
      /*  THE LAST ELEMENT WAS THE ELEMENT TO BE DELETED. WE NEED NOT COPY IT.             */
      /* --------------------------------------------------------------------------------- */
      return;
    }//if
  }//if
  /* --------------------------------------------------------------------------------- */
  /*  THE DELETED ELEMENT IS NOT THE LAST. WE READ THE LAST ELEMENT AND OVERWRITE THE  */
  /*  DELETED ELEMENT.                                                                 */
  /* --------------------------------------------------------------------------------- */
  tdelContainerptr = operationRecPtr.p->elementContainer;
  tdelForward = operationRecPtr.p->elementIsforward;
  deleteElement(signal);
}//Dbacc::commitdelete()

/* --------------------------------------------------------------------------------- */
/* DELETE_ELEMENT                                                                    */
/*        INPUT: FRAGRECPTR, POINTER TO A FRAGMENT RECORD                            */
/*               LAST_PAGEPTR, POINTER TO THE PAGE OF THE LAST ELEMENT               */
/*               DEL_PAGEPTR, POINTER TO THE PAGE OF THE DELETED ELEMENT             */
/*               TLAST_ELEMENTPTR, ELEMENT POINTER OF THE LAST ELEMENT               */
/*               TDEL_ELEMENTPTR, ELEMENT POINTER OF THE DELETED ELEMENT             */
/*               TLAST_FORWARD, DIRECTION OF LAST ELEMENT                            */
/*               TDEL_FORWARD, DIRECTION OF DELETED ELEMENT                          */
/*               TDEL_CONTAINERPTR, CONTAINER POINTER OF DELETED ELEMENT             */
/*        DESCRIPTION: COPY LAST ELEMENT TO DELETED ELEMENT AND UPDATE UNDO LOG AND  */
/*                     UPDATE ANY ACTIVE OPERATION ON THE MOVED ELEMENT.             */
/* --------------------------------------------------------------------------------- */
void Dbacc::deleteElement(Signal* signal) 
{
  OperationrecPtr deOperationRecPtr;
  Uint32 tdeIndex;
  Uint32 tlastMoveElemptr;
  Uint32 tdelMoveElemptr;
  Uint32 guard31;

  if (tlastElementptr >= 2048)
    goto deleteElement_index_error1;
  {
    const Uint32 tdeElemhead = lastPageptr.p->word32[tlastElementptr];
    tlastMoveElemptr = tlastElementptr;
    tdelMoveElemptr = tdelElementptr;
    guard31 = fragrecptr.p->elementLength - 1;
    for (tdeIndex = 0; tdeIndex <= guard31; tdeIndex++) {
      dbgWord32(delPageptr, tdelMoveElemptr, lastPageptr.p->word32[tlastMoveElemptr]);
      if ((tlastMoveElemptr >= 2048) ||
	  (tdelMoveElemptr >= 2048))
	goto deleteElement_index_error2;
      delPageptr.p->word32[tdelMoveElemptr] = lastPageptr.p->word32[tlastMoveElemptr];
      tdelMoveElemptr = tdelMoveElemptr + tdelForward;
      tlastMoveElemptr = tlastMoveElemptr + tlastForward;
    }//for
    if (ElementHeader::getLocked(tdeElemhead)) {
      /* --------------------------------------------------------------------------------- */
      /* THE LAST ELEMENT IS LOCKED AND IS THUS REFERENCED BY AN OPERATION RECORD. WE NEED */
      /* TO UPDATE THE OPERATION RECORD WITH THE NEW REFERENCE TO THE ELEMENT.             */
      /* --------------------------------------------------------------------------------- */
      deOperationRecPtr.i = ElementHeader::getOpPtrI(tdeElemhead);
      ptrCheckGuard(deOperationRecPtr, coprecsize, operationrec);
      deOperationRecPtr.p->elementPage = delPageptr.i;
      deOperationRecPtr.p->elementContainer = tdelContainerptr;
      deOperationRecPtr.p->elementPointer = tdelElementptr;
      deOperationRecPtr.p->elementIsforward = tdelForward;
      /* --------------------------------------------------------------------------------- */
      // We need to take extreme care to not install locked records after system restart.
      // An undo of the delete will reinstall the moved record. We have to ensure that the
      // lock is removed to ensure that no such thing happen.
      /* --------------------------------------------------------------------------------- */
      Uint32 eh = ElementHeader::setUnlocked(0, LHBits16());
      lastPageptr.p->word32[tlastElementptr] = eh;
    }//if
    return;
  }

 deleteElement_index_error1:
  arrGuard(tlastElementptr, 2048);
  return;

 deleteElement_index_error2:
  arrGuard(tdelMoveElemptr + guard31, 2048);
  arrGuard(tlastMoveElemptr, 2048);
  return;

}//Dbacc::deleteElement()

/* --------------------------------------------------------------------------------- */
/* GET_LAST_AND_REMOVE                                                               */
/*        INPUT:                                                                     */
/*               LAST_PAGEPTR       PAGE POINTER OF FIRST CONTAINER IN SEARCH OF LAST*/
/*               TLAST_CONTAINERPTR CONTAINER INDEX OF THE SAME                      */
/*               TLAST_CONTAINERHEAD CONTAINER HEADER OF THE SAME                    */
/*               TLAST_PAGEINDEX    PAGE INDEX OF THE SAME                           */
/*               TLAST_FORWARD      CONTAINER DIRECTION OF THE SAME                  */
/*               TLAST_CONTAINERLEN CONTAINER LENGTH OF THE SAME                     */
/*               LAST_PREVPAGEPTR   PAGE POINTER OF PREVIOUS CONTAINER OF THE SAME   */
/*               TLAST_PREVCONPTR   CONTAINER INDEX OF PREVIOUS CONTAINER OF THE SAME*/
/*                                                                                   */
/*       OUTPUT:                                                                     */
/*               ALL VARIABLES FROM INPUT BUT NOW CONTAINING INFO ABOUT LAST         */
/*               CONTAINER.                                                          */
/*               TLAST_ELEMENTPTR   LAST ELEMENT POINTER IN LAST CONTAINER           */
/* --------------------------------------------------------------------------------- */
void Dbacc::getLastAndRemove(Signal* signal, ContainerHeader& containerhead)
{
 GLR_LOOP_10:
  if (containerhead.getNextEnd() != 0) {
    jam();
    lastPrevpageptr.i = lastPageptr.i;
    lastPrevpageptr.p = lastPageptr.p;
    tlastPrevconptr = tlastContainerptr;
    tlastPageindex = containerhead.getNextIndexNumber();
    if (!containerhead.isNextOnSamePage()) {
      jam();
      arrGuard(tlastContainerptr + 1, 2048);
      lastPageptr.i = lastPageptr.p->word32[tlastContainerptr + 1];
      ptrCheckGuard(lastPageptr, cpagesize, page8);
    }//if
    tlastContainerptr = mul_ZBUF_SIZE(tlastPageindex);
    if (containerhead.getNextEnd() == ZLEFT) {
      jam();
      tlastForward = ZTRUE;
      tlastContainerptr = tlastContainerptr + ZHEAD_SIZE;
    } else if (containerhead.getNextEnd() == ZRIGHT) {
      jam();
      tlastForward = cminusOne;
      tlastContainerptr = ((tlastContainerptr + ZHEAD_SIZE) + ZBUF_SIZE) - Container::HEADER_SIZE;
    } else {
      ndbrequire(false);
      return;
    }//if
    arrGuard(tlastContainerptr, 2048);
    containerhead = lastPageptr.p->word32[tlastContainerptr];
    tlastContainerlen = containerhead.getLength();
    ndbrequire(tlastContainerlen >= ((Uint32)Container::HEADER_SIZE + fragrecptr.p->elementLength));
    goto GLR_LOOP_10;
  }//if
  tlastContainerlen = tlastContainerlen - fragrecptr.p->elementLength;
  if (tlastForward == ZTRUE) {
    jam();
    tlastElementptr = tlastContainerptr + tlastContainerlen;
  } else {
    jam();
    tlastElementptr = (tlastContainerptr + (Container::HEADER_SIZE - 1)) - tlastContainerlen;
  }//if
  rlPageptr = lastPageptr;
  trlPageindex = tlastPageindex;
  if (containerhead.isUsingBothEnds()) {
    /* --------------------------------------------------------------------------------- */
    /*       WE HAVE OWNERSHIP OF BOTH PARTS OF THE CONTAINER ENDS.                      */
    /* --------------------------------------------------------------------------------- */
    if (tlastContainerlen < Container::DOWN_LIMIT) {
      /* --------------------------------------------------------------------------------- */
      /*       WE HAVE DECREASED THE SIZE BELOW THE DOWN LIMIT, WE MUST GIVE UP THE OTHER  */
      /*       SIDE OF THE BUFFER.                                                         */
      /* --------------------------------------------------------------------------------- */
      containerhead.clearUsingBothEnds();
      if (tlastForward == ZTRUE) {
        jam();
        turlIndex = tlastContainerptr + (ZBUF_SIZE - Container::HEADER_SIZE);
        releaseRightlist(signal);
      } else {
        jam();
        tullIndex = tlastContainerptr - (ZBUF_SIZE - Container::HEADER_SIZE);
        releaseLeftlist(signal);
      }//if
    }//if
  }//if
  if (tlastContainerlen <= 2) {
    ndbrequire(tlastContainerlen == 2);
    if (lastPrevpageptr.i != RNIL) {
      jam();
      /* --------------------------------------------------------------------------------- */
      /*  THE LAST CONTAINER IS EMPTY AND IS NOT THE FIRST CONTAINER WHICH IS NOT REMOVED. */
      /*  DELETE THE LAST CONTAINER AND UPDATE THE PREVIOUS CONTAINER. ALSO PUT THIS       */
      /*  CONTAINER IN FREE CONTAINER LIST OF THE PAGE.                                    */
      /* --------------------------------------------------------------------------------- */
      ndbrequire(tlastPrevconptr < 2048);
      Uint32 tglrTmp = ContainerHeader(lastPrevpageptr.p->word32[tlastPrevconptr]).clearNext();
      dbgWord32(lastPrevpageptr, tlastPrevconptr, tglrTmp);
      lastPrevpageptr.p->word32[tlastPrevconptr] = tglrTmp;
      ndbrequire(ContainerHeader(rlPageptr.p->word32[tlastContainerptr]).isInUse());
      if (tlastForward == ZTRUE) {
        jam();
        tullIndex = tlastContainerptr;
        releaseLeftlist(signal);
      } else {
        jam();
        turlIndex = tlastContainerptr;
        releaseRightlist(signal);
      }//if
      return;
    }//if
  }//if
  ContainerHeader conthead = containerhead;
  conthead.setLength(tlastContainerlen);
  dbgWord32(lastPageptr, tlastContainerptr, conthead);
  arrGuard(tlastContainerptr, 2048);
  lastPageptr.p->word32[tlastContainerptr] = conthead;
}//Dbacc::getLastAndRemove()

/* --------------------------------------------------------------------------------- */
/* RELEASE_LEFTLIST                                                                  */
/*       INPUT:                                                                      */
/*               RL_PAGEPTR              PAGE POINTER OF CONTAINER TO BE RELEASED    */
/*               TRL_PAGEINDEX           PAGE INDEX OF CONTAINER TO BE RELEASED      */
/*               TURL_INDEX              INDEX OF CONTAINER TO BE RELEASED           */
/*               TRL_REL_CON             TRUE IF CONTAINER RELEASED OTHERWISE ONLY   */
/*                                       A PART IS RELEASED.                         */
/*                                                                                   */
/*       OUTPUT:                                                                     */
/*               NONE                                                                */
/*                                                                                   */
/*          THE FREE LIST OF LEFT FREE BUFFER IN THE PAGE WILL BE UPDATE             */
/*     TULL_INDEX IS INDEX TO THE FIRST WORD IN THE LEFT SIDE OF THE BUFFER          */
/* --------------------------------------------------------------------------------- */
void Dbacc::releaseLeftlist(Signal* signal) 
{
  Uint32 tullTmp;
  Uint32 tullTmp1;

  dbgWord32(rlPageptr, tullIndex + 1, Container::NO_CONTAINER_INDEX);
  arrGuard(tullIndex + 1, 2048);
  rlPageptr.p->word32[tullIndex + 1] = Container::NO_CONTAINER_INDEX;
  tullTmp1 = (rlPageptr.p->word32[ZPOS_EMPTY_LIST] >> 7) & 0x7f;
  dbgWord32(rlPageptr, tullIndex, tullTmp1);
  arrGuard(tullIndex, 2048);
  rlPageptr.p->word32[tullIndex] = tullTmp1;
  if (tullTmp1 <= Container::MAX_CONTAINER_INDEX) {
    jam();
    tullTmp1 = mul_ZBUF_SIZE(tullTmp1);
    tullTmp1 = (tullTmp1 + ZHEAD_SIZE) + 1;
    dbgWord32(rlPageptr, tullTmp1, trlPageindex);
    rlPageptr.p->word32[tullTmp1] = trlPageindex;	/* UPDATES PREV POINTER IN THE NEXT FREE */
  } else {
    ndbrequire(tullTmp1 == Container::NO_CONTAINER_INDEX);
  }//if
  tullTmp = rlPageptr.p->word32[ZPOS_EMPTY_LIST];
  tullTmp = (((tullTmp >> 14) << 14) | (trlPageindex << 7)) | (tullTmp & 0x7f);
  dbgWord32(rlPageptr, ZPOS_EMPTY_LIST, tullTmp);
  rlPageptr.p->word32[ZPOS_EMPTY_LIST] = tullTmp;
  dbgWord32(rlPageptr, ZPOS_ALLOC_CONTAINERS, rlPageptr.p->word32[ZPOS_ALLOC_CONTAINERS] - 1);
  rlPageptr.p->word32[ZPOS_ALLOC_CONTAINERS] = rlPageptr.p->word32[ZPOS_ALLOC_CONTAINERS] - 1;
  ndbrequire(rlPageptr.p->word32[ZPOS_ALLOC_CONTAINERS] <= ZNIL);
  if (((rlPageptr.p->word32[ZPOS_EMPTY_LIST] >> ZPOS_PAGE_TYPE_BIT) & 3) == 1) {
    jam();
    colPageptr = rlPageptr;
    ptrCheck(colPageptr, cpagesize, page8);
    checkoverfreelist(signal);
  }//if
}//Dbacc::releaseLeftlist()

/* --------------------------------------------------------------------------------- */
/* RELEASE_RIGHTLIST                                                                 */
/*       INPUT:                                                                      */
/*               RL_PAGEPTR              PAGE POINTER OF CONTAINER TO BE RELEASED    */
/*               TRL_PAGEINDEX           PAGE INDEX OF CONTAINER TO BE RELEASED      */
/*               TURL_INDEX              INDEX OF CONTAINER TO BE RELEASED           */
/*               TRL_REL_CON             TRUE IF CONTAINER RELEASED OTHERWISE ONLY   */
/*                                       A PART IS RELEASED.                         */
/*                                                                                   */
/*       OUTPUT:                                                                     */
/*               NONE                                                                */
/*                                                                                   */
/*         THE FREE LIST OF RIGHT FREE BUFFER IN THE PAGE WILL BE UPDATE.            */
/*         TURL_INDEX IS INDEX TO THE FIRST WORD IN THE RIGHT SIDE OF                */
/*         THE BUFFER, WHICH IS THE LAST WORD IN THE BUFFER.                         */
/* --------------------------------------------------------------------------------- */
void Dbacc::releaseRightlist(Signal* signal) 
{
  Uint32 turlTmp1;
  Uint32 turlTmp;

  dbgWord32(rlPageptr, turlIndex + 1, Container::NO_CONTAINER_INDEX);
  arrGuard(turlIndex + 1, 2048);
  rlPageptr.p->word32[turlIndex + 1] = Container::NO_CONTAINER_INDEX;
  turlTmp1 = rlPageptr.p->word32[ZPOS_EMPTY_LIST] & 0x7f;
  dbgWord32(rlPageptr, turlIndex, turlTmp1);
  arrGuard(turlIndex, 2048);
  rlPageptr.p->word32[turlIndex] = turlTmp1;
  if (turlTmp1 <= Container::MAX_CONTAINER_INDEX) {
    jam();
    turlTmp = mul_ZBUF_SIZE(turlTmp1);
    turlTmp = turlTmp + ((ZHEAD_SIZE + ZBUF_SIZE) - (Container::HEADER_SIZE - 1));
    dbgWord32(rlPageptr, turlTmp, trlPageindex);
    rlPageptr.p->word32[turlTmp] = trlPageindex;	/* UPDATES PREV POINTER IN THE NEXT FREE */
  } else {
    ndbrequire(turlTmp1 == Container::NO_CONTAINER_INDEX);
  }//if
  turlTmp = rlPageptr.p->word32[ZPOS_EMPTY_LIST];
  dbgWord32(rlPageptr, ZPOS_EMPTY_LIST, ((turlTmp >> 7) << 7) | trlPageindex);
  rlPageptr.p->word32[ZPOS_EMPTY_LIST] = ((turlTmp >> 7) << 7) | trlPageindex;
  dbgWord32(rlPageptr, ZPOS_ALLOC_CONTAINERS, rlPageptr.p->word32[ZPOS_ALLOC_CONTAINERS] - 1);
  rlPageptr.p->word32[ZPOS_ALLOC_CONTAINERS] = rlPageptr.p->word32[ZPOS_ALLOC_CONTAINERS] - 1;
  ndbrequire(rlPageptr.p->word32[ZPOS_ALLOC_CONTAINERS] <= ZNIL);
  if (((rlPageptr.p->word32[ZPOS_EMPTY_LIST] >> ZPOS_PAGE_TYPE_BIT) & 3) == 1) {
    jam();
    colPageptr = rlPageptr;
    checkoverfreelist(signal);
  }//if
}//Dbacc::releaseRightlist()

/* --------------------------------------------------------------------------------- */
/* CHECKOVERFREELIST                                                                 */
/*        INPUT: COL_PAGEPTR, POINTER OF AN OVERFLOW PAGE RECORD.                    */
/*        DESCRIPTION: CHECKS IF THE PAGE HAVE TO PUT IN FREE LIST OF OVER FLOW      */
/*                     PAGES. WHEN IT HAVE TO, AN OVERFLOW REC PTR WILL BE ALLOCATED */
/*                     TO KEEP NFORMATION  ABOUT THE PAGE.                           */
/* --------------------------------------------------------------------------------- */
void Dbacc::checkoverfreelist(Signal* signal) 
{
  Uint32 tcolTmp;

// always an overflow page
  tcolTmp = colPageptr.p->word32[ZPOS_ALLOC_CONTAINERS];
  if (tcolTmp == 0) // Just got empty
  {
    jam();
    ropPageptr = colPageptr;
    releaseOverpage(signal);
  }
  else if (tcolTmp == ZFREE_LIMIT) // Just got sparse
  {
    jam();
    LocalContainerPageList fulllist(*this, fragrecptr.p->fullpages);
    LocalContainerPageList sparselist(*this, fragrecptr.p->sparsepages);
    fulllist.remove(colPageptr);
    sparselist.addFirst(colPageptr);
  }//if
}//Dbacc::checkoverfreelist()

/* ------------------------------------------------------------------------- */
/* ------------------------------------------------------------------------- */
/* ------------------------------------------------------------------------- */
/*                                                                           */
/*       END OF DELETE MODULE                                                */
/*                                                                           */
/* ------------------------------------------------------------------------- */
/* ------------------------------------------------------------------------- */
/* ------------------------------------------------------------------------- */
/* ------------------------------------------------------------------------- */
/* ------------------------------------------------------------------------- */
/* ------------------------------------------------------------------------- */
/*                                                                           */
/*       COMMIT AND ABORT MODULE                                             */
/*                                                                           */
/* ------------------------------------------------------------------------- */
/* ------------------------------------------------------------------------- */
/* ------------------------------------------------------------------------- */
/* ------------------------------------------------------------------------- */
/* ABORT_OPERATION                                                           */
/*DESCRIPTION: AN OPERATION RECORD CAN BE IN A LOCK QUEUE OF AN ELEMENT OR   */
/*OWNS THE LOCK. BY THIS SUBROUTINE THE LOCK STATE OF THE OPERATION WILL     */
/*BE CHECKED. THE OPERATION RECORD WILL BE REMOVED FROM THE QUEUE IF IT      */
/*BELONGED TO ANY ONE, OTHERWISE THE ELEMENT HEAD WILL BE UPDATED.           */
/* ------------------------------------------------------------------------- */

/**
 * 
 * P0 - P1 - P2 - P3
 * S0
 * S1
 * S2
 */
void
Dbacc::abortParallelQueueOperation(Signal* signal, OperationrecPtr opPtr)
{
  jam();
  OperationrecPtr nextP;
  OperationrecPtr prevP;
  OperationrecPtr loPtr;

  Uint32 opbits = opPtr.p->m_op_bits;
  Uint32 opstate = opbits & Operationrec::OP_STATE_MASK;
  nextP.i = opPtr.p->nextParallelQue;
  prevP.i = opPtr.p->prevParallelQue;
  loPtr.i = opPtr.p->m_lock_owner_ptr_i;

  ndbassert(! (opbits & Operationrec::OP_LOCK_OWNER));
  ndbassert(opbits & Operationrec::OP_RUN_QUEUE);

  ptrCheckGuard(prevP, coprecsize, operationrec);    
  ndbassert(prevP.p->nextParallelQue == opPtr.i);
  prevP.p->nextParallelQue = nextP.i;
  
  if (nextP.i != RNIL)
  {
    ptrCheckGuard(nextP, coprecsize, operationrec);
    ndbassert(nextP.p->prevParallelQue == opPtr.i);
    nextP.p->prevParallelQue = prevP.i;
  }
  else if (prevP.i != loPtr.i)
  {
    jam();
    ptrCheckGuard(loPtr, coprecsize, operationrec);
    ndbassert(loPtr.p->m_op_bits & Operationrec::OP_LOCK_OWNER);
    ndbassert(loPtr.p->m_lo_last_parallel_op_ptr_i == opPtr.i);
    loPtr.p->m_lo_last_parallel_op_ptr_i = prevP.i;
    prevP.p->m_lock_owner_ptr_i = loPtr.i;
    
    /**
     * Abort P3...check start next
     */
    startNext(signal, prevP);
    validate_lock_queue(prevP);
    return;
  }
  else
  {
    jam();
    /**
     * P0 - P1
     *
     * Abort P1, check start next
     */
    ndbassert(prevP.p->m_op_bits & Operationrec::OP_LOCK_OWNER);
    prevP.p->m_lo_last_parallel_op_ptr_i = RNIL;
    startNext(signal, prevP);
    validate_lock_queue(prevP);
    return;
  }

  /**
   * Abort P1/P2
   */
  if (opbits & Operationrec::OP_LOCK_MODE)
  {
    Uint32 nextbits = nextP.p->m_op_bits;
    while ((nextbits & Operationrec::OP_LOCK_MODE) == 0)
    {
      ndbassert(nextbits & Operationrec::OP_ACC_LOCK_MODE);
      nextbits &= ~(Uint32)Operationrec::OP_ACC_LOCK_MODE;
      nextP.p->m_op_bits = nextbits;
      
      if (nextP.p->nextParallelQue != RNIL)
      {
	nextP.i = nextP.p->nextParallelQue;
	ptrCheckGuard(nextP, coprecsize, operationrec);
	nextbits = nextP.p->m_op_bits;
      }
      else
      {
	break;
      }
    }
  }

  /**
   * Abort P1, P2
   */
  if (opstate == Operationrec::OP_STATE_RUNNING)
  {
    jam();
    startNext(signal, prevP);
    validate_lock_queue(prevP);
    return;
  }
  
  ndbassert(opstate == Operationrec::OP_STATE_EXECUTED ||
	    opstate == Operationrec::OP_STATE_WAITING);
  
  /**
   * Scan to last of run queue
   */
  while (nextP.p->nextParallelQue != RNIL)
  {
    jam();
    nextP.i = nextP.p->nextParallelQue;
    ptrCheckGuard(nextP, coprecsize, operationrec);
  }

#ifdef VM_TRACE
  loPtr.i = nextP.p->m_lock_owner_ptr_i;
  ptrCheckGuard(loPtr, coprecsize, operationrec);
  ndbassert(loPtr.p->m_op_bits & Operationrec::OP_LOCK_OWNER);
  ndbassert(loPtr.p->m_lo_last_parallel_op_ptr_i == nextP.i);
#endif
  startNext(signal, nextP);
  validate_lock_queue(nextP);
  
  return;
}

void 
Dbacc::abortSerieQueueOperation(Signal* signal, OperationrecPtr opPtr) 
{
  jam();
  OperationrecPtr prevS, nextS;
  OperationrecPtr prevP, nextP;
  OperationrecPtr loPtr;

  Uint32 opbits = opPtr.p->m_op_bits;

  prevS.i = opPtr.p->prevSerialQue;
  nextS.i = opPtr.p->nextSerialQue;

  prevP.i = opPtr.p->prevParallelQue;
  nextP.i = opPtr.p->nextParallelQue;

  ndbassert((opbits & Operationrec::OP_LOCK_OWNER) == 0);
  ndbassert((opbits & Operationrec::OP_RUN_QUEUE) == 0);
  
  if (prevP.i != RNIL)
  {
    /**
     * We're not list head...
     */
    ptrCheckGuard(prevP, coprecsize, operationrec);    
    ndbassert(prevP.p->nextParallelQue == opPtr.i);
    prevP.p->nextParallelQue = nextP.i;

    if (nextP.i != RNIL)
    {
      ptrCheckGuard(nextP, coprecsize, operationrec);      
      ndbassert(nextP.p->prevParallelQue == opPtr.i);
      ndbassert((nextP.p->m_op_bits & Operationrec::OP_STATE_MASK) == 
		Operationrec::OP_STATE_WAITING);
      nextP.p->prevParallelQue = prevP.i;
      
      if ((prevP.p->m_op_bits & Operationrec::OP_ACC_LOCK_MODE) == 0 &&
	  opbits & Operationrec::OP_LOCK_MODE)
      {
	/**
	 * Scan right in parallel queue to fix OP_ACC_LOCK_MODE
	 */
	while ((nextP.p->m_op_bits & Operationrec::OP_LOCK_MODE) == 0)
	{
	  ndbassert(nextP.p->m_op_bits & Operationrec::OP_ACC_LOCK_MODE);
	  nextP.p->m_op_bits &= ~(Uint32)Operationrec::OP_ACC_LOCK_MODE;
	  nextP.i = nextP.p->nextParallelQue;
	  if (nextP.i == RNIL)
	    break;
	  ptrCheckGuard(nextP, coprecsize, operationrec);	  
	}
      }
    }
    validate_lock_queue(prevP);
    return;
  }
  else
  {
    /**
     * We're a list head
     */
    ptrCheckGuard(prevS, coprecsize, operationrec);      
    ndbassert(prevS.p->nextSerialQue == opPtr.i);
    
    if (nextP.i != RNIL)
    {
      /**
       * Promote nextP to list head
       */
      ptrCheckGuard(nextP, coprecsize, operationrec);      
      ndbassert(nextP.p->prevParallelQue == opPtr.i);
      prevS.p->nextSerialQue = nextP.i;
      nextP.p->prevParallelQue = RNIL;
      nextP.p->nextSerialQue = nextS.i;
      if (nextS.i != RNIL)
      {
	jam();
	ptrCheckGuard(nextS, coprecsize, operationrec); 	
	ndbassert(nextS.p->prevSerialQue == opPtr.i);
	nextS.p->prevSerialQue = nextP.i;
	validate_lock_queue(prevS);
	return;
      }
      else
      {
	// nextS is RNIL, i.e we're last in serie queue...
	// we must update lockOwner.m_lo_last_serial_op_ptr_i
	loPtr = prevS;
	while ((loPtr.p->m_op_bits & Operationrec::OP_LOCK_OWNER) == 0)
	{
	  loPtr.i = loPtr.p->prevSerialQue;
	  ptrCheckGuard(loPtr, coprecsize, operationrec);	  
	}
	ndbassert(loPtr.p->m_lo_last_serial_op_ptr_i == opPtr.i);
	loPtr.p->m_lo_last_serial_op_ptr_i = nextP.i;
	validate_lock_queue(loPtr);
	return;
      }
    }
    
    if (nextS.i == RNIL)
    {
      /**
       * Abort S2
       */

      // nextS is RNIL, i.e we're last in serie queue...
      // and we have no parallel queue, 
      // we must update lockOwner.m_lo_last_serial_op_ptr_i
      prevS.p->nextSerialQue = RNIL;
      
      loPtr = prevS;
      while ((loPtr.p->m_op_bits & Operationrec::OP_LOCK_OWNER) == 0)
      {
	loPtr.i = loPtr.p->prevSerialQue;
	ptrCheckGuard(loPtr, coprecsize, operationrec);	  
      }
      ndbassert(loPtr.p->m_lo_last_serial_op_ptr_i == opPtr.i);
      if (prevS.i != loPtr.i)
      {
	jam();
	loPtr.p->m_lo_last_serial_op_ptr_i = prevS.i;
      }
      else
      {
	loPtr.p->m_lo_last_serial_op_ptr_i = RNIL;
      }
      validate_lock_queue(loPtr);
    }
    else if (nextP.i == RNIL)
    {
      ptrCheckGuard(nextS, coprecsize, operationrec);      
      ndbassert(nextS.p->prevSerialQue == opPtr.i);
      prevS.p->nextSerialQue = nextS.i;
      nextS.p->prevSerialQue = prevS.i;
      
      if (prevS.p->m_op_bits & Operationrec::OP_LOCK_OWNER)
      {
	/**
	 * Abort S0
	 */
	OperationrecPtr lastOp;
	lastOp.i = prevS.p->m_lo_last_parallel_op_ptr_i;
	if (lastOp.i != RNIL)
	{
	  jam();
	  ptrCheckGuard(lastOp, coprecsize, operationrec);	
	  ndbassert(lastOp.p->m_lock_owner_ptr_i == prevS.i);
	}
	else
	{
	  jam();
	  lastOp = prevS;
	}
	startNext(signal, lastOp);
	validate_lock_queue(lastOp);
      }
      else
      {
	validate_lock_queue(prevS);
      }
    }
  }
}


void Dbacc::abortOperation(Signal* signal) 
{
  Uint32 opbits = operationRecPtr.p->m_op_bits;

  validate_lock_queue(operationRecPtr);

  if (opbits & Operationrec::OP_LOCK_OWNER) 
  {
    takeOutLockOwnersList(signal, operationRecPtr);
    opbits &= ~(Uint32)Operationrec::OP_LOCK_OWNER;
    if (opbits & Operationrec::OP_INSERT_IS_DONE)
    { 
      jam();
      opbits |= Operationrec::OP_ELEMENT_DISAPPEARED;
    }//if
    operationRecPtr.p->m_op_bits = opbits;
    const bool queue = (operationRecPtr.p->nextParallelQue != RNIL ||
			operationRecPtr.p->nextSerialQue != RNIL);
    
    if (queue)
    {
      jam();
      release_lockowner(signal, operationRecPtr, false);
    } 
    else 
    {
      /* -------------------------------------------------------------------
       * WE ARE OWNER OF THE LOCK AND NO OTHER OPERATIONS ARE QUEUED. 
       * IF INSERT OR STANDBY WE DELETE THE ELEMENT OTHERWISE WE REMOVE 
       * THE LOCK FROM THE ELEMENT.
       * ------------------------------------------------------------------ */
      if ((opbits & Operationrec::OP_ELEMENT_DISAPPEARED) == 0)
      {
        jam();
	Page8Ptr aboPageidptr;
	Uint32 taboElementptr;
	Uint32 tmp2Olq;

        taboElementptr = operationRecPtr.p->elementPointer;
        aboPageidptr.i = operationRecPtr.p->elementPage;
        tmp2Olq = ElementHeader::setUnlocked(operationRecPtr.p->scanBits, operationRecPtr.p->reducedHashValue);
        ptrCheckGuard(aboPageidptr, cpagesize, page8);
        dbgWord32(aboPageidptr, taboElementptr, tmp2Olq);
        arrGuard(taboElementptr, 2048);
        aboPageidptr.p->word32[taboElementptr] = tmp2Olq;
        return;
      } 
      else 
      {
        jam();
        commitdelete(signal);
      }//if
    }//if
  } 
  else if (opbits & Operationrec::OP_RUN_QUEUE)
  {
    abortParallelQueueOperation(signal, operationRecPtr);
  }
  else
  {
    abortSerieQueueOperation(signal, operationRecPtr);
  }
}

void
Dbacc::commitDeleteCheck()
{
  OperationrecPtr opPtr;
  OperationrecPtr lastOpPtr;
  OperationrecPtr deleteOpPtr;
  Uint32 elementDeleted = 0;
  bool deleteCheckOngoing = true;
  LHBits32 hashValue;
  lastOpPtr = operationRecPtr;
  opPtr.i = operationRecPtr.p->nextParallelQue;
  while (opPtr.i != RNIL) {
    jam();
    ptrCheckGuard(opPtr, coprecsize, operationrec);
    lastOpPtr = opPtr;
    opPtr.i = opPtr.p->nextParallelQue;
  }//while
  deleteOpPtr = lastOpPtr;
  do {
    Uint32 opbits = deleteOpPtr.p->m_op_bits;
    Uint32 op = opbits & Operationrec::OP_MASK;
    if (op == ZDELETE) {
      jam();
      /* -------------------------------------------------------------------
       * IF THE CURRENT OPERATION TO BE COMMITTED IS A DELETE OPERATION DUE TO
       * A SCAN-TAKEOVER THE ACTUAL DELETE WILL BE PERFORMED BY THE PREVIOUS 
       * OPERATION (SCAN) IN THE PARALLEL QUEUE WHICH OWNS THE LOCK.
       * THE PROBLEM IS THAT THE SCAN OPERATION DOES NOT HAVE A HASH VALUE 
       * ASSIGNED TO IT SO WE COPY IT FROM THIS OPERATION.
       *
       * WE ASSUME THAT THIS SOLUTION WILL WORK BECAUSE THE ONLY WAY A 
       * SCAN CAN PERFORM A DELETE IS BY BEING FOLLOWED BY A NORMAL 
       * DELETE-OPERATION THAT HAS A HASH VALUE.
       * ----------------------------------------------------------------- */
      hashValue = deleteOpPtr.p->hashValue;
      elementDeleted = Operationrec::OP_ELEMENT_DISAPPEARED;
      deleteCheckOngoing = false;
    } else if (op == ZREAD || op == ZSCAN_OP) {
      /* -------------------------------------------------------------------
       * We are trying to find out whether the commit will in the end delete 
       * the tuple. Normally the delete will be the last operation in the 
       * list of operations on this. It is however possible to issue reads 
       * and scans in the same savepoint as the delete operation was issued 
       * and these can end up after the delete in the list of operations 
       * in the parallel queue. Thus if we discover a read or a scan 
       * we have to continue scanning the list looking for a delete operation.
       */
      deleteOpPtr.i = deleteOpPtr.p->prevParallelQue;
      if (opbits & Operationrec::OP_LOCK_OWNER) {
        jam();
        deleteCheckOngoing = false;
      } else {
        jam();
        ptrCheckGuard(deleteOpPtr, coprecsize, operationrec);
      }//if
    } else {
      jam();
      /* ------------------------------------------------------------------ */
      /* Finding an UPDATE or INSERT before finding a DELETE 
       * means we cannot be deleting as the end result of this transaction.
       */
      deleteCheckOngoing = false;
    }//if
  } while (deleteCheckOngoing);
  opPtr = lastOpPtr;
  do {
    jam();
    opPtr.p->m_op_bits |= Operationrec::OP_COMMIT_DELETE_CHECK;
    if (elementDeleted) {
      jam();
      opPtr.p->m_op_bits |= elementDeleted;
      opPtr.p->hashValue = hashValue;
    }//if
    opPtr.i = opPtr.p->prevParallelQue;
    if (opPtr.p->m_op_bits & Operationrec::OP_LOCK_OWNER) {
      jam();
      break;
    }//if
    ptrCheckGuard(opPtr, coprecsize, operationrec);
  } while (true);
}//Dbacc::commitDeleteCheck()

/* ------------------------------------------------------------------------- */
/* COMMIT_OPERATION                                                          */
/* INPUT: OPERATION_REC_PTR, POINTER TO AN OPERATION RECORD                  */
/* DESCRIPTION: THE OPERATION RECORD WILL BE TAKE OUT OF ANY LOCK QUEUE.     */
/*         IF IT OWNS THE ELEMENT LOCK. HEAD OF THE ELEMENT WILL BE UPDATED. */
/* ------------------------------------------------------------------------- */
void Dbacc::commitOperation(Signal* signal) 
{
  validate_lock_queue(operationRecPtr);

  Uint32 opbits = operationRecPtr.p->m_op_bits;
  Uint32 op = opbits & Operationrec::OP_MASK;
  ndbrequire((opbits & Operationrec::OP_STATE_MASK) == Operationrec::OP_STATE_EXECUTED);
  if ((opbits & Operationrec::OP_COMMIT_DELETE_CHECK) == 0 && 
      (op != ZREAD && op != ZSCAN_OP))
  {
    jam();
    /*  This method is used to check whether the end result of the transaction
        will be to delete the tuple. In this case all operation will be marked
        with elementIsDisappeared = true to ensure that the last operation
        committed will remove the tuple. We only run this once per transaction
        (commitDeleteCheckFlag = true if performed earlier) and we don't
        execute this code when committing a scan operation since committing
        a scan operation only means that the scan is continuing and the scan
        lock is released.
    */
    commitDeleteCheck();
    opbits = operationRecPtr.p->m_op_bits;
  }//if

  ndbassert(opbits & Operationrec::OP_RUN_QUEUE);
  
  if (opbits & Operationrec::OP_LOCK_OWNER) 
  {
    takeOutLockOwnersList(signal, operationRecPtr);
    opbits &= ~(Uint32)Operationrec::OP_LOCK_OWNER;
    operationRecPtr.p->m_op_bits = opbits;
    
    const bool queue = (operationRecPtr.p->nextParallelQue != RNIL ||
			operationRecPtr.p->nextSerialQue != RNIL);
    
    if (!queue && (opbits & Operationrec::OP_ELEMENT_DISAPPEARED) == 0) 
    {
      /* 
       * This is the normal path through the commit for operations owning the
       * lock without any queues and not a delete operation.
       */
      Page8Ptr coPageidptr;
      Uint32 tcoElementptr;
      Uint32 tmp2Olq;
      
      coPageidptr.i = operationRecPtr.p->elementPage;
      tcoElementptr = operationRecPtr.p->elementPointer;
      tmp2Olq = ElementHeader::setUnlocked(operationRecPtr.p->scanBits, operationRecPtr.p->reducedHashValue);
      ptrCheckGuard(coPageidptr, cpagesize, page8);
      dbgWord32(coPageidptr, tcoElementptr, tmp2Olq);
      arrGuard(tcoElementptr, 2048);
      coPageidptr.p->word32[tcoElementptr] = tmp2Olq;
      return;
    }
    else if (queue)
    {
      jam();
      /*
       * The case when there is a queue lined up.
       * Release the lock and pass it to the next operation lined up.
       */
      release_lockowner(signal, operationRecPtr, true);
      return;
    } 
    else 
    {
      jam();
      /*
       * No queue and elementIsDisappeared is true. 
       * We perform the actual delete operation.
       */
      commitdelete(signal);
      return;
    }//if
  } 
  else 
  {
    /**
     * THE OPERATION DOES NOT OWN THE LOCK. IT MUST BE IN A LOCK QUEUE OF THE
     * ELEMENT.
     */
    jam();
    OperationrecPtr prev, next, lockOwner;
    prev.i = operationRecPtr.p->prevParallelQue;
    next.i = operationRecPtr.p->nextParallelQue;
    lockOwner.i = operationRecPtr.p->m_lock_owner_ptr_i;
    ptrCheckGuard(prev, coprecsize, operationrec);
    
    prev.p->nextParallelQue = next.i;
    if (next.i != RNIL) 
    {
      jam();
      ptrCheckGuard(next, coprecsize, operationrec);
      next.p->prevParallelQue = prev.i;
    }
    else if (prev.p->m_op_bits & Operationrec::OP_LOCK_OWNER)
    {
      jam();
      ndbassert(lockOwner.i == prev.i);
      prev.p->m_lo_last_parallel_op_ptr_i = RNIL;
      next = prev;
    }
    else
    {
      jam();
      /**
       * Last operation in parallell queue
       */
      ndbassert(prev.i != lockOwner.i);
      ptrCheckGuard(lockOwner, coprecsize, operationrec);      
      ndbassert(lockOwner.p->m_op_bits & Operationrec::OP_LOCK_OWNER);
      lockOwner.p->m_lo_last_parallel_op_ptr_i = prev.i;
      prev.p->m_lock_owner_ptr_i = lockOwner.i;
      next = prev;
    }
    
    /**
     * Check possible lock upgrade
     */
    if(opbits & Operationrec::OP_ACC_LOCK_MODE)
    {
      jam();

      /**
       * Not lock owner...committing a exclusive operation...
       *
       * e.g
       *   T1(R) T1(X)
       *   T2(R/X)
       *
       *   If T1(X) commits T2(R/X) is not supposed to run
       *     as T1(R) should also commit
       *
       * e.g
       *   T1(R) T1(X) T1*(R)
       *   T2(R/X)
       *
       *   If T1*(R) commits T2(R/X) is not supposed to run
       *     as T1(R),T2(x) should also commit
       */
      validate_lock_queue(prev);
      return;
    }

    /**
     * We committed a shared lock
     *   Check if we can start next...
     */
    while(next.p->nextParallelQue != RNIL)
    {
      jam();
      next.i = next.p->nextParallelQue;
      ptrCheckGuard(next, coprecsize, operationrec);
      
      if ((next.p->m_op_bits & Operationrec::OP_STATE_MASK) != 
	  Operationrec::OP_STATE_EXECUTED)
      {
	jam();
	return;
      }
    }
    
    startNext(signal, next);
    
    validate_lock_queue(prev);
  }
}//Dbacc::commitOperation()

void 
Dbacc::release_lockowner(Signal* signal, OperationrecPtr opPtr, bool commit)
{
  OperationrecPtr nextP;
  OperationrecPtr nextS;
  OperationrecPtr newOwner;
  OperationrecPtr lastP;
  
  Uint32 opbits = opPtr.p->m_op_bits;
  nextP.i = opPtr.p->nextParallelQue;
  nextS.i = opPtr.p->nextSerialQue;
  lastP.i = opPtr.p->m_lo_last_parallel_op_ptr_i;
  Uint32 lastS = opPtr.p->m_lo_last_serial_op_ptr_i;

  ndbassert(lastP.i != RNIL || lastS != RNIL);
  ndbassert(nextP.i != RNIL || nextS.i != RNIL);

  enum {
    NOTHING,
    CHECK_LOCK_UPGRADE,
    START_NEW
  } action = NOTHING;

  if (nextP.i != RNIL)
  {
    jam();
    ptrCheckGuard(nextP, coprecsize, operationrec);
    newOwner = nextP;

    if (lastP.i == newOwner.i)
    {
      newOwner.p->m_lo_last_parallel_op_ptr_i = RNIL;
      lastP = nextP;
    }
    else
    {
      ptrCheckGuard(lastP, coprecsize, operationrec);
      newOwner.p->m_lo_last_parallel_op_ptr_i = lastP.i;
      lastP.p->m_lock_owner_ptr_i = newOwner.i;
    }
    
    newOwner.p->m_lo_last_serial_op_ptr_i = lastS;
    newOwner.p->nextSerialQue = nextS.i;
    
    if (nextS.i != RNIL)
    {
      jam();
      ptrCheckGuard(nextS, coprecsize, operationrec);
      ndbassert(nextS.p->prevSerialQue == opPtr.i);
      nextS.p->prevSerialQue = newOwner.i;
    }
    
    if (commit)
    {
      if ((opbits & Operationrec::OP_ACC_LOCK_MODE) == ZREADLOCK)
      {
	jam();
	/**
	 * Lock owner...committing a shared operation...
	 * this can be a lock upgrade
	 *
	 * e.g
	 *   T1(R) T2(R)
	 *   T2(X)
	 *
	 *   If T1(R) commits T2(X) is supposed to run
	 *
	 * e.g
	 *   T1(X) T1(R)
	 *   T2(R)
	 *
	 *   If T1(X) commits, then T1(R) _should_ commit before T2(R) is
	 *     allowed to proceed
	 */
	action = CHECK_LOCK_UPGRADE;
      }
      else
      {
	jam();
	newOwner.p->m_op_bits |= Operationrec::OP_LOCK_MODE;
      }
    }
    else
    {
      /**
       * Aborting an operation can *always* lead to lock upgrade
       */
      action = CHECK_LOCK_UPGRADE;
      Uint32 opstate = opbits & Operationrec::OP_STATE_MASK;
      if (opstate != Operationrec::OP_STATE_EXECUTED)
      {
	ndbassert(opstate == Operationrec::OP_STATE_RUNNING);
	if (opbits & Operationrec::OP_ELEMENT_DISAPPEARED)
	{
	  jam();
	  report_dealloc(signal, opPtr.p);
	  newOwner.p->localdata[0] = ~(Uint32)0;
	  newOwner.p->localdata[1] = ~(Uint32)0;
	}
	else
	{
	  jam();
	  newOwner.p->localdata[0] = opPtr.p->localdata[0];
	  newOwner.p->localdata[1] = opPtr.p->localdata[1];
	}
	action = START_NEW;
      }
      
      /**
       * Update ACC_LOCK_MODE
       */
      if (opbits & Operationrec::OP_LOCK_MODE)
      {
	Uint32 nextbits = nextP.p->m_op_bits;
	while ((nextbits & Operationrec::OP_LOCK_MODE) == 0)
	{
	  ndbassert(nextbits & Operationrec::OP_ACC_LOCK_MODE);
	  nextbits &= ~(Uint32)Operationrec::OP_ACC_LOCK_MODE;
	  nextP.p->m_op_bits = nextbits;
	  
	  if (nextP.p->nextParallelQue != RNIL)
	  {
	    nextP.i = nextP.p->nextParallelQue;
	    ptrCheckGuard(nextP, coprecsize, operationrec);
	    nextbits = nextP.p->m_op_bits;
	  }
	  else
	  {
	    break;
	  }
	}
      }
    }
  }
  else
  {
    jam();
    ptrCheckGuard(nextS, coprecsize, operationrec);
    newOwner = nextS;
    
    newOwner.p->m_op_bits |= Operationrec::OP_RUN_QUEUE;
    
    if (opbits & Operationrec::OP_ELEMENT_DISAPPEARED)
    {
      report_dealloc(signal, opPtr.p);
      newOwner.p->localdata[0] = ~(Uint32)0;
      newOwner.p->localdata[1] = ~(Uint32)0;
    }
    else
    {
      jam();
      newOwner.p->localdata[0] = opPtr.p->localdata[0];
      newOwner.p->localdata[1] = opPtr.p->localdata[1];
    }
    
    lastP = newOwner;
    while (lastP.p->nextParallelQue != RNIL)
    {
      lastP.i = lastP.p->nextParallelQue;
      ptrCheckGuard(lastP, coprecsize, operationrec);      
      lastP.p->m_op_bits |= Operationrec::OP_RUN_QUEUE;
    }
    
    if (newOwner.i != lastP.i)
    {
      jam();
      newOwner.p->m_lo_last_parallel_op_ptr_i = lastP.i;
    }
    else
    {
      jam();
      newOwner.p->m_lo_last_parallel_op_ptr_i = RNIL;
    }

    if (newOwner.i != lastS)
    {
      jam();
      newOwner.p->m_lo_last_serial_op_ptr_i = lastS;
    }
    else
    {
      jam();
      newOwner.p->m_lo_last_serial_op_ptr_i = RNIL;
    }
    
    action = START_NEW;
  }
  
  insertLockOwnersList(signal, newOwner);
  
  /**
   * Copy op info, and store op in element
   *
   */
  {
    newOwner.p->elementPage = opPtr.p->elementPage;
    newOwner.p->elementIsforward = opPtr.p->elementIsforward;
    newOwner.p->elementPointer = opPtr.p->elementPointer;
    newOwner.p->elementContainer = opPtr.p->elementContainer;
    newOwner.p->scanBits = opPtr.p->scanBits;
    newOwner.p->reducedHashValue = opPtr.p->reducedHashValue;
    newOwner.p->m_op_bits |= (opbits & Operationrec::OP_ELEMENT_DISAPPEARED);
    if (opbits & Operationrec::OP_ELEMENT_DISAPPEARED)
    {
      /* ------------------------------------------------------------------- */
      // If the elementIsDisappeared is set then we know that the 
      // hashValue is also set since it always originates from a 
      // committing abort or a aborting insert. 
      // Scans do not initialise the hashValue and must have this 
      // value initialised if they are
      // to successfully commit the delete.
      /* ------------------------------------------------------------------- */
      jam();
      newOwner.p->hashValue = opPtr.p->hashValue;
    }//if

    Page8Ptr pagePtr;
    pagePtr.i = newOwner.p->elementPage;
    ptrCheckGuard(pagePtr, cpagesize, page8);
    const Uint32 tmp = ElementHeader::setLocked(newOwner.i);
    arrGuard(newOwner.p->elementPointer, 2048);
    pagePtr.p->word32[newOwner.p->elementPointer] = tmp;
  }
  
  switch(action){
  case NOTHING:
    validate_lock_queue(newOwner);
    return;
  case START_NEW:
    startNew(signal, newOwner);
    validate_lock_queue(newOwner);
    return;
  case CHECK_LOCK_UPGRADE:
    startNext(signal, lastP);
    validate_lock_queue(lastP);
    break;
  }
  
}

void
Dbacc::startNew(Signal* signal, OperationrecPtr newOwner) 
{
  OperationrecPtr save = operationRecPtr;
  operationRecPtr = newOwner;
  
  Uint32 opbits = newOwner.p->m_op_bits;
  Uint32 op = opbits & Operationrec::OP_MASK;
  Uint32 opstate = (opbits & Operationrec::OP_STATE_MASK);
  ndbassert(opstate == Operationrec::OP_STATE_WAITING);
  ndbassert(opbits & Operationrec::OP_LOCK_OWNER);
  const bool deleted = opbits & Operationrec::OP_ELEMENT_DISAPPEARED;
  Uint32 errCode = 0;

  opbits &= opbits & ~(Uint32)Operationrec::OP_STATE_MASK;
  opbits |= Operationrec::OP_STATE_RUNNING;
  
  if (op == ZSCAN_OP && (opbits & Operationrec::OP_LOCK_REQ) == 0)
    goto scan;

  if (deleted)
  {
    jam();
    if (op != ZINSERT && op != ZWRITE)
    {
      errCode = ZREAD_ERROR;
      goto ref;
    }
    
    opbits &= ~(Uint32)Operationrec::OP_MASK;
    opbits &= ~(Uint32)Operationrec::OP_ELEMENT_DISAPPEARED;
    opbits |= (op = ZINSERT);
    opbits |= Operationrec::OP_INSERT_IS_DONE;
    goto conf;
  }
  else if (op == ZINSERT)
  {
    jam();
    errCode = ZWRITE_ERROR;
    goto ref;
  }
  else if (op == ZWRITE)
  {
    jam();
    opbits &= ~(Uint32)Operationrec::OP_MASK;
    opbits |= (op = ZUPDATE);
    goto conf;
  }

conf:
  newOwner.p->m_op_bits = opbits;

  sendAcckeyconf(signal);
  sendSignal(newOwner.p->userblockref, GSN_ACCKEYCONF, 
	     signal, 6, JBB);

  operationRecPtr = save;
  return;
  
scan:
  jam();
  newOwner.p->m_op_bits = opbits;
  
  takeOutScanLockQueue(newOwner.p->scanRecPtr);
  putReadyScanQueue(signal, newOwner.p->scanRecPtr);

  operationRecPtr = save;
  return;
  
ref:
  newOwner.p->m_op_bits = opbits;
  
  signal->theData[0] = newOwner.p->userptr;
  signal->theData[1] = errCode;
  sendSignal(newOwner.p->userblockref, GSN_ACCKEYREF, signal, 
	     2, JBB);
  
  operationRecPtr = save;
  return;
}

/**
 * takeOutLockOwnersList
 *
 * Description: Take out an operation from the doubly linked 
 * lock owners list on the fragment.
 *
 */
void Dbacc::takeOutLockOwnersList(Signal* signal,
				  const OperationrecPtr& outOperPtr) 
{
  const Uint32 Tprev = outOperPtr.p->prevLockOwnerOp;
  const Uint32 Tnext = outOperPtr.p->nextLockOwnerOp;
#ifdef VM_TRACE
  // Check that operation is already in the list
  OperationrecPtr tmpOperPtr;
  bool inList = false;
  tmpOperPtr.i = fragrecptr.p->lockOwnersList;
  while (tmpOperPtr.i != RNIL){
    ptrCheckGuard(tmpOperPtr, coprecsize, operationrec);
    if (tmpOperPtr.i == outOperPtr.i)
      inList = true;
    tmpOperPtr.i = tmpOperPtr.p->nextLockOwnerOp;
  }
  ndbrequire(inList == true);
#endif
  
  ndbassert(outOperPtr.p->m_op_bits & Operationrec::OP_LOCK_OWNER);
  
  // Fast path through the code for the common case.
  if ((Tprev == RNIL) && (Tnext == RNIL)) {
    ndbrequire(fragrecptr.p->lockOwnersList == outOperPtr.i);
    fragrecptr.p->lockOwnersList = RNIL;
    return;
  } 

  // Check previous operation 
  if (Tprev != RNIL) {    
    jam();
    arrGuard(Tprev, coprecsize);
    operationrec[Tprev].nextLockOwnerOp = Tnext;
  } else {
    fragrecptr.p->lockOwnersList = Tnext;
  }//if

  // Check next operation
  if (Tnext == RNIL) {
    return;
  } else {
    jam();
    arrGuard(Tnext, coprecsize);
    operationrec[Tnext].prevLockOwnerOp = Tprev;
  }//if

  return;
}//Dbacc::takeOutLockOwnersList()

/**
 * insertLockOwnersList
 *
 * Description: Insert an operation first in the dubly linked lock owners 
 * list on the fragment.
 *
 */
void Dbacc::insertLockOwnersList(Signal* signal, 
				 const OperationrecPtr& insOperPtr) 
{
  OperationrecPtr tmpOperPtr;
#ifdef VM_TRACE
  // Check that operation is not already in list
  tmpOperPtr.i = fragrecptr.p->lockOwnersList;
  while(tmpOperPtr.i != RNIL){
    ptrCheckGuard(tmpOperPtr, coprecsize, operationrec);
    ndbrequire(tmpOperPtr.i != insOperPtr.i);
    tmpOperPtr.i = tmpOperPtr.p->nextLockOwnerOp;    
  }
#endif
  tmpOperPtr.i = fragrecptr.p->lockOwnersList;
  
  ndbrequire(! (insOperPtr.p->m_op_bits & Operationrec::OP_LOCK_OWNER));

  insOperPtr.p->m_op_bits |= Operationrec::OP_LOCK_OWNER;
  insOperPtr.p->prevLockOwnerOp = RNIL;
  insOperPtr.p->nextLockOwnerOp = tmpOperPtr.i;
  
  fragrecptr.p->lockOwnersList = insOperPtr.i;
  if (tmpOperPtr.i == RNIL) {
    return;
  } else {
    jam();
    ptrCheckGuard(tmpOperPtr, coprecsize, operationrec);
    tmpOperPtr.p->prevLockOwnerOp = insOperPtr.i;
  }//if
}//Dbacc::insertLockOwnersList()


/* --------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------- */
/*                                                                                   */
/*       END OF COMMIT AND ABORT MODULE                                              */
/*                                                                                   */
/* --------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------- */
/* ALLOC_OVERFLOW_PAGE                                                               */
/*          DESCRIPTION:                                                             */
/* --------------------------------------------------------------------------------- */
void Dbacc::allocOverflowPage(Signal* signal) 
{
  tresult = 0;
  if (cfreepages.isEmpty())
  {
    jam();  
    zpagesize_error("Dbacc::allocOverflowPage");
    tresult = ZPAGESIZE_ERROR;
    return;
  }//if
  seizePage(signal);
  ndbrequire(tresult <= ZLIMIT_OF_ERROR);
  {
    LocalContainerPageList sparselist(*this, fragrecptr.p->sparsepages);
    sparselist.addLast(spPageptr);
  }
  iopPageptr = spPageptr;
  initOverpage(signal);
}//Dbacc::allocOverflowPage()

/* --------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------- */
/*                                                                                   */
/*       EXPAND/SHRINK MODULE                                                        */
/*                                                                                   */
/* --------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------- */
/* ******************--------------------------------------------------------------- */
/*EXPANDCHECK                                        EXPAND BUCKET ORD               */
/* SENDER: ACC,    LEVEL B         */
/*   INPUT:   FRAGRECPTR, POINTS TO A FRAGMENT RECORD.                               */
/*   DESCRIPTION: A BUCKET OF A FRAGMENT PAGE WILL BE EXPAND INTO TWO BUCKETS        */
/*                                 ACCORDING TO LH3.                                 */
/* ******************--------------------------------------------------------------- */
/* ******************--------------------------------------------------------------- */
/* EXPANDCHECK                                        EXPAND BUCKET ORD              */
/* ******************------------------------------+                                 */
/* SENDER: ACC,    LEVEL B         */
/* A BUCKET OF THE FRAGMENT WILL   */
/* BE EXPANDED ACORDING TO LH3,    */
/* AND COMMIT TRANSACTION PROCESS  */
/* WILL BE CONTINUED */
Uint32 Dbacc::checkScanExpand(Signal* signal, Uint32 splitBucket)
{
  Uint32 Ti;
  Uint32 TreturnCode = 0;
  Uint32 TPageIndex;
  Uint32 TDirInd;
  Uint32 TSplit;
  Uint32 TreleaseInd = 0;
  Uint32 TreleaseScanBucket;
  Uint32 TreleaseScanIndicator[MAX_PARALLEL_SCANS_PER_FRAG];
  Page8Ptr TPageptr;
  ScanRecPtr TscanPtr;

  TSplit = splitBucket;
  for (Ti = 0; Ti < MAX_PARALLEL_SCANS_PER_FRAG; Ti++) {
    TreleaseScanIndicator[Ti] = 0;
    if (fragrecptr.p->scan[Ti] != RNIL) {
      //-------------------------------------------------------------
      // A scan is ongoing on this particular local fragment. We have
      // to check its current state.
      //-------------------------------------------------------------
      TscanPtr.i = fragrecptr.p->scan[Ti];
      ptrCheckGuard(TscanPtr, cscanRecSize, scanRec);
      if (TscanPtr.p->activeLocalFrag == fragrecptr.i) {
        if (TscanPtr.p->scanBucketState ==  ScanRec::FIRST_LAP) {
          if (TSplit == TscanPtr.p->nextBucketIndex) {
            jam();
	    //-------------------------------------------------------------
	    // We are currently scanning this bucket. We cannot split it
	    // simultaneously with the scan. We have to pass this offer for
	    // splitting the bucket.
	    //-------------------------------------------------------------
            TreturnCode = 1;
            return TreturnCode;
          } else if (TSplit > TscanPtr.p->nextBucketIndex) {
            jam();
	    //-------------------------------------------------------------
	    // This bucket has not yet been scanned. We must reset the scanned
	    // bit indicator for this scan on this bucket.
	    //-------------------------------------------------------------
            TreleaseScanIndicator[Ti] = 1;
            TreleaseInd = 1;
          } else {
            jam();
          }//if
        } else if (TscanPtr.p->scanBucketState ==  ScanRec::SECOND_LAP) {
          jam();
	  //-------------------------------------------------------------
	  // We are performing a second lap to handle buckets that was
	  // merged during the first lap of scanning. During this second
	  // lap we do not allow any splits or merges.
	  //-------------------------------------------------------------
          TreturnCode = 1;
          return TreturnCode;
        } else {
          ndbrequire(TscanPtr.p->scanBucketState ==  ScanRec::SCAN_COMPLETED);
          jam();
	  //-------------------------------------------------------------
	  // The scan is completed and we can thus go ahead and perform
	  // the split.
	  //-------------------------------------------------------------
        }//if
      }//if
    }//if
  }//for
  if (TreleaseInd == 1) {
    TreleaseScanBucket = TSplit;
    TPageIndex = fragrecptr.p->getPageIndex(TreleaseScanBucket);
    TDirInd = fragrecptr.p->getPageNumber(TreleaseScanBucket);
    TPageptr.i = getPagePtr(fragrecptr.p->directory, TDirInd);
    ptrCheckGuard(TPageptr, cpagesize, page8);
    for (Ti = 0; Ti < MAX_PARALLEL_SCANS_PER_FRAG; Ti++) {
      if (TreleaseScanIndicator[Ti] == 1) {
        jam();
        scanPtr.i = fragrecptr.p->scan[Ti];
        ptrCheckGuard(scanPtr, cscanRecSize, scanRec);
        rsbPageidptr = TPageptr;
        trsbPageindex = TPageIndex;
        releaseScanBucket(signal);
      }//if
    }//for
  }//if
  return TreturnCode;
}//Dbacc::checkScanExpand()

void Dbacc::execEXPANDCHECK2(Signal* signal) 
{
  jamEntry();

  if(refToBlock(signal->getSendersBlockRef()) == DBLQH)
  {
    jam();
    return;
  }

  fragrecptr.i = signal->theData[0];
  tresult = 0;	/* 0= FALSE,1= TRUE,> ZLIMIT_OF_ERROR =ERRORCODE */
  ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
  fragrecptr.p->expandOrShrinkQueued = false;
  if (fragrecptr.p->slack > 0) {
    jam();
    /* IT MEANS THAT IF SLACK > ZERO */
    /*--------------------------------------------------------------*/
    /*       THE SLACK HAS IMPROVED AND IS NOW ACCEPTABLE AND WE    */
    /*       CAN FORGET ABOUT THE EXPAND PROCESS.                   */
    /*--------------------------------------------------------------*/
    if (ERROR_INSERTED(3002))
      debug_lh_vars("SLK");
    if (fragrecptr.p->dirRangeFull == ZTRUE) {
      jam();
      fragrecptr.p->dirRangeFull = ZFALSE;
    }
    return;
  }//if
  if (fragrecptr.p->sparsepages.isEmpty())
  {
    jam();
    allocOverflowPage(signal);
    if (tresult > ZLIMIT_OF_ERROR) {
      jam();
      /*--------------------------------------------------------------*/
      /* WE COULD NOT ALLOCATE ANY OVERFLOW PAGE. THUS WE HAVE TO STOP*/
      /* THE EXPAND SINCE WE CANNOT GUARANTEE ITS COMPLETION.         */
      /*--------------------------------------------------------------*/
      return;
    }//if
  }//if
  if (cfreepages.isEmpty())
  {
    jam();
    /*--------------------------------------------------------------*/
    /* WE HAVE TO STOP THE EXPAND PROCESS SINCE THERE ARE NO FREE   */
    /* PAGES. THIS MEANS THAT WE COULD BE FORCED TO CRASH SINCE WE  */
    /* CANNOT COMPLETE THE EXPAND. TO AVOID THE CRASH WE EXIT HERE. */
    /*--------------------------------------------------------------*/
    return;
  }//if

  if (fragrecptr.p->level.isFull())
  {
    jam();
    /*
     * The level structure does not allow more buckets.
     * Do not expand.
     */
    return;
  }

  Uint32 splitBucket;
  Uint32 receiveBucket;

  bool doSplit = fragrecptr.p->level.getSplitBucket(splitBucket, receiveBucket);

  // Check that splitted bucket is not currently scanned
  if (doSplit && checkScanExpand(signal, splitBucket) == 1) {
    jam();
    /*--------------------------------------------------------------*/
    // A scan state was inconsistent with performing an expand
    // operation.
    /*--------------------------------------------------------------*/
    return;
  }//if

  /*--------------------------------------------------------------------------*/
  /*       WE START BY FINDING THE PAGE, THE PAGE INDEX AND THE PAGE DIRECTORY*/
  /*       OF THE NEW BUCKET WHICH SHALL RECEIVE THE ELEMENT WHICH HAVE A 1 IN*/
  /*       THE NEXT HASH BIT. THIS BIT IS USED IN THE SPLIT MECHANISM TO      */
  /*       DECIDE WHICH ELEMENT GOES WHERE.                                   */
  /*--------------------------------------------------------------------------*/

  texpDirInd = fragrecptr.p->getPageNumber(receiveBucket);
  if (fragrecptr.p->getPageIndex(receiveBucket) == 0)
  { // Need new bucket
    expPageptr.i = RNIL;
  }
  else
  {
    expPageptr.i = getPagePtr(fragrecptr.p->directory, texpDirInd);
#ifdef VM_TRACE
    require(expPageptr.i != RNIL);
#endif
  }
  if (expPageptr.i == RNIL) {
    jam();
    seizePage(signal);
    if (tresult > ZLIMIT_OF_ERROR) {
      jam();
      return;
    }//if
    if (!setPagePtr(fragrecptr.p->directory, texpDirInd, spPageptr.i))
    {
      jam();
      // TODO: should release seized page
      tresult = ZDIR_RANGE_FULL_ERROR;
      return;
    }
    tipPageId = texpDirInd;
    inpPageptr = spPageptr;
    initPage(signal);
    expPageptr = spPageptr;
  } else {
    ptrCheckGuard(expPageptr, cpagesize, page8);
  }//if

  fragrecptr.p->expReceivePageptr = expPageptr.i;
  fragrecptr.p->expReceiveIndex = fragrecptr.p->getPageIndex(receiveBucket);
  /*--------------------------------------------------------------------------*/
  /*       THE NEXT ACTION IS TO FIND THE PAGE, THE PAGE INDEX AND THE PAGE   */
  /*       DIRECTORY OF THE BUCKET TO BE SPLIT.                               */
  /*--------------------------------------------------------------------------*/
  cexcPageindex = fragrecptr.p->getPageIndex(splitBucket);
  texpDirInd = fragrecptr.p->getPageNumber(splitBucket);
  excPageptr.i = getPagePtr(fragrecptr.p->directory, texpDirInd);
#ifdef VM_TRACE
  require(excPageptr.i != RNIL);
#endif
  fragrecptr.p->expSenderIndex = cexcPageindex;
  fragrecptr.p->expSenderPageptr = excPageptr.i;
  if (excPageptr.i == RNIL) {
    jam();
    endofexpLab(signal);	/* EMPTY BUCKET */
    return;
  }//if
  fragrecptr.p->expReceiveForward = ZTRUE;
  ptrCheckGuard(excPageptr, cpagesize, page8);
  expandcontainer(signal);
  endofexpLab(signal);
  return;
}//Dbacc::execEXPANDCHECK2()
  
void Dbacc::endofexpLab(Signal* signal) 
{
  fragrecptr.p->slack += fragrecptr.p->maxloadfactor;
  fragrecptr.p->expandCounter++;
  fragrecptr.p->level.expand();
  Uint32 noOfBuckets = fragrecptr.p->level.getSize();
  Uint32 Thysteres = fragrecptr.p->maxloadfactor - fragrecptr.p->minloadfactor;
  fragrecptr.p->slackCheck = Int64(noOfBuckets) * Thysteres;
  if (fragrecptr.p->slack < 0 && !fragrecptr.p->level.isFull())
  {
    jam();
    /* IT MEANS THAT IF SLACK < ZERO */
    /* --------------------------------------------------------------------------------- */
    /*       IT IS STILL NECESSARY TO EXPAND THE FRAGMENT EVEN MORE. START IT FROM HERE  */
    /*       WITHOUT WAITING FOR NEXT COMMIT ON THE FRAGMENT.                            */
    /* --------------------------------------------------------------------------------- */
    signal->theData[0] = fragrecptr.i;
    fragrecptr.p->expandOrShrinkQueued = true;
    sendSignal(cownBlockref, GSN_EXPANDCHECK2, signal, 1, JBB);
  }//if
  return;
}//Dbacc::endofexpLab()

void Dbacc::execDEBUG_SIG(Signal* signal) 
{
  jamEntry();
  expPageptr.i = signal->theData[0];

  progError(__LINE__, NDBD_EXIT_SR_UNDOLOG);
  return;
}//Dbacc::execDEBUG_SIG()

LHBits32 Dbacc::getElementHash(OperationrecPtr& oprec)
{
  jam();
  ndbassert(!oprec.isNull());

  // Only calculate hash value if operation does not already have a complete hash value
  if (oprec.p->hashValue.valid_bits() < fragrecptr.p->MAX_HASH_VALUE_BITS)
  {
    jam();
    Uint32 localkey[2];
    localkey[0] = oprec.p->localdata[0];
    localkey[1] = oprec.p->localdata[1];
    Uint32 len = readTablePk(localkey[0], localkey[1], ElementHeader::setLocked(oprec.i), oprec);
    if (len > 0)
      oprec.p->hashValue = LHBits32(md5_hash((Uint64*)ckeys, len));
  }
  return oprec.p->hashValue;
}

LHBits32 Dbacc::getElementHash(Uint32 const* elemptr, Int32 forward)
{
  jam();
  assert(ElementHeader::getUnlocked(*elemptr));

  Uint32 elemhead = *elemptr;
  Uint32 localkey[2];
  elemptr += forward;
  localkey[0] = *elemptr;
  if (likely(fragrecptr.p->localkeylen == 1))
  {
    jam();
    localkey[1] = Local_key::ref2page_idx(localkey[0]);
    localkey[0] = Local_key::ref2page_id(localkey[0]);
  }
  else
  {
    jam();
    elemptr += forward;
    localkey[1] = *elemptr;
  }
  OperationrecPtr oprec;
  oprec.i = RNIL;
  Uint32 len = readTablePk(localkey[0], localkey[1], elemhead, oprec);
  if (len > 0)
  {
    jam();
    return LHBits32(md5_hash((Uint64*)ckeys, len));
  }
  else
  { // Return an invalid hash value if no data
    jam();
    return LHBits32();
  }
}

LHBits32 Dbacc::getElementHash(Uint32 const* elemptr, Int32 forward, OperationrecPtr& oprec)
{
  jam();

  if (!oprec.isNull())
  {
    jam();
    return getElementHash(oprec);
  }

  Uint32 elemhead = *elemptr;
  if (ElementHeader::getUnlocked(elemhead))
  {
    jam();
    return getElementHash(elemptr, forward);
  }
  else
  {
    jam();
    oprec.i = ElementHeader::getOpPtrI(elemhead);
    ptrCheckGuard(oprec, coprecsize, operationrec);
    return getElementHash(oprec);
  }
}

/* --------------------------------------------------------------------------------- */
/* EXPANDCONTAINER                                                                   */
/*        INPUT: EXC_PAGEPTR (POINTER TO THE ACTIVE PAGE RECORD)                     */
/*               CEXC_PAGEINDEX (INDEX OF THE BUCKET).                               */
/*                                                                                   */
/*        DESCRIPTION: THE HASH VALUE OF ALL ELEMENTS IN THE CONTAINER WILL BE       */
/*                  CHECKED. SOME OF THIS ELEMENTS HAVE TO MOVE TO THE NEW CONTAINER */
/* --------------------------------------------------------------------------------- */
void Dbacc::expandcontainer(Signal* signal) 
{
  ContainerHeader containerhead;
  LHBits32 texcHashvalue;
  Uint32 texcTmp;
  Uint32 texcIndex;
  Uint32 guard20;

  cexcPrevpageptr = RNIL;
  cexcPrevconptr = 0;
  cexcForward = ZTRUE;
 EXP_CONTAINER_LOOP:
  cexcContainerptr = mul_ZBUF_SIZE(cexcPageindex);
  if (cexcForward == ZTRUE) {
    jam();
    cexcContainerptr = cexcContainerptr + ZHEAD_SIZE;
    cexcElementptr = cexcContainerptr + Container::HEADER_SIZE;
  } else {
    jam();
    cexcContainerptr = ((cexcContainerptr + ZHEAD_SIZE) + ZBUF_SIZE) - Container::HEADER_SIZE;
    cexcElementptr = cexcContainerptr - 1;
  }//if
  arrGuard(cexcContainerptr, 2048);
  containerhead = excPageptr.p->word32[cexcContainerptr];
  cexcContainerlen = containerhead.getLength();
  cexcMovedLen = Container::HEADER_SIZE;
  if (cexcContainerlen <= Container::HEADER_SIZE) {
    ndbrequire(cexcContainerlen >= Container::HEADER_SIZE);
    jam();
    goto NEXT_ELEMENT;
  }//if
 NEXT_ELEMENT_LOOP:
  idrOperationRecPtr.i = RNIL;
  ptrNull(idrOperationRecPtr);
  /* --------------------------------------------------------------------------------- */
  /*       CEXC_PAGEINDEX         PAGE INDEX OF CURRENT CONTAINER BEING EXAMINED.      */
  /*       CEXC_CONTAINERPTR      INDEX OF CURRENT CONTAINER BEING EXAMINED.           */
  /*       CEXC_ELEMENTPTR        INDEX OF CURRENT ELEMENT BEING EXAMINED.             */
  /*       EXC_PAGEPTR            PAGE WHERE CURRENT ELEMENT RESIDES.                  */
  /*       CEXC_PREVPAGEPTR        PAGE OF PREVIOUS CONTAINER.                         */
  /*       CEXC_PREVCONPTR        INDEX OF PREVIOUS CONTAINER                          */
  /*       CEXC_FORWARD           DIRECTION OF CURRENT CONTAINER                       */
  /* --------------------------------------------------------------------------------- */
  arrGuard(cexcElementptr, 2048);
  tidrElemhead = excPageptr.p->word32[cexcElementptr];
  bool move;
  if (ElementHeader::getLocked(tidrElemhead))
  {
    jam();
    idrOperationRecPtr.i = ElementHeader::getOpPtrI(tidrElemhead);
    ptrCheckGuard(idrOperationRecPtr, coprecsize, operationrec);
    ndbassert(idrOperationRecPtr.p->reducedHashValue.valid_bits() >= 1);
    move = idrOperationRecPtr.p->reducedHashValue.get_bit(1);
    idrOperationRecPtr.p->reducedHashValue.shift_out();
    if (!fragrecptr.p->enough_valid_bits(idrOperationRecPtr.p->reducedHashValue))
    {
      jam();
      idrOperationRecPtr.p->reducedHashValue =
        fragrecptr.p->level.reduceForSplit(getElementHash(idrOperationRecPtr));
    }
  }
  else
  {
    jam();
    LHBits16 reducedHashValue = ElementHeader::getReducedHashValue(tidrElemhead);
    ndbassert(reducedHashValue.valid_bits() >= 1);
    move = reducedHashValue.get_bit(1);
    reducedHashValue.shift_out();
    if (!fragrecptr.p->enough_valid_bits(reducedHashValue))
    {
      jam();
      reducedHashValue =
        fragrecptr.p->level.reduceForSplit(getElementHash(&excPageptr.p->word32[cexcElementptr], cexcForward));
    }
    tidrElemhead = ElementHeader::setReducedHashValue(tidrElemhead, reducedHashValue);
  }
  if (!move)
  {
    jam();
    if (ElementHeader::getUnlocked(tidrElemhead))
      excPageptr.p->word32[cexcElementptr] = tidrElemhead;
    /* --------------------------------------------------------------------------------- */
    /*       THIS ELEMENT IS NOT TO BE MOVED. WE CALCULATE THE WHEREABOUTS OF THE NEXT   */
    /*       ELEMENT AND PROCEED WITH THAT OR END THE SEARCH IF THERE ARE NO MORE        */
    /*       ELEMENTS IN THIS CONTAINER.                                                 */
    /* --------------------------------------------------------------------------------- */
    goto NEXT_ELEMENT;
  }//if
  /* --------------------------------------------------------------------------------- */
  /*       THE HASH BIT WAS SET AND WE SHALL MOVE THIS ELEMENT TO THE NEW BUCKET.      */
  /*       WE START BY READING THE ELEMENT TO BE ABLE TO INSERT IT INTO THE NEW BUCKET.*/
  /*       THEN WE INSERT THE ELEMENT INTO THE NEW BUCKET. THE NEXT STEP IS TO DELETE  */
  /*       THE ELEMENT FROM THIS BUCKET. THIS IS PERFORMED BY REPLACING IT WITH THE    */
  /*       LAST ELEMENT IN THE BUCKET. IF THIS ELEMENT IS TO BE MOVED WE MOVE IT AND   */
  /*       GET THE LAST ELEMENT AGAIN UNTIL WE EITHER FIND ONE THAT STAYS OR THIS      */
  /*       ELEMENT IS THE LAST ELEMENT.                                                */
  /* --------------------------------------------------------------------------------- */
  texcTmp = cexcElementptr + cexcForward;
  guard20 = fragrecptr.p->localkeylen - 1;
  for (texcIndex = 0; texcIndex <= guard20; texcIndex++) {
    arrGuard(texcIndex, 2);
    arrGuard(texcTmp, 2048);
    clocalkey[texcIndex] = excPageptr.p->word32[texcTmp];
    texcTmp = texcTmp + cexcForward;
  }//for
  tidrPageindex = fragrecptr.p->expReceiveIndex;
  idrPageptr.i = fragrecptr.p->expReceivePageptr;
  ptrCheckGuard(idrPageptr, cpagesize, page8);
  tidrForward = fragrecptr.p->expReceiveForward;
  insertElement(signal);
  fragrecptr.p->expReceiveIndex = tidrPageindex;
  fragrecptr.p->expReceivePageptr = idrPageptr.i;
  fragrecptr.p->expReceiveForward = tidrForward;
 REMOVE_LAST_LOOP:
  jam();
  lastPageptr.i = excPageptr.i;
  lastPageptr.p = excPageptr.p;
  tlastContainerptr = cexcContainerptr;
  lastPrevpageptr.i = cexcPrevpageptr;
  ptrCheck(lastPrevpageptr, cpagesize, page8);
  tlastPrevconptr = cexcPrevconptr;
  arrGuard(tlastContainerptr, 2048);
  {
  ContainerHeader containerhead = lastPageptr.p->word32[tlastContainerptr];
  tlastContainerlen = containerhead.getLength();
  tlastForward = cexcForward;
  tlastPageindex = cexcPageindex;
  getLastAndRemove(signal, containerhead);
  }
  if (excPageptr.i == lastPageptr.i) {
    if (cexcElementptr == tlastElementptr) {
      jam();
      /* --------------------------------------------------------------------------------- */
      /*       THE CURRENT ELEMENT WAS ALSO THE LAST ELEMENT.                              */
      /* --------------------------------------------------------------------------------- */
      return;
    }//if
  }//if
  /* --------------------------------------------------------------------------------- */
  /*       THE CURRENT ELEMENT WAS NOT THE LAST ELEMENT. IF THE LAST ELEMENT SHOULD    */
  /*       STAY WE COPY IT TO THE POSITION OF THE CURRENT ELEMENT, OTHERWISE WE INSERT */
  /*       INTO THE NEW BUCKET, REMOVE IT AND TRY WITH THE NEW LAST ELEMENT.           */
  /* --------------------------------------------------------------------------------- */
  idrOperationRecPtr.i = RNIL;
  ptrNull(idrOperationRecPtr);
  arrGuard(tlastElementptr, 2048);
  tidrElemhead = lastPageptr.p->word32[tlastElementptr];
  if (ElementHeader::getLocked(tidrElemhead))
  {
    jam();
    idrOperationRecPtr.i = ElementHeader::getOpPtrI(tidrElemhead);
    ptrCheckGuard(idrOperationRecPtr, coprecsize, operationrec);
    ndbassert(idrOperationRecPtr.p->reducedHashValue.valid_bits() >= 1);
    move = idrOperationRecPtr.p->reducedHashValue.get_bit(1);
    idrOperationRecPtr.p->reducedHashValue.shift_out();
    if (!fragrecptr.p->enough_valid_bits(idrOperationRecPtr.p->reducedHashValue))
    {
      jam();
      idrOperationRecPtr.p->reducedHashValue =
        fragrecptr.p->level.reduceForSplit(getElementHash(idrOperationRecPtr));
    }
  }
  else
  {
    jam();
    LHBits16 reducedHashValue = ElementHeader::getReducedHashValue(tidrElemhead);
    ndbassert(reducedHashValue.valid_bits() > 0);
    move = reducedHashValue.get_bit(1);
    reducedHashValue.shift_out();
    if (!fragrecptr.p->enough_valid_bits(reducedHashValue))
    {
      jam();
      reducedHashValue =
        fragrecptr.p->level.reduceForSplit(getElementHash(&lastPageptr.p->word32[tlastElementptr], tlastForward));
    }
    tidrElemhead = ElementHeader::setReducedHashValue(tidrElemhead, reducedHashValue);
  }
  if (!move)
  {
    jam();
    if (ElementHeader::getUnlocked(tidrElemhead))
      lastPageptr.p->word32[tlastElementptr] = tidrElemhead;
    /* --------------------------------------------------------------------------------- */
    /*       THE LAST ELEMENT IS NOT TO BE MOVED. WE COPY IT TO THE CURRENT ELEMENT.     */
    /* --------------------------------------------------------------------------------- */
    delPageptr = excPageptr;
    tdelContainerptr = cexcContainerptr;
    tdelForward = cexcForward;
    tdelElementptr = cexcElementptr;
    deleteElement(signal);
  } else {
    jam();
    /* --------------------------------------------------------------------------------- */
    /*       THE LAST ELEMENT IS ALSO TO BE MOVED.                                       */
    /* --------------------------------------------------------------------------------- */
    texcTmp = tlastElementptr + tlastForward;
    for (texcIndex = 0; texcIndex < fragrecptr.p->localkeylen; texcIndex++) {
      arrGuard(texcIndex, 2);
      arrGuard(texcTmp, 2048);
      clocalkey[texcIndex] = lastPageptr.p->word32[texcTmp];
      texcTmp = texcTmp + tlastForward;
    }//for
    tidrPageindex = fragrecptr.p->expReceiveIndex;
    idrPageptr.i = fragrecptr.p->expReceivePageptr;
    ptrCheckGuard(idrPageptr, cpagesize, page8);
    tidrForward = fragrecptr.p->expReceiveForward;
    insertElement(signal);
    fragrecptr.p->expReceiveIndex = tidrPageindex;
    fragrecptr.p->expReceivePageptr = idrPageptr.i;
    fragrecptr.p->expReceiveForward = tidrForward;
    goto REMOVE_LAST_LOOP;
  }//if
 NEXT_ELEMENT:
  arrGuard(cexcContainerptr, 2048);
  containerhead = excPageptr.p->word32[cexcContainerptr];
  cexcMovedLen = cexcMovedLen + fragrecptr.p->elementLength;
  if (containerhead.getLength() > cexcMovedLen) {
    jam();
    /* --------------------------------------------------------------------------------- */
    /*       WE HAVE NOT YET MOVED THE COMPLETE CONTAINER. WE PROCEED WITH THE NEXT      */
    /*       ELEMENT IN THE CONTAINER. IT IS IMPORTANT TO READ THE CONTAINER LENGTH      */
    /*       FROM THE CONTAINER HEADER SINCE IT MIGHT CHANGE BY REMOVING THE LAST        */
    /*       ELEMENT IN THE BUCKET.                                                      */
    /* --------------------------------------------------------------------------------- */
    cexcElementptr = cexcElementptr + (cexcForward * fragrecptr.p->elementLength);
    goto NEXT_ELEMENT_LOOP;
  }//if
  if (containerhead.getNextEnd() != 0) {
    jam();
    /* --------------------------------------------------------------------------------- */
    /*       WE PROCEED TO THE NEXT CONTAINER IN THE BUCKET.                             */
    /* --------------------------------------------------------------------------------- */
    cexcPrevpageptr = excPageptr.i;
    cexcPrevconptr = cexcContainerptr;
    nextcontainerinfoExp(signal, containerhead);
    goto EXP_CONTAINER_LOOP;
  }//if
}//Dbacc::expandcontainer()

/* ******************--------------------------------------------------------------- */
/* SHRINKCHECK                                        JOIN BUCKET ORD                */
/*                                                   SENDER: ACC,    LEVEL B         */
/*   INPUT:   FRAGRECPTR, POINTS TO A FRAGMENT RECORD.                               */
/*   DESCRIPTION: TWO BUCKET OF A FRAGMENT PAGE WILL BE JOINED TOGETHER              */
/*                                 ACCORDING TO LH3.                                 */
/* ******************--------------------------------------------------------------- */
/* ******************--------------------------------------------------------------- */
/* SHRINKCHECK                                            JOIN BUCKET ORD            */
/* ******************------------------------------+                                 */
/*   SENDER: ACC,    LEVEL B       */
/* TWO BUCKETS OF THE FRAGMENT     */
/* WILL BE JOINED  ACORDING TO LH3 */
/* AND COMMIT TRANSACTION PROCESS  */
/* WILL BE CONTINUED */
Uint32 Dbacc::checkScanShrink(Signal* signal, Uint32 sourceBucket, Uint32 destBucket)
{
  Uint32 Ti;
  Uint32 TreturnCode = 0;
  Uint32 TPageIndex;
  Uint32 TDirInd;
  Uint32 TmergeDest;
  Uint32 TmergeSource;
  Uint32 TreleaseScanBucket;
  Uint32 TreleaseInd = 0;
  Uint32 TreleaseScanIndicator[MAX_PARALLEL_SCANS_PER_FRAG];
  Page8Ptr TPageptr;
  ScanRecPtr TscanPtr;

  TmergeDest = destBucket;
  TmergeSource = sourceBucket;
  for (Ti = 0; Ti < MAX_PARALLEL_SCANS_PER_FRAG; Ti++) {
    TreleaseScanIndicator[Ti] = 0;
    if (fragrecptr.p->scan[Ti] != RNIL) {
      TscanPtr.i = fragrecptr.p->scan[Ti];
      ptrCheckGuard(TscanPtr, cscanRecSize, scanRec);
      if (TscanPtr.p->activeLocalFrag == fragrecptr.i) {
	//-------------------------------------------------------------
	// A scan is ongoing on this particular local fragment. We have
	// to check its current state.
	//-------------------------------------------------------------
        if (TscanPtr.p->scanBucketState ==  ScanRec::FIRST_LAP) {
          jam();
          if ((TmergeDest == TscanPtr.p->nextBucketIndex) ||
              (TmergeSource == TscanPtr.p->nextBucketIndex)) {
            jam();
	    //-------------------------------------------------------------
	    // We are currently scanning one of the buckets involved in the
	    // merge. We cannot merge while simultaneously performing a scan.
	    // We have to pass this offer for merging the buckets.
	    //-------------------------------------------------------------
            TreturnCode = 1;
            return TreturnCode;
          } else if (TmergeDest < TscanPtr.p->nextBucketIndex) {
            jam();
            TreleaseScanIndicator[Ti] = 1;
            TreleaseInd = 1;
          }//if
        } else if (TscanPtr.p->scanBucketState ==  ScanRec::SECOND_LAP) {
          jam();
	  //-------------------------------------------------------------
	  // We are performing a second lap to handle buckets that was
	  // merged during the first lap of scanning. During this second
	  // lap we do not allow any splits or merges.
	  //-------------------------------------------------------------
          TreturnCode = 1;
          return TreturnCode;
        } else if (TscanPtr.p->scanBucketState ==  ScanRec::SCAN_COMPLETED) {
          jam();
	  //-------------------------------------------------------------
	  // The scan is completed and we can thus go ahead and perform
	  // the split.
	  //-------------------------------------------------------------
        } else {
          jam();
          sendSystemerror(signal, __LINE__);
          return TreturnCode;
        }//if
      }//if
    }//if
  }//for
  if (TreleaseInd == 1) {
    jam();
    TreleaseScanBucket = TmergeSource;
    TPageIndex = fragrecptr.p->getPageIndex(TreleaseScanBucket);
    TDirInd = fragrecptr.p->getPageNumber(TreleaseScanBucket);
    TPageptr.i = getPagePtr(fragrecptr.p->directory, TDirInd);
    ptrCheckGuard(TPageptr, cpagesize, page8);
    for (Ti = 0; Ti < MAX_PARALLEL_SCANS_PER_FRAG; Ti++) {
      if (TreleaseScanIndicator[Ti] == 1) {
        jam();
        scanPtr.i = fragrecptr.p->scan[Ti];
        ptrCheckGuard(scanPtr, cscanRecSize, scanRec);
        rsbPageidptr.i = TPageptr.i;
        rsbPageidptr.p = TPageptr.p;
        trsbPageindex = TPageIndex;
        releaseScanBucket(signal);
        if (TmergeDest < scanPtr.p->minBucketIndexToRescan) {
          jam();
	  //-------------------------------------------------------------
	  // We have to keep track of the starting bucket to Rescan in the
	  // second lap.
	  //-------------------------------------------------------------
          scanPtr.p->minBucketIndexToRescan = TmergeDest;
        }//if
        if (TmergeDest > scanPtr.p->maxBucketIndexToRescan) {
          jam();
	  //-------------------------------------------------------------
	  // We have to keep track of the ending bucket to Rescan in the
	  // second lap.
	  //-------------------------------------------------------------
          scanPtr.p->maxBucketIndexToRescan = TmergeDest;
        }//if
      }//if
    }//for
  }//if
  return TreturnCode;
}//Dbacc::checkScanShrink()

void Dbacc::execSHRINKCHECK2(Signal* signal) 
{
  jamEntry();
  fragrecptr.i = signal->theData[0];
  ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
  fragrecptr.p->expandOrShrinkQueued = false;
  tresult = 0;	/* 0= FALSE,1= TRUE,> ZLIMIT_OF_ERROR =ERRORCODE */
  if (fragrecptr.p->slack <= fragrecptr.p->slackCheck) {
    jam();
    /* TIME FOR JOIN BUCKETS PROCESS */
    /*--------------------------------------------------------------*/
    /*       NO LONGER NECESSARY TO SHRINK THE FRAGMENT.            */
    /*--------------------------------------------------------------*/
    return;
  }//if
  if (fragrecptr.p->slack < 0) {
    jam();
    /*--------------------------------------------------------------*/
    /* THE SLACK IS NEGATIVE, IN THIS CASE WE WILL NOT NEED ANY     */
    /* SHRINK.                                                      */
    /*--------------------------------------------------------------*/
    return;
  }//if
  if (fragrecptr.p->sparsepages.isEmpty())
  {
    jam();
    allocOverflowPage(signal);
    if (tresult > ZLIMIT_OF_ERROR) {
      jam();
      return;
    }//if
  }//if
  if (cfreepages.isEmpty())
  {
    jam();
    /*--------------------------------------------------------------*/
    /* WE HAVE TO STOP THE SHRINK PROCESS SINCE THERE ARE NO FREE   */
    /* PAGES. THIS MEANS THAT WE COULD BE FORCED TO CRASH SINCE WE  */
    /* CANNOT COMPLETE THE SHRINK. TO AVOID THE CRASH WE EXIT HERE. */
    /*--------------------------------------------------------------*/
    return;
  }//if

  if (fragrecptr.p->level.isEmpty())
  {
    jam();
    /* no need to shrink empty hash table */
    return;
  }

  // Since expandCounter guards more shrinks than expands and
  // all fragments starts with a full page of buckets
  ndbassert(fragrecptr.p->getPageNumber(fragrecptr.p->level.getTop()) > 0);

  Uint32 mergeSourceBucket;
  Uint32 mergeDestBucket;
  bool doMerge = fragrecptr.p->level.getMergeBuckets(mergeSourceBucket, mergeDestBucket);

  ndbassert(doMerge); // Merge always needed since we never shrink below one page of buckets

  /* check that neither of source or destination bucket are currently scanned */
  if (doMerge && checkScanShrink(signal, mergeSourceBucket, mergeDestBucket) == 1) {
    jam();
    /*--------------------------------------------------------------*/
    // A scan state was inconsistent with performing a shrink
    // operation.
    /*--------------------------------------------------------------*/
    return;
  }//if
  
  if (ERROR_INSERTED(3002))
    debug_lh_vars("SHR");
  if (fragrecptr.p->dirRangeFull == ZTRUE) {
    jam();
    fragrecptr.p->dirRangeFull = ZFALSE;
  }

  shrink_adjust_reduced_hash_value(mergeDestBucket);

  /*--------------------------------------------------------------------------*/
  /*       WE START BY FINDING THE NECESSARY INFORMATION OF THE BUCKET TO BE  */
  /*       REMOVED WHICH WILL SEND ITS ELEMENTS TO THE RECEIVING BUCKET.      */
  /*--------------------------------------------------------------------------*/
  cexcPageindex = fragrecptr.p->getPageIndex(mergeSourceBucket);
  texpDirInd = fragrecptr.p->getPageNumber(mergeSourceBucket);
  excPageptr.i = getPagePtr(fragrecptr.p->directory, texpDirInd);
  fragrecptr.p->expSenderIndex = cexcPageindex;
  fragrecptr.p->expSenderPageptr = excPageptr.i;
  fragrecptr.p->expSenderDirIndex = texpDirInd;
  /*--------------------------------------------------------------------------*/
  /*       WE NOW PROCEED BY FINDING THE NECESSARY INFORMATION ABOUT THE      */
  /*       RECEIVING BUCKET.                                                  */
  /*--------------------------------------------------------------------------*/
  texpDirInd = fragrecptr.p->getPageNumber(mergeDestBucket);
  fragrecptr.p->expReceivePageptr = getPagePtr(fragrecptr.p->directory, texpDirInd);
  fragrecptr.p->expReceiveIndex = fragrecptr.p->getPageIndex(mergeDestBucket);
  fragrecptr.p->expReceiveForward = ZTRUE;
  if (excPageptr.i == RNIL) {
    jam();
    endofshrinkbucketLab(signal);	/* EMPTY BUCKET */
    return;
  }//if
  /*--------------------------------------------------------------------------*/
  /*       INITIALISE THE VARIABLES FOR THE SHRINK PROCESS.                   */
  /*--------------------------------------------------------------------------*/
  ptrCheckGuard(excPageptr, cpagesize, page8);
  cexcForward = ZTRUE;
  cexcContainerptr = mul_ZBUF_SIZE(cexcPageindex);
  cexcContainerptr = cexcContainerptr + ZHEAD_SIZE;
  arrGuard(cexcContainerptr, 2048);
  ContainerHeader containerhead = excPageptr.p->word32[cexcContainerptr];
  cexcContainerlen = containerhead.getLength();
  if (cexcContainerlen <= Container::HEADER_SIZE) {
    ndbrequire(cexcContainerlen == Container::HEADER_SIZE);
  } else {
    jam();
    shrinkcontainer(signal);
  }//if
  /*--------------------------------------------------------------------------*/
  /*       THIS CONTAINER IS NOT YET EMPTY AND WE REMOVE ALL THE ELEMENTS.    */
  /*--------------------------------------------------------------------------*/
  if (containerhead.isUsingBothEnds()) {
    jam();
    rlPageptr = excPageptr;
    trlPageindex = cexcPageindex;
    turlIndex = cexcContainerptr + (ZBUF_SIZE - Container::HEADER_SIZE);
    releaseRightlist(signal);
  }//if
  ContainerHeader conthead;
  conthead.initInUse();
  dbgWord32(excPageptr, cexcContainerptr, conthead);
  arrGuard(cexcContainerptr, 2048);
  excPageptr.p->word32[cexcContainerptr] = conthead;
  if (containerhead.getNextEnd() == 0) {
    jam();
    endofshrinkbucketLab(signal);
    return;
  }//if
  nextcontainerinfoExp(signal, containerhead);
  do {
    cexcContainerptr = mul_ZBUF_SIZE(cexcPageindex);
    if (cexcForward == ZTRUE) {
      jam();
      cexcContainerptr = cexcContainerptr + ZHEAD_SIZE;
    } else {
      jam();
      cexcContainerptr = ((cexcContainerptr + ZHEAD_SIZE) + ZBUF_SIZE) - Container::HEADER_SIZE;
    }//if
    arrGuard(cexcContainerptr, 2048);
    containerhead = excPageptr.p->word32[cexcContainerptr];
    cexcContainerlen = containerhead.getLength();
    ndbrequire(cexcContainerlen > Container::HEADER_SIZE);
    /*--------------------------------------------------------------------------*/
    /*       THIS CONTAINER IS NOT YET EMPTY AND WE REMOVE ALL THE ELEMENTS.    */
    /*--------------------------------------------------------------------------*/
    shrinkcontainer(signal);
    cexcPrevpageptr = excPageptr.i;
    cexcPrevpageindex = cexcPageindex;
    cexcPrevforward = cexcForward;
    if (containerhead.getNextEnd() != 0) {
      jam();
      /*--------------------------------------------------------------------------*/
      /*       WE MUST CALL THE NEXT CONTAINER INFO ROUTINE BEFORE WE RELEASE THE */
      /*       CONTAINER SINCE THE RELEASE WILL OVERWRITE THE NEXT POINTER.       */
      /*--------------------------------------------------------------------------*/
      nextcontainerinfoExp(signal, containerhead);
    }//if
    rlPageptr.i = cexcPrevpageptr;
    ptrCheckGuard(rlPageptr, cpagesize, page8);
    trlPageindex = cexcPrevpageindex;
    if (cexcPrevforward == ZTRUE) {
      jam();
      if (containerhead.isUsingBothEnds()) {
        jam();
        turlIndex = cexcContainerptr + (ZBUF_SIZE - Container::HEADER_SIZE);
        releaseRightlist(signal);
      }//if
      ndbrequire(ContainerHeader(rlPageptr.p->word32[cexcContainerptr]).isInUse());
      tullIndex = cexcContainerptr;
      releaseLeftlist(signal);
    } else {
      jam();
      if (containerhead.isUsingBothEnds()) {
        jam();
        tullIndex = cexcContainerptr - (ZBUF_SIZE - Container::HEADER_SIZE);
        releaseLeftlist(signal);
      }//if
      ndbrequire(ContainerHeader(rlPageptr.p->word32[cexcContainerptr]).isInUse());
      turlIndex = cexcContainerptr;
      releaseRightlist(signal);
    }//if
  } while (containerhead.getNextEnd() != 0);
  endofshrinkbucketLab(signal);
  return;
}//Dbacc::execSHRINKCHECK2()

void Dbacc::endofshrinkbucketLab(Signal* signal) 
{
  fragrecptr.p->level.shrink();
  fragrecptr.p->expandCounter--;
  fragrecptr.p->slack -= fragrecptr.p->maxloadfactor;
  if (fragrecptr.p->expSenderIndex == 0) {
    jam();
    if (fragrecptr.p->expSenderPageptr != RNIL) {
      jam();
      rpPageptr.i = fragrecptr.p->expSenderPageptr;
      ptrCheckGuard(rpPageptr, cpagesize, page8);
      releasePage(signal);
      unsetPagePtr(fragrecptr.p->directory, fragrecptr.p->expSenderDirIndex);
    }//if
    if ((fragrecptr.p->getPageNumber(fragrecptr.p->level.getSize()) & 0xff) == 0) {
      jam();
      DynArr256 dir(directoryPool, fragrecptr.p->directory);
      DynArr256::ReleaseIterator iter;
      Uint32 relcode;
#ifdef VM_TRACE
      Uint32 count = 0;
#endif
      dir.init(iter);
      while ((relcode = dir.trim(fragrecptr.p->expSenderDirIndex, iter)) != 0)
      {
#ifdef VM_TRACE
        count++;
        ndbrequire(count <= 256);
#endif
      }
    }//if
  }//if
  if (fragrecptr.p->slack > 0) {
    jam();
    /*--------------------------------------------------------------*/
    /* THE SLACK IS POSITIVE, IN THIS CASE WE WILL CHECK WHETHER    */
    /* WE WILL CONTINUE PERFORM ANOTHER SHRINK.                     */
    /*--------------------------------------------------------------*/
    Uint32 noOfBuckets = fragrecptr.p->level.getSize();
    Uint32 Thysteresis = fragrecptr.p->maxloadfactor - fragrecptr.p->minloadfactor;
    fragrecptr.p->slackCheck = Int64(noOfBuckets) * Thysteresis;
    if (fragrecptr.p->slack > Thysteresis) {
      /*--------------------------------------------------------------*/
      /*       IT IS STILL NECESSARY TO SHRINK THE FRAGMENT MORE. THIS*/
      /*       CAN HAPPEN WHEN A NUMBER OF SHRINKS GET REJECTED       */
      /*       DURING A LOCAL CHECKPOINT. WE START A NEW SHRINK       */
      /*       IMMEDIATELY FROM HERE WITHOUT WAITING FOR A COMMIT TO  */
      /*       START IT.                                              */
      /*--------------------------------------------------------------*/
      if (fragrecptr.p->expandCounter > 0) {
        jam();
	/*--------------------------------------------------------------*/
	/*       IT IS VERY IMPORTANT TO NOT TRY TO SHRINK MORE THAN    */
	/*       WAS EXPANDED. IF MAXP IS SET TO A VALUE BELOW 63 THEN  */
	/*       WE WILL LOSE RECORDS SINCE GETDIRINDEX CANNOT HANDLE   */
	/*       SHRINKING BELOW 2^K - 1 (NOW 63). THIS WAS A BUG THAT  */
	/*       WAS REMOVED 2000-05-12.                                */
	/*--------------------------------------------------------------*/
        signal->theData[0] = fragrecptr.i;
        ndbrequire(!fragrecptr.p->expandOrShrinkQueued);
        fragrecptr.p->expandOrShrinkQueued = true;
        sendSignal(cownBlockref, GSN_SHRINKCHECK2, signal, 1, JBB);
      }//if
    }//if
  }//if
  ndbrequire(fragrecptr.p->getPageNumber(fragrecptr.p->level.getSize()) > 0);
  return;
}//Dbacc::endofshrinkbucketLab()

/* --------------------------------------------------------------------------------- */
/* SHRINKCONTAINER                                                                   */
/*        INPUT: EXC_PAGEPTR (POINTER TO THE ACTIVE PAGE RECORD)                     */
/*               CEXC_CONTAINERLEN (LENGTH OF THE CONTAINER).                        */
/*               CEXC_CONTAINERPTR (ARRAY INDEX OF THE CONTAINER).                   */
/*               CEXC_FORWARD (CONTAINER FORWARD (+1) OR BACKWARD (-1))              */
/*                                                                                   */
/*        DESCRIPTION: SCAN ALL ELEMENTS IN DESTINATION BUCKET BEFORE MERGE          */
/*               AND ADJUST THE STORED REDUCED HASH VALUE (SHIFT IN ZERO).           */
/* --------------------------------------------------------------------------------- */
void
Dbacc::shrink_adjust_reduced_hash_value(Uint32 bucket_number)
{
  /*
   * Note: function are a copy paste from getElement() with modified inner loop
   * instead of finding a specific element, scan through all and modify.
   */
  Uint32 tgeElementHeader;
  Uint32 tgeElemStep;
  Uint32 tgePageindex;
  Uint32 tgeNextptrtype;
  register Uint32 tgeRemLen;
  register Uint32 TelemLen = fragrecptr.p->elementLength;
  const Uint32 localkeylen = fragrecptr.p->localkeylen;

  tgePageindex = fragrecptr.p->getPageIndex(bucket_number);
  gePageptr.i = getPagePtr(fragrecptr.p->directory, fragrecptr.p->getPageNumber(bucket_number));
  ptrCheckGuard(gePageptr, cpagesize, page8);

  ndbrequire(TelemLen == ZELEM_HEAD_SIZE + localkeylen);
  tgeNextptrtype = ZLEFT;

  /* Loop through all containers in a bucket */
  do {
    tgeContainerptr = mul_ZBUF_SIZE(tgePageindex);
    if (tgeNextptrtype == ZLEFT)
    {
      jam();
      tgeContainerptr = tgeContainerptr + ZHEAD_SIZE;
      tgeElementptr = tgeContainerptr + Container::HEADER_SIZE;
      tgeElemStep = TelemLen;
      tgeForward = 1;
      ndbrequire(tgeContainerptr < 2048);
      tgeRemLen = ContainerHeader(gePageptr.p->word32[tgeContainerptr]).getLength();
      ndbrequire((tgeContainerptr + tgeRemLen - 1) < 2048);
    }
    else if (tgeNextptrtype == ZRIGHT)
    {
      jam();
      tgeContainerptr = tgeContainerptr + ((ZHEAD_SIZE + ZBUF_SIZE) - Container::HEADER_SIZE);
      tgeElementptr = tgeContainerptr - 1;
      tgeElemStep = 0 - TelemLen;
      tgeForward = (Uint32)-1;
      ndbrequire(tgeContainerptr < 2048);
      tgeRemLen = ContainerHeader(gePageptr.p->word32[tgeContainerptr]).getLength();
      ndbrequire((tgeContainerptr - tgeRemLen) < 2048);
    }
    else
    {
      jam();
      jamLine(tgeNextptrtype);
      ndbrequire(false);
    }//if
    if (tgeRemLen >= Container::HEADER_SIZE + TelemLen)
    {
      ndbrequire(tgeRemLen <= ZBUF_SIZE);
      /* ------------------------------------------------------------------- */
      /* Loop through all elements in a container */
      do
      {
        tgeElementHeader = gePageptr.p->word32[tgeElementptr];
        tgeRemLen = tgeRemLen - TelemLen;
        /*
         * Adjust the stored reduced hash value for element, shifting in a zero
         */
        if (ElementHeader::getLocked(tgeElementHeader))
        {
          jam();
          OperationrecPtr oprec;
          oprec.i = ElementHeader::getOpPtrI(tgeElementHeader);
          ptrCheckGuard(oprec, coprecsize, operationrec);
          oprec.p->reducedHashValue.shift_in(false);
        }
        else
        {
          jam();
          LHBits16 reducedHashValue = ElementHeader::getReducedHashValue(tgeElementHeader);
          reducedHashValue.shift_in(false);
          tgeElementHeader = ElementHeader::setReducedHashValue(tgeElementHeader, reducedHashValue);
          gePageptr.p->word32[tgeElementptr] = tgeElementHeader;
        }
        if (tgeRemLen <= Container::HEADER_SIZE)
        {
          break;
        }
        tgeElementptr = tgeElementptr + tgeElemStep;
      } while (true);
    }//if
    ndbrequire(tgeRemLen == Container::HEADER_SIZE);
    ContainerHeader containerhead = gePageptr.p->word32[tgeContainerptr];
    tgeNextptrtype = containerhead.getNextEnd();
    if (tgeNextptrtype == 0)
    {
      jam();
      return;	/* NO MORE CONTAINER */
    }//if
    tgePageindex = containerhead.getNextIndexNumber();	/* NEXT CONTAINER PAGE INDEX 7 BITS */
    ndbrequire((tgePageindex <= Container::MAX_CONTAINER_INDEX) || (tgePageindex == Container::NO_CONTAINER_INDEX));
    if (!containerhead.isNextOnSamePage())
    {
      jam();
      gePageptr.i = gePageptr.p->word32[tgeContainerptr + 1];  /* NEXT PAGE I */
      ptrCheckGuard(gePageptr, cpagesize, page8);
    }//if
  } while (1);

  return;
}//Dbacc::shrink_adjust_reduced_hash_value()

void Dbacc::shrinkcontainer(Signal* signal) 
{
  Uint32 tshrElementptr;
  Uint32 tshrRemLen;
  Uint32 tshrInc;
  Uint32 tshrTmp;
  Uint32 tshrIndex;
  Uint32 guard21;
  tshrRemLen = cexcContainerlen - Container::HEADER_SIZE;
  tshrInc = fragrecptr.p->elementLength;
  if (cexcForward == ZTRUE) {
    jam();
    tshrElementptr = cexcContainerptr + Container::HEADER_SIZE;
  } else {
    jam();
    tshrElementptr = cexcContainerptr - 1;
  }//if
 SHR_LOOP:
  idrOperationRecPtr.i = RNIL;
  ptrNull(idrOperationRecPtr);
  /* --------------------------------------------------------------------------------- */
  /*       THE CODE BELOW IS ALL USED TO PREPARE FOR THE CALL TO INSERT_ELEMENT AND    */
  /*       HANDLE THE RESULT FROM INSERT_ELEMENT. INSERT_ELEMENT INSERTS THE ELEMENT   */
  /*       INTO ANOTHER BUCKET.                                                        */
  /* --------------------------------------------------------------------------------- */
  arrGuard(tshrElementptr, 2048);
  tidrElemhead = excPageptr.p->word32[tshrElementptr];
  if (ElementHeader::getLocked(tidrElemhead)) {
    jam();
    /* --------------------------------------------------------------------------------- */
    /*       IF THE ELEMENT IS LOCKED WE MUST UPDATE THE ELEMENT INFO IN THE OPERATION   */
    /*       RECORD OWNING THE LOCK. WE DO THIS BY READING THE OPERATION RECORD POINTER  */
    /*       FROM THE ELEMENT HEADER.                                                    */
    /* --------------------------------------------------------------------------------- */
    idrOperationRecPtr.i = ElementHeader::getOpPtrI(tidrElemhead);
    ptrCheckGuard(idrOperationRecPtr, coprecsize, operationrec);
    idrOperationRecPtr.p->reducedHashValue.shift_in(true);
  }//if
  else
  {
    LHBits16 reducedHashValue = ElementHeader::getReducedHashValue(tidrElemhead);
    reducedHashValue.shift_in(true);
    tidrElemhead = ElementHeader::setReducedHashValue(tidrElemhead, reducedHashValue);
  }
  tshrTmp = tshrElementptr + cexcForward;
  guard21 = fragrecptr.p->localkeylen - 1;
  for (tshrIndex = 0; tshrIndex <= guard21; tshrIndex++) {
    arrGuard(tshrIndex, 2);
    arrGuard(tshrTmp, 2048);
    clocalkey[tshrIndex] = excPageptr.p->word32[tshrTmp];
    tshrTmp = tshrTmp + cexcForward;
  }//for
  tidrPageindex = fragrecptr.p->expReceiveIndex;
  idrPageptr.i = fragrecptr.p->expReceivePageptr;
  ptrCheckGuard(idrPageptr, cpagesize, page8);
  tidrForward = fragrecptr.p->expReceiveForward;
  insertElement(signal);
  /* --------------------------------------------------------------------------------- */
  /*       TAKE CARE OF RESULT FROM INSERT_ELEMENT.                                    */
  /* --------------------------------------------------------------------------------- */
  fragrecptr.p->expReceiveIndex = tidrPageindex;
  fragrecptr.p->expReceivePageptr = idrPageptr.i;
  fragrecptr.p->expReceiveForward = tidrForward;
  if (tshrRemLen < tshrInc) {
    jam();
    sendSystemerror(signal, __LINE__);
  }//if
  tshrRemLen = tshrRemLen - tshrInc;
  if (tshrRemLen != 0) {
    jam();
    tshrElementptr = tshrTmp;
    goto SHR_LOOP;
  }//if
}//Dbacc::shrinkcontainer()

/* --------------------------------------------------------------------------------- */
/* NEXTCONTAINERINFO_EXP                                                             */
/*        DESCRIPTION:THE CONTAINER HEAD WILL BE CHECKED TO CALCULATE INFORMATION    */
/*                    ABOUT NEXT CONTAINER IN THE BUCKET.                            */
/*          INPUT:       CEXC_CONTAINERHEAD                                          */
/*                       CEXC_CONTAINERPTR                                           */
/*                       EXC_PAGEPTR                                                 */
/*          OUTPUT:                                                                  */
/*             CEXC_PAGEINDEX (INDEX FROM WHICH PAGE INDEX CAN BE CALCULATED.        */
/*             EXC_PAGEPTR (PAGE REFERENCE OF NEXT CONTAINER)                        */
/*             CEXC_FORWARD                                                          */
/* --------------------------------------------------------------------------------- */
void Dbacc::nextcontainerinfoExp(Signal* signal, ContainerHeader const containerhead)
{
  /* THE NEXT CONTAINER IS IN THE SAME PAGE */
  cexcPageindex = containerhead.getNextIndexNumber();	/* NEXT CONTAINER PAGE INDEX 7 BITS */
  if (containerhead.getNextEnd() == ZLEFT) {
    jam();
    cexcForward = ZTRUE;
  } else if (containerhead.getNextEnd() == ZRIGHT) {
    jam();
    cexcForward = cminusOne;
  } else {
    jam();
    sendSystemerror(signal, __LINE__);
    cexcForward = 0;	/* DUMMY FOR COMPILER */
  }//if
  if (!containerhead.isNextOnSamePage()) {
    jam();
    /* NEXT CONTAINER IS IN AN OVERFLOW PAGE */
    arrGuard(cexcContainerptr + 1, 2048);
    excPageptr.i = excPageptr.p->word32[cexcContainerptr + 1];
    ptrCheckGuard(excPageptr, cpagesize, page8);
  }//if
}//Dbacc::nextcontainerinfoExp()

void Dbacc::initFragAdd(Signal* signal,
                        FragmentrecPtr regFragPtr) 
{
  const AccFragReq * const req = (AccFragReq*)&signal->theData[0];  
  Uint32 minLoadFactor = (req->minLoadFactor * ZBUF_SIZE) / 100;
  Uint32 maxLoadFactor = (req->maxLoadFactor * ZBUF_SIZE) / 100;
  if (ERROR_INSERTED(3003)) // use small LoadFactors to force sparse hash table
  {
    jam();
    minLoadFactor = 1;
    maxLoadFactor = 2; 
  }
  if (minLoadFactor >= maxLoadFactor) {
    jam();
    minLoadFactor = maxLoadFactor - 1;
  }//if
  regFragPtr.p->fragState = ACTIVEFRAG;
  // NOTE: next line must match calculation in Dblqh::execLQHFRAGREQ
  regFragPtr.p->myfid = req->fragId;
  regFragPtr.p->myTableId = req->tableId;
  ndbrequire(req->kValue == 6);
  ndbrequire(req->kValue == regFragPtr.p->k);
  regFragPtr.p->expandCounter = 0;

  /**
   * Only allow shrink during SR
   *   - to make sure we don't run out of pages during REDO log execution
   *
   * Is later restored to 0 by LQH at end of REDO log execution
   */
  regFragPtr.p->expandOrShrinkQueued = false;
  regFragPtr.p->level.setSize(1 << req->kValue);
  regFragPtr.p->minloadfactor = minLoadFactor;
  regFragPtr.p->maxloadfactor = maxLoadFactor;
  regFragPtr.p->slack = Int64(regFragPtr.p->level.getSize()) * maxLoadFactor;
  regFragPtr.p->localkeylen = req->localKeyLen;
  regFragPtr.p->nodetype = (req->reqInfo >> 4) & 0x3;
  regFragPtr.p->keyLength = req->keyLength;
  ndbrequire(req->keyLength != 0);
  regFragPtr.p->elementLength = ZELEM_HEAD_SIZE + regFragPtr.p->localkeylen;
  Uint32 Tmp1 = regFragPtr.p->level.getSize();
  Uint32 Tmp2 = regFragPtr.p->maxloadfactor - regFragPtr.p->minloadfactor;
  regFragPtr.p->slackCheck = Int64(Tmp1) * Tmp2;
  regFragPtr.p->mytabptr = req->tableId;
  regFragPtr.p->roothashcheck = req->kValue + req->lhFragBits;
  regFragPtr.p->noOfElements = 0;
  regFragPtr.p->m_commit_count = 0; // stable results
  for (Uint32 i = 0; i < MAX_PARALLEL_SCANS_PER_FRAG; i++) {
    regFragPtr.p->scan[i] = RNIL;
  }//for
  
  Uint32 hasCharAttr = g_key_descriptor_pool.getPtr(req->tableId)->hasCharAttr;
  regFragPtr.p->hasCharAttr = hasCharAttr;
}//Dbacc::initFragAdd()

void Dbacc::initFragGeneral(FragmentrecPtr regFragPtr)
{
  new (&regFragPtr.p->directory) DynArr256::Head();

  regFragPtr.p->lockOwnersList = RNIL;

  regFragPtr.p->activeDataPage = 0;
  regFragPtr.p->hasCharAttr = ZFALSE;
  regFragPtr.p->dirRangeFull = ZFALSE;
  regFragPtr.p->nextAllocPage = 0;
  regFragPtr.p->fragState = FREEFRAG;

  regFragPtr.p->sparsepages.init();
  regFragPtr.p->fullpages.init();
}//Dbacc::initFragGeneral()


void Dbacc::execACC_SCANREQ(Signal* signal) 
{
  jamEntry();
  AccScanReq * req = (AccScanReq*)&signal->theData[0];
  tuserptr = req->senderData;
  tuserblockref = req->senderRef;
  tabptr.i = req->tableId;
  tfid = req->fragmentNo;
  tscanFlag = req->requestInfo;
  tscanTrid1 = req->transId1;
  tscanTrid2 = req->transId2;
  
  tresult = 0;
  ptrCheckGuard(tabptr, ctablesize, tabrec);
  ndbrequire(getfragmentrec(signal, fragrecptr, tfid));
  
  Uint32 i;
  for (i = 0; i < MAX_PARALLEL_SCANS_PER_FRAG; i++) {
    jam();
    if (fragrecptr.p->scan[i] == RNIL) {
      jam();
      break;
    }
  }
  ndbrequire(i != MAX_PARALLEL_SCANS_PER_FRAG);
  ndbrequire(cfirstFreeScanRec != RNIL);
  seizeScanRec(signal);

  fragrecptr.p->scan[i] = scanPtr.i;
  scanPtr.p->scanBucketState =  ScanRec::FIRST_LAP;
  scanPtr.p->scanLockMode = AccScanReq::getLockMode(tscanFlag);
  scanPtr.p->scanReadCommittedFlag = AccScanReq::getReadCommittedFlag(tscanFlag);
  
  /* TWELVE BITS OF THE ELEMENT HEAD ARE SCAN */
  /* CHECK BITS. THE MASK NOTES WHICH BIT IS */
  /* ALLOCATED FOR THE ACTIVE SCAN */
  scanPtr.p->scanMask = 1 << i;
  scanPtr.p->scanUserptr = tuserptr;
  scanPtr.p->scanUserblockref = tuserblockref;
  scanPtr.p->scanTrid1 = tscanTrid1;
  scanPtr.p->scanTrid2 = tscanTrid2;
  scanPtr.p->scanLockHeld = 0;
  scanPtr.p->scanOpsAllocated = 0;
  scanPtr.p->scanFirstActiveOp = RNIL;
  scanPtr.p->scanFirstQueuedOp = RNIL;
  scanPtr.p->scanLastQueuedOp = RNIL;
  scanPtr.p->scanFirstLockedOp = RNIL;
  scanPtr.p->scanLastLockedOp = RNIL;
  scanPtr.p->scanState = ScanRec::WAIT_NEXT;
  initScanFragmentPart(signal);

  /* ************************ */
  /*  ACC_SCANCONF            */
  /* ************************ */
  signal->theData[0] = scanPtr.p->scanUserptr;
  signal->theData[1] = scanPtr.i;
  signal->theData[2] = 1; /* NR OF LOCAL FRAGMENT */
  signal->theData[3] = fragrecptr.p->fragmentid;
  signal->theData[4] = RNIL;
  signal->theData[7] = AccScanConf::ZNOT_EMPTY_FRAGMENT;
  sendSignal(scanPtr.p->scanUserblockref, GSN_ACC_SCANCONF, signal, 8, JBB);
  /* NOT EMPTY FRAGMENT */
  return;
}//Dbacc::execACC_SCANREQ()

/* ******************--------------------------------------------------------------- */
/*  NEXT_SCANREQ                                       REQUEST FOR NEXT ELEMENT OF   */
/* ******************------------------------------+   A FRAGMENT.                   */
/*   SENDER: LQH,    LEVEL B       */
void Dbacc::execNEXT_SCANREQ(Signal* signal) 
{
  Uint32 tscanNextFlag;
  jamEntry();
  scanPtr.i = signal->theData[0];
  operationRecPtr.i = signal->theData[1];
  tscanNextFlag = signal->theData[2];
  /* ------------------------------------------ */
  /* 1 = ZCOPY_NEXT  GET NEXT ELEMENT           */
  /* 2 = ZCOPY_NEXT_COMMIT COMMIT THE           */
  /* ACTIVE ELEMENT AND GET THE NEXT ONE        */
  /* 3 = ZCOPY_COMMIT COMMIT THE ACTIVE ELEMENT */
  /* 4 = ZCOPY_REPEAT GET THE ACTIVE ELEMENT    */
  /* 5 = ZCOPY_ABORT RELOCK THE ACTIVE ELEMENT  */
  /* 6 = ZCOPY_CLOSE THE SCAN PROCESS IS READY  */
  /* ------------------------------------------ */
  tresult = 0;
  ptrCheckGuard(scanPtr, cscanRecSize, scanRec);
  ndbrequire(scanPtr.p->scanState == ScanRec::WAIT_NEXT);

  switch (tscanNextFlag) {
  case NextScanReq::ZSCAN_NEXT:
    jam();
    /*empty*/;
    break;
  case NextScanReq::ZSCAN_NEXT_COMMIT:
  case NextScanReq::ZSCAN_COMMIT:
    jam();
    /* --------------------------------------------------------------------- */
    /* COMMIT ACTIVE OPERATION. 
     * SEND NEXT SCAN ELEMENT IF IT IS ZCOPY_NEXT_COMMIT.
     * --------------------------------------------------------------------- */
    ptrCheckGuard(operationRecPtr, coprecsize, operationrec); 
    fragrecptr.i = operationRecPtr.p->fragptr;
    ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
    if (!scanPtr.p->scanReadCommittedFlag) {
      commitOperation(signal);
    }//if
    operationRecPtr.p->m_op_bits = Operationrec::OP_INITIAL;
    takeOutActiveScanOp(signal);
    releaseOpRec(signal);
    scanPtr.p->scanOpsAllocated--;
    if (tscanNextFlag == NextScanReq::ZSCAN_COMMIT) {
      jam();
      signal->theData[0] = scanPtr.p->scanUserptr;
      Uint32 blockNo = refToMain(scanPtr.p->scanUserblockref);
      EXECUTE_DIRECT(blockNo, GSN_NEXT_SCANCONF, signal, 1);
      return;
    }//if
    break;
  case NextScanReq::ZSCAN_CLOSE:
    jam();
    fragrecptr.i = scanPtr.p->activeLocalFrag;
    ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
    /* ---------------------------------------------------------------------
     * THE SCAN PROCESS IS FINISHED. RELOCK ALL LOCKED EL. 
     * RELESE ALL INVOLVED REC.
     * ------------------------------------------------------------------- */
    releaseScanLab(signal);
    return;
    break;
  default:
    ndbrequire(false);
    break;
  }//switch
  signal->theData[0] = scanPtr.i;
  signal->theData[1] = AccCheckScan::ZNOT_CHECK_LCP_STOP;
  execACC_CHECK_SCAN(signal);
  return;
}//Dbacc::execNEXT_SCANREQ()

void Dbacc::checkNextBucketLab(Signal* signal) 
{
  Page8Ptr nsPageptr;
  Page8Ptr cscPageidptr;
  Page8Ptr gnsPageidptr;
  Page8Ptr tnsPageidptr;
  Uint32 tnsElementptr;
  Uint32 tnsContainerptr;
  Uint32 tnsIsLocked;
  Uint32 tnsCopyDir;

  tnsCopyDir = fragrecptr.p->getPageNumber(scanPtr.p->nextBucketIndex);
  tnsPageidptr.i = getPagePtr(fragrecptr.p->directory, tnsCopyDir);
  ptrCheckGuard(tnsPageidptr, cpagesize, page8);
  gnsPageidptr.i = tnsPageidptr.i;
  gnsPageidptr.p = tnsPageidptr.p;
  tgsePageindex = fragrecptr.p->getPageIndex(scanPtr.p->nextBucketIndex);
  gsePageidptr.i = gnsPageidptr.i;
  gsePageidptr.p = gnsPageidptr.p;
  if (!getScanElement(signal)) {
    scanPtr.p->nextBucketIndex++;
    if (scanPtr.p->scanBucketState ==  ScanRec::SECOND_LAP) {
      if (scanPtr.p->nextBucketIndex > scanPtr.p->maxBucketIndexToRescan) {
	/* ---------------------------------------------------------------- */
	// We have finished the rescan phase. 
	// We are ready to proceed with the next fragment part.
	/* ---------------------------------------------------------------- */
        jam();
        checkNextFragmentLab(signal);
        return;
      }//if
    } else if (scanPtr.p->scanBucketState ==  ScanRec::FIRST_LAP) {
      if (fragrecptr.p->level.getTop() < scanPtr.p->nextBucketIndex) {
	/* ---------------------------------------------------------------- */
	// All buckets have been scanned a first time.
	/* ---------------------------------------------------------------- */
        if (scanPtr.p->minBucketIndexToRescan == 0xFFFFFFFF) {
          jam();
	  /* -------------------------------------------------------------- */
	  // We have not had any merges behind the scan. 
	  // Thus it is not necessary to perform any rescan any buckets 
	  // and we can proceed immediately with the next fragment part.
	  /* --------------------------------------------------------------- */
          checkNextFragmentLab(signal);
          return;
        } else {
          jam();
	  /* --------------------------------------------------------------------------------- */
	  // Some buckets are in the need of rescanning due to merges that have moved records
	  // from in front of the scan to behind the scan. During the merges we kept track of
	  // which buckets that need a rescan. We start with the minimum and end with maximum.
	  /* --------------------------------------------------------------------------------- */
          scanPtr.p->nextBucketIndex = scanPtr.p->minBucketIndexToRescan;
	  scanPtr.p->scanBucketState =  ScanRec::SECOND_LAP;
          if (scanPtr.p->maxBucketIndexToRescan > fragrecptr.p->level.getTop()) {
            jam();
	    /* --------------------------------------------------------------------------------- */
	    // If we have had so many merges that the maximum is bigger than the number of buckets
	    // then we will simply satisfy ourselves with scanning to the end. This can only happen
	    // after bringing down the total of buckets to less than half and the minimum should
	    // be 0 otherwise there is some problem.
	    /* --------------------------------------------------------------------------------- */
            if (scanPtr.p->minBucketIndexToRescan != 0) {
              jam();
              sendSystemerror(signal, __LINE__);
              return;
            }//if
            scanPtr.p->maxBucketIndexToRescan = fragrecptr.p->level.getTop();
          }//if
        }//if
      }//if
    }//if
    if ((scanPtr.p->scanBucketState ==  ScanRec::FIRST_LAP) &&
        (scanPtr.p->nextBucketIndex <= scanPtr.p->startNoOfBuckets)) {
      /* --------------------------------------------------------------------------------- */
      // We will only reset the scan indicator on the buckets that existed at the start of the
      // scan. The others will be handled by the split and merge code.
      /* --------------------------------------------------------------------------------- */
      trsbPageindex = fragrecptr.p->getPageIndex(scanPtr.p->nextBucketIndex);
      if (trsbPageindex != 0) {
        jam();
        rsbPageidptr.i = gnsPageidptr.i;
        rsbPageidptr.p = gnsPageidptr.p;
      } else {
        jam();
        tmpP = fragrecptr.p->getPageNumber(scanPtr.p->nextBucketIndex);
        cscPageidptr.i = getPagePtr(fragrecptr.p->directory, tmpP);
        ptrCheckGuard(cscPageidptr, cpagesize, page8);
        trsbPageindex = fragrecptr.p->getPageIndex(scanPtr.p->nextBucketIndex);
        rsbPageidptr.i = cscPageidptr.i;
        rsbPageidptr.p = cscPageidptr.p;
      }//if
      releaseScanBucket(signal);
    }//if
    signal->theData[0] = scanPtr.i;
    signal->theData[1] = AccCheckScan::ZCHECK_LCP_STOP;
    sendSignal(cownBlockref, GSN_ACC_CHECK_SCAN, signal, 2, JBB);
    return;
  }//if
  /* ----------------------------------------------------------------------- */
  /*	AN ELEMENT WHICH HAVE NOT BEEN SCANNED WAS FOUND. WE WILL PREPARE IT */
  /*	TO BE SENT TO THE LQH BLOCK FOR FURTHER PROCESSING.                  */
  /*    WE ASSUME THERE ARE OPERATION RECORDS AVAILABLE SINCE LQH SHOULD HAVE*/
  /*    GUARANTEED THAT THROUGH EARLY BOOKING.                               */
  /* ----------------------------------------------------------------------- */
  tnsIsLocked = tgseIsLocked;
  tnsElementptr = tgseElementptr;
  tnsContainerptr = tgseContainerptr;
  nsPageptr.i = gsePageidptr.i;
  nsPageptr.p = gsePageidptr.p;
  seizeOpRec(signal);
  tisoIsforward = tgseIsforward;
  tisoContainerptr = tnsContainerptr;
  tisoElementptr = tnsElementptr;
  isoPageptr.i = nsPageptr.i;
  isoPageptr.p = nsPageptr.p;
  initScanOpRec(signal);
 
  if (!tnsIsLocked){
    if (!scanPtr.p->scanReadCommittedFlag) {
      jam();
      slPageidptr = nsPageptr;
      tslElementptr = tnsElementptr;
      setlock(signal);
      insertLockOwnersList(signal, operationRecPtr);
      operationRecPtr.p->m_op_bits |= 
	Operationrec::OP_STATE_RUNNING | Operationrec::OP_RUN_QUEUE;
    }//if
  } else {
    arrGuard(tnsElementptr, 2048);
    queOperPtr.i = 
      ElementHeader::getOpPtrI(nsPageptr.p->word32[tnsElementptr]);
    ptrCheckGuard(queOperPtr, coprecsize, operationrec);
    if (queOperPtr.p->m_op_bits & Operationrec::OP_ELEMENT_DISAPPEARED ||
	Local_key::isInvalid(queOperPtr.p->localdata[0],
                             queOperPtr.p->localdata[1]))
    {
      jam();
      /* ------------------------------------------------------------------ */
      // If the lock owner indicates the element is disappeared then 
      // we will not report this tuple. We will continue with the next tuple.
      /* ------------------------------------------------------------------ */
      operationRecPtr.p->m_op_bits = Operationrec::OP_INITIAL;
      releaseOpRec(signal);
      scanPtr.p->scanOpsAllocated--;
      signal->theData[0] = scanPtr.i;
      signal->theData[1] = AccCheckScan::ZCHECK_LCP_STOP;
      sendSignal(cownBlockref, GSN_ACC_CHECK_SCAN, signal, 2, JBB);
      return;
    }//if
    if (!scanPtr.p->scanReadCommittedFlag) {
      Uint32 return_result;
      if (scanPtr.p->scanLockMode == ZREADLOCK) {
        jam();
        return_result = placeReadInLockQueue(queOperPtr);
      } else {
        jam();
        return_result = placeWriteInLockQueue(queOperPtr);
      }//if
      if (return_result == ZSERIAL_QUEUE) {
	/* -----------------------------------------------------------------
	 * WE PLACED THE OPERATION INTO A SERIAL QUEUE AND THUS WE HAVE TO 
	 * WAIT FOR THE LOCK TO BE RELEASED. WE CONTINUE WITH THE NEXT ELEMENT
	 * ----------------------------------------------------------------- */
        putOpScanLockQue();	/* PUT THE OP IN A QUE IN THE SCAN REC */
        signal->theData[0] = scanPtr.i;
        signal->theData[1] = AccCheckScan::ZCHECK_LCP_STOP;
        sendSignal(cownBlockref, GSN_ACC_CHECK_SCAN, signal, 2, JBB);
        return;
      } else if (return_result != ZPARALLEL_QUEUE) {
        jam();
	/* ----------------------------------------------------------------- */
	// The tuple is either not committed yet or a delete in 
	// the same transaction (not possible here since we are a scan). 
	// Thus we simply continue with the next tuple.
	/* ----------------------------------------------------------------- */
	operationRecPtr.p->m_op_bits = Operationrec::OP_INITIAL;
        releaseOpRec(signal);
	scanPtr.p->scanOpsAllocated--;
        signal->theData[0] = scanPtr.i;
        signal->theData[1] = AccCheckScan::ZCHECK_LCP_STOP;
        sendSignal(cownBlockref, GSN_ACC_CHECK_SCAN, signal, 2, JBB);
        return;
      }//if
      ndbassert(return_result == ZPARALLEL_QUEUE);
    }//if
  }//if
  /* ----------------------------------------------------------------------- */
  // Committed read proceed without caring for locks immediately 
  // down here except when the tuple was deleted permanently 
  // and no new operation has inserted it again.
  /* ----------------------------------------------------------------------- */
  putActiveScanOp(signal);
  sendNextScanConf(signal);
  return;
}//Dbacc::checkNextBucketLab()


void Dbacc::checkNextFragmentLab(Signal* signal) 
{
  scanPtr.p->scanBucketState =  ScanRec::SCAN_COMPLETED;
  // The scan is completed. ACC_CHECK_SCAN will perform all the necessary 
  // checks to see
  // what the next step is.
  signal->theData[0] = scanPtr.i;
  signal->theData[1] = AccCheckScan::ZCHECK_LCP_STOP;
  execACC_CHECK_SCAN(signal);
  return;
}//Dbacc::checkNextFragmentLab()

void Dbacc::initScanFragmentPart(Signal* signal)
{
  Page8Ptr cnfPageidptr;
  /* ----------------------------------------------------------------------- */
  // Set the active fragment part.
  // Set the current bucket scanned to the first.
  // Start with the first lap.
  // Remember the number of buckets at start of the scan.
  // Set the minimum and maximum to values that will always be smaller and 
  //    larger than.
  // Reset the scan indicator on the first bucket.
  /* ----------------------------------------------------------------------- */
  scanPtr.p->activeLocalFrag = fragrecptr.i;
  scanPtr.p->nextBucketIndex = 0;	/* INDEX OF SCAN BUCKET */
  scanPtr.p->scanBucketState = ScanRec::FIRST_LAP;
  scanPtr.p->startNoOfBuckets = fragrecptr.p->level.getTop();
  scanPtr.p->minBucketIndexToRescan = 0xFFFFFFFF;
  scanPtr.p->maxBucketIndexToRescan = 0;
  cnfPageidptr.i = getPagePtr(fragrecptr.p->directory, 0);
  ptrCheckGuard(cnfPageidptr, cpagesize, page8);
  trsbPageindex = fragrecptr.p->getPageIndex(scanPtr.p->nextBucketIndex);
  rsbPageidptr.i = cnfPageidptr.i;
  rsbPageidptr.p = cnfPageidptr.p;
  releaseScanBucket(signal);
}//Dbacc::initScanFragmentPart()

/* -------------------------------------------------------------------------
 * FLAG = 6 = ZCOPY_CLOSE THE SCAN PROCESS IS READY OR ABORTED. 
 * ALL OPERATION IN THE ACTIVE OR WAIT QUEUE ARE RELEASED, 
 * SCAN FLAG OF ROOT FRAG IS RESET AND THE SCAN RECORD IS RELEASED.
 * ------------------------------------------------------------------------ */
void Dbacc::releaseScanLab(Signal* signal) 
{
  releaseAndCommitActiveOps(signal);
  releaseAndCommitQueuedOps(signal);
  releaseAndAbortLockedOps(signal);

  fragrecptr.i = scanPtr.p->activeLocalFrag;
  ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
  for (tmp = 0; tmp < MAX_PARALLEL_SCANS_PER_FRAG; tmp++) {
    jam();
    if (fragrecptr.p->scan[tmp] == scanPtr.i) {
      jam();
      fragrecptr.p->scan[tmp] = RNIL;
    }//if
  }//for
  // Stops the heartbeat.
  signal->theData[0] = scanPtr.p->scanUserptr;
  signal->theData[1] = RNIL;
  signal->theData[2] = RNIL;
  sendSignal(scanPtr.p->scanUserblockref, GSN_NEXT_SCANCONF, signal, 3, JBB);
  releaseScanRec(signal);
  return;
}//Dbacc::releaseScanLab()


void Dbacc::releaseAndCommitActiveOps(Signal* signal) 
{
  OperationrecPtr trsoOperPtr;
  operationRecPtr.i = scanPtr.p->scanFirstActiveOp;
  while (operationRecPtr.i != RNIL) {
    jam();
    ptrCheckGuard(operationRecPtr, coprecsize, operationrec);
    trsoOperPtr.i = operationRecPtr.p->nextOp;
    fragrecptr.i = operationRecPtr.p->fragptr;
    ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
    if (!scanPtr.p->scanReadCommittedFlag) {
      jam();
      if ((operationRecPtr.p->m_op_bits & Operationrec::OP_STATE_MASK) ==
	  Operationrec::OP_STATE_EXECUTED)
      {
	commitOperation(signal);
      }
      else
      {
	abortOperation(signal);
      }
    }//if
    operationRecPtr.p->m_op_bits = Operationrec::OP_INITIAL;
    takeOutActiveScanOp(signal);
    releaseOpRec(signal);
    scanPtr.p->scanOpsAllocated--;
    operationRecPtr.i = trsoOperPtr.i;
  }//if
}//Dbacc::releaseAndCommitActiveOps()


void Dbacc::releaseAndCommitQueuedOps(Signal* signal) 
{
  OperationrecPtr trsoOperPtr;
  operationRecPtr.i = scanPtr.p->scanFirstQueuedOp;
  while (operationRecPtr.i != RNIL) {
    jam();
    ptrCheckGuard(operationRecPtr, coprecsize, operationrec);
    trsoOperPtr.i = operationRecPtr.p->nextOp;
    fragrecptr.i = operationRecPtr.p->fragptr;
    ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
    if (!scanPtr.p->scanReadCommittedFlag) {
      jam();
      if ((operationRecPtr.p->m_op_bits & Operationrec::OP_STATE_MASK) ==
	  Operationrec::OP_STATE_EXECUTED)
      {
	commitOperation(signal);
      }
      else
      {
	abortOperation(signal);
      }
    }//if
    operationRecPtr.p->m_op_bits = Operationrec::OP_INITIAL;
    takeOutReadyScanQueue(signal);
    releaseOpRec(signal);
    scanPtr.p->scanOpsAllocated--;
    operationRecPtr.i = trsoOperPtr.i;
  }//if
}//Dbacc::releaseAndCommitQueuedOps()

void Dbacc::releaseAndAbortLockedOps(Signal* signal) {

  OperationrecPtr trsoOperPtr;
  operationRecPtr.i = scanPtr.p->scanFirstLockedOp;
  while (operationRecPtr.i != RNIL) {
    jam();
    ptrCheckGuard(operationRecPtr, coprecsize, operationrec);
    trsoOperPtr.i = operationRecPtr.p->nextOp;
    fragrecptr.i = operationRecPtr.p->fragptr;
    ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
    if (!scanPtr.p->scanReadCommittedFlag) {
      jam();
      abortOperation(signal);
    }//if
    takeOutScanLockQueue(scanPtr.i);
    operationRecPtr.p->m_op_bits = Operationrec::OP_INITIAL;
    releaseOpRec(signal);
    scanPtr.p->scanOpsAllocated--;
    operationRecPtr.i = trsoOperPtr.i;
  }//if
}//Dbacc::releaseAndAbortLockedOps()

/* 3.18.3  ACC_CHECK_SCAN */
/* ******************--------------------------------------------------------------- */
/* ACC_CHECK_SCAN                                                                    */
/*          ENTER ACC_CHECK_SCAN WITH                                                */
/*                    SCAN_PTR                                                       */
/* ******************--------------------------------------------------------------- */
/* ******************--------------------------------------------------------------- */
/* ACC_CHECK_SCAN                                                                    */
/* ******************------------------------------+                                 */
void Dbacc::execACC_CHECK_SCAN(Signal* signal) 
{
  Uint32 TcheckLcpStop;
  jamEntry();
  scanPtr.i = signal->theData[0];
  TcheckLcpStop = signal->theData[1];
  ptrCheckGuard(scanPtr, cscanRecSize, scanRec);
  while (scanPtr.p->scanFirstQueuedOp != RNIL) {
    jam();
    //---------------------------------------------------------------------
    // An operation has been released from the lock queue. 
    // We are in the parallel queue of this tuple. We are 
    // ready to report the tuple now.
    //------------------------------------------------------------------------
    operationRecPtr.i = scanPtr.p->scanFirstQueuedOp;
    ptrCheckGuard(operationRecPtr, coprecsize, operationrec);
    takeOutReadyScanQueue(signal);
    fragrecptr.i = operationRecPtr.p->fragptr;
    ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
    if (operationRecPtr.p->m_op_bits & Operationrec::OP_ELEMENT_DISAPPEARED) 
    {
      jam();
      abortOperation(signal);
      operationRecPtr.p->m_op_bits = Operationrec::OP_INITIAL;
      releaseOpRec(signal);
      scanPtr.p->scanOpsAllocated--;
      continue;
    }//if
    putActiveScanOp(signal);
    sendNextScanConf(signal);
    return;
  }//while


  if ((scanPtr.p->scanBucketState == ScanRec::SCAN_COMPLETED) &&
      (scanPtr.p->scanLockHeld == 0)) {
    jam();
    //----------------------------------------------------------------------------
    // The scan is now completed and there are no more locks outstanding. Thus we
    // we will report the scan as completed to LQH.
    //----------------------------------------------------------------------------
    signal->theData[0] = scanPtr.p->scanUserptr;
    signal->theData[1] = RNIL;
    signal->theData[2] = RNIL;
    sendSignal(scanPtr.p->scanUserblockref, GSN_NEXT_SCANCONF, signal, 3, JBB);
    return;
  }//if
  if (TcheckLcpStop == AccCheckScan::ZCHECK_LCP_STOP) {
  //---------------------------------------------------------------------------
  // To ensure that the block of the fragment occurring at the start of a local
  // checkpoint is not held for too long we insert a release and reacquiring of
  // that lock here. This is performed in LQH. If we are blocked or if we have
  // requested a sleep then we will receive RNIL in the returning signal word.
  //---------------------------------------------------------------------------
    signal->theData[0] = scanPtr.p->scanUserptr;
    signal->theData[1] =
                    ((scanPtr.p->scanLockHeld >= ZSCAN_MAX_LOCK) ||
                     (scanPtr.p->scanBucketState ==  ScanRec::SCAN_COMPLETED));
    EXECUTE_DIRECT(DBLQH, GSN_CHECK_LCP_STOP, signal, 2);
    jamEntry();
    if (signal->theData[0] == RNIL) {
      jam();
      return;
    }//if
  }//if
  /**
   * If we have more than max locks held OR
   * scan is completed AND at least one lock held
   *  - Inform LQH about this condition
   */
  if ((scanPtr.p->scanLockHeld >= ZSCAN_MAX_LOCK) ||
      (cfreeopRec == RNIL) ||
      ((scanPtr.p->scanBucketState == ScanRec::SCAN_COMPLETED) &&
       (scanPtr.p->scanLockHeld > 0))) {
    jam();
    signal->theData[0] = scanPtr.p->scanUserptr;
    signal->theData[1] = RNIL; // No operation is returned
    signal->theData[2] = 512;  // MASV  
    sendSignal(scanPtr.p->scanUserblockref, GSN_NEXT_SCANCONF, signal, 3, JBB);
    return;
  }
  if (scanPtr.p->scanBucketState == ScanRec::SCAN_COMPLETED) {
    jam();
    signal->theData[0] = scanPtr.i;
    signal->theData[1] = AccCheckScan::ZCHECK_LCP_STOP;
    execACC_CHECK_SCAN(signal);
    return;
  }//if

  fragrecptr.i = scanPtr.p->activeLocalFrag;
  ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
  checkNextBucketLab(signal);
  return;
}//Dbacc::execACC_CHECK_SCAN()

/* ******************---------------------------------------------------- */
/* ACC_TO_REQ                                       PERFORM A TAKE OVER   */
/* ******************-------------------+                                 */
/*   SENDER: LQH,    LEVEL B       */
void Dbacc::execACC_TO_REQ(Signal* signal) 
{
  OperationrecPtr tatrOpPtr;

  jamEntry();
  tatrOpPtr.i = signal->theData[1];     /*  OPER PTR OF ACC                */
  ptrCheckGuard(tatrOpPtr, coprecsize, operationrec);
  if ((tatrOpPtr.p->m_op_bits & Operationrec::OP_MASK) == ZSCAN_OP) 
  {
    tatrOpPtr.p->transId1 = signal->theData[2];
    tatrOpPtr.p->transId2 = signal->theData[3];
    validate_lock_queue(tatrOpPtr);
  } else {
    jam();
    signal->theData[0] = cminusOne;
    signal->theData[1] = ZTO_OP_STATE_ERROR;
  }//if
  return;
}//Dbacc::execACC_TO_REQ()

/* --------------------------------------------------------------------------------- */
/* CONTAINERINFO                                                                     */
/*        INPUT:                                                                     */
/*               CI_PAGEIDPTR (PAGE POINTER WHERE CONTAINER RESIDES)                 */
/*               TCI_PAGEINDEX (INDEX OF CONTAINER, USED TO CALCULATE PAGE INDEX)    */
/*               TCI_ISFORWARD (DIRECTION OF CONTAINER FORWARD OR BACKWARD)          */
/*                                                                                   */
/*        OUTPUT:                                                                    */
/*               TCI_CONTAINERPTR (A POINTER TO THE HEAD OF THE CONTAINER)           */
/*               TCI_CONTAINERLEN (LENGTH OF THE CONTAINER                           */
/*               TCI_CONTAINERHEAD (THE HEADER OF THE CONTAINER)                     */
/*                                                                                   */
/*        DESCRIPTION: THE ADDRESS OF THE CONTAINER WILL BE CALCULATED AND           */
/*                     ALL INFORMATION ABOUT THE CONTAINER WILL BE READ              */
/* --------------------------------------------------------------------------------- */
void Dbacc::containerinfo(Signal* signal, ContainerHeader& containerhead)
{
  tciContainerptr = mul_ZBUF_SIZE(tciPageindex);
  if (tciIsforward == ZTRUE) {
    jam();
    tciContainerptr = tciContainerptr + ZHEAD_SIZE;
  } else {
    jam();
    tciContainerptr = ((tciContainerptr + ZHEAD_SIZE) + ZBUF_SIZE) - Container::HEADER_SIZE;
  }//if
  arrGuard(tciContainerptr, 2048);
  containerhead = ciPageidptr.p->word32[tciContainerptr];
  tciContainerlen = containerhead.getLength();
}//Dbacc::containerinfo()

/* --------------------------------------------------------------------------------- */
/* GET_SCAN_ELEMENT                                                                  */
/*       INPUT:          GSE_PAGEIDPTR                                               */
/*                       TGSE_PAGEINDEX                                              */
/*       OUTPUT:         TGSE_IS_LOCKED (IF TRESULT /= ZFALSE)                       */
/*                       GSE_PAGEIDPTR                                               */
/*                       TGSE_PAGEINDEX                                              */
/* --------------------------------------------------------------------------------- */
bool Dbacc::getScanElement(Signal* signal) 
{
  ContainerHeader containerhead;
  tgseIsforward = ZTRUE;
 NEXTSEARCH_SCAN_LOOP:
  ciPageidptr.i = gsePageidptr.i;
  ciPageidptr.p = gsePageidptr.p;
  tciPageindex = tgsePageindex;
  tciIsforward = tgseIsforward;
  containerinfo(signal, containerhead);
  sscPageidptr.i = gsePageidptr.i;
  sscPageidptr.p = gsePageidptr.p;
  tsscContainerlen = tciContainerlen;
  tsscContainerptr = tciContainerptr;
  tsscIsforward = tciIsforward;
  if (searchScanContainer(signal)) {
    jam();
    tgseIsLocked = tsscIsLocked;
    tgseElementptr = tsscElementptr;
    tgseContainerptr = tsscContainerptr;
    return true;
  }//if
  if (containerhead.getNextEnd() != 0) {
    jam();
    nciPageidptr.i = gsePageidptr.i;
    nciPageidptr.p = gsePageidptr.p;
    tnciContainerptr = tciContainerptr;
    nextcontainerinfo(signal, containerhead);
    tgsePageindex = tnciPageindex;
    gsePageidptr.i = nciPageidptr.i;
    gsePageidptr.p = nciPageidptr.p;
    tgseIsforward = tnciIsforward;
    goto NEXTSEARCH_SCAN_LOOP;
  }//if
  return false;
}//Dbacc::getScanElement()

/* --------------------------------------------------------------------------------- */
/*  INIT_SCAN_OP_REC                                                                 */
/* --------------------------------------------------------------------------------- */
void Dbacc::initScanOpRec(Signal* signal) 
{
  Uint32 tisoLocalPtr;
  Uint32 localkeylen = fragrecptr.p->localkeylen;

  scanPtr.p->scanOpsAllocated++;

  Uint32 opbits = 0;
  opbits |= ZSCAN_OP;
  opbits |= scanPtr.p->scanLockMode ? (Uint32) Operationrec::OP_LOCK_MODE : 0;
  opbits |= scanPtr.p->scanLockMode ? (Uint32) Operationrec::OP_ACC_LOCK_MODE : 0;
  opbits |= (scanPtr.p->scanReadCommittedFlag ? 
             (Uint32) Operationrec::OP_EXECUTED_DIRTY_READ : 0);
  opbits |= Operationrec::OP_COMMIT_DELETE_CHECK;
  operationRecPtr.p->userptr = RNIL;
  operationRecPtr.p->scanRecPtr = scanPtr.i;
  operationRecPtr.p->fid = fragrecptr.p->myfid;
  operationRecPtr.p->fragptr = fragrecptr.i;
  operationRecPtr.p->nextParallelQue = RNIL;
  operationRecPtr.p->prevParallelQue = RNIL;
  operationRecPtr.p->nextSerialQue = RNIL;
  operationRecPtr.p->prevSerialQue = RNIL;
  operationRecPtr.p->transId1 = scanPtr.p->scanTrid1;
  operationRecPtr.p->transId2 = scanPtr.p->scanTrid2;
  operationRecPtr.p->elementIsforward = tisoIsforward;
  operationRecPtr.p->elementContainer = tisoContainerptr;
  operationRecPtr.p->elementPointer = tisoElementptr;
  operationRecPtr.p->elementPage = isoPageptr.i;
  operationRecPtr.p->m_op_bits = opbits;
  tisoLocalPtr = tisoElementptr + tisoIsforward;

  arrGuard(tisoLocalPtr, 2048);
  Uint32 Tkey1 = isoPageptr.p->word32[tisoLocalPtr];
  tisoLocalPtr = tisoLocalPtr + tisoIsforward;
  if (localkeylen == 1)
  {
    operationRecPtr.p->localdata[0] = Local_key::ref2page_id(Tkey1);
    operationRecPtr.p->localdata[1] = Local_key::ref2page_idx(Tkey1);
  }
  else
  {
    arrGuard(tisoLocalPtr, 2048);
    operationRecPtr.p->localdata[0] = Tkey1;
    operationRecPtr.p->localdata[1] = isoPageptr.p->word32[tisoLocalPtr];
  }
  operationRecPtr.p->hashValue.clear();
  operationRecPtr.p->tupkeylen = fragrecptr.p->keyLength;
  operationRecPtr.p->xfrmtupkeylen = 0; // not used
}//Dbacc::initScanOpRec()

/* --------------------------------------------------------------------------------- */
/* NEXTCONTAINERINFO                                                                 */
/*        DESCRIPTION:THE CONTAINER HEAD WILL BE CHECKED TO CALCULATE INFORMATION    */
/*                    ABOUT NEXT CONTAINER IN THE BUCKET.                            */
/*          INPUT:       TNCI_CONTAINERHEAD                                          */
/*                       NCI_PAGEIDPTR                                               */
/*                       TNCI_CONTAINERPTR                                           */
/*          OUTPUT:                                                                  */
/*             TNCI_PAGEINDEX (INDEX FROM WHICH PAGE INDEX CAN BE CALCULATED).       */
/*             TNCI_ISFORWARD (IS THE NEXT CONTAINER FORWARD (+1) OR BACKWARD (-1)   */
/*             NCI_PAGEIDPTR (PAGE REFERENCE OF NEXT CONTAINER)                      */
/* --------------------------------------------------------------------------------- */
void Dbacc::nextcontainerinfo(Signal* signal, ContainerHeader const containerhead)
{
  /* THE NEXT CONTAINER IS IN THE SAME PAGE */
  tnciPageindex = containerhead.getNextIndexNumber();	/* NEXT CONTAINER PAGE INDEX 7 BITS */
  if (containerhead.getNextEnd() == ZLEFT) {
    jam();
    tnciIsforward = ZTRUE;
  } else {
    jam();
    tnciIsforward = cminusOne;
  }//if
  if (!containerhead.isNextOnSamePage()) {
    jam();
    /* NEXT CONTAINER IS IN AN OVERFLOW PAGE */
    arrGuard(tnciContainerptr + 1, 2048);
    nciPageidptr.i = nciPageidptr.p->word32[tnciContainerptr + 1];
    ptrCheckGuard(nciPageidptr, cpagesize, page8);
  }//if
}//Dbacc::nextcontainerinfo()

/* --------------------------------------------------------------------------------- */
/* PUT_ACTIVE_SCAN_OP                                                                */
/* --------------------------------------------------------------------------------- */
void Dbacc::putActiveScanOp(Signal* signal) 
{
  OperationrecPtr pasOperationRecPtr;
  pasOperationRecPtr.i = scanPtr.p->scanFirstActiveOp;
  if (pasOperationRecPtr.i != RNIL) {
    jam();
    ptrCheckGuard(pasOperationRecPtr, coprecsize, operationrec);
    pasOperationRecPtr.p->prevOp = operationRecPtr.i;
  }//if
  operationRecPtr.p->nextOp = pasOperationRecPtr.i;
  operationRecPtr.p->prevOp = RNIL;
  scanPtr.p->scanFirstActiveOp = operationRecPtr.i;
}//Dbacc::putActiveScanOp()

/**
 * putOpScanLockQueue
 *
 * Description: Put an operation in the doubly linked 
 * lock list on a scan record. The list is used to 
 * keep track of which operations belonging
 * to the scan are put in serial lock list of another 
 * operation
 *
 * @note Use takeOutScanLockQueue to remove an operation
 *       from the list
 *
 */
void Dbacc::putOpScanLockQue() 
{

#ifdef VM_TRACE
  // DEBUG CODE
  // Check that there are as many operations in the lockqueue as 
  // scanLockHeld indicates
  OperationrecPtr tmpOp;
  int numLockedOpsBefore = 0;
  tmpOp.i = scanPtr.p->scanFirstLockedOp;
  while(tmpOp.i != RNIL){
    numLockedOpsBefore++;
    ptrCheckGuard(tmpOp, coprecsize, operationrec);
    if (tmpOp.p->nextOp == RNIL)
    {
      ndbrequire(tmpOp.i == scanPtr.p->scanLastLockedOp);
    }
    tmpOp.i = tmpOp.p->nextOp;
  } 
  ndbrequire(numLockedOpsBefore==scanPtr.p->scanLockHeld);
#endif

  OperationrecPtr pslOperationRecPtr;
  ScanRec theScanRec;
  theScanRec = *scanPtr.p;

  pslOperationRecPtr.i = scanPtr.p->scanLastLockedOp;
  operationRecPtr.p->prevOp = pslOperationRecPtr.i;
  operationRecPtr.p->nextOp = RNIL;
  if (pslOperationRecPtr.i != RNIL) {
    jam();
    ptrCheckGuard(pslOperationRecPtr, coprecsize, operationrec);
    pslOperationRecPtr.p->nextOp = operationRecPtr.i;
  } else {
    jam();
    scanPtr.p->scanFirstLockedOp = operationRecPtr.i;
  }//if
  scanPtr.p->scanLastLockedOp = operationRecPtr.i;
  scanPtr.p->scanLockHeld++;

}//Dbacc::putOpScanLockQue()

/* --------------------------------------------------------------------------------- */
/* PUT_READY_SCAN_QUEUE                                                              */
/* --------------------------------------------------------------------------------- */
void Dbacc::putReadyScanQueue(Signal* signal, Uint32 scanRecIndex) 
{
  OperationrecPtr prsOperationRecPtr;
  ScanRecPtr TscanPtr;

  TscanPtr.i = scanRecIndex;
  ptrCheckGuard(TscanPtr, cscanRecSize, scanRec);

  prsOperationRecPtr.i = TscanPtr.p->scanLastQueuedOp;
  operationRecPtr.p->prevOp = prsOperationRecPtr.i;
  operationRecPtr.p->nextOp = RNIL;
  TscanPtr.p->scanLastQueuedOp = operationRecPtr.i;
  if (prsOperationRecPtr.i != RNIL) {
    jam();
    ptrCheckGuard(prsOperationRecPtr, coprecsize, operationrec);
    prsOperationRecPtr.p->nextOp = operationRecPtr.i;
  } else {
    jam();
    TscanPtr.p->scanFirstQueuedOp = operationRecPtr.i;
  }//if
}//Dbacc::putReadyScanQueue()

/* --------------------------------------------------------------------------------- */
/* RELEASE_SCAN_BUCKET                                                               */
// Input:
//   rsbPageidptr.i     Index to page where buckets starts
//   rsbPageidptr.p     Pointer to page where bucket starts
//   trsbPageindex      Page index of starting container in bucket
/* --------------------------------------------------------------------------------- */
void Dbacc::releaseScanBucket(Signal* signal) 
{
  ContainerHeader containerhead;
  Uint32 trsbIsforward;

  trsbIsforward = ZTRUE;
 NEXTRELEASESCANLOOP:
  ciPageidptr.i = rsbPageidptr.i;
  ciPageidptr.p = rsbPageidptr.p;
  tciPageindex = trsbPageindex;
  tciIsforward = trsbIsforward;
  containerinfo(signal, containerhead);
  rscPageidptr.i = rsbPageidptr.i;
  rscPageidptr.p = rsbPageidptr.p;
  trscContainerlen = tciContainerlen;
  trscContainerptr = tciContainerptr;
  trscIsforward = trsbIsforward;
  releaseScanContainer(signal);
  if (containerhead.getNextEnd() != 0) {
    jam();
    nciPageidptr.i = rsbPageidptr.i;
    nciPageidptr.p = rsbPageidptr.p;
    tnciContainerptr = tciContainerptr;
    nextcontainerinfo(signal, containerhead);
    rsbPageidptr.i = nciPageidptr.i;
    rsbPageidptr.p = nciPageidptr.p;
    trsbPageindex = tnciPageindex;
    trsbIsforward = tnciIsforward;
    goto NEXTRELEASESCANLOOP;
  }//if
}//Dbacc::releaseScanBucket()

/* --------------------------------------------------------------------------------- */
/*  RELEASE_SCAN_CONTAINER                                                           */
/*       INPUT:           TRSC_CONTAINERLEN                                          */
/*                        RSC_PAGEIDPTR                                              */
/*                        TRSC_CONTAINERPTR                                          */
/*                        TRSC_ISFORWARD                                             */
/*                        SCAN_PTR                                                   */
/*                                                                                   */
/*            DESCRIPTION: SEARCHS IN A CONTAINER, AND THE SCAN BIT OF THE ELEMENTS  */
/*                            OF THE CONTAINER IS RESET                              */
/* --------------------------------------------------------------------------------- */
void Dbacc::releaseScanContainer(Signal* signal) 
{
  OperationrecPtr rscOperPtr;
  Uint32 trscElemStep;
  Uint32 trscElementptr;
  Uint32 trscElemlens;
  Uint32 trscElemlen;

  if (trscContainerlen < 4) {
    if (trscContainerlen != Container::HEADER_SIZE) {
      jam();
      sendSystemerror(signal, __LINE__);
    }//if
    return;	/* 2 IS THE MINIMUM SIZE OF THE ELEMENT */
  }//if
  trscElemlens = trscContainerlen - Container::HEADER_SIZE;
  trscElemlen = fragrecptr.p->elementLength;
  if (trscIsforward == 1) {
    jam();
    trscElementptr = trscContainerptr + Container::HEADER_SIZE;
    trscElemStep = trscElemlen;
  } else {
    jam();
    trscElementptr = trscContainerptr - 1;
    trscElemStep = 0 - trscElemlen;
  }//if
  do {
    arrGuard(trscElementptr, 2048);
    const Uint32 eh = rscPageidptr.p->word32[trscElementptr];
    const Uint32 scanMask = scanPtr.p->scanMask;
    if (ElementHeader::getUnlocked(eh)) {
      jam();
      const Uint32 tmp = ElementHeader::clearScanBit(eh, scanMask);
      dbgWord32(rscPageidptr, trscElementptr, tmp);
      rscPageidptr.p->word32[trscElementptr] = tmp;
    } else {
      jam();
      rscOperPtr.i = ElementHeader::getOpPtrI(eh);
      ptrCheckGuard(rscOperPtr, coprecsize, operationrec);
      rscOperPtr.p->scanBits &= ~scanMask;
    }//if
    trscElemlens = trscElemlens - trscElemlen;
    trscElementptr = trscElementptr + trscElemStep;
  } while (trscElemlens > 1);
  if (trscElemlens != 0) {
    jam();
    sendSystemerror(signal, __LINE__);
  }//if
}//Dbacc::releaseScanContainer()

/* --------------------------------------------------------------------------------- */
/* RELEASE_SCAN_REC                                                                  */
/* --------------------------------------------------------------------------------- */
void Dbacc::releaseScanRec(Signal* signal) 
{  
  // Check that all ops this scan has allocated have been 
  // released
  ndbrequire(scanPtr.p->scanOpsAllocated==0);

  // Check that all locks this scan might have aquired 
  // have been properly released
  ndbrequire(scanPtr.p->scanLockHeld == 0);
  ndbrequire(scanPtr.p->scanFirstLockedOp == RNIL);
  ndbrequire(scanPtr.p->scanLastLockedOp == RNIL);

  // Check that all active operations have been 
  // properly released
  ndbrequire(scanPtr.p->scanFirstActiveOp == RNIL);

  // Check that all queued operations have been 
  // properly released
  ndbrequire(scanPtr.p->scanFirstQueuedOp == RNIL);
  ndbrequire(scanPtr.p->scanLastQueuedOp == RNIL);

  // Put scan record in free list
  scanPtr.p->scanNextfreerec = cfirstFreeScanRec;
  scanPtr.p->scanState = ScanRec::SCAN_DISCONNECT;
  cfirstFreeScanRec = scanPtr.i;

}//Dbacc::releaseScanRec()

/* --------------------------------------------------------------------------------- */
/*  SEARCH_SCAN_CONTAINER                                                            */
/*       INPUT:           TSSC_CONTAINERLEN                                          */
/*                        TSSC_CONTAINERPTR                                          */
/*                        TSSC_ISFORWARD                                             */
/*                        SSC_PAGEIDPTR                                              */
/*                        SCAN_PTR                                                   */
/*       OUTPUT:          TSSC_IS_LOCKED                                             */
/*                                                                                   */
/*            DESCRIPTION: SEARCH IN A CONTAINER TO FIND THE NEXT SCAN ELEMENT.      */
/*                    TO DO THIS THE SCAN BIT OF THE ELEMENT HEADER IS CHECKED. IF   */
/*                    THIS BIT IS ZERO, IT IS SET TO ONE AND THE ELEMENT IS RETURNED.*/
/* --------------------------------------------------------------------------------- */
bool Dbacc::searchScanContainer(Signal* signal) 
{
  OperationrecPtr sscOperPtr;
  Uint32 tsscScanBits;
  Uint32 tsscElemlens;
  Uint32 tsscElemlen;
  Uint32 tsscElemStep;

  if (tsscContainerlen < 4) {
    jam();
    return false;	/* 2 IS THE MINIMUM SIZE OF THE ELEMENT */
  }//if
  tsscElemlens = tsscContainerlen - Container::HEADER_SIZE;
  tsscElemlen = fragrecptr.p->elementLength;
  /* LENGTH OF THE ELEMENT */
  if (tsscIsforward == 1) {
    jam();
    tsscElementptr = tsscContainerptr + Container::HEADER_SIZE;
    tsscElemStep = tsscElemlen;
  } else {
    jam();
    tsscElementptr = tsscContainerptr - 1;
    tsscElemStep = 0 - tsscElemlen;
  }//if
 SCANELEMENTLOOP001:
  arrGuard(tsscElementptr, 2048);
  const Uint32 eh = sscPageidptr.p->word32[tsscElementptr];
  tsscIsLocked = ElementHeader::getLocked(eh);
  if (!tsscIsLocked){
    jam();
    tsscScanBits = ElementHeader::getScanBits(eh);
    if ((scanPtr.p->scanMask & tsscScanBits) == 0) {
      jam();
      const Uint32 tmp = ElementHeader::setScanBit(eh, scanPtr.p->scanMask);
      dbgWord32(sscPageidptr, tsscElementptr, tmp);
      sscPageidptr.p->word32[tsscElementptr] = tmp;
      return true;
    }//if
  } else {
    jam();
    sscOperPtr.i = ElementHeader::getOpPtrI(eh);
    ptrCheckGuard(sscOperPtr, coprecsize, operationrec);
    if ((sscOperPtr.p->scanBits & scanPtr.p->scanMask) == 0) {
      jam();
      sscOperPtr.p->scanBits |= scanPtr.p->scanMask;
      return true;
    }//if
  }//if
  /* THE ELEMENT IS ALREADY SENT. */
  /* SEARCH FOR NEXT ONE */
  tsscElemlens = tsscElemlens - tsscElemlen;
  if (tsscElemlens > 1) {
    jam();
    tsscElementptr = tsscElementptr + tsscElemStep;
    goto SCANELEMENTLOOP001;
  }//if
  return false;
}//Dbacc::searchScanContainer()

/* --------------------------------------------------------------------------------- */
/*  SEND THE RESPONSE NEXT_SCANCONF AND POSSIBLE KEYINFO SIGNALS AS WELL.            */
/* --------------------------------------------------------------------------------- */
void Dbacc::sendNextScanConf(Signal* signal) 
{
  Uint32 blockNo = refToMain(scanPtr.p->scanUserblockref);

  jam();
  /** ---------------------------------------------------------------------
   * LQH WILL NOT HAVE ANY USE OF THE TUPLE KEY LENGTH IN THIS CASE AND 
   * SO WE DO NOT PROVIDE IT. IN THIS CASE THESE VALUES ARE UNDEFINED. 
   * ---------------------------------------------------------------------- */
  signal->theData[0] = scanPtr.p->scanUserptr;
  signal->theData[1] = operationRecPtr.i;
  signal->theData[2] = operationRecPtr.p->fid;
  signal->theData[3] = operationRecPtr.p->localdata[0];
  signal->theData[4] = operationRecPtr.p->localdata[1];
  EXECUTE_DIRECT(blockNo, GSN_NEXT_SCANCONF, signal, 5);
  return;
}//Dbacc::sendNextScanConf()

/* --------------------------------------------------------------------------------- */
/* SETLOCK                                                                           */
/*          DESCRIPTION:SETS LOCK ON AN ELEMENT. INFORMATION ABOUT THE ELEMENT IS    */
/*                      SAVED IN THE ELEMENT HEAD.A COPY OF THIS INFORMATION WILL    */
/*                       BE PUT IN THE OPERATION RECORD. A FIELD IN THE  HEADER OF   */
/*                       THE ELEMENT POINTS TO THE OPERATION RECORD.                 */
/* --------------------------------------------------------------------------------- */
void Dbacc::setlock(Signal* signal) 
{
  Uint32 tselTmp1;

  arrGuard(tslElementptr, 2048);
  tselTmp1 = slPageidptr.p->word32[tslElementptr];
  operationRecPtr.p->scanBits = ElementHeader::getScanBits(tselTmp1);
  operationRecPtr.p->reducedHashValue = ElementHeader::getReducedHashValue(tselTmp1);

  tselTmp1 = ElementHeader::setLocked(operationRecPtr.i);
  dbgWord32(slPageidptr, tslElementptr, tselTmp1);
  slPageidptr.p->word32[tslElementptr] = tselTmp1;
}//Dbacc::setlock()

/* --------------------------------------------------------------------------------- */
/*  TAKE_OUT_ACTIVE_SCAN_OP                                                          */
/*         DESCRIPTION: AN ACTIVE SCAN OPERATION IS BELOGED TO AN ACTIVE LIST OF THE */
/*                      SCAN RECORD. BY THIS SUBRUTIN THE LIST IS UPDATED.           */
/* --------------------------------------------------------------------------------- */
void Dbacc::takeOutActiveScanOp(Signal* signal) 
{
  OperationrecPtr tasOperationRecPtr;

  if (operationRecPtr.p->prevOp != RNIL) {
    jam();
    tasOperationRecPtr.i = operationRecPtr.p->prevOp;
    ptrCheckGuard(tasOperationRecPtr, coprecsize, operationrec);
    tasOperationRecPtr.p->nextOp = operationRecPtr.p->nextOp;
  } else {
    jam();
    scanPtr.p->scanFirstActiveOp = operationRecPtr.p->nextOp;
  }//if
  if (operationRecPtr.p->nextOp != RNIL) {
    jam();
    tasOperationRecPtr.i = operationRecPtr.p->nextOp;
    ptrCheckGuard(tasOperationRecPtr, coprecsize, operationrec);
    tasOperationRecPtr.p->prevOp = operationRecPtr.p->prevOp;
  }//if
}//Dbacc::takeOutActiveScanOp()

/**
 * takeOutScanLockQueue
 *
 * Description: Take out an operation from the doubly linked 
 * lock list on a scan record.
 *
 * @note Use putOpScanLockQue to insert a operation in 
 *       the list
 *
 */
void Dbacc::takeOutScanLockQueue(Uint32 scanRecIndex) 
{
  OperationrecPtr tslOperationRecPtr;
  ScanRecPtr TscanPtr;

  TscanPtr.i = scanRecIndex;
  ptrCheckGuard(TscanPtr, cscanRecSize, scanRec);

  if (operationRecPtr.p->prevOp != RNIL) {
    jam();
    tslOperationRecPtr.i = operationRecPtr.p->prevOp;
    ptrCheckGuard(tslOperationRecPtr, coprecsize, operationrec);
    tslOperationRecPtr.p->nextOp = operationRecPtr.p->nextOp;
  } else {
    jam();
    // Check that first are pointing at operation to take out
    ndbrequire(TscanPtr.p->scanFirstLockedOp==operationRecPtr.i);
    TscanPtr.p->scanFirstLockedOp = operationRecPtr.p->nextOp;
  }//if
  if (operationRecPtr.p->nextOp != RNIL) {
    jam();
    tslOperationRecPtr.i = operationRecPtr.p->nextOp;
    ptrCheckGuard(tslOperationRecPtr, coprecsize, operationrec);
    tslOperationRecPtr.p->prevOp = operationRecPtr.p->prevOp;
  } else {
    jam();
    // Check that last are pointing at operation to take out
    ndbrequire(TscanPtr.p->scanLastLockedOp==operationRecPtr.i);
    TscanPtr.p->scanLastLockedOp = operationRecPtr.p->prevOp;
  }//if
  TscanPtr.p->scanLockHeld--;

#ifdef VM_TRACE
  // DEBUG CODE
  // Check that there are as many operations in the lockqueue as 
  // scanLockHeld indicates
  OperationrecPtr tmpOp;
  int numLockedOps = 0;
  tmpOp.i = TscanPtr.p->scanFirstLockedOp;
  while(tmpOp.i != RNIL){
    numLockedOps++;
    ptrCheckGuard(tmpOp, coprecsize, operationrec);
    if (tmpOp.p->nextOp == RNIL)
    {
      ndbrequire(tmpOp.i == TscanPtr.p->scanLastLockedOp);
    }
    tmpOp.i = tmpOp.p->nextOp;
  } 
  ndbrequire(numLockedOps==TscanPtr.p->scanLockHeld);
#endif
}//Dbacc::takeOutScanLockQueue()

/* --------------------------------------------------------------------------------- */
/* TAKE_OUT_READY_SCAN_QUEUE                                                         */
/* --------------------------------------------------------------------------------- */
void Dbacc::takeOutReadyScanQueue(Signal* signal) 
{
  OperationrecPtr trsOperationRecPtr;

  if (operationRecPtr.p->prevOp != RNIL) {
    jam();
    trsOperationRecPtr.i = operationRecPtr.p->prevOp;
    ptrCheckGuard(trsOperationRecPtr, coprecsize, operationrec);
    trsOperationRecPtr.p->nextOp = operationRecPtr.p->nextOp;
  } else {
    jam();
    scanPtr.p->scanFirstQueuedOp = operationRecPtr.p->nextOp;
  }//if
  if (operationRecPtr.p->nextOp != RNIL) {
    jam();
    trsOperationRecPtr.i = operationRecPtr.p->nextOp;
    ptrCheckGuard(trsOperationRecPtr, coprecsize, operationrec);
    trsOperationRecPtr.p->prevOp = operationRecPtr.p->prevOp;
  } else {
    jam();
    scanPtr.p->scanLastQueuedOp = operationRecPtr.p->nextOp;
  }//if
}//Dbacc::takeOutReadyScanQueue()

/* --------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------- */
/*                                                                                   */
/*       END OF SCAN MODULE                                                          */
/*                                                                                   */
/* --------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------- */

bool Dbacc::getfragmentrec(Signal* signal, FragmentrecPtr& rootPtr, Uint32 fid) 
{
  for (Uint32 i = 0; i < NDB_ARRAY_SIZE(tabptr.p->fragholder); i++) {
    jam();
    if (tabptr.p->fragholder[i] == fid) {
      jam();
      fragrecptr.i = tabptr.p->fragptrholder[i];
      ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
      return true;
    }//if
  }//for
  return false;
}//Dbacc::getrootfragmentrec()

/* --------------------------------------------------------------------------------- */
/* INIT_OVERPAGE                                                                     */
/*         INPUT. IOP_PAGEPTR, POINTER TO AN OVERFLOW PAGE RECORD                    */
/*         DESCRIPTION: CONTAINERS AND FREE LISTS OF THE PAGE, GET INITIALE VALUE    */
/*         ACCORDING TO LH3 AND PAGE STRUCTOR DESCRIPTION OF NDBACC BLOCK            */
/* --------------------------------------------------------------------------------- */
void Dbacc::initOverpage(Signal* signal) 
{
  Uint32 tiopPrevFree;
  Uint32 tiopNextFree;

  // Clear page, but keep page list entries
  // Setting word32[ALLOC_CONTAINERS] and word32[CHECK_SUM] to zero is essential
  Uint32 nextPage = iopPageptr.p->word32[Page8::NEXT_PAGE];
  Uint32 prevPage = iopPageptr.p->word32[Page8::PREV_PAGE];
  bzero(iopPageptr.p->word32, sizeof(iopPageptr.p->word32));
  iopPageptr.p->word32[Page8::NEXT_PAGE] = nextPage;
  iopPageptr.p->word32[Page8::PREV_PAGE] = prevPage;

  iopPageptr.p->word32[Page8::EMPTY_LIST] = (1 << ZPOS_PAGE_TYPE_BIT);
  /* --------------------------------------------------------------------------------- */
  /*       INITIALISE PREVIOUS PART OF DOUBLY LINKED LIST FOR LEFT CONTAINERS.         */
  /* --------------------------------------------------------------------------------- */
  tiopIndex = ZHEAD_SIZE + 1;
  iopPageptr.p->word32[tiopIndex] = Container::NO_CONTAINER_INDEX;
  for (tiopPrevFree = 0; tiopPrevFree <= Container::MAX_CONTAINER_INDEX - 1; tiopPrevFree++) {
    tiopIndex = tiopIndex + ZBUF_SIZE;
    iopPageptr.p->word32[tiopIndex] = tiopPrevFree;
  }//for
  /* --------------------------------------------------------------------------------- */
  /*       INITIALISE NEXT PART OF DOUBLY LINKED LIST FOR LEFT CONTAINERS.             */
  /* --------------------------------------------------------------------------------- */
  tiopIndex = ZHEAD_SIZE;
  for (tiopNextFree = 1; tiopNextFree <= Container::MAX_CONTAINER_INDEX; tiopNextFree++) {
    iopPageptr.p->word32[tiopIndex] = tiopNextFree;
    tiopIndex = tiopIndex + ZBUF_SIZE;
  }//for
  iopPageptr.p->word32[tiopIndex] = Container::NO_CONTAINER_INDEX;	/* LEFT_LIST IS UPDATED */
  /* --------------------------------------------------------------------------------- */
  /*       INITIALISE PREVIOUS PART OF DOUBLY LINKED LIST FOR RIGHT CONTAINERS.        */
  /* --------------------------------------------------------------------------------- */
  tiopIndex = (ZBUF_SIZE + ZHEAD_SIZE) - 1;
  iopPageptr.p->word32[tiopIndex] = Container::NO_CONTAINER_INDEX;
  for (tiopPrevFree = 0; tiopPrevFree <= Container::MAX_CONTAINER_INDEX - 1; tiopPrevFree++) {
    tiopIndex = tiopIndex + ZBUF_SIZE;
    iopPageptr.p->word32[tiopIndex] = tiopPrevFree;
  }//for
  /* --------------------------------------------------------------------------------- */
  /*       INITIALISE NEXT PART OF DOUBLY LINKED LIST FOR RIGHT CONTAINERS.            */
  /* --------------------------------------------------------------------------------- */
  tiopIndex = (ZBUF_SIZE + ZHEAD_SIZE) - 2;
  for (tiopNextFree = 1; tiopNextFree <= Container::MAX_CONTAINER_INDEX; tiopNextFree++) {
    iopPageptr.p->word32[tiopIndex] = tiopNextFree;
    tiopIndex = tiopIndex + ZBUF_SIZE;
  }//for
  iopPageptr.p->word32[tiopIndex] = Container::NO_CONTAINER_INDEX;	/* RIGHT_LIST IS UPDATED */
}//Dbacc::initOverpage()

/* --------------------------------------------------------------------------------- */
/* INIT_PAGE                                                                         */
/*         INPUT. INP_PAGEPTR, POINTER TO A PAGE RECORD                              */
/*         DESCRIPTION: CONTAINERS AND FREE LISTS OF THE PAGE, GET INITIALE VALUE    */
/*         ACCORDING TO LH3 AND PAGE STRUCTOR DISACRIPTION OF NDBACC BLOCK           */
/* --------------------------------------------------------------------------------- */
void Dbacc::initPage(Signal* signal) 
{
  Uint32 tinpIndex;
  Uint32 tinpTmp;
  Uint32 tinpPrevFree;
  Uint32 tinpNextFree;

  for (tiopIndex = 0; tiopIndex <= 2047; tiopIndex++) {
    // Do not clear page list
    if (tiopIndex == Page8::NEXT_PAGE) continue;
    if (tiopIndex == Page8::PREV_PAGE) continue;

    inpPageptr.p->word32[tiopIndex] = 0;
  }//for
  /* --------------------------------------------------------------------------------- */
  /*       SET PAGE ID FOR USE OF CHECKPOINTER.                                        */
  /*       PREPARE CONTAINER HEADERS INDICATING EMPTY CONTAINERS WITHOUT NEXT.         */
  /* --------------------------------------------------------------------------------- */
  inpPageptr.p->word32[ZPOS_PAGE_ID] = tipPageId;
  ContainerHeader tinpTmp1;
  tinpTmp1.initInUse();
  /* --------------------------------------------------------------------------------- */
  /*       INITIALISE ZNO_CONTAINERS PREDEFINED HEADERS ON LEFT SIZE.                  */
  /* --------------------------------------------------------------------------------- */
  tinpIndex = ZHEAD_SIZE;
  for (tinpTmp = 0; tinpTmp <= ZNO_CONTAINERS - 1; tinpTmp++) {
    inpPageptr.p->word32[tinpIndex] = tinpTmp1;
    tinpIndex = tinpIndex + ZBUF_SIZE;
  }//for
  /* WORD32(ZPOS_EMPTY_LIST) DATA STRUCTURE:*/
  /*--------------------------------------- */
  /*| PAGE TYPE|LEFT FREE|RIGHT FREE        */
  /*|     1    |  LIST   |  LIST            */
  /*|    BIT   | 7 BITS  | 7 BITS           */
  /*--------------------------------------- */
  /* --------------------------------------------------------------------------------- */
  /*       INITIALISE FIRST POINTER TO DOUBLY LINKED LIST OF FREE CONTAINERS.          */
  /*       INITIALISE LEFT FREE LIST TO 64 AND RIGHT FREE LIST TO ZERO.                */
  /*       ALSO INITIALISE PAGE TYPE TO NOT OVERFLOW PAGE.                             */
  /* --------------------------------------------------------------------------------- */
  tinpTmp = (ZNO_CONTAINERS << 7);
  inpPageptr.p->word32[ZPOS_EMPTY_LIST] = tinpTmp;
  /* --------------------------------------------------------------------------------- */
  /*       INITIALISE PREVIOUS PART OF DOUBLY LINKED LIST FOR RIGHT CONTAINERS.        */
  /* --------------------------------------------------------------------------------- */
  tinpIndex = (ZHEAD_SIZE + ZBUF_SIZE) - 1;
  inpPageptr.p->word32[tinpIndex] = Container::NO_CONTAINER_INDEX;
  for (tinpPrevFree = 0; tinpPrevFree <= Container::MAX_CONTAINER_INDEX - 1; tinpPrevFree++) {
    tinpIndex = tinpIndex + ZBUF_SIZE;
    inpPageptr.p->word32[tinpIndex] = tinpPrevFree;
  }//for
  /* --------------------------------------------------------------------------------- */
  /*       INITIALISE NEXT PART OF DOUBLY LINKED LIST FOR RIGHT CONTAINERS.            */
  /* --------------------------------------------------------------------------------- */
  tinpIndex = (ZHEAD_SIZE + ZBUF_SIZE) - 2;
  for (tinpNextFree = 1; tinpNextFree <= Container::MAX_CONTAINER_INDEX; tinpNextFree++) {
    inpPageptr.p->word32[tinpIndex] = tinpNextFree;
    tinpIndex = tinpIndex + ZBUF_SIZE;
  }//for
  inpPageptr.p->word32[tinpIndex] = Container::NO_CONTAINER_INDEX;
  /* --------------------------------------------------------------------------------- */
  /*       INITIALISE PREVIOUS PART OF DOUBLY LINKED LIST FOR LEFT CONTAINERS.         */
  /*       THE FIRST ZNO_CONTAINERS ARE NOT PUT INTO FREE LIST SINCE THEY ARE          */
  /*       PREDEFINED AS OCCUPIED.                                                     */
  /* --------------------------------------------------------------------------------- */
  tinpIndex = (ZNO_CONTAINERS * ZBUF_SIZE) + ZHEAD_SIZE;
  for (tinpNextFree = ZNO_CONTAINERS + 1; tinpNextFree <= Container::MAX_CONTAINER_INDEX; tinpNextFree++) {
    inpPageptr.p->word32[tinpIndex] = tinpNextFree;
    tinpIndex = tinpIndex + ZBUF_SIZE;
  }//for
  inpPageptr.p->word32[tinpIndex] = Container::NO_CONTAINER_INDEX;
  /* --------------------------------------------------------------------------------- */
  /*       INITIALISE NEXT PART OF DOUBLY LINKED LIST FOR LEFT CONTAINERS.             */
  /*       THE FIRST ZNO_CONTAINERS ARE NOT PUT INTO FREE LIST SINCE THEY ARE          */
  /*       PREDEFINED AS OCCUPIED.                                                     */
  /* --------------------------------------------------------------------------------- */
  tinpIndex = ((ZNO_CONTAINERS * ZBUF_SIZE) + ZHEAD_SIZE) + 1;
  inpPageptr.p->word32[tinpIndex] = Container::NO_CONTAINER_INDEX;
  for (tinpPrevFree = ZNO_CONTAINERS; tinpPrevFree <= Container::MAX_CONTAINER_INDEX - 1; tinpPrevFree++) {
    tinpIndex = tinpIndex + ZBUF_SIZE;
    inpPageptr.p->word32[tinpIndex] = tinpPrevFree;
  }//for
  /* --------------------------------------------------------------------------------- */
  /*       INITIALISE HEADER POSITIONS NOT CURRENTLY USED AND ENSURE USE OF OVERFLOW   */
  /*       RECORD POINTER ON THIS PAGE LEADS TO ERROR.                                 */
  /* --------------------------------------------------------------------------------- */
  inpPageptr.p->word32[ZPOS_CHECKSUM] = 0;
  inpPageptr.p->word32[ZPOS_ALLOC_CONTAINERS] = 0;
}//Dbacc::initPage()

/* --------------------------------------------------------------------------------- */
/* RELEASE OP RECORD                                                                 */
/*         PUT A FREE OPERATION IN A FREE LIST OF THE OPERATIONS                     */
/* --------------------------------------------------------------------------------- */
void Dbacc::releaseOpRec(Signal* signal) 
{
#if 0
  // DEBUG CODE
  // Check that the operation to be released isn't 
  // already in the list of free operations
  // Since this code loops through the entire list of free operations
  // it's only enabled in VM_TRACE mode
  OperationrecPtr opRecPtr;
  bool opInList = false;
  opRecPtr.i = cfreeopRec;
  while (opRecPtr.i != RNIL){
    if (opRecPtr.i == operationRecPtr.i){
      opInList = true;
      break;
    }
    ptrCheckGuard(opRecPtr, coprecsize, operationrec);
    opRecPtr.i = opRecPtr.p->nextOp;
  }
  ndbrequire(opInList == false);
#endif
  ndbrequire(operationRecPtr.p->m_op_bits == Operationrec::OP_INITIAL);

  operationRecPtr.p->nextOp = cfreeopRec;
  cfreeopRec = operationRecPtr.i;	/* UPDATE FREE LIST OF OP RECORDS */
  operationRecPtr.p->prevOp = RNIL;
  operationRecPtr.p->m_op_bits = Operationrec::OP_INITIAL;
}//Dbacc::releaseOpRec()

/* --------------------------------------------------------------------------------- */
/* RELEASE_OVERPAGE                                                                  */
/* --------------------------------------------------------------------------------- */
void Dbacc::releaseOverpage(Signal* signal) 
{
  jam();
  {
    LocalContainerPageList sparselist(*this, fragrecptr.p->sparsepages);
    sparselist.remove(ropPageptr);
  }
  jam();
  rpPageptr = ropPageptr;
  releasePage(signal);
}//Dbacc::releaseOverpage()


/* ------------------------------------------------------------------------- */
/* RELEASE_PAGE                                                              */
/* ------------------------------------------------------------------------- */
void Dbacc::releasePage(Signal* signal) 
{
  jam();
  ndbassert(g_acc_pages_used[instance()] == cnoOfAllocatedPages);
  LocalPage8List freelist(*this, cfreepages);
#ifdef VM_TRACE
//  ndbrequire(!freelist.find(rpPageptr));
#endif
  freelist.addFirst(rpPageptr);
  cnoOfAllocatedPages--;
  ndbassert(freelist.count() + cnoOfAllocatedPages == cpageCount);

  g_acc_pages_used[instance()] = cnoOfAllocatedPages;

  if (cnoOfAllocatedPages < m_maxAllocPages)
    m_oom = false;
}//Dbacc::releasePage()

/* --------------------------------------------------------------------------------- */
/* SEIZE    FRAGREC                                                                  */
/* --------------------------------------------------------------------------------- */
void Dbacc::seizeFragrec(Signal* signal) 
{
  RSS_OP_ALLOC(cnoOfFreeFragrec);
  fragrecptr.i = cfirstfreefrag;
  ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
  cfirstfreefrag = fragrecptr.p->nextfreefrag;
  fragrecptr.p->nextfreefrag = RNIL;
}//Dbacc::seizeFragrec()

/* --------------------------------------------------------------------------------- */
/* SEIZE_OP_REC                                                                      */
/* --------------------------------------------------------------------------------- */
void Dbacc::seizeOpRec(Signal* signal) 
{
  operationRecPtr.i = cfreeopRec;
  ptrCheckGuard(operationRecPtr, coprecsize, operationrec);
  cfreeopRec = operationRecPtr.p->nextOp;	/* UPDATE FREE LIST OF OP RECORDS */
  /* PUTS OPERTION RECORD PTR IN THE LIST */
  /* OF OPERATION IN CONNECTION RECORD */
  operationRecPtr.p->nextOp = RNIL;
}//Dbacc::seizeOpRec()

/** 
 * A ZPAGESIZE_ERROR has occured, out of index pages
 * Print some debug info if debug compiled
 */
void Dbacc::zpagesize_error(const char* where){
  DEBUG(where << endl
	<< "  ZPAGESIZE_ERROR" << endl
        << "  cfreepages.count()=" << cfreepages.getCount() << endl
	<< "  cpagesize=" <<cpagesize<<endl
	<< "  cnoOfAllocatedPages="<<cnoOfAllocatedPages);
}


/* --------------------------------------------------------------------------------- */
/* SEIZE_PAGE                                                                        */
/* --------------------------------------------------------------------------------- */
void Dbacc::seizePage(Signal* signal) 
{
  jam();
  ndbassert(g_acc_pages_used[instance()] == cnoOfAllocatedPages);
  tresult = 0;
  if (cfreepages.isEmpty() || m_oom)
  {
    jam();
    zpagesize_error("Dbacc::seizePage");
    tresult = ZPAGESIZE_ERROR;
  }
  else
  {
    jam();
    LocalPage8List freelist(*this, cfreepages);
    freelist.removeFirst(spPageptr);
    cnoOfAllocatedPages++;
    ndbassert(freelist.count() + cnoOfAllocatedPages == cpageCount);

    if (cnoOfAllocatedPages >= m_maxAllocPages)
      m_oom = true;

    if (cnoOfAllocatedPages > cnoOfAllocatedPagesMax)
      cnoOfAllocatedPagesMax = cnoOfAllocatedPages;

    g_acc_pages_used[instance()] = cnoOfAllocatedPages;
  }

}//Dbacc::seizePage()

/* --------------------------------------------------------------------------------- */
/* SEIZE_ROOTFRAGREC                                                                 */
/* --------------------------------------------------------------------------------- */

/* --------------------------------------------------------------------------------- */
/* SEIZE_SCAN_REC                                                                    */
/* --------------------------------------------------------------------------------- */
void Dbacc::seizeScanRec(Signal* signal) 
{
  scanPtr.i = cfirstFreeScanRec;
  ptrCheckGuard(scanPtr, cscanRecSize, scanRec);
  ndbrequire(scanPtr.p->scanState == ScanRec::SCAN_DISCONNECT);
  cfirstFreeScanRec = scanPtr.p->scanNextfreerec;
}//Dbacc::seizeScanRec()

/* --------------------------------------------------------------------------------- */
/* SEIZE_SR_VERSION_REC                                                              */
/* --------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------- */
/* SEND_SYSTEMERROR                                                                  */
/* --------------------------------------------------------------------------------- */
void Dbacc::sendSystemerror(Signal* signal, int line)
{
  progError(line, NDBD_EXIT_PRGERR);
}//Dbacc::sendSystemerror()

void Dbacc::execDBINFO_SCANREQ(Signal *signal)
{
  jamEntry();
  DbinfoScanReq req= *(DbinfoScanReq*)signal->theData;
  const Ndbinfo::ScanCursor* cursor =
    CAST_CONSTPTR(Ndbinfo::ScanCursor, DbinfoScan::getCursorPtr(&req));

  Ndbinfo::Ratelimit rl;

  switch(req.tableId){
  case Ndbinfo::POOLS_TABLEID:
  {
    jam();

    Ndbinfo::pool_entry pools[] =
    {
      { "Index memory",
        cnoOfAllocatedPages,
        cpageCount,
        sizeof(Page8),
        cnoOfAllocatedPagesMax,
        { CFG_DB_INDEX_MEM,0,0,0 }},
      { NULL, 0,0,0,0,{ 0,0,0,0 }}
    };

    static const size_t num_config_params =
      sizeof(pools[0].config_params)/sizeof(pools[0].config_params[0]);
    Uint32 pool = cursor->data[0];
    BlockNumber bn = blockToMain(number());
    while(pools[pool].poolname)
    {
      jam();
      Ndbinfo::Row row(signal, req);
      row.write_uint32(getOwnNodeId());
      row.write_uint32(bn);           // block number
      row.write_uint32(instance());   // block instance
      row.write_string(pools[pool].poolname);

      row.write_uint64(pools[pool].used);
      row.write_uint64(pools[pool].total);
      row.write_uint64(pools[pool].used_hi);
      row.write_uint64(pools[pool].entry_size);
      for (size_t i = 0; i < num_config_params; i++)
        row.write_uint32(pools[pool].config_params[i]);
      ndbinfo_send_row(signal, req, row, rl);
      pool++;
      if (rl.need_break(req))
      {
        jam();
        ndbinfo_send_scan_break(signal, req, rl, pool);
        return;
      }
    }
  }
  default:
    break;
  }

  ndbinfo_send_scan_conf(signal, req, rl);
}

void
Dbacc::execDUMP_STATE_ORD(Signal* signal)
{
  DumpStateOrd * const dumpState = (DumpStateOrd *)&signal->theData[0];
  if (dumpState->args[0] == DumpStateOrd::AccDumpOneScanRec){
    Uint32 recordNo = RNIL;
    if (signal->length() == 2)
      recordNo = dumpState->args[1];
    else 
      return;

    if (recordNo >= cscanRecSize) 
      return;
    
    scanPtr.i = recordNo;
    ptrAss(scanPtr, scanRec);
    infoEvent("Dbacc::ScanRec[%d]: state=%d, transid(0x%x, 0x%x)",
	      scanPtr.i, scanPtr.p->scanState,scanPtr.p->scanTrid1,
	      scanPtr.p->scanTrid2);
    infoEvent(" activeLocalFrag=%d, nextBucketIndex=%d",
	      scanPtr.p->activeLocalFrag,
	      scanPtr.p->nextBucketIndex);
    infoEvent(" scanNextfreerec=%d firstActOp=%d firstLockedOp=%d, "
	      "scanLastLockedOp=%d firstQOp=%d lastQOp=%d",
	      scanPtr.p->scanNextfreerec,
	      scanPtr.p->scanFirstActiveOp,
	      scanPtr.p->scanFirstLockedOp,
	      scanPtr.p->scanLastLockedOp,
	      scanPtr.p->scanFirstQueuedOp,
	      scanPtr.p->scanLastQueuedOp);
    infoEvent(" scanUserP=%d, startNoBuck=%d, minBucketIndexToRescan=%d, "
	      "maxBucketIndexToRescan=%d",
	      scanPtr.p->scanUserptr,
	      scanPtr.p->startNoOfBuckets,
	      scanPtr.p->minBucketIndexToRescan,
	      scanPtr.p->maxBucketIndexToRescan);
    infoEvent(" scanBucketState=%d, scanLockHeld=%d, userBlockRef=%d, "
	      "scanMask=%d scanLockMode=%d",
	      scanPtr.p->scanBucketState,
	      scanPtr.p->scanLockHeld,
	      scanPtr.p->scanUserblockref,
	      scanPtr.p->scanMask,
	      scanPtr.p->scanLockMode);
    return;
  }

  // Dump all ScanRec(ords)
  if (dumpState->args[0] == DumpStateOrd::AccDumpAllScanRec){
    Uint32 recordNo = 0;
    if (signal->length() == 1)
      infoEvent("ACC: Dump all ScanRec - size: %d",
		cscanRecSize);
    else if (signal->length() == 2)
      recordNo = dumpState->args[1];
    else
      return;
    
    dumpState->args[0] = DumpStateOrd::AccDumpOneScanRec;
    dumpState->args[1] = recordNo;
    execDUMP_STATE_ORD(signal);
    
    if (recordNo < cscanRecSize-1){
      dumpState->args[0] = DumpStateOrd::AccDumpAllScanRec;
      dumpState->args[1] = recordNo+1;
      sendSignal(reference(), GSN_DUMP_STATE_ORD, signal, 2, JBB);
    }
    return;
  }

  // Dump all active ScanRec(ords)
  if (dumpState->args[0] == DumpStateOrd::AccDumpAllActiveScanRec){
    Uint32 recordNo = 0;
    if (signal->length() == 1)
      infoEvent("ACC: Dump active ScanRec - size: %d",
		cscanRecSize);
    else if (signal->length() == 2)
      recordNo = dumpState->args[1];
    else
      return;

    ScanRecPtr sp;
    sp.i = recordNo;
    ptrAss(sp, scanRec);
    if (sp.p->scanState != ScanRec::SCAN_DISCONNECT){
      dumpState->args[0] = DumpStateOrd::AccDumpOneScanRec;
      dumpState->args[1] = recordNo;
      execDUMP_STATE_ORD(signal);
    }

    if (recordNo < cscanRecSize-1){
      dumpState->args[0] = DumpStateOrd::AccDumpAllActiveScanRec;
      dumpState->args[1] = recordNo+1;
      sendSignal(reference(), GSN_DUMP_STATE_ORD, signal, 2, JBB);
    }
    return;
  }

  if(dumpState->args[0] == DumpStateOrd::EnableUndoDelayDataWrite){
    ndbout << "Dbacc:: delay write of datapages for table = " 
	   << dumpState->args[1]<< endl;
    c_errorInsert3000_TableId = dumpState->args[1];
    SET_ERROR_INSERT_VALUE(3000);
    return;
  }

  if(dumpState->args[0] == DumpStateOrd::AccDumpOneOperationRec){
    Uint32 recordNo = RNIL;
    if (signal->length() == 2)
      recordNo = dumpState->args[1];
    else 
      return;

    if (recordNo >= coprecsize) 
      return;
    
    OperationrecPtr tmpOpPtr;
    tmpOpPtr.i = recordNo;
    ptrAss(tmpOpPtr, operationrec);
    infoEvent("Dbacc::operationrec[%d]: transid(0x%x, 0x%x)",
	      tmpOpPtr.i, tmpOpPtr.p->transId1,
	      tmpOpPtr.p->transId2);
    infoEvent("elementIsforward=%d, elementPage=%d, elementPointer=%d ",
	      tmpOpPtr.p->elementIsforward, tmpOpPtr.p->elementPage, 
	      tmpOpPtr.p->elementPointer);
    infoEvent("fid=%d, fragptr=%d ",
              tmpOpPtr.p->fid, tmpOpPtr.p->fragptr);
    infoEvent("hashValue=%d", tmpOpPtr.p->hashValue.pack());
    infoEvent("nextLockOwnerOp=%d, nextOp=%d, nextParallelQue=%d ",
	      tmpOpPtr.p->nextLockOwnerOp, tmpOpPtr.p->nextOp, 
	      tmpOpPtr.p->nextParallelQue);
    infoEvent("nextSerialQue=%d, prevOp=%d ",
	      tmpOpPtr.p->nextSerialQue, 
	      tmpOpPtr.p->prevOp);
    infoEvent("prevLockOwnerOp=%d, prevParallelQue=%d",
	      tmpOpPtr.p->prevLockOwnerOp, tmpOpPtr.p->nextParallelQue);
    infoEvent("prevSerialQue=%d, scanRecPtr=%d",
	      tmpOpPtr.p->prevSerialQue, tmpOpPtr.p->scanRecPtr);
    infoEvent("m_op_bits=0x%x, scanBits=%d, reducedHashValue=%x ",
              tmpOpPtr.p->m_op_bits, tmpOpPtr.p->scanBits, tmpOpPtr.p->reducedHashValue.pack());
    return;
  }

  if(dumpState->args[0] == DumpStateOrd::AccDumpNumOpRecs){

    Uint32 freeOpRecs = 0;
    OperationrecPtr opRecPtr;
    opRecPtr.i = cfreeopRec;
    while (opRecPtr.i != RNIL){
      freeOpRecs++;
      ptrCheckGuard(opRecPtr, coprecsize, operationrec);
      opRecPtr.i = opRecPtr.p->nextOp;
    }

    infoEvent("Dbacc::OperationRecords: num=%d, free=%d",	      
	      coprecsize, freeOpRecs);

    return;
  }
  if(dumpState->args[0] == DumpStateOrd::AccDumpFreeOpRecs){

    OperationrecPtr opRecPtr;
    opRecPtr.i = cfreeopRec;
    while (opRecPtr.i != RNIL){
      
      dumpState->args[0] = DumpStateOrd::AccDumpOneOperationRec;
      dumpState->args[1] = opRecPtr.i;
      execDUMP_STATE_ORD(signal);

      ptrCheckGuard(opRecPtr, coprecsize, operationrec);
      opRecPtr.i = opRecPtr.p->nextOp;
    }


    return;
  }

  if(dumpState->args[0] == DumpStateOrd::AccDumpNotFreeOpRecs){
    Uint32 recordStart = RNIL;
    if (signal->length() == 2)
      recordStart = dumpState->args[1];
    else 
      return;

    if (recordStart >= coprecsize) 
      return;

    for (Uint32 i = recordStart; i < coprecsize; i++){

      bool inFreeList = false;
      OperationrecPtr opRecPtr;
      opRecPtr.i = cfreeopRec;
      while (opRecPtr.i != RNIL){
	if (opRecPtr.i == i){
	  inFreeList = true;
	  break;
	}
	ptrCheckGuard(opRecPtr, coprecsize, operationrec);
	opRecPtr.i = opRecPtr.p->nextOp;
      }
      if (inFreeList == false){
	dumpState->args[0] = DumpStateOrd::AccDumpOneOperationRec;
	dumpState->args[1] = i;
	execDUMP_STATE_ORD(signal);	
      }
    }
    return;
  }

#if 0
  if (type == 100) {
    RelTabMemReq * const req = (RelTabMemReq *)signal->getDataPtrSend();
    req->primaryTableId = 2;
    req->secondaryTableId = RNIL;
    req->userPtr = 2;
    req->userRef = DBDICT_REF;
    sendSignal(cownBlockref, GSN_REL_TABMEMREQ, signal,
               RelTabMemReq::SignalLength, JBB);
    return;
  }//if
  if (type == 101) {
    RelTabMemReq * const req = (RelTabMemReq *)signal->getDataPtrSend();
    req->primaryTableId = 4;
    req->secondaryTableId = 5;
    req->userPtr = 4;
    req->userRef = DBDICT_REF;
    sendSignal(cownBlockref, GSN_REL_TABMEMREQ, signal,
               RelTabMemReq::SignalLength, JBB);
    return;
  }//if
  if (type == 102) {
    RelTabMemReq * const req = (RelTabMemReq *)signal->getDataPtrSend();
    req->primaryTableId = 6;
    req->secondaryTableId = 8;
    req->userPtr = 6;
    req->userRef = DBDICT_REF;
    sendSignal(cownBlockref, GSN_REL_TABMEMREQ, signal,
               RelTabMemReq::SignalLength, JBB);
    return;
  }//if
  if (type == 103) {
    DropTabFileReq * const req = (DropTabFileReq *)signal->getDataPtrSend();
    req->primaryTableId = 2;
    req->secondaryTableId = RNIL;
    req->userPtr = 2;
    req->userRef = DBDICT_REF;
    sendSignal(cownBlockref, GSN_DROP_TABFILEREQ, signal,
               DropTabFileReq::SignalLength, JBB);
    return;
  }//if
  if (type == 104) {
    DropTabFileReq * const req = (DropTabFileReq *)signal->getDataPtrSend();
    req->primaryTableId = 4;
    req->secondaryTableId = 5;
    req->userPtr = 4;
    req->userRef = DBDICT_REF;
    sendSignal(cownBlockref, GSN_DROP_TABFILEREQ, signal,
               DropTabFileReq::SignalLength, JBB);
    return;
  }//if
  if (type == 105) {
    DropTabFileReq * const req = (DropTabFileReq *)signal->getDataPtrSend();
    req->primaryTableId = 6;
    req->secondaryTableId = 8;
    req->userPtr = 6;
    req->userRef = DBDICT_REF;
    sendSignal(cownBlockref, GSN_DROP_TABFILEREQ, signal,
               DropTabFileReq::SignalLength, JBB);
    return;
  }//if
#endif

  if (signal->theData[0] == DumpStateOrd::SchemaResourceSnapshot)
  {
    RSS_OP_SNAPSHOT_SAVE(cnoOfFreeFragrec);
    return;
  }

  if (signal->theData[0] == DumpStateOrd::SchemaResourceCheckLeak)
  {
    RSS_OP_SNAPSHOT_CHECK(cnoOfFreeFragrec);
    return;
  }
}//Dbacc::execDUMP_STATE_ORD()

void
Dbacc::execREAD_PSEUDO_REQ(Signal* signal){
  jamEntry();
  fragrecptr.i = signal->theData[0];
  Uint32 attrId = signal->theData[1];
  ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
  Uint64 tmp;
  switch(attrId){
  case AttributeHeader::ROW_COUNT:
    tmp = fragrecptr.p->noOfElements;
    break;
  case AttributeHeader::COMMIT_COUNT:
    tmp = fragrecptr.p->m_commit_count;
    break;
  default:
    tmp = 0;
  }
  memcpy(signal->theData, &tmp, 8); /* must be memcpy, gives strange results on
				     * ithanium gcc (GCC) 3.4.1 smp linux 2.4
				     * otherwise
				     */
  //  Uint32 * src = (Uint32*)&tmp;
  //  signal->theData[0] = src[0];
  //  signal->theData[1] = src[1];
}

void
Dbacc::execNODE_STATE_REP(Signal* signal)
{
  jamEntry();
  const NodeStateRep* rep = CAST_CONSTPTR(NodeStateRep,
                                          signal->getDataPtr());

  if (rep->nodeState.startLevel == NodeState::SL_STARTED)
  {
    jam();

    const ndb_mgm_configuration_iterator * p =
      m_ctx.m_config.getOwnConfigIterator();
    ndbrequire(p != 0);

    Uint32 free_pct = 5;
    ndb_mgm_get_int_parameter(p, CFG_DB_FREE_PCT, &free_pct);

    m_free_pct = free_pct;
    m_maxAllocPages = (cpagesize * (100 - free_pct)) / 100;
    if (cnoOfAllocatedPages >= m_maxAllocPages)
      m_oom = true;
  }
  SimulatedBlock::execNODE_STATE_REP(signal);
}

#ifdef VM_TRACE
void
Dbacc::debug_lh_vars(const char* where)
{
  Uint32 b = fragrecptr.p->level.getTop();
  Uint32 di = fragrecptr.p->getPageNumber(b);
  Uint32 ri = di >> 8;
  ndbout
    << "DBACC: " << where << ":"
    << " frag:" << fragrecptr.p->myTableId
    << "/" << fragrecptr.p->myfid
    << " slack:" << fragrecptr.p->slack
    << "/" << fragrecptr.p->slackCheck
    << " top:" << fragrecptr.p->level.getTop()
    << " di:" << di
    << " ri:" << ri
    << " full:" << fragrecptr.p->dirRangeFull
    << "\n";
}
#endif
