blob: d1130e64eb6546aa3b72a3da14ae0b7112ffab88 [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: ItemExprList.C
* Description: Trees of ItemExpr object seen as lists and vice versa
*
* Created: 7/7/95
* Language: C++
*
*
******************************************************************************
*/
#include "Sqlcomp.h"
#include "ItemOther.h"
#include "ItemLog.h"
#include "ItemSubq.h"
#include "ItemFunc.h"
#include "ItemFuncUDF.h"
// -----------------------------------------------------------------------
// methods for class ItemExprList
// -----------------------------------------------------------------------
void ItemExprList::insertTree(ItemExpr *tree,
OperatorTypeEnum backBoneType,
NABoolean flattenSBQ, NABoolean flattenUDF)
{
if (tree->getOperatorType() == backBoneType)
{
for (Int32 i = 0; i < tree->getArity(); i++)
{
// Check for NULL list for right linear trees. That is, arity may be
// two, but second child is NULL.
ItemExpr *child = tree->child(i);
if (child)
insertTree(tree->child(i), backBoneType, flattenSBQ, flattenUDF);
}
}
else if (tree->getOperatorType() == ITM_ONE_ROW)
{
Aggregate *agr = (Aggregate *)tree;
if (agr->isOneRowTransformed_)
{
for (Int32 i = 0; i < tree->getArity(); i++)
insertTree(tree->child(i), backBoneType, flattenSBQ, flattenUDF);
}
else
{
// do nothing, postpone this processing until OneRow transformation
// is done
}
}
else if ((flattenSBQ AND tree->isASubquery()) OR
(flattenUDF AND
(tree->getOperatorType() == ITM_USER_DEF_FUNCTION)) AND
(NOT tree->nodeIsTransformed()))
// Added the extra check for transformation above to avoid any issues
// where we might flatten a subquery/MVF a second time around while
// we deal with ValueIdProxies.
// The ValueIdProxy->needToTransformChild()
// flag should be sufficient, but it never hurts to be safe.
{
ValueIdList cols;
NABoolean haveRDesc(FALSE);
if (tree->isASubquery())
{
// flatten the subquery select list
RETDesc *retDesc = ((Subquery*)tree)->getSubquery()->getRETDesc();
if (retDesc)
{
retDesc->getColumnList()->getValueIdList(cols);
if (cols.entries() > 1)
{
haveRDesc = TRUE;
}
}
}
else if (tree->getOperatorType() == ITM_USER_DEF_FUNCTION)
{
// flatten the UDF by adding the additional outputs to the tree
const RoutineDesc *rDesc = ((UDFunction *)tree)->getRoutineDesc();
if (rDesc && rDesc->getOutputColumnList().entries() > 1)
{
cols = rDesc->getOutputColumnList();
haveRDesc = TRUE;
}
}
if (haveRDesc == TRUE)
{
for (CollIndex i = 0; i < cols.entries(); i++)
{
ValueId proxyId;
proxyId = cols[i];
// We create a ValueIdProxy for each element in the subquery's
// select list or for each output parameter of a MVF. The first
// one of these will be marked to be transformed. This allows
// us to get the correct degree of statements containing MVFs or
// subquery with degree > 1 at bind time.
ValueIdProxy *proxyOutput =
new (CmpCommon::statementHeap())
ValueIdProxy( tree->getValueId(),
proxyId,
i);
proxyOutput->synthTypeAndValueId();
// Make sure we transform the subquery or MVF
if (i == 0 ) proxyOutput->setTransformChild(TRUE);
insert(proxyOutput);
}
}
else
insert(tree); // we are processing a valueId of a UDFunction
// or subquery before we have bound it. Just insert
// its valueId and we'll have to deal with it later..
}
else
insert(tree);
}
//----------------------------------------------------------------------------
ItemExpr *
ItemExprList::convertToItemExpr(ItemExprShapeEnum treeShape) const
{
const ItemExprList & list = *this;
if( ! (list.entries() > 0 ) )
{
return NULL;
}
ItemExpr * result = list[0];
for (CollIndex i(1) ; i < list.entries() ; i++)
{
ItemExpr * pLeftSun = NULL;
ItemExpr * pRightSun = NULL;
switch(treeShape)
{
case LEFT_LINEAR_TREE:
pLeftSun = result;
pRightSun = list[i];
break;
case RIGHT_LINEAR_TREE:
pLeftSun = list[i];
pRightSun = result;
break;
case BUSHY_TREE:
default:
CMPASSERT(FALSE);
break;
};
result = new(heap_) ItemList(pLeftSun, pRightSun);
} // for
return result;
} // convertToItemExpr
void ItemExprList::unparse(NAString &result, PhaseEnum phase,
UnparseFormatEnum form, TableDesc* tabId) const
{
NABoolean firstTime = TRUE;
for (CollIndex j = 0; j < entries(); j++)
{
NAString unparsed;
if (at(j) != NULL) {
at(j)->unparse(unparsed, phase, form, tabId);
if ( !firstTime )
result += ",";
else
firstTime = FALSE;
result += unparsed;
}
}
}
//----------------------------------------------------------------------------
void ItemExprList::print(FILE* ofd, const char* indent, const char* title) const
{
if (entries() > 0)
{
#pragma nowarn(1506) // warning elimination
BUMP_INDENT(indent);
#pragma warn(1506) // warning elimination
NAString unparsed;
unparse(unparsed, DEFAULT_PHASE, EXPLAIN_FORMAT);
fprintf(ofd, "%s%s%s\n", NEW_INDENT, title, unparsed.data());
}
} // ItemExprList::print()
// To be called from the debugger.
void ItemExprList::display() const
{
ItemExprList::print();
} // ItemExprList::display()
// -----------------------------------------------------------------------
// methods for class ItemExprTreeAsList
// -----------------------------------------------------------------------
// make a new object that allows viewing a given tree as a list
ItemExprTreeAsList::ItemExprTreeAsList(ExprValueId *treePtr,
OperatorTypeEnum op,
ItemExprShapeEnum shape)
{
treePtr_ = treePtr;
operator_ = op;
shape_ = shape;
if (shape == BUSHY_TREE)
ABORT("can't handle bushy trees for now");
}
// return number of entries
Lng32 ItemExprTreeAsList::entries() const
{
ItemExpr *aNode = *treePtr_;
Lng32 result = 0;
while (aNode != NULL)
{
result++;
if (aNode->getOperatorType() == operator_ AND
aNode->getArity() >= 2)
{
if (shape_ == LEFT_LINEAR_TREE)
aNode = aNode->child(0);
else
if (shape_ == RIGHT_LINEAR_TREE)
aNode = aNode->child(1);
else
ABORT("can't do other than right-linear trees");
}
else
aNode = NULL;
}
return result;
}
// insert a new entry.
// The new entry is created at the end of the current tree.
void ItemExprTreeAsList::insert(ItemExpr *treeToInsert)
{
if (treePtr_->getPtr() == NULL)
{
*treePtr_ = treeToInsert;
}
else
{
if (shape_ == RIGHT_LINEAR_TREE)
{
switch (operator_)
{
case ITM_AND:
*treePtr_ = new(CmpCommon::statementHeap())
BiLogic(operator_,
treeToInsert,
treePtr_->getPtr());
break;
case ITM_ITEM_LIST:
{
ItemExpr *aNode = treePtr_->getPtr();
ItemExpr *pNode = treePtr_->getPtr();
if (aNode->getOperatorType() != operator_ AND
aNode->getArity() < 2)
{
// case of current number of entries equal to 1.
*treePtr_ = new(CmpCommon::statementHeap())
ItemList(treePtr_->getPtr(),
treeToInsert);
}
else
{
// current number of entries > 1
while (aNode != NULL)
{
if (aNode->getOperatorType() == operator_ AND
aNode->getArity() >= 2)
{
if (shape_ == RIGHT_LINEAR_TREE)
{
pNode = aNode;
aNode = aNode->child(1);
}
else
ABORT("can't do other than right-linear trees");
}
else
aNode = NULL;
}
pNode->child(1) = new(CmpCommon::statementHeap())
ItemList(pNode->child(1),
treeToInsert);
}
}
break;
default:
ABORT("Can't do backbones with this node type");
}
}
else
ABORT("can only insert into right-linear trees");
}
}
// insert a new entry to the top of the ItemExprTree.
// as of R1.5 this method is called only for ODBC initiated dynamic
// rowset queries. This method is supported only for RIGHT_LINEAR_TREEs
// with operator = ITM_ITEM_LIST.
// Calling this method to an ItemExpr* called newItem will result
// in the following tree, where old1, old2, old3 are previously inserted
// ItemExpr* with old1 being inserted first, then old2 and then old3.
//
// ITEM_LIST
// / \
// newItem ITEM_LIST
// / \
// old1 ITEM_LIST
// / \
// old2 old3
//
void ItemExprTreeAsList::insertAtTop(ItemExpr *treeToInsert)
{
if (!((shape_ == RIGHT_LINEAR_TREE) && (operator_ == ITM_ITEM_LIST)))
ABORT("can only do right-linear trees with a backbone type of ITEM_LIST ");
if (treePtr_->getPtr() == NULL)
{
*treePtr_ = treeToInsert;
}
else
{
*treePtr_ = new(CmpCommon::statementHeap())
ItemList(treeToInsert, treePtr_->getPtr());
}
}
// remove an element that is given by its value
ItemExpr * ItemExprTreeAsList::remove(ItemExpr *treeToRemove)
{
ItemExpr *andNodePtr = treePtr_->getPtr();
ItemExpr *predecessor = NULL;
NABoolean found = FALSE;
// assume the predicate is represented in right-linear
// form:
//
// op
// / \
// A op
// / \
// B ...
//
// and search for the <op> node that is directly above the
// predicate that we want to remove.
if (shape_ != RIGHT_LINEAR_TREE)
ABORT("delete is supported for right linear trees only");
// is the node to delete the only node?
if (treeToRemove == andNodePtr)
{
found = TRUE;
delete andNodePtr;
*treePtr_ = NULL;
}
else
// traverse the backbone looking for "treeToRemove"
while (andNodePtr != NULL AND
andNodePtr->getOperatorType() == operator_ AND
andNodePtr->getArity() == 2 AND
NOT found)
{
// did we find the right node?
if (andNodePtr->child(0).getPtr() == treeToRemove)
{
found = TRUE;
// take "andNode" out of the original tree
if (predecessor != NULL)
{
predecessor->child(1) = andNodePtr->child(1);
}
else
*treePtr_ = andNodePtr->child(1);
// set all children of "andNode" to NULL, then delete it
andNodePtr->child(0) = NULL;
andNodePtr->child(1) = NULL;
delete andNodePtr;
andNodePtr = NULL;
}
else
{
predecessor = andNodePtr;
andNodePtr = andNodePtr->child(1);
}
}
if (found)
return treeToRemove;
else
return NULL;
}
// check whether an element is in the collection
NABoolean ItemExprTreeAsList::contains(const ItemExpr *treeToCheck)
{
ItemExpr *aNodePtr = *treePtr_;
while (aNodePtr != NULL)
{
if (aNodePtr == treeToCheck)
return TRUE;
if (aNodePtr->getOperatorType() == operator_ AND
aNodePtr->getArity() >= 2)
{
if (shape_ == RIGHT_LINEAR_TREE)
aNodePtr = aNodePtr->child(1);
else
ABORT("can't do other than right-linear trees");
}
else
aNodePtr = NULL;
}
return FALSE;
}
// index access (both reference and value)
ItemExpr * ItemExprTreeAsList::operator [] (CollIndex i)
{
// think of three different cases:
//
// a) i is out of range (< 0 or >= #entries)
//
// b) the node we are looking for is neither the only nor the last
// node in the backbone
//
// c) we are looking for the last element in the backbone (which
// may be the only element)
//
ItemExpr *aNodePtr = *treePtr_;
Int32 j = (Int32) i; // j may become negative, i may be unsigned
if (j < 0)
return NULL; // case a
if (aNodePtr->getOperatorType() != operator_ AND
j == 0)
return aNodePtr; // case b
while (aNodePtr != NULL AND j >= 0)
{
if (aNodePtr->getOperatorType() == operator_ AND
aNodePtr->getArity() >= 2)
{
if (shape_ == LEFT_LINEAR_TREE)
{
if (j == 0)
aNodePtr = aNodePtr->child(1); // case b
else
aNodePtr = aNodePtr->child(0);
}
else if (shape_ == RIGHT_LINEAR_TREE)
{
if (j == 0)
aNodePtr = aNodePtr->child(0); // case b
else
aNodePtr = aNodePtr->child(1);
}
else
ABORT("can't do bushy trees");
}
j--;
}
// if we are looking for the only element, the while loop
// is not executed at all
return aNodePtr;
}