| /********************************************************************** |
| // @@@ 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_ = ¶msTuppDesc_; |
| |
| // 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 = ¶msTuple_[explainTdb().getOffsetStmtPattern()]; |
| Lng32 paramsTupleLen = explainTdb().getLengthStmtPattern(); |
| if (explainTdb().getVCIndOffsetStmtPattern() != -1) |
| { |
| paramsTupleLen = |
| explainTdb().getAttrStmtPattern()->getLength |
| (¶msTuple_[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; |
| } |