blob: cf6bf06d21c63aad3b482e842997c7dc4f853b2b [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: ExplainTuple.cpp
* Description:
*
* Created: 5/6/98
* Language: C++
*
*
*
*
****************************************************************************
*/
#include "ExplainTupleMaster.h"
#include "ComPackDefs.h"
#include "dfs2rec.h"
#include "exp_tuple_desc.h"
#include "csconvert.h"
// Methods that implement the Explain Tree (ExplainDesc
// and ExplainTuple nodes). An Explain Tree consists of an
// ExplainDesc root which points to a single child ExplainTuple.
// Each ExplainTuple can in turn point to upto two children
// ExplainTuple's.
// The first version of Explain. This is a simple attempt to
// have some handle on the version of explain used to create
// an Explain Tree that might be stored in a module. This version
// number should be changed whenever the data structures of the
// Explain Tree change
// #define EXPLAIN_VERSION 100
// ExplainDesc constructor:
// - Initializes version_ and explainTreeRoot_.
//
// - Initializes recLength_ and numCols_ based on the input
// explainDesc.
//
// - constructs an array of ExplainTupleColDesc to describe the
// layout of the explain tuple. Uses the input explainDesc
// to get info. (This array will be stored with the explain Tree
// and should be used for all accesses to an explain tuple.
//
// Called from RelRoot::codeGen()
ExplainDesc::ExplainDesc(Lng32 numCols, Lng32 recLength, Space *space)
: // version_(EXPLAIN_VERSION), // handled now by NAVersionedObject
explainTreeRoot_(0), numCols_(numCols), recLength_(recLength),
NAVersionedObject(-1)
{
// Allocate space for the array of explainTupleColDesc. This array
// will have numCols_ entries and will be used to access the Explain Tuple.
explainCols_ = (ExplainTupleColDesc *)
new(space) char[sizeof(ExplainTupleColDesc) * numCols_];
// Method setColDescr must be called for each or the numCols_
// allocated column descriptors to set them!!!
}
// Pack the Explain Descriptor and all its children
Long ExplainDesc::pack(void *space)
{
// REVISIT
// explainCols_.packArray(space,numCols_);
explainCols_.pack(space);
explainTreeRoot_.pack(space);
return NAVersionedObject::pack(space);
}
// Unpack the Explain Descriptor and all its children
Lng32 ExplainDesc::unpack(void * base, void * reallocator)
{
if(explainCols_.unpack(base)) return -1;
if(explainTreeRoot_.unpack(base, reallocator)) return -1;
return NAVersionedObject::unpack(base, reallocator);
}
// ExplainTuple Constructor:
// - initializes some data members
//
// - sets explainDesc_ to point to root of tree (from input)
//
// - sets up eye catcher
//
// - sets up child pointers (from inputs)
//
// - sets up childrens' parent pointers to point to this node.
//
// Called by RelExpr::addExplainInfo()
//
// Note that ExplainTuple::init() actually allocates the explainTuple_.
// init() could/should probably be pulled into the costructor.
ExplainTuple::ExplainTuple(ExplainTuple *leftChild,
ExplainTuple *rightChild,
ExplainDesc *explainDesc)
: state_(NO_EXPLAIN_INFO),
explainTupleData_(),
descCursor_(0),
explainDesc_(explainDesc),
parent_(0),
explainTupleStr_(NULL),
NAVersionedObject(-1)
{
// Eye catcher. Helps with debugging.
eyeCatcher_[0] = 'E';
eyeCatcher_[1] = 'X';
eyeCatcher_[2] = 'T';
eyeCatcher_[3] = 'P';
// Initialize the child pointers.
child(0) = leftChild;
child(1) = rightChild;
// Make the Explain Tree linked both ways.
if(leftChild)
leftChild->setParent(this);
if(rightChild)
rightChild->setParent(this);
usedRecLength_ = getRecLength();
}
ExplainTuple::~ExplainTuple()
{
if (explainTupleStr_)
delete explainTupleStr_;
}
short ExplainTuple::genExplainTupleData(Space * space)
{
if (explainTupleStr_)
{
// find out used record size by removing unused bytes in description field.
Lng32 descrColSize = getColLength(EX_COL_DESCRIPT);
Lng32 usedRecLength = getRecLength() - (descrColSize - descCursor_);
setUsedRecLength(usedRecLength);
explainTupleData_ = space->allocateAndCopyToAlignedSpace(
explainTupleStr_,
usedRecLength);
delete explainTupleStr_;
explainTupleStr_ = NULL;
}
else
{
setUsedRecLength(getRecLength());
}
return 0;
}
// Pack this node and all it children
Long ExplainTuple::pack(void *space)
{
explainTupleData_.pack(space);
// Since the explainDesc is at the start of the Fragment,
// the offset will end up being zero. During unpack, a zero
// offset would be mistaken for a NULL pointer. To get around
// this, subtract one from the offset. Must also do something
// similar when unpacking.
explainDesc_++;
explainDesc_.packShallow(space);
// Convert the parent pointer, but don't follow.
parent_.pack(space);
// Follow the child pointers to traverse the whole tree.
children_[0].pack(space);
children_[1].pack(space);
return NAVersionedObject::pack(space);
}
// unpack this node and all it children
Lng32 ExplainTuple::unpack(void * base, void * reallocator)
{
// Unpack the children pointers and the children nodes.
if(children_[0].unpack(base, reallocator)) return -1;
if(children_[1].unpack(base, reallocator)) return -1;
if(parent_.unpack(base, reallocator)) return -1;
// Since the explainDesc is at the start of the Fragment,
// the offset will be zero. During unpack, a zero
// offset would be mistaken for a NULL pointer. To get around
// this, subtract one from the offset after packing. Then to
// convert back to a pointer, must subtract 1 after converting.
if(explainDesc_.unpackShallow(base)) return -1;
explainDesc_--;
if(explainTupleData_.unpack(base)) return -1;
return NAVersionedObject::unpack(base, reallocator);
}
// init :
// - Allocates explainTuple_ for this node and initializes it
// with some defaults.
//
// Called by RelExpr::addExplainInfo() right after the constructor
// for this class. This method should probably be pulled into the
// constructor. It was originally done differently.
//
// This is a method on ExplainTupleMaster since it should only be
// called during the generate phase (not at run time)
//
Int32
ExplainTupleMaster::init(Space *space, NABoolean doExplainSpaceOpt)
{
descCursor_ = 0;
explainTupleStr_ = NULL;
explainTupleData_ = (NABasicPtr)NULL;
// if space opt is to be done, start by allocating max length.
// this will be trimmed and reset in ExplainTuple::genExplainData at end.
if (doExplainSpaceOpt)
explainTupleStr_ = new char[getRecLength()];
else
explainTupleData_ = new (space) char[getRecLength()];
setModuleName("");
setStatementName("");
setPlanId(0);
setSeqNum(0);
setOperator("Unknown");
setChildSeqNum(0,0);
setChildSeqNum(1,0);
setTableName("");
setCardinality(0.0);
setOperatorCost(0.0);
setTotalCost(0.0);
setDetailCost(0);
setDescription("");
return(0);
}
// setCol - set the specified column of the explain Tuple with the
// value pointed to by 'value'.
//
// - col - the column number to set
// - value - a pointer to the value
// - dataLength - the length of the data pointed to by value
// (only meaningful for VarChar fields)
// - cursor - the position within the field to start placing 'value'
// (only meaningful for Char fields and only used
// with the 'description' field.
void
ExplainTuple::setCol(Int32 col,
void const *value,
UInt32 dataLength, // Used only by VarChar fields
UInt32 cursor)
{
char * e = NULL;
if (explainTupleStr_)
e = explainTupleStr_;
else if (explainTupleData_)
e = explainTupleData_;
if (e)
{
// Get the relative position of this column in the explainTuple.
// For varchars len is the declared varchar max length
UInt32 len = (UInt32) getColLength(col);
UInt32 offset = (UInt32) getColOffset(col);
Int16 dataType = (Int16) getColDataType(col);
char *v = (char *)value;
char *p = NULL;
UInt32 nullOffset = 0;
UInt32 vcOffset = 0;
NABoolean isNullable = (getColNullFlag(col) != 0);
UInt32 colOffset = ExpTupleDesc::sqlarkExplodedOffsets(offset, len,
dataType,
isNullable,
&nullOffset,
&vcOffset);
// If the column can have NULL values...
if (isNullable)
{
p = e + nullOffset;
// and if the value is non-null or something has
// already been placed in this field.
if (v || (cursor > 0))
{
// Set the NULL indicator to non NULL
*p++ = (char)0;
*p++ = (char)0;
}
else
{
// set the null indicator to NULL
*p++ = (char)-1;
*p++ = (char)-1;
}
}
// If this column is a VarChar, the actual length field must be set.
if (DFS2REC::isAnyVarChar(dataType))
{
// Desired length is minimum of:
// - declared size of this column (len)
// - length of value (dataLength) + offset into this field (cursor)
UInt32 desiredLength = ((cursor + dataLength) < len) ? (cursor + dataLength) : len;
// Bytes to copy is minimum of:
// - remaining space (len - cursor)
// - dataLength
UInt32 bytesToCopy = 0;
if (desiredLength < len)
{
bytesToCopy = dataLength;//if all fits, copy everything
}
else
{
if (len > cursor)
{
bytesToCopy = len - cursor;//space exists, but need to truncate
}
else
{
bytesToCopy = 0; //no space available
}
}
if (bytesToCopy)
{
// Set the length field.
p = e + vcOffset;
Int16 actualLength = (Int16) desiredLength;
*p++ = ((char *)&actualLength)[0];
*p++ = ((char *)&actualLength)[1];
}
len = bytesToCopy; // len is now number of bytes to copy.
}
// Destination pointer is now indexed into field by cursor.
p = e + colOffset + cursor;
// If field is Fixed Char. it must be pad extended to the declared size.
// Otherwise, just copy the bytes.
if (dataType == REC_BYTE_F_ASCII)
for(UInt32 i = 0; i < len; i++)
{
if(v && *v)
*p++ = *v++;
else
*p++ = ' ';
}
else if (v)
for (UInt32 i = 0; i < len; i++)
*p++ = *v++;
//in case of VarChar, need to check for data truncation and indicate accordingly
///whenever len is zero => nothing was copied => no need to check
if (len && DFS2REC::isAnyVarChar(dataType))
{
//if we tried putting in more than there is space for, we must have truncated
if ((cursor + dataLength) > (UInt32)getColLength(col))
{
//replace the last two characters by the end of line character and the truncation indicator
//NOTE: Must be careful not to damage a multi-byte UTF8 character, so we ensure
// that *(p-2) [i.e. where the '*' will be put] is either an ASCII character
// or it is the first byte of a multi-byte UTF8 character.
//
if ( (*(p-2)) & 0x80 ) // If non-ASCII char
{
char *pSt = findStartOfChar( p-2, e + colOffset + 2 );
// Note: We don't care if pSt -> valid UTF8 or not.
while ( p > (pSt + 2) ) *--p = '\0'; // replace char with zeroes
}
*--p = '\0';
*--p = '*';
}
}
// Update the state of this node.
state_ = SOME_EXPLAIN_INFO;
}
}
// Below are specific routines to set the specific
// columns of the explain tuple. They are very dependent
// on the structure of the tuple. In particular, they
// assume the ordering of the columns and the type of
// each column.
// setModuleName:
// a method on ExplainTuple - is called from executor.
void
ExplainTuple::setModuleName(const char *modName)
{
setCol(EX_COL_MODNAME, modName, 0, 0);
}
// setStatementName:
// a method on ExplainTuple - is called from executor.
void
ExplainTuple::setStatementName(const char *stmtName)
{
setCol(EX_COL_STMTNAME, stmtName, 0, 0);
}
void
ExplainTupleMaster::setPlanId(Int64 planId)
{
setCol(EX_COL_PLANID, &planId, 0, 0);
}
void
ExplainTuple::setSeqNum(Int32 seqNum)
{
setCol(EX_COL_SEQNUM, &seqNum, 0, 0);
}
Int32
ExplainTuple::getSeqNum()
{
Int32 retval = 0;
char * e = NULL;
if (explainTupleStr_)
e = explainTupleStr_;
else if (explainTupleData_)
e = explainTupleData_;
if (e)
{
// Get the relative position of this column in the explainTuple.
UInt32 len = (UInt32) getColLength(EX_COL_SEQNUM);
UInt32 offset = (UInt32) getColOffset(EX_COL_SEQNUM);
Int16 dataType = (Int16) getColDataType(EX_COL_SEQNUM);
NABoolean isNullable = (getColNullFlag(EX_COL_SEQNUM) != 0);
UInt32 nullOffset = 0;
UInt32 colOffset = ExpTupleDesc::sqlarkExplodedOffsets(offset,
len, dataType, isNullable,
&nullOffset);
if ((isNullable) &&
(*((Int16 *) ((char *)e + nullOffset)) != 0))
{
// the NULL flag is set, return a -1 in this case
return -1;
}
// Get pointer to seqnum and cast it to an Int32
Int32 *r = ((Int32 *) ((char *)e + colOffset));
retval = *r;
}
return retval;
}
void
ExplainTupleMaster::setOperator(const char *op)
{
setCol(EX_COL_OPER, op, 0, 0);
}
void
ExplainTuple::setChildSeqNum(Int32 child, Int32 seqNum)
{
Int32 col = (child == 0 ? EX_COL_LEFTSEQNUM : EX_COL_RIGHTSEQNUM);
// A value of zero for seqNum indicates a NULL value,
// so pass a NULL pointer.
if(seqNum == 0)
setCol(col, 0, 0, 0);
else
setCol(col, &seqNum, 0, 0);
}
void
ExplainTupleMaster::setTableName(const char *tabName)
{
setCol(EX_COL_TABLENAME, tabName, 0, 0);
}
void
ExplainTupleMaster::setCardinality(double card)
{
float fcard = (float) card;
setCol(EX_COL_CARD, &fcard, 0, 0);
}
void
ExplainTupleMaster::setOperatorCost(double opCost)
{
float fopCost = (float) opCost;
setCol(EX_COL_OPCOST, &fopCost, 0, 0);
}
void
ExplainTupleMaster::setTotalCost(double totCost)
{
float ftotCost = (float) totCost;
setCol(EX_COL_TOTCOST, &ftotCost, 0, 0);
}
void
ExplainTupleMaster::setDetailCost(char *detail)
{
if(detail)
setCol(EX_COL_DETCOST, detail, str_len(detail), 0);
else
setCol(EX_COL_DETCOST, 0, 0, 0);
}
void
ExplainTupleMaster::setDescription(const char *desc)
{
setCol(EX_COL_DESCRIPT, desc, str_len(desc), descCursor_);
// Maintain the cursor for the description field. This is the
// only field that uses a cursor.
descCursor_ += str_len(desc);
}