@@ -1724,9 +1724,9 @@ short ExExplainTcb::getExplainFromRepos(char * qid, Lng32 qidLen)
   if (vi->get(0, ptr, len))
     goto label_error2;
   
-  explainFragLen_ = str_decoded_len(len); // remove trailing null terminator
+  explainFragLen_ = str_decoded_len(len - 1); // remove trailing null terminator
   explainFrag_ = new(getHeap()) char[explainFragLen_];
-  if (str_decode(explainFrag_, explainFragLen_, ptr, len) < 0)
+  if (str_decode(explainFrag_, explainFragLen_, ptr, len - 1) < 0)
     {
       diagsArea = pEntryDown->getAtp()->getDiagsArea();
       ExRaiseSqlError(getGlobals()->getDefaultHeap(), 
/**********************************************************************
// @@@ START COPYRIGHT @@@
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.
//
// @@@ END COPYRIGHT @@@
**********************************************************************/
/* -*-C++-*-
******************************************************************************
*
* File:         ExExplain.C
* Description:  Methods for the tdb and tcb of a Explain operation
*
*
* Created:      4/17/96
* Language:     C++
*
*
*
*
******************************************************************************
*/

//
// This file contains all the executor methods associated
// with a explain operator
//
#include "ex_stdh.h"
#include "ComTdb.h"
#include "ex_tcb.h"
#include "ex_root.h"
#include "ex_queue.h"
#include "ExExplain.h"
#include "ExplainTuple.h"
#include "ex_expr.h"
#include "exp_like.h"
#include "ComQueue.h"
#include "sql_id.h"
#include "SqlStats.h"
#include "ssmpipc.h"
#include "ComCextdecs.h"

// Default Constructor.  This is used by the ex_tdb::fixupVTblPtr()
// routine which fixes up the virtual function table pointer after
// a node has been unpacked.

//
// Build a explain tcb from an explain TDB.
// Allocates a new ExExplainTcb.  
// Initializes the paramsTuple.
// Adds this tcb to the schedulers task list.
ex_tcb *
ExExplainTdb::build(ex_globals * glob)
{

  // Allocate and initialize a new explain TCB.
  ExExplainTcb *explainTcb = new(glob->getSpace()) ExExplainTcb(*this, glob);

  // initParamsTuple: Allocate the paramsTuple_, the paramsAtp, the
  // modName_ buffer and the stmtPattern_ buffer.  The paramsTuple_ will
  // hold the result of evaluating the paramsExpr.  The modName_ and
  // stmtPattern_ buffers are used to hold a NULL terminated copy of the
  // parameters.  Also, initialize the paramsTuppDesc_ to point to the
  // paramsTuple_ and the paramsTupp_ to point to the paramsTuppDesc_.
  // This is done to create the necessary tuple structure so that the
  // paramsExpr can be evaluateed.
  explainTcb->initParamsTuple(getTupleLength(),
			      criDescParams_,
			      getLengthModName(),
			      getLengthStmtPattern());

  // add the explain tcb to the scheduler's task list.
  explainTcb->registerSubtasks();
  
  return (explainTcb);
}



//
//  TCB procedures
//

//
// Constructor for ExExplainTcb.  Called by the build function of the
// ExExplainTdb.  This will initialize the internal state of the explain
// Tcb and allocate the queues used to communicate with the parent.
// Fixup all expression used by the TCB.

ExExplainTcb::ExExplainTcb(const ExExplainTdb &explainTdb, ex_globals *glob)
: ex_tcb(explainTdb, 1, glob),
  stmtList_(0),
  currentStmt_(0),
  explainTree_(0),
  currentExplain_(0),
  cameFrom_(0),
  seqNum_(0),
  matchNo_(0),
  debugCounter_(0),
  workState_(EXPL_IDLE),
  paramsTuple_(0),
  modName_(NULL),
  modDir_(NULL),
  stmtPattern_(0),
  qid_(NULL),
  reposQid_(NULL),
  explainAddr_(-1),
  explainFromAddrProcessed_(FALSE),
  explainStmt_(NULL),
  explainPlan_(NULL),
  explainPlanLen_(0),
  explainFrag_(NULL),
  explainFragLen_(0),
  diagsArea_(NULL),
  retryAttempts_(0),
  stmtName_(NULL)
{

  Space * space = (glob ? glob->getSpace() : 0);
  CollHeap * heap = (glob ? glob->getDefaultHeap() : 0);

  // Allocate the buffer pool
  // Allocate the specified number of buffers each can hold 5 tuples.
  pool_ = new(space) sql_buffer_pool(explainTdb.numBuffers_,
				     explainTdb.bufferSize_,
				     space);

  // Allocate the queues used to communicate with parent
  qParent_.down = new(space) ex_queue(ex_queue::DOWN_QUEUE,
				      explainTdb.queueSizeDown_,
				      explainTdb.criDescDown_,
				      space);


  qParent_.up = new(space) ex_queue(ex_queue::UP_QUEUE,
				    explainTdb.queueSizeUp_,
				    explainTdb.criDescUp_,
				    space);

  // fixup expressions

  if (getScanPred())
    (void) getScanPred()->fixup(0, getExpressionMode(), this,
				space, heap, FALSE, glob);

  if (getParamsExpr())
    (void) getParamsExpr()->fixup(0, getExpressionMode(), this,
				  space, heap, FALSE, glob);

}

// Destructor for explain tcb
ExExplainTcb::~ExExplainTcb()
{
  if (qid_)
  {
    NADELETEBASIC(qid_, getHeap());
    qid_ = NULL;
  }
  if (stmtName_)
  {
    NADELETEBASIC(stmtName_, getHeap());
    stmtName_ = NULL;
  }
  delete qParent_.up;
  delete qParent_.down;
}
  
// Free Resources (what should this do?)
void ExExplainTcb::freeResources()
{
  if (qid_)
  {
    NADELETEBASIC(qid_, getHeap());
    qid_ = NULL;
  }
  if (stmtName_)
  {
    NADELETEBASIC(stmtName_, getHeap());
    stmtName_ = NULL;
  }
  delete qParent_.up;
  delete qParent_.down;
  retryAttempts_ = 0;
}

// Explain has no children
Int32
ExExplainTcb::numChildren() const
{
  return(0);
}

const ex_tcb *ExExplainTcb::getChild(Int32 /*pos*/) const
{
  ex_assert(0,"Explain TCB has 0 children");
  return NULL;
}

ex_queue_pair  
ExExplainTcb::getParentQueue() const
{
  return (qParent_);
};


// loadModule: load the named module if it has not already been loaded.
// assumes that the buffer modName_ has been allocated and contains the
// name of the module calculated by evaluating the paramsExpr.  Also,
// initialize the statement list (stmtList_) to point to the statement
// list of the current context.  The context is only available from the
// master executor.
Int32
ExExplainTcb::loadModule()
{
  Int32 rc = 0;

  // Get the globals stucture of the master executor.
  ExExeStmtGlobals *exeGlob = getGlobals()->castToExExeStmtGlobals();
  ExMasterStmtGlobals *masterGlob = exeGlob->castToExMasterStmtGlobals();

  // If there are no master globals, this is not the master executor.
  // The optimizer should ensure that this node executes in the master.
  ex_assert(masterGlob,"ExExplainTcb : No master globals Available\n");

  // Get the current context from the master globals.
  ContextCli *context = masterGlob->getContext();

  // There should always be a valid pointer to the current context
  ex_assert(context,"ExExplainTcb : No context Available\n");

  // If the module name is NULL, the current module should be used,
  // so no module needs to be loaded.  getNextExplainTree() should
  // interpret a NULL module name as the current module.
  if(!isNullModName())
    {
      // Construct a module ID to be used to search the module list
      // and possible load a new module.
      SQLMODULE_ID moduleId;

      // Added for multi charset module names
      init_SQLMODULE_ID(&moduleId, 
			SQLCLI_CURRENT_VERSION,
			modName_,
			0, SQLCHARSETSTRING_ISO88591, strlen(modName_)
		       );
      //moduleId.module_name = modName_;
    }
  
  // Get a pointer to the statement list for this context.
  stmtList_ = context->getStatementList();
  stmtList_->position();
  
  currentStmt_ = 0;

  return rc;
}

//  visitNode: visit a node of the explainTree.  This routine is called
//  by traverseTree() on each node of the explainTree.  visitNode will
//  check to make sure that this node has been initialized and has been
//  populated with explain Info.  If it has, it will then apply the 
//  scan predicate (if it exists) to the node and if true will insert
//  the explain Tuple into the parent up queue.
Int32
ExExplainTcb::visitNode()
{
  
  ex_queue_entry *pEntryDown = qParent_.down->getHeadEntry();

  ex_assert(!qParent_.up->isFull(),
	    "Explain: Visiting a node when parant queue is full\n");
      
  
  // If there is actually a buffer allocated and it contains some
  // Explain Info. then apply the predicate and if true, insert
  // onto the parents up queue
  if(currentExplain_->getState() == ExplainTuple::SOME_EXPLAIN_INFO) 
    {
      
      // Get a pointer to the available entry in the parent up queue.
      // It is guaranteed to be available since we checked in
      // traverseTree() before calling this routine.
      ex_queue_entry *pEntryUp = qParent_.up->getTailEntry();
      
      // Copy given values from parent down queue to parent up queue.
      pEntryUp->copyAtp(pEntryDown);

      // Get a new tuple from the POOL.
      // If we can't get one now, return and will check again later.
      // Should eventually be able to get one.

      if (pool_->get_free_tuple(pEntryUp->getTupp(explainTdb().getTuppIndex()),
				currentExplain_->getUsedRecLength()))
	return 0;

      // Keep track of the sequence number for each node.
      // The sequence number is a enumeration of the nodes in
      // traversal order.  (traverseTree() traverses post-order)
      seqNum_++;
      
      // Set the module name and statement name for this node.
      // These fields are set at run time since they are not
      // know at generate time.
      
      if(isNullModName())
	currentExplain_->setModuleName(0);
      else
	currentExplain_->setModuleName(modName_);
      
      if ((qid_ == NULL) && (explainAddr_ == -1) && (explainStmt_ == NULL) &&
          (explainPlan_ == NULL))
        currentExplain_->setStatementName(currentStmt_->getIdentifier());
      else
        currentExplain_->setStatementName(stmtName_);

      // Copy row from Explain Tree to buffer.
      // This should probably be a move expression of some sort,
      // but for now this seems to work fine.

      str_cpy_all(pEntryUp->getTupp(explainTdb().getTuppIndex()).getDataPointer(),
		  currentExplain_->getExplainTuple(),
		  currentExplain_->getUsedRecLength());

      // Apply scan Predicate to the explain Info. (now in the
      // tail of the parent up queue.  If the predicat evaluates
      // to TRUE or is non-existant, the commit (insert) this queue
      // entry. Otherwise, ignore it.
      
      ex_expr::exp_return_type predVal = ex_expr::EXPR_TRUE;
      
      if (getScanPred()) 
	predVal = getScanPred()->eval(pEntryUp->getAtp(), 0);
      
      if(predVal == ex_expr::EXPR_TRUE) {
	// Finialize the queue entry, then insert it
	matchNo_++;
	pEntryUp->upState.status = ex_queue::Q_OK_MMORE;
	pEntryUp->upState.parentIndex = pEntryDown->downState.parentIndex;
	pEntryUp->upState.downIndex = qParent_.down->getHeadIndex();  
	pEntryUp->upState.setMatchNo(matchNo_);
	
	qParent_.up->insert();
      }
      else if (predVal == ex_expr::EXPR_ERROR) {
        pEntryUp->upState.status = ex_queue::Q_SQLERROR;
        pEntryUp->upState.parentIndex = pEntryDown->downState.parentIndex;
        pEntryUp->upState.downIndex = qParent_.down->getHeadIndex();
	pEntryUp->upState.setMatchNo(matchNo_);

        qParent_.up->insert();

        workState_ = EXPL_DONE;
        return 0;
      }
    }
  return 1;
}

// traverseTree: Do a post-order traversal of the explainTree.  As each
// node is visited (visitNode()) it could potentially insert one tuple
// into the parents up queue.  This routine will return when either the
// traversal is complete (EXPL_TRAV_DONE) or if the parent up queue is
// full (EXPL_TRAV_QUEUEFULL).  If the queue is full, the work procedure
// will return to the scheduler. When the work procedure is called again,
// it will call traverseTree() which will resume traversing the
// explainTree where it left off.  traverseTree must therefore keep
// enough state in the TCB to be able to resume the traversal (a simple
// recursive traversal will not work since it must be able to return and
// then be restarted). This routine is initially called by the work
// procedure when it has found a matching statement name within the named
// module.  Before being called, currentExplain_ is initialized to point
// to the root of the explainTree and cameFrom_ is initialized to NULL.
// currentExplain_ will be maintained to always point to the node of the
// explainTree that is currently being visited.  cameFrom_ is used to
// determine which child was last visited. After a node has been visited,
// the cameFrom_ pointer is set to point to this node and the traversal
// follows the parent pointer to the parent node.  The parent node then
// iterates over all the children pointers.  If the cameFrom_ pointer is
// NULL, cameFrom_ is set to NULL and the traversal follows that child
// pointer.  If the cameFrom_ pointer is equal to the child pointer (this
// was the last child visited), cameFrom_ is set to NULL so that the 
// traversal will visit the next child.


ExExplainTcb::traverseReturnCode ExExplainTcb::traverseTree() 
{
  
  // Get a pointer to the current request that we are working on
  ex_queue_entry *pEntryDown = qParent_.down->getHeadEntry();
  
  // Visit every node in the explainTree
  while(currentExplain_) 
    {
      // If the parent queue is full, return.
      // traverseTree() will be called again to resume the
      // traversal
      if (qParent_.up->isFull()) 
	return EXPL_TRAV_QUEUEFULL;
      
      // Get the type of request we are dealing with
      const ex_queue::down_request request = pEntryDown->downState.request;
      
      // If the request has been canceled or if the request has
      // been satisfied, the return to the work procedure.
      // The work procedure will see if there are any more trees
      // to traverse.  If so, traverseTree() will be called again.
      // If not the work procedure will put the NO_DATA reply onto the
      // parent up queue and then see if there are any more requests
      // to process
      if ((request == ex_queue::GET_NOMORE) ||
	  ((request == ex_queue::GET_N) && 
	   ((UInt32)pEntryDown->downState.requestValue <= matchNo_)))
	return EXPL_TRAV_DONE;
      
      // Visit each child node.  If we have already visited this
      // child, cameFrom_ will be non-NULL.  If this is the last
      // child we visited, cameFrom_ will be equal to child(i).
      // Note that the loop index is altered from within the body
      // of the loop.
      for(Int32 i = 0; i < currentExplain_->numChildren(); )
	{
	  if(cameFrom_ == 0) 
	    // We have not visited this child.  If there is a valid
	    // child pointer, follow it.
	    {
	      // Follow child pointer down the explainTree
	      if(currentExplain_->child(i))
		{
		  currentExplain_ = currentExplain_->child(i);
		  
		  // Reset the child index
		  i = 0;
		}
	      else
		// A NULL child pointer (ignore this child)
		i++;
	    }
	  else if (cameFrom_ == currentExplain_->child(i))
	    {
	      // Reset the cameFrom_ pointer so the traversal will
	      // follow the next child pointer (if there is one)
	      cameFrom_ = 0;
	      i++;
	    }
	  else
	    {
	      // This node has already been visited. Still looking
	      // for child equal to cameFrom_.
	      i++;
	    }
	}

      // We have Visited all the children of this node.
      // Make sure that we don't visit them again.
      cameFrom_ = currentExplain_;
      
      // Visit This node.
      if(!visitNode()) {
        if (workState_ == EXPL_DONE)  
          return EXPL_TRAV_ERROR;
        else
	  // Not really full, but same result.
	  return EXPL_TRAV_NOBUFFERS;
      }
      
      // This node is complete.  Follow the parent pointer up the tree
      // and set cameFrom_ to indicate that this node has just been
      // visited.
      cameFrom_ = currentExplain_;
      currentExplain_ = cameFrom_->getParent();
    }
  
  // Traversal of this tree is complete.
  return EXPL_TRAV_DONE;
}
// process explainStmt_. Get explain info from repository 
// and set it up to be pointed to by explainAddr_
// That will be used later to return explain details.
short ExExplainTcb::processExplainStmt()
{
  Lng32 cliRC = 0;

  CliGlobals *cliGlobals = getGlobals()->castToExExeStmtGlobals()->
	                castToExMasterStmtGlobals()->getCliGlobals();
  ExeCliInterface cliInterface(getHeap(), 0, cliGlobals->currContext());
  ex_queue_entry *pEntryDown = qParent_.down->getHeadEntry();
  ComDiagsArea *diagsArea = NULL;

  if (! explainStmt_)
    return 0;

  if (explainFrag_)
    NADELETEBASIC(explainFrag_, getHeap());
  explainFrag_ = NULL;
  
  cliRC = cliInterface.executeImmediate("control session 'EXPLAIN' 'ON';");
  if (cliRC < 0)
    {
      goto label_error;
    }

  cliRC = cliInterface.executeImmediatePrepare(explainStmt_);
  if (cliRC < 0)
    {
      goto label_error;
    }

  cliRC = cliInterface.setupExplainData();
  if (cliRC < 0)
    {
      goto label_error;
    }

  if (cliInterface.getExplainDataPtr())
    {
      explainFragLen_ = str_decoded_len(cliInterface.getExplainDataLen());
      explainFrag_ = new(getHeap()) char[explainFragLen_];
      if (str_decode(explainFrag_, explainFragLen_, 
                     cliInterface.getExplainDataPtr(), cliInterface.getExplainDataLen()) < 0)
        {
          diagsArea = pEntryDown->getAtp()->getDiagsArea();
          ExRaiseSqlError(getGlobals()->getDefaultHeap(),
                          &diagsArea, EXE_NO_EXPLAIN_INFO);
          if (diagsArea != pEntryDown->getAtp()->getDiagsArea())
            pEntryDown->getAtp()->setDiagsArea(diagsArea);
          goto label_error2;
        }

      // skip ExplainReposInfo and point explainAddr to actual explain structures.
      setExplainAddr((Int64)&explainFrag_[sizeof(ExplainReposInfo)]);  
    }
  else
    {
      diagsArea = pEntryDown->getAtp()->getDiagsArea();
      ExRaiseSqlError(getGlobals()->getDefaultHeap(),
                      &diagsArea, EXE_NO_EXPLAIN_INFO);
      if (diagsArea != pEntryDown->getAtp()->getDiagsArea())
        pEntryDown->getAtp()->setDiagsArea(diagsArea);
      goto label_error2;
    }

label_return:
  cliInterface.executeImmediate("control session reset 'EXPLAIN';");
  return 0;
  
label_error:
  diagsArea = pEntryDown->getAtp()->getDiagsArea();

  if (diagsArea == NULL)
    diagsArea = ComDiagsArea::allocate(getGlobals()->getDefaultHeap());
  
  cliInterface.retrieveSQLDiagnostics(diagsArea);
  
  if (diagsArea != pEntryDown->getAtp()->getDiagsArea())
    pEntryDown->getAtp()->setDiagsArea(diagsArea);

label_error2:
  cliInterface.executeImmediate("control session reset 'EXPLAIN';");

  return -1;
}

// process explainStmt_. Get explain info from repository 
// and set it up to be pointed to by explainAddr_
// That will be used later to return explain details.
short ExExplainTcb::processExplainPlan()
{
  Lng32 cliRC = 0;

  CliGlobals *cliGlobals = getGlobals()->castToExExeStmtGlobals()->
	                castToExMasterStmtGlobals()->getCliGlobals();
  ex_queue_entry *pEntryDown = qParent_.down->getHeadEntry();
  ComDiagsArea *diagsArea = NULL;

  if (! explainPlan_)
    return 0;

  if (explainFrag_)
    NADELETEBASIC(explainFrag_, getHeap());
  explainFrag_ = NULL;
  
  explainFragLen_ = str_decoded_len(explainPlanLen_);
  explainFrag_ = new(getHeap()) char[explainFragLen_];
  if (str_decode(explainFrag_, explainFragLen_, explainPlan_, explainPlanLen_) < 0)
    return 0;

  // explain repos info header is at the beginning of stored data.
  ExplainReposInfo * eri = (ExplainReposInfo*)explainFrag_;
  
  // skip ExplainReposInfo and point explainAddr to actual explain structures.
  setExplainAddr((Int64)&explainFrag_[sizeof(ExplainReposInfo)]);  
  setReposQid(NULL, 0);

  return 0;
}

// Explain work procedure:  The explain work procedure is responsible for
// implementing the explain operator.  Given a request it will:
//
//  - calculate the parameters to the explain function (module name
//    and statement pattern)
//
//  - loaded the named module if necessary
//
//  - travserse the list of statements and for each statement that
//    matches the named module and the statement pattern it will:
//
//    - extracted, copy and unpack the explain fragment for the statement
//
//    - traverse the explainTree, inserting qualifying tuples into the
//      parent up queue.
//
// The work procedure is implemented as a State Machine.  The current
// state is stored in the TCB as workState_.  The state machine has more
// states that are necessary since the work procedure can only return
// while in certain states.  The states are:
//
//     EXPL_IDLE - Waiting for a request.  This is the initial state.  It
//     stays in this state until a request is placed in the parent down
//     queue, in which case it will go to EXPL_GET_PARAMS
//                              
//     EXPL_GET_PARAMS - A request has been received.  The paramExpr() will
//     be evaluated to calculate the values of the explain functions
//     parameters.  This state will always proceed to 
//     EXPL_GETNEXT_EXPLAINTREE.  The work procedure will never return
//     while in this state.
//
//     EXPL_GETNEXT_EXPLAINTREE - The statement list will be traversed to 
//     find the next matching statements.  If none exist, then the state
//     will go to EXPL_DONE.  If one exists, the state will go to
//     EXPL_TRAVERSE_EXPLAINTREE.  The work procedure will never return 
//     while in this state
//
//     EXPL_TRAVERSE_EXPLAINTREE - Traverse the explainTree.  If the parent
//     up queue becomes full, return to the sceduler and then restart in
//     this state when called again.  This state will always go to state
//     EXPL_GETNEXT_EXPLAINTREE.
//
//     EXPL_DONE - Done with all statements that match the module name
//     and statement pattern.  Place the NO_DATA entry on the parent up
//     queue, then go to state EXPL_IDLE to get the next request.

short ExExplainTcb::work()
{
  Int32 rc = 0;
  char *explainFrag;
  Lng32 topNodeOffset;
  char * fragStart;
  Lng32 fragLen;

  ex_queue_entry *pEntryUp;	// A pointer the the next available entry
  // of the parent up queue.
  
  ex_queue_entry *pEntryDown;	// A pointer to the current request entry
  // in the parent down queue.
  CliGlobals *cliGlobals = getGlobals()->castToExExeStmtGlobals()->
	                castToExMasterStmtGlobals()->getCliGlobals();
  // State Machine Loop
  while(1)
    {
      switch(workState_)
	{
	case EXPL_IDLE:
	  
	  // Looking for inputs from parent
	  
	  if (qParent_.down->isEmpty())
	    return WORK_OK;
	  
	  // We have some inputs from the parent.
	  // Determine the values of the parameters
	  workState_ = EXPL_GET_PARAMS;
	  break;
	  
	case EXPL_GET_PARAMS:
	  ex_assert(getParamsExpr(), "No parameters provided!\n");
	  
	  // Pointer to request entry in parent down queue
	  pEntryDown = qParent_.down->getHeadEntry();
	  
	  // Copy all tuple pointers in the request into the
	  // work ATP (paramsAtp_).
	  paramsAtp_->copyAtp(pEntryDown->getAtp());
	  
	  // Add a pointer to the empty paramsTuple into the last position
	  // of the paramsAtp_.  The paramsTuple will be populated
	  // will the values of the parameters after evaluating the 
	  // paramsExpr.
	  paramsAtp_->getTupp(explainTdb().criDescParams()->noTuples() - 1)
	    = paramsTupp_;
	  
	  // Calculate the input parameters and put results in paramsTupp_
          ex_expr::exp_return_type retCode;
	  retCode = getParamsExpr()->eval(paramsAtp_, 0);
          if (retCode == ex_expr::EXPR_ERROR) {
            if (qParent_.up->isFull())
              return WORK_OK;   

            ex_queue_entry *pEntryUp = qParent_.up->getTailEntry();

            pEntryUp->copyAtp(paramsAtp_);
            pEntryUp->upState.status = ex_queue::Q_SQLERROR;
            pEntryUp->upState.parentIndex = pEntryDown->downState.parentIndex;
            pEntryUp->upState.downIndex = qParent_.down->getHeadIndex();
	    pEntryUp->upState.setMatchNo(matchNo_);

            qParent_.up->insert();

            workState_ = EXPL_DONE;
            break;
          }
	  
	  // Copy the parameters out of the paramsTuple and into buffers
	  // (modName_ and stmtPattern_) allocated by initParamsTuple().
	  // This is so that the parameters can be Null Terminated and
	  // treated as C-strings
	  copyParameters();
	  
          if (explainStmt_)
            {
              // query has been specified as a param. Process it and set up explain info.
              rc = processExplainStmt();
              if (rc < 0)
                {
                  workState_ = EXPL_ERROR;
                  break;
                }
            }
          
        if (explainPlan_)
            {
              // packed explain plan has been specified as a param. 
              // Process it and set up explain info.
              rc = processExplainPlan();
              if (rc < 0)
                {
                  workState_ = EXPL_ERROR;
                  break;
                }
            }
          
          if (reposQid_)
            {
              rc = getExplainFromRepos(reposQid_, strlen(reposQid_));
              if (rc < 0)
                {
                  workState_ = EXPL_ERROR;
                  break;
                }
              
            }

	  // starts with an '/'. It is an oss pathname.
	  if ((modDir_) &&
	      ((strlen(modDir_) >= 3) &&
               (strncmp(modDir_, "/E/", 3) == 0) ||
               (strncmp(modDir_, "/G/", 3) == 0)))
	    {
	      // Pathname cannot be an Expand or a Guardian name.
	      // Return error.
	      workState_ = EXPL_ERROR; // add a 'real' error out here. TBD.
	      break;
	    }

	  // Load the named module if necessary.  Also initialize the
	  // pointer to the statement list (stmtList_) of the current
	  // context.
	  rc = loadModule();
	  if (rc < 0)
	    workState_ = EXPL_ERROR;
	  else
	    // Find all statements that match the module name and
	    // statement pattern.
	    workState_ = EXPL_GETNEXT_EXPLAINTREE;
	  break;
	  
	case EXPL_GETNEXT_EXPLAINTREE:
          // When explainTree_ is not null and if the explain is for a given qid
          // explain is already done, swit
          if (qid_ != NULL && explainTree_ != NULL)
          {
            explainTree_ = NULL;
            workState_ = EXPL_DONE;
            break;
          }

          if (qid_ != NULL)
          {
            workState_ = EXPL_SEND_TO_SSMP;
            break;
          }
	  // Find the next statement that matches the module name
	  // and statement pattern and return a pointer to the explainTree.
	  // Otherwise return NULL.  If a statement is found, the EXPLAIN
	  // fragment of this statement will be copied and unpacked.
	  // When the traversal of the tree is complete, the memory 
	  // allocated for the tree should be freed.  Currently, it
	  // is set up so that the root of the tree is at the first
	  // location of the buffer, so explainTree_ can be used to
	  // delete this buffer.
	  explainTree_ = getNextExplainTree();
	  if(!explainTree_)
	    {
	      // If an error occurred, return error indication.
	      // If no more matching statements were found, the
	      // request is complete. Return EOD indication. 
	      // getNextExplainTree has changed the workState_
	      // to EXPL_ERROR or EXPL_DONE.
	      break;
	    }
	  
	  // There is an explainTree to be traversed.  Initialize
	  // the state used by the traversal and then travers the tree.
	  currentExplain_ = explainTree_->getExplainTreeRoot();
	  cameFrom_ = 0;
	  seqNum_ = 0;
	  
	  workState_ = EXPL_TRAVERSE_EXPLAINTREE;
	  break;
	  
	case EXPL_TRAVERSE_EXPLAINTREE:
	  
	  switch(traverseTree())
	    {
	    case EXPL_TRAV_DONE:
	      break;
	    case EXPL_TRAV_NOBUFFERS:
	      return WORK_POOL_BLOCKED;
	    case EXPL_TRAV_QUEUEFULL:
	      return WORK_OK;
            case EXPL_TRAV_ERROR:
              break;
	    default:
	      return WORK_BAD_ERROR;
	    }
	  
          if (workState_ != EXPL_DONE)
            {
              if (qid_ != NULL)
                NADELETEBASIC((char *)explainTree_, cliGlobals->getIpcHeap());
              else if ((explainAddr_ == -1) && (explainStmt_ == NULL))
	        NADELETEBASIC((char *)explainTree_, getHeap());
	      workState_ = EXPL_GETNEXT_EXPLAINTREE;
            }
	  break;
        case EXPL_SEND_TO_SSMP:
          {
            RtsExplainFrag *explainInfo = sendToSsmp();
            ex_queue_entry *pEntryDown = qParent_.down->getHeadEntry();
            ComDiagsArea *diagsArea =
              pEntryDown->getAtp()->getDiagsArea();
            if (explainInfo == NULL)
            {
              // Pointer to request entry in parent down queue
              ExRaiseSqlError(getGlobals()->getDefaultHeap(),
                              &diagsArea, EXE_NO_EXPLAIN_INFO);
              if (diagsArea != pEntryDown->getAtp()->getDiagsArea())
                pEntryDown->getAtp()->setDiagsArea(diagsArea);
              workState_ = EXPL_ERROR;
              break;
            }
            explainFrag =(char *)explainInfo->getExplainFrag();
            fragLen = explainInfo->getExplainFragLen();
            topNodeOffset = explainInfo->getTopNodeOffset();
            // Get a 'pointer' to the root node of the explain Tree
            // in the newly copied fragment.
            fragStart = (char *)explainFrag + topNodeOffset;

            // For now the root of the tree must be at the start of
            // the EXPLAIN fragment because the root of the tree is
            // used as a handle on the fragment to delete it.
            if (fragStart != (char *)explainFrag)
            {
              NADELETEBASIC(explainFrag, getHeap());
              explainFrag = NULL;
              ex_assert(0,
                        "Explain: explainTree root is not at the"
                        "beginning of the fragment\n");
            }

            // Unpack the EXPLAIN Fragment
            ExplainDesc *expDesc = (ExplainDesc *)fragStart;

            // Set up space for reallocating objects during unpacking when
            // there is a difference in image sizes at version migration.
            //
            ExplainDesc dummyExpDesc;
            if ((expDesc = (ExplainDesc *)
                  expDesc->driveUnpack(explainFrag,&dummyExpDesc,cliGlobals->getIpcHeap())) == NULL )
            {
              // ERROR during unpacking.
              // Most likely case is verison-unsupported.
              ExRaiseSqlError(getGlobals()->getDefaultHeap(), 
                              &diagsArea, EXE_NO_EXPLAIN_INFO);
              if (diagsArea != pEntryDown->getAtp()->getDiagsArea())
                pEntryDown->getAtp()->setDiagsArea(diagsArea);
              
              workState_ = EXPL_ERROR;
              break;
            }
            explainTree_ = expDesc;
            // There is an explainTree to be traversed.  Initialize
            // the state used by the traversal and then travers the tree.
            currentExplain_ = explainTree_->getExplainTreeRoot();
            cameFrom_ = 0;
            seqNum_ = 0;
            workState_ = EXPL_TRAVERSE_EXPLAINTREE;
          }
          break;
	case EXPL_ERROR:
	  
	  // Must insert Q_SQLERROR in parent up queue.
	  // If Parent can't take anymore, try again later.
	  
	  if (qParent_.up->isFull())
	    return WORK_OK;
	  
	  pEntryUp = qParent_.up->getTailEntry();
	  pEntryDown = qParent_.down->getHeadEntry();

	  pEntryUp->copyAtp(pEntryDown);
	  pEntryUp->upState.status = ex_queue::Q_SQLERROR;
	  pEntryUp->upState.parentIndex = pEntryDown->downState.parentIndex;
	  pEntryUp->upState.downIndex = qParent_.down->getHeadIndex();  
	  pEntryUp->upState.setMatchNo(matchNo_);
	  
	  qParent_.up->insert();
	  
	  workState_ = EXPL_DONE;
	  break;

	case EXPL_DONE:
	  
	  // Must insert NO_DATA in parent up queue.
	  // If Parent can't take anymore, try again later.
	  
	  if (qParent_.up->isFull())
	    return WORK_OK;
	  
	  pEntryUp = qParent_.up->getTailEntry();
	  pEntryDown = qParent_.down->getHeadEntry();
	  
	  pEntryUp->upState.status = ex_queue::Q_NO_DATA;
	  pEntryUp->upState.parentIndex = pEntryDown->downState.parentIndex;
	  pEntryUp->upState.downIndex = qParent_.down->getHeadIndex();  
	  pEntryUp->upState.setMatchNo(matchNo_);
	  
	  qParent_.up->insert();
	  
	  // this parent request has been processed. 
	  qParent_.down->removeHead();
	  
	  cameFrom_ = 0;
	  matchNo_ = 0;
	  seqNum_ = 0;
	  explainTree_ = 0;
	  currentExplain_ = 0;
	  
	  // Wait for another request
	  workState_ = EXPL_IDLE;
	  break;
	}
    }
}


ExplainDesc *
ExExplainTcb::getNextExplainTree(Int64 explainFragAddr)
{
  if (NOT explainFromAddrProcessed_)
    {
      // Unpack the EXPLAIN Fragment
      char * explainFrag = (char*)explainFragAddr;
      
      ExplainDesc *expDesc = (ExplainDesc*)explainFrag;

      // Set up space for reallocating objects during unpacking when
      // there is a difference in image sizes at version migration.
      //
      
      ex_queue_entry *pEntryDown = qParent_.down->getHeadEntry();
      ComDiagsArea *diagsArea = 
        pEntryDown->getAtp()->getDiagsArea();
      ExplainDesc dummyExpDesc;
      if ( (expDesc = (ExplainDesc *)
            expDesc->driveUnpack(explainFrag,&dummyExpDesc,getSpace())) == NULL )
        {
          // ERROR during unpacking.
          // Most likely case is verison-unsupported.
           ExRaiseSqlError(getGlobals()->getDefaultHeap(), 
                          &diagsArea, EXE_NO_EXPLAIN_INFO);
          if (diagsArea != pEntryDown->getAtp()->getDiagsArea())
            pEntryDown->getAtp()->setDiagsArea(diagsArea);
          
          workState_ = EXPL_ERROR;
          return 0;
        }
      else
        {
          // The expDescPtr might have been relocated after unpacking
          // due to a version upgrade. Return its new location.
          //
          explainFromAddrProcessed_ = TRUE;
          
          return expDesc;
        }
    }

  explainFromAddrProcessed_ = FALSE;
 
  workState_ = EXPL_DONE;

  return 0;
}

// getNextExplainTree: Get the next explainTree from the next statement
// that matches the parameters of the xplain function (module name and
// statement pattern.  getNextExplainTree uses the Like pattern matching
// objects and methods to perform any wild card matching.  It assumes that
// the modName_ and stmtPattern_ have been initialized by a call to
// copyParameters() and that the named module has been loaded and stmtList_
// has been initialized to point to the statement list of the current
// context.  When a statement is found that matches the module name and
// statement pattern, the Explain Fragment of the statement is copied
// and unpacked.

ExplainDesc *
ExExplainTcb::getNextExplainTree()
{
  if (explainAddr_ != -1)
    {
      return getNextExplainTree(explainAddr_);
    }

  // If the statement pattern is NULL then no statements match
  if(!isNullStmtPattern())
    {
      // Define the pattern string
      // The Wild Card character is '\'
      LikePatternString 
	patternString(stmtPattern_, 
		      (stmtPattern_ ? str_len(stmtPattern_) : 0), 
		      CharInfo::ISO88591,
		      "\\", 1 );

      // Define a pattern for the pattern string
      LikePattern pattern(patternString, getHeap());
      if (pattern.error())
	{
          ex_queue_entry *pEntryDown = qParent_.down->getHeadEntry();
          ComDiagsArea *diagsArea = 
            pEntryDown->getAtp()->getDiagsArea();
          ExRaiseSqlError(getGlobals()->getDefaultHeap(), 
                          &diagsArea,  EXE_INVALID_ESCAPE_SEQUENCE);
          if (diagsArea != pEntryDown->getAtp()->getDiagsArea())
            pEntryDown->getAtp()->setDiagsArea(diagsArea);
          
	  workState_ = EXPL_ERROR;
	  return 0;
	}
      
      // A pointer to the statement being considered for a match
      Statement *stmt;

      // Reset the cursor to the beginning of the list of statements.
      // This is necessary since the stmtList_ cursor is altered
      // between calls to this routine.  Because of this, the position
      // of the last statement last considered has to be reestablished.
      stmtList_->position();

      // currentStmt_ contains a pointer to the last statement considered.
      // Find this statement in the statement list.
      if(currentStmt_)
	while(stmt = (Statement *)stmtList_->getNext())
	  {
	    if(stmt == currentStmt_) break;
	  }
      
      // Starting at the next statement, search the statement list for
      // a statement that matches the module name and statement pattern
      while(stmt = (Statement *)stmtList_->getNext())
	{
	  // module name of statement being considered.
	  const char *moduleName = stmt->getModuleId()->module_name;

	  // Added for multi charset module names
          Lng32 moduleNameLen = getModNameLen(stmt->getModuleId());

	  // statement name of statement being considered
	  const char *ident = stmt->getIdentifier();

	  Int32 length = str_len(modName_);

	  // Calculate max length of modName_ (explain function parameter)
	  // and moduleName (module name of statement being considered)
	  if(moduleName)
	    length =
	      (length > moduleNameLen) ? length : moduleNameLen;

	  // Module Name matches if both are NULL or if both compare equal.
	  // It should be that if the module name parameter is NULL then
	  // the module name matches if this is the current module.
	  Int32 modNameMatches = 
	    ((isNullModName() && !moduleName) ||
	     (!isNullModName() &&
	      moduleName &&
	      (str_cmp(modName_, moduleName, length) == 0)));

	  if(modNameMatches &&
	     (pattern.matches(ident, (ident ? str_len(ident) : 0), CharInfo::UTF8) == TRUE) &&
	     stmt->getRootTdb())
          {
	      // Remember the current statement, so that the position
	      // in the statement list can be reestablished.
	      currentStmt_ = stmt;

	      ex_assert(stmt->getRootTdb()->getNodeType() == ComTdb::ex_ROOT,
			"Invalid TDB Tree");

	      // Get the fragment directory of the statement.
	      ExFragDir *fragDir = 
		((ex_root_tdb *)(stmt->getRootTdb()))->getFragDir();

              Lng32 fragOffset;
              Lng32 fragLen;
              Lng32 topNodeOffset;
              char * fragStart;
              if (fragDir->getExplainFragDirEntry(fragOffset, fragLen, topNodeOffset) == 0)
              {
              
	        // Allocate a buffer big enough to hold the EXPLAIN
	        // fragment.
	        char *explainFrag = new(getHeap()) char[fragLen + 10];

	        // Get a 'pointer' to the start of the fragment.
	        char * fragBase = ((char *)stmt->getRootTdb() + fragOffset);
	      
	        // Copy all bytes of fragment to  newly allocated buffer
	        str_cpy_all(explainFrag, fragBase, fragLen);
	      
	        // Get a 'pointer' to the root node of the explain Tree
	        // in the newly copied fragment.
	        fragStart = explainFrag + topNodeOffset;

	        // For now the root of the tree must be at the start of
	        // the EXPLAIN fragment because the root of the tree is
	        // used as a handle on the fragment to delete it.
	        if (fragStart != explainFrag)
	        {
		  NADELETEBASIC(explainFrag, getHeap());
		  explainFrag = NULL;
		  ex_assert(0,
			    "Explain: explainTree root is not at the"
			    "beginning of the fragment\n");
                }
              // Unpack the EXPLAIN Fragment
	      ExplainDesc *expDesc = (ExplainDesc *)fragStart;

	      // Set up space for reallocating objects during unpacking when
	      // there is a difference in image sizes at version migration.
	      //
	      ExplainDesc dummyExpDesc;
	      if ( (expDesc = (ExplainDesc *)
	            expDesc->driveUnpack(explainFrag,&dummyExpDesc,stmt->getUnpackSpace())) == NULL )
	      {
		// ERROR during unpacking.
		// Most likely case is version-unsupported.
		//
                // Add code for erroring handling !!!
		workState_ = EXPL_ERROR;
		NADELETEBASIC(explainFrag, getHeap());
		return 0;
	      }
	      else
	      {
		// The expDescPtr might have been relocated after unpacking
		// due to a version upgrade. Return its new location.
		//
		return expDesc;
	      }
            }
            else
            {
              // error case. Add an error entry.
	      // Pointer to request entry in parent down queue
	      ex_queue_entry *pEntryDown = qParent_.down->getHeadEntry();
	      ComDiagsArea *diagsArea = 
		pEntryDown->getAtp()->getDiagsArea();
	      ExRaiseSqlError(getGlobals()->getDefaultHeap(), 
			      &diagsArea, EXE_NO_EXPLAIN_INFO);
	      if (diagsArea != pEntryDown->getAtp()->getDiagsArea())
		pEntryDown->getAtp()->setDiagsArea(diagsArea);
  	    
	      workState_ = EXPL_ERROR;
	      return 0;
            }
         }
      }
  }

  // No more statements match.  Reset currentStmt and return NULL.
  currentStmt_ = 0;

  workState_ = EXPL_DONE;
  return 0;
}


// initParamsTuple: Allocate the paramsTuple_, the paramsAtp, the
// modName_ buffer and the stmtPattern_ buffer.  The paramsTuple_ will
// hold the result of evaluating the paramsExpr.  The modName_ and
// stmtPattern_ buffers are used to hold a NULL terminated copy of the
// parameters.  Also, initialize the paramsTuppDesc_ to point to the
// paramsTuple_ and the paramsTupp_ to point to the paramsTuppDesc_.
// This is done to create the necessary tuple structure so that the
// paramsExpr can be evaluateed.
//
// Arguments:
//
// tupleLength - length of the paramsTuple_ to be allocated
// criDescParams - criDesc descripting the paramsAtp_
// lengthModName - length of the modName_ buffer to be allocated
// lengthStmtPattern - length of the stmtPattern_ buffer to be allocated
void
ExExplainTcb::initParamsTuple(Lng32 tupleLength,
			      ex_cri_desc *criDescParams,
			      Lng32 lengthModName,
			      Lng32 lengthStmtPattern)
{

  // Allocate the required buffers
  paramsTuple_ = new(getSpace()) char[tupleLength];
  modName_ = new(getSpace()) char[lengthModName + 1];
  modDir_  = new(getSpace()) char[1024 + 1]; // use a literal here. TBD.
  stmtPattern_ = new(getSpace()) char[lengthStmtPattern + 1];
  
  // Initialize the tuple structure so it can be used when
  // evaluating the paramsExpr
  paramsTuppDesc_.init(tupleLength, 0, paramsTuple_);
  paramsTupp_.init();
  paramsTupp_ = &paramsTuppDesc_;
  
  // Allocate the ATP used to hold the inputs given from the parent
  // request and the paramsTuple to be populated by evaluation of
  // the paramsExpr.  
  paramsAtp_ = allocateAtp(criDescParams, getSpace());
  
}


// copyParameters : copy the parameters from the paramsTuple_ to
// the buffers (modName_ and stmtPattern_).  Remove any trailing
// spaces and NULL Terminate.
void
ExExplainTcb::copyParameters()
{
  Lng32 i;
  
  if(!isNullModName())
    {
      // Copy each byte of the module name to modName_
      for(i = 0; i < explainTdb().getLengthModName(); i++)
	modName_[i] = paramsTuple_[explainTdb().getOffsetModName() + i];

      // NULL Terminate.
      modName_[i] = '\0';
      
      // Remove any trailing spaces (replace with NULL character)
      for(i = explainTdb().getLengthModName() - 1; i >= 0; i--)
	if(modName_[i] == ' ')
	  modName_[i] = '\0';
	else
	  break;

      // find out if this modName_ contains an oss pathname.
      // If it does, then use the oss pathname to search for the module.
      modDir_[0] = 0;
      if (modName_[0] == '/')
	{
	  Lng32 len = (Int32)strlen(modName_);

	  // find the directory name.
	  Lng32 i = len-1;
	  NABoolean done = FALSE;
	  NABoolean dQuoteSeen = FALSE;
	  while ((i > 0) && (NOT done))
	    {
	      if (modName_[i] == '"')
		dQuoteSeen = NOT dQuoteSeen;

	      if (NOT dQuoteSeen)
		{
		  if (modName_[i] == '/')
		    {
		      done = TRUE;
		      continue;
		    }
		}
	      i--;
	    }

	  i++;
	  strncpy(modDir_, modName_, i);
	  modDir_[i] = 0;

	  Int32 j = 0;
	  while (i < len)
	    modName_[j++] = modName_[i++];
	  modName_[j] = 0;

	} // modDir prepended to modName
    }
  else
    // If it is a NULL Value, make string zero length
    modName_[0] = '\0';
  
  if(!isNullStmtPattern())
    {
      // Copy each byte of stmt pattern to stmtPattern_
      char * paramsTupleData =  &paramsTuple_[explainTdb().getOffsetStmtPattern()];
      Lng32 paramsTupleLen = explainTdb().getLengthStmtPattern();
      if (explainTdb().getVCIndOffsetStmtPattern() != -1)
        {
          paramsTupleLen = 
            explainTdb().getAttrStmtPattern()->getLength
            (&paramsTuple_[explainTdb().getVCIndOffsetStmtPattern()]);
        }

      str_cpy_all(stmtPattern_, paramsTupleData, paramsTupleLen);
      i = paramsTupleLen;

      // NULL Terminate.
      stmtPattern_[i] = '\0';

      // Remove any trailing spaces (replace with NULL character)
      for(i = paramsTupleLen - 1; i >= 0; i--)
        {
          if(stmtPattern_[i] == ' ')
            stmtPattern_[i] = '\0';
          else
            break;
        }
    }
  else
    // If it is a NULL Value, make string zero length
    stmtPattern_[0] = '\0';

  if (modName_[0] == '\0')
  {
    Lng32 len = str_len(stmtPattern_);
    if (len > strlen("QID=") && str_ncmp(stmtPattern_, "QID=", 4) == 0)
      setQid(&stmtPattern_[4], len-4);
    else if (len > strlen("EXPLAIN_QID=") && str_ncmp(stmtPattern_, 
                                                    "EXPLAIN_QID=", strlen("EXPLAIN_QID=")) == 0)
      setReposQid(&stmtPattern_[strlen("EXPLAIN_QID=")], len-strlen("EXPLAIN_QID="));
    else if (len > strlen("EXPLAIN_STMT=") && 
             str_ncmp(stmtPattern_, "EXPLAIN_STMT=", strlen("EXPLAIN_STMT=")) == 0)
      {
        setExplainStmt(&stmtPattern_[strlen("EXPLAIN_STMT=")], len-strlen("EXPLAIN_STMT="));
      }
    else if (len > strlen("EXPLAIN_PLAN=") && 
             str_ncmp(stmtPattern_, "EXPLAIN_PLAN=", strlen("EXPLAIN_PLAN=")) == 0)
      {
        setExplainPlan(&stmtPattern_[strlen("EXPLAIN_PLAN=")], len-strlen("EXPLAIN_PLAN="));
      }
  }
}

// isNullModName: Returns true (1) if the module name parameter stored
// in the paramsTuple_ is NULL, false (0) otherwise.
short
ExExplainTcb::isNullModName()
{
  // If the module name attribute is Nullable
  if(explainTdb().getAttrModName()->getNullFlag())
    {
      // Get the offset to the Null indicator in the paramsTuple_
      Lng32 nullOffset = explainTdb().getAttrModName()->getNullIndOffset();

      // We are assuming that the sizeof the Null Indicator is the
      // size of a short.
      ex_assert((explainTdb().getAttrModName()->getNullIndicatorLength() ==
		 sizeof(short)),
		"Null indicator not equal to sizeof(short)\n");

      // Return the value of the null indicator
      return *(short *)&(paramsTuple_[nullOffset]);
    }
  else
    // If not NULLable, it can't be NULL
    return 0;
  
}

// isNullStmtPattern: Returns true (1) if the statement pattern parameter
// stored in the paramsTuple_ is NULL, false (0) otherwise.
short
ExExplainTcb::isNullStmtPattern()
{
  // If the statement parameter attribute is Nullable
  if(explainTdb().getAttrStmtPattern()->getNullFlag())
    {
      // Get the offset to the Null indicator in the paramsTuple_
      Lng32 nullOffset = 
	explainTdb().getAttrStmtPattern()->getNullIndOffset();
      
      // We are assuming that the sizeof the Null Indicator is the
      // size of a short.
      ex_assert((explainTdb().getAttrStmtPattern()->
		 getNullIndicatorLength() == sizeof(short)),
		"Null indicator not equal to sizeof(short)\n");
      
      // Return the value of the null indicator
      return *(short *)&(paramsTuple_[nullOffset]);
    }
  else
    // If not NULLable, it can't be NULL
    return 0;
}

void ExExplainTcb::setQid(char *qid, Lng32 len)
{
  if (qid_)
  {
    NADELETEBASIC(qid_, getHeap());
  }
  qid_ = new (getHeap()) char[len+1];
  str_cpy_all(qid_, qid, len);
  qid_[len] = '\0';
  stmtName_ = new (getHeap()) char[60+1]; // showddl for explain returns 60
  if (getStmtNameInQid(qid_, len, stmtName_, (short)61) != 0)
  {
    str_cpy_all(stmtName_, "Unknown Stmt", 12);
    stmtName_[12] = '\0';
  }
}

void ExExplainTcb::setReposQid(char *reposQid, Lng32 len)
{
  if (reposQid_)
    {
      NADELETEBASIC(reposQid_, getHeap());
    }

  reposQid_ = NULL;

  if (! reposQid)
    return;

  reposQid_ = new (getHeap()) char[len+1];
  str_cpy_all(reposQid_, reposQid, len);
  reposQid_[len] = '\0';
}

void ExExplainTcb::setExplainAddr(char *addr, Lng32 len)
{
  explainAddr_ = str_atoi(addr, len);
}

void ExExplainTcb::setExplainAddr(Int64 addr)
{
  explainAddr_ = addr;
}

void ExExplainTcb::setExplainStmt(char *stmt, Lng32 len)
{
  explainStmt_ = stmt;
}

void ExExplainTcb::setExplainPlan(char *plan, Lng32 len)
{
  explainPlan_ = plan;
  explainPlanLen_ = len;
}

RtsExplainFrag *ExExplainTcb::sendToSsmp()
{
  RtsExplainFrag *explainFrag = NULL;
  RtsExplainReq *explainReq;
  CliGlobals *cliGlobals = getGlobals()->castToExExeStmtGlobals()->
                        castToExMasterStmtGlobals()->getCliGlobals();
  ContextCli *context = cliGlobals->currContext();
  if (cliGlobals->getStatsGlobals() == NULL)
  {
    // Runtime Stats not running.
    // Fill in diagsArea with details and return NULL
    IpcAllocateDiagsArea(diagsArea_, getHeap());
    (*diagsArea_) << DgSqlCode(-EXE_RTS_NOT_STARTED);
    return NULL;
  }

  ExMasterStats *masterStats;

  if (strcasecmp(qid_, "CURRENT") == 0) {
     if (context->getStats() != NULL && (masterStats = context->getStats()->getMasterStats()) != NULL) {
        setQid(masterStats->getQueryId(), masterStats->getQueryIdLen()); 
     }
  }

  // Verify that we have valid parameters for each kind of request. If not, fill in the diagsArea
  // and return NULL.
  ExSsmpManager *ssmpManager = cliGlobals->getSsmpManager();
  short cpu;
  char nodeName[MAX_SEGMENT_NAME_LEN+1];
  if (getMasterCpu(qid_, (Lng32)str_len(qid_), nodeName, MAX_SEGMENT_NAME_LEN+1, cpu) == -1)
  {
    nodeName[0] = '\0';
    cpu = -1;
    IpcAllocateDiagsArea(diagsArea_, getHeap());
    (*diagsArea_) << DgSqlCode(-EXE_RTS_INVALID_QID) << DgString0(stmtPattern_);
    return NULL;
  }

  IpcServer *ssmpServer = ssmpManager->getSsmpServer(nodeName, cpu, diagsArea_);
  if (ssmpServer == NULL)
    return NULL; // diags are in diagsArea_

  //Create the SsmpClientMsgStream on the IpcHeap, since we don't dispose of it immediately.
  //We just add it to the list of completed messages in the IpcEnv, and it is disposed of later.
  //If we create it on the ExStatsTcb's heap, that heap gets deallocated when the statement is
  //finished, and we can corrupt some other statement's heap later on when we deallocate this stream.
 SsmpClientMsgStream *ssmpMsgStream  = new (cliGlobals->getIpcHeap())
        SsmpClientMsgStream((NAHeap *)cliGlobals->getIpcHeap(), ssmpManager);

  ssmpMsgStream->addRecipient(ssmpServer->getControlConnection());
  RtsHandle rtsHandle = (RtsHandle) this;

  // Retrieve the Rts collection interval and active queries. If they are valid, calculate the timeout
  // and send to the SSMP process.
  SessionDefaults *sd = cliGlobals->currContext()->getSessionDefaults();
  Lng32 RtsTimeout;
  if (sd)
    RtsTimeout = sd->getRtsTimeout();
  else
    RtsTimeout = 0;
  explainReq = new (cliGlobals->getIpcHeap()) RtsExplainReq(rtsHandle, cliGlobals->getIpcHeap(),
            qid_, str_len(qid_));
  *ssmpMsgStream << *explainReq;
  if (RtsTimeout != 0)
  {
    // We have a valid value for the timeout, so we use it by converting it to centiseconds.
    RtsTimeout = RtsTimeout * 100;
  }
  else
    //Use the default value of 4 seconds, or 400 centiseconds.
    RtsTimeout = 400;

  // Send the message
  ssmpMsgStream->send(FALSE, -1);
  Int64 startTime = NA_JulianTimestamp();
  Int64 currTime;
  Int64 elapsedTime;
  IpcTimeout timeout = (IpcTimeout) RtsTimeout;
  while (timeout > 0 && ssmpMsgStream->hasIOPending())
  {
    ssmpMsgStream->waitOnMsgStream(timeout);
    currTime = NA_JulianTimestamp();
    elapsedTime = (currTime - startTime) / 10000;
    timeout = (IpcTimeout)(RtsTimeout - elapsedTime);
  }
  if (ssmpMsgStream->getState() == IpcMessageStream::ERROR_STATE && retryAttempts_ < 3)
  {
    explainReq->decrRefCount();
    DELAY(100);
    retryAttempts_++;
    explainFrag = sendToSsmp();
    retryAttempts_ = 0;
    return explainFrag;
  }
  if (ssmpMsgStream->getState() == IpcMessageStream::BREAK_RECEIVED)
  {
    // Break received - set diags area
    IpcAllocateDiagsArea(diagsArea_, getHeap());
    (*diagsArea_) << DgSqlCode(-EXE_CANCELED);

    return NULL;
  }
  if (! ssmpMsgStream->isReplyReceived())
  {
    IpcAllocateDiagsArea(diagsArea_, getHeap());
    (*diagsArea_) << DgSqlCode(-EXE_RTS_TIMED_OUT)
      << DgString0(stmtPattern_) << DgInt0(RtsTimeout/100) ;
    return NULL;
  }
  retryAttempts_ = 0;
  explainFrag = ssmpMsgStream->getExplainFrag();
  explainReq->decrRefCount();
  return explainFrag;
}

short ExExplainTcb::getExplainData(
                                        ex_root_tdb * rootTdb,
                                        char * explain_ptr,
                                        Int32 explain_buf_len,
                                        Int32 * ret_explain_len,
                                        ComDiagsArea *diagsArea,
                                        CollHeap * heap)
{
  Lng32 cliRC = 0;

  *ret_explain_len = 0;
 
  Lng32 fragOffset;
  Lng32 fragLen;
  Lng32 topNodeOffset;

  diagsArea->clear();

  if (rootTdb->getFragDir()->getExplainFragDirEntry
                 (fragOffset, fragLen, topNodeOffset) != 0)
    {
      cliRC = -EXE_NO_EXPLAIN_INFO;
      ExRaiseSqlError(heap, &diagsArea, EXE_NO_EXPLAIN_INFO);

      return cliRC;
    }

  // data stored is explain repos header followed by actual explain tdb data.
  Int32 storedExplLen = sizeof(ExplainReposInfo) + fragLen;
  Lng32 encodedFragLen = str_encoded_len(storedExplLen);
  *ret_explain_len = encodedFragLen;
  
  if ((! explain_ptr) || 
      (explain_buf_len < (*ret_explain_len + 1/*null terminator*/)))
    {
      cliRC = -CLI_GENCODE_BUFFER_TOO_SMALL;

      ExRaiseSqlError(heap, &diagsArea, CLI_GENCODE_BUFFER_TOO_SMALL);

      return cliRC;
    }

  // allocate space for explain tdb and header repos info
  char * fragExplPtr = ((char *)rootTdb)+fragOffset;
  char * storedExplData = new(heap) char[storedExplLen];
  ExplainReposInfo * eri = (ExplainReposInfo*)storedExplData;
  eri->init(); // initialize explain repos header
  memcpy(&storedExplData[sizeof(ExplainReposInfo)], fragExplPtr, fragLen);

  // encode it before returning
  str_encode(explain_ptr, encodedFragLen, storedExplData, storedExplLen);

  // null terminate explain fragment
  explain_ptr[encodedFragLen] = 0;

  NADELETEBASIC(storedExplData, heap);

  return 0;
}

short ExExplainTcb::getExplainFromRepos(char * qid, Lng32 qidLen)
{
  Lng32 cliRC = 0;

  CliGlobals *cliGlobals = getGlobals()->castToExExeStmtGlobals()->
	                castToExMasterStmtGlobals()->getCliGlobals();
  ExeCliInterface cliInterface(getHeap(), 0, cliGlobals->currContext());

  ex_queue_entry *pEntryDown = qParent_.down->getHeadEntry();
  ComDiagsArea *diagsArea =
    pEntryDown->getAtp()->getDiagsArea();

  if (! qid || (qidLen == 0))
    return -1;

  if (explainFrag_)
    NADELETEBASIC(explainFrag_, getHeap());
  explainFrag_ = NULL;
  explainFragLen_ = 0;

  ExplainReposInfo * eri = NULL;
  Queue *infoList = NULL;

  // query text and explain info is stored as the first entry for this query id. Retrieve it.
  char * queryBuf = new(getHeap()) char[4000];
  str_sprintf(queryBuf, "select [first 1] explain_plan from %s.\"%s\".%s where query_id = '%s' and explain_plan is not null and char_length(explain_plan) > 0 order by exec_start_utc_ts ",
              TRAFODION_SYSCAT_LIT, SEABASE_REPOS_SCHEMA, REPOS_METRIC_QUERY_TABLE, qid);
  OutputInfo * vi = NULL;
  char * ptr = NULL;
  Lng32 len = 0;
  
  if (cliInterface.initializeInfoList(infoList, TRUE))
    {
      goto label_error;
    }

  cliRC = cliInterface.fetchAllRows(infoList, queryBuf, 0, FALSE, FALSE);
  if (cliRC < 0)
    {
      goto label_error;
    }

  if ((infoList->numEntries() == 0) ||
      (infoList->numEntries() > 1))
    {
      diagsArea = pEntryDown->getAtp()->getDiagsArea();
      ExRaiseSqlError(getGlobals()->getDefaultHeap(), 
                      &diagsArea, EXE_NO_QID_EXPLAIN_INFO);
      if (diagsArea != pEntryDown->getAtp()->getDiagsArea())
        pEntryDown->getAtp()->setDiagsArea(diagsArea);

      goto label_error2;
    }

  infoList->position();
  vi = (OutputInfo*)infoList->getCurr();
  
  if (vi->get(0, ptr, len))
    goto label_error2;
  
  explainFragLen_ = str_decoded_len(len); // remove trailing null terminator
  explainFrag_ = new(getHeap()) char[explainFragLen_];
  if (str_decode(explainFrag_, explainFragLen_, ptr, len) < 0)
    {
      diagsArea = pEntryDown->getAtp()->getDiagsArea();
      ExRaiseSqlError(getGlobals()->getDefaultHeap(), 
                      &diagsArea, EXE_NO_EXPLAIN_INFO);
      if (diagsArea != pEntryDown->getAtp()->getDiagsArea())
        pEntryDown->getAtp()->setDiagsArea(diagsArea);

      goto label_error2;
    }

  // explain repos info header is at the beginning of stored data.
  eri = (ExplainReposInfo*)explainFrag_;
  if (eri->rtci_.numChunks_ > 0)
    {
      // explain data is stored in multiple chunks/rows in METRIC_TEXT table.
      // Get data from there and glue it.
      // TBD. Not yet supported.
      // Return error.
      goto label_error;
    }
    
  // skip ExplainReposInfo and point explainAddr to actual explain structures.
  setExplainAddr((Int64)&explainFrag_[sizeof(ExplainReposInfo)]);  
  setReposQid(NULL, 0);

  NADELETEBASIC(queryBuf, getHeap());

  return 0;

 label_error:
  diagsArea = pEntryDown->getAtp()->getDiagsArea();

  if (diagsArea == NULL)
    diagsArea = ComDiagsArea::allocate(getGlobals()->getDefaultHeap());
  
  cliInterface.retrieveSQLDiagnostics(diagsArea);
  
  if (diagsArea != pEntryDown->getAtp()->getDiagsArea())
    pEntryDown->getAtp()->setDiagsArea(diagsArea);

label_error2:

  NADELETEBASIC(queryBuf, getHeap());
  return -1;
}

short ExExplainTcb::storeExplainInRepos(
                                        CliGlobals * cliGlobals,
                                        Int64* execStartUtcTs,
                                        char * qid, Lng32 qidLen,
                                        char * explainData, Lng32 explainDataLen)
{
  Lng32 cliRC = 0;

  ContextCli * currContext = cliGlobals->currContext();
  CollHeap * heap = currContext->exCollHeap();
  ExeCliInterface cliInterface(heap, 0, currContext);
  ComDiagsArea *diagsArea = &currContext->diags();

  if (! qid || (qidLen == 0) || (!execStartUtcTs))
    return -1;

  diagsArea->clear();

  char * queryBuf = NULL;
  Int64 rowsAffected = 0;
  char * explainVarcharBuf = NULL;
  if (explainDataLen > REPOS_MAX_EXPLAIN_PLAN_LEN)
    {
      cliRC = -EXE_EXPLAIN_PLAN_TOO_LARGE;
      ExRaiseSqlError(heap, &diagsArea, EXE_EXPLAIN_PLAN_TOO_LARGE);
      goto label_error;
    }

  queryBuf = new(heap) char[4000];
  str_sprintf(queryBuf, "update %s.\"%s\".%s set explain_plan = cast(? as varchar(%d) not null) where exec_start_utc_ts = CONVERTTIMESTAMP(%ld)  and query_id = '%s' ",
              TRAFODION_SYSCAT_LIT, SEABASE_REPOS_SCHEMA, REPOS_METRIC_QUERY_TABLE, 
              REPOS_MAX_EXPLAIN_PLAN_LEN,
              *execStartUtcTs, qid);

  explainVarcharBuf = new(heap) char[sizeof(Lng32) + explainDataLen];
  *(Lng32 *)explainVarcharBuf = explainDataLen;
  memcpy(&explainVarcharBuf[sizeof(Lng32)], explainData, explainDataLen);
  cliRC = cliInterface.executeImmediateCEFC(queryBuf, 
                                            explainVarcharBuf, sizeof(Lng32) + explainDataLen,
                                            NULL, NULL, &rowsAffected);
  if (cliRC < 0)
    {
      goto label_error;
    }

  if (rowsAffected == 0)
    {
      ExRaiseSqlError(heap, &diagsArea, EXE_NO_QID_EXPLAIN_INFO);
      cliRC = -EXE_NO_QID_EXPLAIN_INFO;

      goto label_error;
    }

  if (queryBuf)
    NADELETEBASIC(queryBuf, heap);

  if (explainVarcharBuf)
    NADELETEBASIC(explainVarcharBuf, heap);

  return 0;

 label_error:
  if (queryBuf)
    NADELETEBASIC(queryBuf, heap);

  if (explainVarcharBuf)
    NADELETEBASIC(explainVarcharBuf, heap);

  return cliRC;
}

The JIRA isn't showing trace fileor debugging info. Can you explain what the issue was with passingin 'len' into str_decoded_len ? Seems like the explainFragLen_ would have had sufficient length to allocate the explain fragment ? Some explanation in the PR or JIRA will help.