blob: 46731e3c63a6cd396f3849f82ee06dc1650afa07 [file] [log] [blame]
/**********************************************************************
// @@@ 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((NAHeap *)getHeap(), 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 - 1); // remove trailing null terminator
explainFrag_ = new(getHeap()) char[explainFragLen_];
if (str_decode(explainFrag_, explainFragLen_, ptr, len - 1) < 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;
}