blob: 88edc4b3516c11ea6c07272b442168ec8a94d13e [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: GenPreCode.C
* Description: Fixes up the query tree before code generation.
* This is the post-opt and pre-gen stage.
* Created: 4/15/95
* Language: C++
*
*
*****************************************************************************
*/
#define SQLPARSERGLOBALS_FLAGS // must precede all #include's
#define SQLPARSERGLOBALS_NADEFAULTS
#include "Platform.h"
#include <math.h>
#include "OperTypeEnum.h"
#include "Sqlcomp.h"
#include "GroupAttr.h"
#include "AllRelExpr.h"
#include "RelPackedRows.h"
#include "Generator.h"
#include "GenExpGenerator.h"
#include "dfs2rec.h"
#include "vegrewritepairs.h"
#include "exp_clause_derived.h"
#include "keycolumns.h"
#include "ValueDesc.h"
#include "BindWA.h"
#include "TriggerDB.h"
#include "Cost.h"
#include "CostMethod.h"
#include "ItmFlowControlFunction.h"
#include "UdfDllInteraction.h"
#include "StmtDDLNode.h"
#include "NATable.h"
#include "NumericType.h"
#include "CmpStatement.h"
#include "OptimizerSimulator.h"
#include "ItemFunc.h"
#include "ControlDB.h"
#include "CmpSeabaseDDL.h"
#include "NAExecTrans.h"
#include "exp_function.h"
#include "SqlParserGlobals.h" // must be last #include
extern ItemExpr * buildComparisonPred ( ItemExpr *, ItemExpr *, ItemExpr *,
OperatorTypeEnum,
NABoolean specialNulls=FALSE //++MV - Irena
);
// -----------------------------------------------------------------------
// generateKeyExpr()
//
// This method is used by the code generator for building expressions
// that are of the form <key column> = <value> for each key column.
//
// Parameters:
//
// const ValueIdSet & externalInputs
// IN : The set of values that are available here and can be
// used for replacing any wildcards that appear in the
// listOfKeyValues.
//
// const ValueIdList & listOfKeyColumns
// IN : A read-only reference to the list of key columns
// corresponding to which certain key values have
// been chosen.
//
// const ValueIdList & listOfKeyValues
// IN : A read-only reference to a list of key values that
// are chosen for the corresponding listOfKeyColumns.
// Values for missing key columns have already been
// computed and supplied in this list.
//
// ValueIdList & listOfKeyExpr
// OUT: An assignment expression of the form <key column> = <value>
// for each key column.
//
// -----------------------------------------------------------------------
static void generateKeyExpr(const ValueIdSet & externalInputs,
const ValueIdList & listOfKeyColumns,
const ValueIdList & listOfKeyValues,
ValueIdList & listOfKeyExpr,
Generator* generator,
NABoolean replicatePredicates = FALSE)
{
ItemExpr * keyExpr;
CollIndex keyCount = listOfKeyColumns.entries();
for (CollIndex keyNum = 0; keyNum < keyCount; keyNum++)
{
// Build the assignment expression.
ItemExpr *ieKeyVal = listOfKeyValues[keyNum].getItemExpr()
->replaceVEGExpressions(externalInputs, externalInputs,
FALSE, NULL, replicatePredicates);
ItemExpr *ieKeyCol = listOfKeyColumns[keyNum].getItemExpr();
ValueId KeyColId = ieKeyCol->getValueId();
keyExpr = new(generator->wHeap()) BiRelat(ITM_EQUAL,
ieKeyCol,
ieKeyVal);
// Synthesize its type for and assign a ValueId to it.
keyExpr->synthTypeAndValueId();
// INsert it in the list of key expressions
listOfKeyExpr.insertAt(keyNum, keyExpr->getValueId());
} // end For Loop
} // static generateKeyExpr()
static NABoolean processConstHBaseKeys(Generator * generator,
RelExpr *relExpr,
const SearchKey *skey,
const IndexDesc *idesc,
const ValueIdSet &executorPreds,
NAList<HbaseSearchKey*> &mySearchKeys,
ListOfUniqueRows &listOfUpdUniqueRows,
ListOfRangeRows &listOfUpdSubsetRows)
{
if (! skey)
return TRUE;
// convert built-in search key to entries with constants, if possible
if (skey->areAllKeysConstants(TRUE))
{
ValueIdSet nonKeyColumnSet;
idesc->getNonKeyColumnSet(nonKeyColumnSet);
// seed keyPreds with only the full key predicate from skey
ValueIdSet keyPreds = skey->getFullKeyPredicates();
// include executorPreds and selection predicates
// but exclude the full key predicates.
ValueIdSet exePreds;
exePreds += executorPreds;
exePreds += relExpr->getSelectionPred();
exePreds.subtractSet(keyPreds);
ValueId falseConst = NULL_VALUE_ID;
if (exePreds.containsFalseConstant(falseConst))
keyPreds += falseConst;
HbaseSearchKey::makeHBaseSearchKeys(
skey,
skey->getIndexDesc()->getIndexKey(),
skey->getIndexDesc()->getOrderOfKeyValues(),
relExpr->getGroupAttr()->getCharacteristicInputs(),
TRUE, /* forward scan */
keyPreds,
nonKeyColumnSet,
idesc,
relExpr->getGroupAttr()->getCharacteristicOutputs(),
mySearchKeys);
// Include any remaining key predicates that have not been
// picked up (to be used as the HBase search keys).
exePreds += keyPreds;
if(falseConst != NULL_VALUE_ID)
{
for (CollIndex i = 0; i<mySearchKeys.entries(); i++ )
{
HbaseSearchKey* searchKey = mySearchKeys[i];
searchKey->setIsFalsePred(TRUE);
}
}
TableDesc *tdesc = NULL;
if (mySearchKeys.entries()>0)
{
switch (relExpr->getOperatorType())
{
case REL_HBASE_ACCESS:
{
HbaseAccess *hba = static_cast<HbaseAccess *>(relExpr);
hba->setSearchKey(NULL);
hba->executorPred() = exePreds;
tdesc = hba->getTableDesc();
}
break;
case REL_HBASE_DELETE:
{
HbaseDelete *hbd = static_cast<HbaseDelete *>(relExpr);
hbd->setSearchKey(NULL);
hbd->beginKeyPred().clear();
hbd->endKeyPred().clear();
hbd->executorPred() = exePreds;
tdesc = hbd->getTableDesc();
}
break;
case REL_HBASE_UPDATE:
{
HbaseUpdate *hbu = static_cast<HbaseUpdate *>(relExpr);
hbu->setSearchKey(NULL);
hbu->beginKeyPred().clear();
hbu->endKeyPred().clear();
hbu->executorPred() = exePreds;
tdesc = hbu->getTableDesc();
}
break;
default:
CMPASSERT(tdesc); // unsupported operator type
break;
} // switch
relExpr->selectionPred().clear();
}
if (HbaseAccess::processSQHbaseKeyPreds(generator,
mySearchKeys,
listOfUpdUniqueRows,
listOfUpdSubsetRows))
return FALSE;
} // key uses all constants
return TRUE;
}
//
// replaceVEGExpressions1() - a helper routine for ItemExpr::replaceVEGExpressions()
//
// NOTE: The code in this routine came from the previous version of
// ItemExpr::replaceVEGExpressions(). It has been pulled out
// into a separate routine so that the C++ compiler will produce
// code that needs signficantly less stack space for the
// recursive ItemExpr::replaceVEGExpressions() routine.
//
ItemExpr * ItemExpr::replaceVEGExpressions1( VEGRewritePairs* lookup )
{
// see if this expression is already in there
ValueId rewritten;
if (lookup->getRewritten(rewritten /* out */, getValueId()))
{
if (rewritten == NULL_VALUE_ID)
return NULL;
else
return rewritten.getItemExpr();
}
return (ItemExpr *)( (char *)(NULL) -1 ) ;
}
//
// replaceVEGExpressions2() - a helper routine for ItemExpr::replaceVEGExpressions()
//
// NOTE: The code in this routine came from the previous version of
// ItemExpr::replaceVEGExpressions(). It has been pulled out
// into a separate routine so that the C++ compiler will produce
// code that needs signficantly less stack space for the
// recursive ItemExpr::replaceVEGExpressions() routine.
//
void ItemExpr::replaceVEGExpressions2( Int32 index
, const ValueIdSet& availableValues
, const ValueIdSet& inputValues
, ValueIdSet& currAvailableValues
, const GroupAttributes * left_ga
, const GroupAttributes * right_ga
)
{
// If we have asked that the EquiPredicate resolve
// each child of the equipred by available values from the
// respectively input GAs, make sure we pick the right one.
// First we find out what GA covers the current EquiPred child
// we are processing (0 or 1), and pick the one that covers, unless
// both GAs do. If both GAs cover, the just make sure we pick a
// different one for each child. The hash join will later fix up
// the predicate expression to match its children.
// If none of the GAs covers, we have a problem...
// This fix was put in to solve solution: 10-100722-1962
ValueIdSet dummy;
NABoolean leftGaCovers = left_ga->covers(child(index)->getValueId(),
inputValues,
dummy);
NABoolean rightGaCovers = right_ga->covers(child(index)->getValueId(),
inputValues,
dummy);
if (leftGaCovers == FALSE && rightGaCovers == FALSE)
{
// for the moment it is assumed that this code is only
// executed for hash and merge joins, and in general each
// side of the expression should be coverd by a child.
// So if we have neither, we have a problem ..
cout << "Unable to pick GA to use: " << getArity() << endl;
CMPASSERT(FALSE);
}
else
{
const GroupAttributes *coveringGa = NULL;
currAvailableValues.clear();
currAvailableValues += inputValues;
if (leftGaCovers && rightGaCovers)
coveringGa = (index == 0 ? left_ga : right_ga);
else
coveringGa = (leftGaCovers ? left_ga : right_ga);
currAvailableValues += coveringGa->getCharacteristicOutputs();
}
}
// -----------------------------------------------------------------------
// ItemExpr::replaceVEGExpressions()
// It performs a top-down, left-to-right tree walk in the ItemExpr tree
// and expands any wildcards (VEGReference or VEGPredicate expressions)
// by replacing them with an expression that belongs to the
// availableValues.
// IF isKeyPredicate is TRUE then the ItemExpr is a KeyPredicate:
// A KeyPredicate is of a restricted form. If we are here it is
// because the predicate is a KeyPredicate. Then, it must satisfy
// very specific characteristics (see Key::isAKeyPredicate(...))
// for instance, one of its sides must be a key column
// This method *guarantees* that a key predicate will be
// generated from the rewritten predicate (i.e. we avoid
// cases like VegRef{T1.A, 2} > 7 being generated like
// 2 > 7 when T1.A is a key column.
// -----------------------------------------------------------------------
ItemExpr * ItemExpr::replaceVEGExpressions
(const ValueIdSet& availableValues,
const ValueIdSet& inputValues,
NABoolean thisIsAnMdamKeyPredicate,
VEGRewritePairs* lookup,
NABoolean replicateExpression,
const ValueIdSet * joinInputAndPotentialOutput,
const IndexDesc * iDesc,
const GroupAttributes * left_ga,
const GroupAttributes * right_ga)
{
// ---------------------------------------------------------------------
// If this expression has already been resolved because it exists in
// availableValues, the replacement of VEGReferences is not required.
// ---------------------------------------------------------------------
if (availableValues.contains(getValueId()))
return this; // terminate processing
ItemExpr* iePtr = this;
if (lookup && replicateExpression) // if lookup table is present
{
ItemExpr* tmpIePtr = ItemExpr::replaceVEGExpressions1( lookup ) ;
if ( tmpIePtr != (ItemExpr *)( (char *)(NULL) -1 ) )
return tmpIePtr ;
};
if (replicateExpression)
iePtr = copyTopNode(0, CmpCommon::statementHeap());
// virtual copy constructor
// -----------------------------------------------------------------------
// In the case of mdam key predicates we need to be careful with
// binary operators whose child is a VegRef that contains both a
// key column and a constant because the rewrite logic for VEGRef
// favors the generation of constants over other ItemExprs. In
// MDAM we *need* to generate the key column and not the constant.
// With the gated logic below we ensure this.
// -----------------------------------------------------------------------
if (thisIsAnMdamKeyPredicate)
{
#if DEBUG
// at the moment it is assumed the left and right ga's are only
// used for hash/merge joins equijoin predicates and with the
// mdamKeyPredicate flag turned off. If this assumption is no longer
// true we need to add some additional code in this "if" clause.
GENASSERT(left_ga == NULL && right_ga == NULL);
#endif
switch (getArity())
{
case 0: // const, VEGRef, and VEGPred have arity 0
break; // If it reached here it means that
// the ItemExpr does not need to do any special
// processing for this operator (i.e. a constant)
// VEG predicates should never reach here
case 1: // Example: T1.A IS NULL
{
ItemExpr *newChild;
// the child must be a key column:
newChild =
child(0)->replaceVEGExpressions(availableValues
,inputValues
,TRUE // no constants!
,lookup
,replicateExpression
,joinInputAndPotentialOutput
,iDesc
);
if (newChild != iePtr->child(0))
{
if (replicateExpression)
iePtr = iePtr->copyTopNode(NULL, CmpCommon::statementHeap());
iePtr->child(0) = newChild;
}
}
break;
case 2:
case 3:
{
// Rewrite children (one of them MUST be a key column, the
// other MUST be a constant or a host var)
ItemExpr
*leftChild = NULL,
*rightChild = NULL,
*thirdChild = NULL;
OperatorTypeEnum newOperType = getOperatorType();
if ((child(0)->getOperatorType() == ITM_VEG_REFERENCE)
OR
(child(1)->getOperatorType() == ITM_VEG_REFERENCE))
{
//---------------------------------------------------------
// Assume we have an expression of
// the form VegRef{T1.A, 2} > 7
//------------------------------------------------------
// Force the generation of a key column by
// telling replacevegexprs not to generate them:
leftChild =
child(0)->replaceVEGExpressions(availableValues
,inputValues
,TRUE // want key col
,lookup
,replicateExpression
,joinInputAndPotentialOutput
,iDesc
);
// generate a constant in this branch
rightChild =
child(1)->replaceVEGExpressions(availableValues
,inputValues
,FALSE // want constant
,lookup
,replicateExpression
,joinInputAndPotentialOutput
,iDesc
);
// However, the above will fail if the predicate is
// of the form
// 7 < VegRef{T1.A,2}, thus, if it failed, redrive with
// the roles reversed:
if (leftChild == NULL OR rightChild == NULL)
{
leftChild =
child(1)->replaceVEGExpressions(availableValues
,inputValues
,TRUE // want constant
,lookup
,replicateExpression
,joinInputAndPotentialOutput
,iDesc
);
rightChild =
child(0)->replaceVEGExpressions(availableValues
,inputValues
,FALSE // want key col
,lookup
,replicateExpression
,joinInputAndPotentialOutput
,iDesc
);
// We have reversed the operands, reverse
// the operator if it is a greater/eq BiRelat operator:
switch(getOperatorType())
{
case ITM_LESS:
case ITM_LESS_EQ:
case ITM_GREATER:
case ITM_GREATER_EQ:
// need to reverse!
newOperType =
((BiRelat*)iePtr)->getReverseOperatorType();
break;
}
} // if need to reverse operands
// now we must have succeeded!
CMPASSERT(leftChild != NULL && rightChild != NULL);
} // if one of the children of the operator is a reference
else
{
// No children are references, normal rewrite:
leftChild =
child(0)->replaceVEGExpressions(availableValues,
inputValues,
FALSE, // constants OK
lookup,
replicateExpression,
joinInputAndPotentialOutput,
iDesc);
rightChild =
child(1)->replaceVEGExpressions(availableValues,
inputValues,
FALSE, // constants OK
lookup,
replicateExpression,
joinInputAndPotentialOutput,
iDesc);
CMPASSERT(leftChild != NULL && rightChild != NULL);
}
if (getArity() == 3)
{ // rewrite the exclusion part of the PA key predicate:
thirdChild =
child(2)->replaceVEGExpressions(availableValues,
inputValues,
thisIsAnMdamKeyPredicate,
lookup,
replicateExpression,
joinInputAndPotentialOutput,
iDesc);
}
if (iePtr->child(0) != (void *)leftChild OR
iePtr->child(1) != (void *)rightChild OR
(thirdChild AND iePtr->child(2) != (void *)thirdChild) OR
iePtr->getOperatorType() != newOperType)
{
// we have to change data members, make a copy of the
// node if other users may share this node
if (replicateExpression)
iePtr = iePtr->copyTopNode(NULL, CmpCommon::statementHeap());
// Set the left and right children of the iePtr
// to their rewritten nodes:
// $$ What happens to all those nodes that were
// $$ replicated and the rewrite failed?
iePtr->child(0) = leftChild;
iePtr->child(1) = rightChild;
if (thirdChild)
iePtr->child(2) = thirdChild;
iePtr->setOperatorType(newOperType);
}
break;
} // case 2, case 3
default:
// $$ modify this when predicates of arity > 3 come into
// $$ existance
cout << "Invalid arity: " << getArity() << endl;
CMPASSERT(FALSE); // No predicates of arity > 3 (so far)
}
}
else // ItemExpr is not an mdam key predicate, go ahead with the rewrite:
for (Lng32 index = 0; index < getArity(); index++)
{
ValueIdSet currAvailableValues(availableValues);
if (left_ga != NULL &&
right_ga != NULL &&
getArity() == 2 )
{
ItemExpr::replaceVEGExpressions2( index
, availableValues
, inputValues
, currAvailableValues
, left_ga
, right_ga
) ;
}
ItemExpr *newChild = child(index)->replaceVEGExpressions(
currAvailableValues,
inputValues,
FALSE, // this is not a key predicate
lookup,
replicateExpression,
joinInputAndPotentialOutput,
iDesc);
if ( newChild->isPreCodeGenNATypeChanged())
iePtr->setpreCodeGenNATypeChangeStatus();
// is the result a different ItemExpr or does iePtr not point to
// the (possibly unchanged) result yet?
if (iePtr->child(index) != (void *)newChild)
{
if (iePtr == this AND replicateExpression)
{
// don't change "this" if it may be shared, make a
// copy instead and also copy the unchanged children
// so far
iePtr = iePtr->copyTopNode(NULL, CmpCommon::statementHeap());
for (Int32 j = 0; j < index; j++)
iePtr->child(j) = this->child(j);
}
iePtr->child(index) = newChild;
}
}
if(lookup && replicateExpression && iePtr != this)
{
iePtr->synthTypeAndValueId(FALSE);
lookup->insert(getValueId(), iePtr->getValueId());
}
return iePtr;
} // ItemExpr::replaceVEGExpressions()
// -----------------------------------------------------------------------
// ValueIdUnion::replaceVEGExpressions()
// The parameter replicateExpression is ignored because the
// ValueIdUnion implements a special policy for rewriting
// an ItemExpr, in that it manages three sets of values.
// -----------------------------------------------------------------------
ItemExpr * ValueIdUnion::replaceVEGExpressions
(const ValueIdSet& availableValues,
const ValueIdSet& inputValues,
NABoolean thisIsAnMdamKeyPredicate,
VEGRewritePairs* lookup,
NABoolean replicateExpression,
const ValueIdSet * joinInputAndPotentialOutput,
const IndexDesc * iDesc,
const GroupAttributes * left_ga,
const GroupAttributes * right_ga)
{
CMPASSERT(NOT thisIsAnMdamKeyPredicate); // sanity check
// we are ignoring the replicateExpression and
// joinInputAndPotentialOutput flags ..
ValueIdUnion * viduPtr = (ValueIdUnion *)this;
// ---------------------------------------------------------------------
// If this expression has already been resolved because it exists in
// availableValues, the replacement of VEGExpressions is not required.
// ---------------------------------------------------------------------
if (availableValues.contains(getValueId()) )
return this;
for(CollIndex i = 0; i < entries(); i++) {
viduPtr->
setSource(i,
(viduPtr->getSource(i).getItemExpr()
->replaceVEGExpressions(availableValues,inputValues,
thisIsAnMdamKeyPredicate,lookup,
FALSE, /* replicateExpression default */
NULL,/*joinInputAndPotentialOutput default*/
iDesc,
left_ga,
right_ga))
->getValueId());
}
// If the result is not this ValueIdUnion
if (viduPtr->getResult() != viduPtr->getValueId())
viduPtr->setResult((viduPtr->getResult().getItemExpr()
->replaceVEGExpressions(availableValues,
inputValues,
thisIsAnMdamKeyPredicate,
lookup,
FALSE,/*replicateExpression*/
NULL, /*joinInputAndPotentialOutput*/
iDesc,
left_ga,
right_ga))
->getValueId());
return this;
} // ValueIdUnion::replaceVEGExpressions()
// -----------------------------------------------------------------------
// VEGPredicate::replaceVEGExpressions()
// The parameter replicateExpression is ignored because the
// VEGPredicate implements a special policy for rewriting
// an ItemExpr. The policies are implemented by replaceVEGPredicate().
// -----------------------------------------------------------------------
ItemExpr * VEGPredicate::replaceVEGExpressions
(const ValueIdSet& availableValues,
const ValueIdSet& inputValues,
NABoolean /* thisIsAnMdamKeyPredicate*/,
VEGRewritePairs* lookup,
NABoolean /*replicateExpression*/,
const ValueIdSet * joinInputAndPotentialOutput,
const IndexDesc * iDesc,
const GroupAttributes * /* left_ga */,
const GroupAttributes * /* right_ga */)
{
// we ignore the thisIsAnMdamKeyPredicate flag, and so we also ignore the
// iDesc for VEGPredicates. No need to guarantee a keyColumn.
return replaceVEGPredicate(availableValues,inputValues,lookup,joinInputAndPotentialOutput);
} // VEGPredicate::replaceVEGExpressions()
// -----------------------------------------------------------------------
// VEGReference::replaceVEGExpressions()
// The parameter replicateExpression is ignored because the
// VEGReference implements a special policy for rewriting
// an ItemExpr. The policies are implemented by replaceVEGReference().
// -----------------------------------------------------------------------
ItemExpr * VEGReference::replaceVEGExpressions
(const ValueIdSet& availableValues,
const ValueIdSet& inputValues,
NABoolean thisIsAnMdamKeyPredicate,
VEGRewritePairs* /*lookup*/,
NABoolean /*replicateExpression*/,
const ValueIdSet * joinInputAndPotentialOutput,
const IndexDesc * iDesc,
const GroupAttributes * /* left_ga */ ,
const GroupAttributes * /* right_ga */ )
{
// we ignore the replicateExpression, lookup and
// joinInputAndPotentialOutput parameters.
return replaceVEGReference(availableValues,inputValues,
thisIsAnMdamKeyPredicate, iDesc);
} // VEGReference::replaceVEGExpressions()
// -----------------------------------------------------------------------
// ItemExpr::replaceOperandsOfInstantiateNull()
// This method is used by the code generator for replacing the
// operands of an ITM_INSTANTIATE_NULL with a value that belongs
// to availableValues.
// -----------------------------------------------------------------------
void ItemExpr::replaceOperandsOfInstantiateNull(
const ValueIdSet & availableValues,
const ValueIdSet & inputValues)
{
switch (getOperatorType())
{
case ITM_INSTANTIATE_NULL:
{
child(0) = child(0)->replaceVEGExpressions(availableValues,inputValues);
break;
}
default:
{
for (Lng32 i = 0; i < getArity(); i++)
{
child(i) = child(i)->replaceVEGExpressions(availableValues,
inputValues);
}
break;
}
}
} // ItemExpr::replaceOperandsOfInstantiateNull()
// -----------------------------------------------------------------------
// VEG::setBridgeValue()
// -----------------------------------------------------------------------
void VEG::setBridgeValue(const ValueId & bridgeValueId)
{
bridgeValues_ += bridgeValueId;
} // VEG::setBridgeValue()
// -----------------------------------------------------------------------
// VEG::markAsReferenced()
// Add a member of the set to the referenced values set to indicate
// that it has been used (at least once) in a "=" predicate that
// was generated by the code generator.
// -----------------------------------------------------------------------
void VEG::markAsReferenced(const ValueId & vid)
{
referencedValues_ += vid;
switch (vid.getItemExpr()->getOperatorType())
{
case ITM_INDEXCOLUMN:
// Also add the ValueId of the column from the base table, which is
// used as the key column for an index.
referencedValues_ += ((IndexColumn *)(vid.getItemExpr()))
->getDefinition();
break;
default:
break;
}
} // VEG::markAsReferenced()
// -----------------------------------------------------------------------
// VEGPredicate::replaceVEGPredicate
//
// This method is used by the code generator for replacing a
// reference to a VEGPredicate with an tree of equality predicates.
// Each equality predicate is between two values that belong to
// the VEG as well as to availableValues.
//
// Terminology :
// ***********
// VEG
// A ValueId Equality Group. It is a set of values such that its members
// have an equality predicate specified on them.
//
// availableValues
// This is the set of values that are available at the relational operator
// with which the VEGPredicate is associated. It is usually the set union
// of the Charactersitic Inputs of the operator with the Characteristic
// Outputs of each of its children.
//
// inputValues
// This is the set of values that is being provided to this node
// from above, and therefore is constant for each invocation of
// the operator when executing.
// This are good values to use to build key predicates.
//
// bridgeValues
// This is a set of values for which "=" predicates MUST be generated
// for correctness as well as to guarantee that transitivity is upheld.
// For example, the following query:
//
// select ax, by, cx, dy
// from (select A.x, B.y from A join B on A.x = B.y) T1(ax,by)
// join (select C.x, D.y from C join D on C.x = D.y) T2(cx,dy)
// on T1.ax = T2.cx
//
// shows two "islands" (self-contained pool of rows) defined by the
// derived tables T1 and T2 respectively. It is possible to deduce
// that A.x = D.y only after the predicate A.x = C.x has been applied.
// The values A.x, C.x establish the transitivity between the two
// islands. Such values are called inter-island links or bridge values.
//
// referencedValues
// A subset of the members of the VEG. Each member in this set is
// referenced in at least one "=" predicate that was generated by
// a call to replaceVEGPredicate.
//
// unboundValues
// The unbound values of a VEG are those that require an "="
// predicate to be generated between them. It is given by
// bridge values union available values intersect members of the VEG.
//
// Note that if the outputs of the join have already been resolved then
// joinInputAndPotentialOutput should really be joinInputAndOutputValues.
// All potential output values are no longer available, only the resolved
// values. Please see similar comment in Hashjoin::PrecodeGen.
// -----------------------------------------------------------------------
ItemExpr * VEGPredicate::replaceVEGPredicate(const ValueIdSet& origAvailableValues,
const ValueIdSet& origInputValues,
VEGRewritePairs* lookup,
const ValueIdSet * joinInputAndPotentialOutput)
{
// If we want processing to be idempotent, check to see if we have
// already written this VEGPredicate. And if so, return the rewritten
// result.
if (lookup) // if lookup table is present
{
// see if this expression is already in there
ValueId rewritten;
if (lookup->getRewritten(rewritten /* out */,getValueId()))
{
if (rewritten == NULL_VALUE_ID)
return NULL;
else
return rewritten.getItemExpr();
}
};
// We assume that inputValues is a (perhaps improper) subset of
// available values. Verify this.
ValueIdSet scratchPad;
scratchPad = origInputValues;
scratchPad -= origAvailableValues;
GenAssert(scratchPad.isEmpty(),"NOT scratchPad.isEmpty()");
// Replace VEGReferences in the members of this VEG.
// Copy values in the set and expand wild cards in the copy.
ValueIdSet vegMembers;
vegMembers.replaceVEGExpressionsAndCopy(getVEG()->getAllValues());
// Constants are not passed as input values but they are available.
// Have availableValues and availableInputs contain the VEG members
// that are constant values.
ValueIdSet availableValues = origAvailableValues;
ValueIdSet inputValues = origInputValues;
ValueIdSet vegConstants;
vegMembers.getConstants(vegConstants);
availableValues += vegConstants;
inputValues += vegConstants;
// If each member of this VEG is referenced in at least one "=" predicate
// that was generated here and there is only one "unbound" value remaining,
// then we are done. Terminate the generation of more "=" predicates.
if ( (vegMembers == getVEG()->getReferencedValues())
AND (getVEG()->getBridgeValues().entries() < 2) )
return NULL;
ItemExpr * rootPtr = NULL;
// We can only bind those values that are available here.
ValueIdSet valuesToBeBound = vegMembers;
valuesToBeBound.intersectSet(availableValues);
ValueIdSet unReferencedValues = vegMembers;
unReferencedValues -= getVEG()->getReferencedValues();
// Compute the set of values that are available, but
// are already referenced and are not a bridge value.
scratchPad = valuesToBeBound;
scratchPad -= unReferencedValues;
scratchPad -= getVEG()->getBridgeValues();
valuesToBeBound -= scratchPad;
// look for an invariant among the input values
ValueIdSet vegInputs = valuesToBeBound;
vegInputs.intersectSet(inputValues);
// If we didn't have any input values that were a member of the
// VEG then pick the invariant from the bridge Values
if (vegInputs.isEmpty())
{
vegInputs = valuesToBeBound;
vegInputs.intersectSet(getVEG()->getBridgeValues());
}
// If no input values are part of the VEG and there are
// no available bridge value then just pick any of the
// remaining (unreferenced) values
if (vegInputs.isEmpty())
{
vegInputs = valuesToBeBound;
}
// look for an invariant value
ValueId iterExprId, invariantExprId;
NABoolean invariantChosen = FALSE;
if (NOT vegInputs.isEmpty())
{
for (invariantExprId = vegInputs.init();
vegInputs.next(invariantExprId);
vegInputs.advance(invariantExprId))
{
//check if the item expr is a non-strict constant
//a strict constant is somethine like cos(1)
//where as cos(?p) can be considered a constant
//in the non-strict definition since it remains
//constant for a given execution of a query - Solution 10-020912-1647
if (invariantExprId.getItemExpr()->doesExprEvaluateToConstant(FALSE))
{
invariantChosen = TRUE;
break;
}
} // endfor
// if invariantExprId does not contain the ValueId of a constant value,
// then it must be initialized to contain any one value from
// the input values.
if (NOT invariantChosen)
{
if (vegInputs.entries() <= 1)
vegInputs.getFirst(invariantExprId);
else {
// The EXISTS query reported in case 10-091027-8459, soln
// 10-091028-5770 exposed a flaw in this code that used to
// implicitly assume that the first element of vegInputs is
// always a valid choice for an invariantExprId. When replacing
// a semijoin's VEGPredicate, the invariantExprId must be a
// member of that semijoin's characteristic output. Otherwise,
// *Join::preCodeGen hjp.replaceVEGExpressions() will silently
// delete that equijoin predicate and incorrectly generate a
// cartesian product.
scratchPad = vegInputs;
if (joinInputAndPotentialOutput) {
// for an outer join, joinInputAndPotentialOutput will have
// instantiate_null wrappers. intersectSetDeep digs into
// those wrappers.
scratchPad.intersectSetDeep(*joinInputAndPotentialOutput);
}
#ifdef _DEBUG
// we want to GenAssert here but regress/core/test027 raises
// a false alarm. So, for now, we don't.
// GenAssert(!scratchPad.isEmpty(),"vegInputs.isEmpty()");
#endif
if (scratchPad.isEmpty())
vegInputs.getFirst(invariantExprId);
else
scratchPad.getFirst(invariantExprId);
}
}
// remove it from further consideration
valuesToBeBound -= invariantExprId;
} // endif (NOT vegInputs.isEmpty())
else // have no values
{
// The predicate pushdown logic places predicates on those
// operators where it knows that values will be available
// for evaluating the predicate.
// If you have reached this point because of a bug,
// ****************************************************************
// DO NOT EVEN CONSIDER COMMENTING OUT THE FOLLOWING ASSERT.
// ****************************************************************
GenAssert(NOT valuesToBeBound.isEmpty(),"valuesToBeBound.isEmpty()");
// ****************************************************************
// YOU WILL BE DELIBERATELY MASKING OUT A SERIOUS BUG IF YOU
// DISABLE THE ASSERT STATEMENT ABOVE. DON'T TOUCH IT!
// ****************************************************************
}
if (valuesToBeBound.entries() >= 1)
{
// Replace this reference to the VEG with a tree of '=' predicates.
for (iterExprId = valuesToBeBound.init();
valuesToBeBound.next(iterExprId);
valuesToBeBound.advance(iterExprId))
{
rootPtr = buildComparisonPred
( rootPtr, iterExprId.getItemExpr(),
invariantExprId.getItemExpr(), ITM_EQUAL,
getSpecialNulls() //++MV - Irena
);
getVEG()->markAsReferenced(iterExprId);
}
}
else
{
// We have only the invariant. Generate an IS NOT NULL if it
// is nullable and has not been compared with someone else.
// MVs:
// If specialNulls option is set, nulls are values (null=null)
// and ITM_IS_NOT_NULL filters out some valid rows also.
// For more info on specialNulls -- see <ItemOther.h>
if (NOT getVEG()->getReferencedValues().contains(invariantExprId) &&
invariantExprId.getType().supportsSQLnull() &&
NOT getVEG()->getVEGPredicate()->getSpecialNulls() // ++MV - Irena
)
{
rootPtr = new(CmpCommon::statementHeap())
UnLogic(ITM_IS_NOT_NULL, invariantExprId.getItemExpr());
}
}
// mark as referenced the invariant. Make it the Bridge value
getVEG()->markAsReferenced(invariantExprId);
getVEG()->removeBridgeValues(valuesToBeBound);
getVEG()->setBridgeValue(invariantExprId);
// Assign a ValueId to the "=" and synthesize the type for the expression.
if (rootPtr != NULL) {
rootPtr->synthTypeAndValueId();
// If there is a lookup table, enter the rewritten tree in the table
if (lookup)
{
if (rootPtr)
lookup->insert(getValueId(),rootPtr->getValueId());
else
lookup->insert(getValueId(),NULL_VALUE_ID);
}
}
// Return the tree of '=' predicates (or NULL)
return rootPtr;
} // VEGPredicate::replaceVEGPredicate()
// -----------------------------------------------------------------------
// VEGReference::replaceVEGReference
// This method is used by the code generator. for replacing a
// VEGReference with one of its candidate values
// thisIsAnMdamKeyPredicate is FALSE by default. However, when
// key predicates are being rewritten, it should be set to TRUE
// when we need to guarantee that a key column must be generated by
// the veg reference.
// In this case,
// then bridge values MUST NOT be usen because we need to pick either
// a constant or a key column (depending on the child we are
// working on (see ItemExpr::replaceVEGExpressions(...))
// -----------------------------------------------------------------------
ItemExpr *
VEGReference::replaceVEGReference(const ValueIdSet &origAvailableValues,
const ValueIdSet &origInputValues,
NABoolean thisIsAnMdamKeyPredicate,
const IndexDesc *iDesc)
{
ItemExpr *result = NULL;
#ifndef _DEBUG
const NABoolean VEG_DEBUG = FALSE;
#else
NABoolean VEG_DEBUG = getenv("VEG_DEBUG") != NULL;
#endif
// We assume that inputValues is a (perhaps improper) subset of
// available values. Verify it.
ValueIdSet scratchPad;
scratchPad = origInputValues;
scratchPad -= origAvailableValues;
GenAssert(scratchPad.isEmpty(),"NOT scratchPad.isEmpty()");
// Copy values in the set and expand wild cards in the copy.
ValueIdSet valuesToBeBound;
valuesToBeBound.replaceVEGExpressionsAndCopy(getVEG()->getAllValues());
// Constants are not passed as input values but they are available
// Have availableValues and availableInputs contain the VEG members
// that are constant values
ValueIdSet availableValues = origAvailableValues;
ValueIdSet inputValues = origInputValues;
// --------------------------------------------------------------------
// Don't add constants if the caller don't want them to be generated
// from this vegref (i.e. when thisIsAnMdamKeyPredicate is TRUE)
// --------------------------------------------------------------------
ValueIdSet vegConstants;
valuesToBeBound.getConstants(vegConstants);
if (NOT thisIsAnMdamKeyPredicate)
{
availableValues += vegConstants;
inputValues += vegConstants;
}
if (VEG_DEBUG)
{
NAString av,iv,vb,vr;
availableValues.unparse(av);
inputValues.unparse(iv);
valuesToBeBound.unparse(vb);
ValueIdSet thisVegRef(getValueId());
thisVegRef.unparse(vr);
cout << endl;
cout << "VEGReference " << getValueId() << " (" << vr << "):" << endl;
cout << "AV: " << av << endl;
cout << "IV: " << iv << endl;
cout << "VB: " << vb << endl;
}
// -----------------------------------------------------------------------
//
// The commented out code implements a different resolution strategies
// for VEGReference. Inputs are no longer favored. This is in order to
// handle peculiar scenario where a predicate is not pushed down to the
// right hand side of a NJ even if it's covered because of the special
// semantics of the NJ itself (left join). The inputs from the operators
// in the right leg of the NJ shouldn't be used to resolve the output
// values since the VEGPred which relates the two hasn't been evaluated.
//
// This support is not ready yet for FCS, and therefore the code has been
// commented out.
// -----------------------------------------------------------------------
#if 0
// non-input available values:
ValueIdSet nonInputAvailableValues = availableValues;
nonInputAvailableValues -= inputValues;
#endif
// We can only bind those values that are available here.
valuesToBeBound.intersectSet(availableValues);
#if 0
// try using nonInputAvailableValues first.
ValueIdSet nonInputValuesToBeBound = valuesToBeBound;
nonInputValuesToBeBound.intersectSet(nonInputAvailableValues);
// try not to use input values since some predicate might not have
// be evaluated yet.
if ( (NOT thisIsAnMdamKeyPredicate) AND
(NOT nonInputValuesToBeBound.isEmpty()) )
{
// Try to pick a bridge value.
ValueIdSet candidateValues = nonInputValuesToBeBound;
candidateValues.intersectSet(getVEG()->getBridgeValues());
// If unsuccessful, try to pick any of the remaining unreferenced.
if (candidateValues.isEmpty())
{
candidateValues = nonInputValuesToBeBound;
}
CMPASSERT(NOT candidateValues.isEmpty());
ValueId resultVid;
candidateValues.getFirst(resultVid);
return resultVid.getItemExpr();
}
#endif
if (thisIsAnMdamKeyPredicate )
{
GenAssert(iDesc != NULL,"VEGReference::replaceVEGReference: Mdam KeyPredicates flag requires an iDesc to go with");
if (iDesc != NULL)
{
ValueIdSet keyCols = iDesc->getIndexKey();
for (ValueId exprId = keyCols.init();
keyCols.next(exprId);
keyCols.advance(exprId))
{
// pick the first value - assuming it is the key column..
if (valuesToBeBound.contains(exprId))
{
result = exprId.getItemExpr();
break;
}
}
}
if (result && NOT (result->getValueId().getType() == getValueId().getType()) )
result->setpreCodeGenNATypeChangeStatus();
return result; // A null is fine here.
}
// look for an invariant among the input values
ValueIdSet vegInputs = valuesToBeBound;
vegInputs.intersectSet(inputValues);
// If we didn't have any input values that were a member of the
// VEG then pick the invariant from the bridge Values
// Do not use bridge values for key predicates:
if ((NOT thisIsAnMdamKeyPredicate) && vegInputs.isEmpty())
{
vegInputs = valuesToBeBound;
vegInputs.intersectSet(getVEG()->getBridgeValues());
if (VEG_DEBUG)
{
NAString vb,br;
valuesToBeBound.unparse(vb);
// Stupid, ValueIdSet::unparse should be declared const;
// for now, just cast away constness...
ValueIdSet(getVEG()->getBridgeValues()).unparse(br);
cout << "VB: " << vb << endl;
cout << "BR: " << br << endl;
}
}
// If no input values are part of the VEG and there are
// no available bridge value then just pick any of the
// remaining (unreferenced) values
if (vegInputs.isEmpty())
{
vegInputs = valuesToBeBound;
}
// look for a constant value
ValueId invariantExprId;
NABoolean invariantChosen = FALSE;
if (NOT vegInputs.isEmpty())
{
for (invariantExprId = vegInputs.init();
vegInputs.next(invariantExprId);
vegInputs.advance(invariantExprId))
{
//check if the item expr is a non-strict constant
//a strict constant is somethine like cos(1)
//where as cos(?p) can be considered a constant
//in the non-strict definition since it remains
//constant for a given execution of a query - Solution 10-020912-1647
if (invariantExprId.getItemExpr()->doesExprEvaluateToConstant(FALSE))
{
invariantChosen = TRUE;
break;
}
} // endfor
// if invariantExprId does not contain the ValueId of a constant value,
// then it must be initialized to contain any one value from
// the input values.
if (NOT invariantChosen)
{
vegInputs.getFirst(invariantExprId);
}
// we found the invariant assign it!
result = invariantExprId.getItemExpr();
CMPASSERT(result != NULL);
} // endif (NOT vegInputs.isEmpty())
else // have no values
{
// It is ok for an MDAM key pred to not have valuesToBeBound because
// this is how ItemExpr::replaceVEGExpressions guarantees the generation of
// key predicates. It expects a NULL pointer sometimes
if (NOT thisIsAnMdamKeyPredicate)
{
// If there is a VEGReference to the value then a member of
// the VEG should be available.
GenAssert(NOT valuesToBeBound.isEmpty(),"valuesToBeBound.isEmpty()");
}
}
// result can be NULL only if thisIsAnMdamKeyPredicate is TRUE (see note above)
if (NOT thisIsAnMdamKeyPredicate)
{
CMPASSERT(result);
}
if (VEG_DEBUG)
{
// coverity cid 10004 thinks result may be null but we know it is not.
// coverity[var_deref_model]
cout << "Result: " << result->getValueId() << endl;
}
// see if NAType has changed, if so need to rebind it
if (result && NOT (result->getValueId().getType() == getValueId().getType()) )
{
result->setpreCodeGenNATypeChangeStatus();
}
return result;
} // VEGReference::replaceVEGReference()
// -----------------------------------------------------------------------
// RelExpr::getOutputValuesOfMyChilren()
// Accumulates the characteristic outputs of all my children for
// operators that have one or more children. Returns the
// potential output values for operators that can have no children.
// -----------------------------------------------------------------------
void RelExpr::getOutputValuesOfMyChildren(ValueIdSet & vs) const
{
ValueIdSet valueMask;
Lng32 nc = getArity();
if (nc > 0)
{
for (Lng32 i = 0; i < nc; i++)
{
valueMask += child(i)->getGroupAttr()->getCharacteristicOutputs();
}
}
else // if leaf operators, use all available values
{
getPotentialOutputValues(valueMask);
}
// Copy values in the set and expand wild cards in the copy.
vs.clear();
vs.replaceVEGExpressionsAndCopy(valueMask);
} // RelExpr::getOutputValuesOfMyChildren()
// -----------------------------------------------------------------------
// RelExpr::getInputValuesFromParentAndChildren()
// Uses getOutputValuesOfMyChildren() to collect the output values
// and adds the characteristic input values of this operator to them.
// -----------------------------------------------------------------------
void RelExpr::getInputValuesFromParentAndChildren(ValueIdSet & vs) const
{
getOutputValuesOfMyChildren(vs);
vs += getGroupAttr()->getCharacteristicInputs();
} // RelExpr::getInputValuesFromParentAndChildren()
// -----------------------------------------------------------------------
// RelExpr::getInputAndPotentialOutputValues()
// Uses getPotentialOutputs() to collect the output values
// and adds the characteristic input values of this operator to them.
// -----------------------------------------------------------------------
void RelExpr::getInputAndPotentialOutputValues(ValueIdSet & vs) const
{
ValueIdSet potentialOutputValues;
getPotentialOutputValues(potentialOutputValues);
potentialOutputValues += getGroupAttr()->getCharacteristicInputs();
vs.clear();
vs.replaceVEGExpressionsAndCopy(potentialOutputValues);
} // RelExpr::getInputAndPotentialOutputValues()
// -----------------------------------------------------------------------
// GenericUpdate::replaceVEGExpressionsAndGet...
// -----------------------------------------------------------------------
void GenericUpdate::getInputValuesFromParentAndChildren(ValueIdSet & vs) const
{
ValueIdSet updTableCols;
ValueIdSet vs2;
updTableCols.insertList (getIndexDesc()->getIndexColumns());
// updTableCols.insertList(getTableDesc()->getColumnVEGList());
vs2.replaceVEGExpressionsAndCopy(updTableCols);
getOutputValuesOfMyChildren(vs);
vs += getGroupAttr()->getCharacteristicInputs();
vs += vs2;
} // GenericUpdate::getInputValuesFromParentAndChildren()
// -----------------------------------------------------------------------
// HbaseDelete::replaceVEGExpressionsAndGet...
// -----------------------------------------------------------------------
void HbaseDelete::getInputValuesFromParentAndChildren(ValueIdSet & vs) const
{
// Do not include IndexColumn as the input values. Otherwise, we will
// have duplicated predicates in Executor predicate in HbaseDelete.
getOutputValuesOfMyChildren(vs);
vs += getGroupAttr()->getCharacteristicInputs();
} // HbaseDelete::getInputValuesFromParentAndChildren()
// -----------------------------------------------------------------------
// RelExpr::preCodeGen()
//
// RelExpr * result
// OUT: a node that calls preCodeGen for its child should replace
// that child with the result value. This allows preCodeGen
// to transform the RelExpr tree. Examples for such trans-
// formations are additional exchange nodes for repartitioning.
// Generator * generator
// INOUT: a global work area with useful helper methods
// const ValueIdSet & externalInputs
// IN: a value id set with values that already have been
// replaced such that they don't contain VEGies any more.
// Use this set to replace VEGies for expressions that depend
// on the characteristic inputs of the node.
// ValueIdSet & pulledNewInputs
// OUT: a set of value ids that the node wants to add to its
// characteristic inputs ("pull" from its parent). There are
// several cases in which we need to add value ids to
// characteristic inputs during preCodeGen:
// a) partition input variables for parallel execution,
// b) the COMMON datetime function which needs to be generated
// by the root node,
// c) an "open cursor timestamp" that helps a materialize node
// to decide whether it can reuse its materialized table.
// -----------------------------------------------------------------------
RelExpr * RelExpr::preCodeGen(Generator * generator,
const ValueIdSet & externalInputs,
ValueIdSet &pulledNewInputs)
{
if (nodeIsPreCodeGenned())
return this;
// Check if the pivs of this operator and it's child are the same.
// If they are not, make them the same.
replacePivs();
// Resolve the VEGReferences and VEGPredicates, if any, that appear
// in the Characteristic Inputs, in terms of the externalInputs.
getGroupAttr()->resolveCharacteristicInputs(externalInputs);
// My Characteristic Inputs become the external inputs for my children.
Int32 nc = getArity();
for (Int32 index = 0; index < nc; index++)
{
ValueIdSet childPulledInputs;
child(index) = child(index)->preCodeGen(generator,
externalInputs,
childPulledInputs);
if (! child(index).getPtr())
return NULL;
// process additional input value ids the child wants
getGroupAttr()->addCharacteristicInputs(childPulledInputs);
pulledNewInputs += childPulledInputs;
}
// The VEG expressions in the selection predicates and the characteristic
// outputs can reference any expression that is either a potential output
// or a characteristic input for this RelExpr. Supply these values for
// rewriting the VEG expressions.
ValueIdSet availableValues;
getInputAndPotentialOutputValues(availableValues);
// Rewrite the selection predicates.
NABoolean replicatePredicates = TRUE;
selectionPred().replaceVEGExpressions
(availableValues,
getGroupAttr()->getCharacteristicInputs(),
FALSE, // no need to generate key predicates here
0 /* no need for idempotence here */,
replicatePredicates
);
getGroupAttr()->resolveCharacteristicOutputs
(availableValues,
getGroupAttr()->getCharacteristicInputs());
markAsPreCodeGenned();
return this;
} // RelExpr::preCodeGen
//
// Recuvsively call the method on each RelExpr node, accumulating
// # of rows from each node.
//
void RelExpr::prepareDopReduction(Generator* generator)
{
pcgEspFragment* currentEspFragmentPCG = generator->getCurrentEspFragmentPCG();
if ( currentEspFragmentPCG )
currentEspFragmentPCG->accumulateRows(getEstRowsUsed());
Int32 nc = getArity();
for (Lng32 i = 0; i < nc; i++)
{
child(i)->prepareDopReduction(generator);
}
}
void Exchange::prepareDopReduction(Generator* generator)
{
pcgEspFragment* parentEspFragPCG = generator->getCurrentEspFragmentPCG();
//
// Save the current pcg fragment and add myself as the child to it.
//
if ( parentEspFragPCG ) {
parentEspFragPCG->accumulateRows(getEstRowsUsed());
parentEspFragPCG->addChild(this);
}
//
// Let the global pointer point at my pcg esp fragment (for the
// fragment rooted at me). Do this only for above-DP2 Exchanges.
// Note a PA is represented by an Exchange with "execute in Master or ESP"
// as location. So a PA exchange with a SCAN as a child will have an empty
// childPcgEsp array.
//
generator->setCurrentEspFragmentPCG
(
(getPhysicalProperty()->getPlanExecutionLocation() != EXECUTE_IN_DP2)
? getEspFragPCG() : NULL
);
child(0)->prepareDopReduction(generator);
//
// Restore the pcg esp fragment
//
generator->setCurrentEspFragmentPCG(parentEspFragPCG);
// Try to reduce the dop and if it fails, invalidate any proposed
// dop reductions for this.
//
if ( parentEspFragPCG &&
parentEspFragPCG ->tryToReduceDoP() == FALSE )
parentEspFragPCG->invalidate();
}
RelExpr * RelRoot::preCodeGen(Generator * generator,
const ValueIdSet & /* externalInputs */,
ValueIdSet &pulledNewInputs)
{
if (nodeIsPreCodeGenned())
return this;
// For all the inputVars, if it is with UNKNOWN data type, make it a
// varchar type. This is from SQL/MP extension. Example query
// select ?p1 from any-table;
if (isTrueRoot()) {
CollIndex i;
ValueId vid;
ValueIdList vidList = inputVars();
for ( i=0; i < vidList.entries(); i++ )
if ((vid=vidList[i]).getType().getTypeQualifier() == NA_UNKNOWN_TYPE) {
vid.coerceType(NA_CHARACTER_TYPE);
}
}
// if root has GET_N indication set, insert a FirstN node.
// Usually this transformation is done in the binder, but in
// some special cases it is not.
// For example, if there is an 'order by' in the query, then
// the Sort node is added by the optimizer. In this case, we
// want to add the FirstN node on top of the Sort node and not
// below it. If we add the FirstN node in the binder, the optimizer
// will add the Sort node on top of the FirstN node. Maybe we
// can teach optimizer to do this.
if ((getFirstNRows() != -1) || (getFirstNRowsParam()))
{
// As of JIRA TRAFODION-2840, the binder always adds the FirstN,
// except in the case of output rowsets. So, the only time we
// should now be going through this code is a SELECT query using
// output rowsets + FirstN + ORDER BY. A follow-on JIRA,
// TRAFODION-2924, will take care of that case and delete this code.
// (As a matter of design, it is highly undesireable to sometimes
// create the FirstN in the Binder and sometimes in the Generator;
// that means that any FirstN-related semantic checks in the
// intervening passes will need two completely separate
// implementations.)
RelExpr * firstn = new(generator->wHeap()) FirstN(child(0),
getFirstNRows(),
needFirstSortedRows(),
getFirstNRowsParam());
// move my child's attributes to the firstN node.
// Estimated rows will be mine.
firstn->setEstRowsUsed(getEstRowsUsed());
firstn->setMaxCardEst(getMaxCardEst());
firstn->setInputCardinality(child(0)->getInputCardinality());
firstn->setPhysicalProperty(child(0)->getPhysicalProperty());
firstn->setGroupAttr(child(0)->getGroupAttr());
//10-060516-6532 -Begin
//When FIRSTN node is created after optimization phase, the cost
//of that node does not matter.But, display_explain and explain
//show zero operator costs and rollup cost which confuses the user.
//Also, the VQP crashes when cost tab for FIRSTN node is selected.
//So, creating a cost object will fix this.
//The operator cost is zero and rollup cost is same as it childs.
Cost* firstnNodecost = new HEAP Cost();
firstn->setOperatorCost(firstnNodecost);
Cost* rollupcost = (Cost *)(child(0)->getRollUpCost());
*rollupcost += *firstnNodecost;
firstn->setRollUpCost(rollupcost);
//10-060516-6532 -End
setChild(0, firstn);
// reset firstN indication in the root node.
setFirstNRows(-1);
setFirstNRowsParam(NULL);
}
if (isTrueRoot())
{
// Set the internal format to use for the plan being generated ...
// Checks the CQD COMPRESSED_INTERNAL_FORMAT to decide whether to use
// SQLARK_EXPLODED_FORMAT or SQLMX_ALIGNED_FORMAT as the internal
// data format
// When the CIF CQD is set to SYSTEM we decide whether to use aligned or exploded format
// as the tuple format for the whole query. In precodeGEn we visit all the copy
// operators (Hash join, hash group by, exchange and sort) in a query
// tree and keep a count of the nodes that are in favor of aligned format and those
// that are in favor of exploded format.
// The final decision about the tuple format for the whole query will depend on those two
// numbers. if the number of nodes in favor of aligned format is greater than those
// in favor of exploded than aligned format is select otherwise exploded is selected
// The function that determine the format for each of the copy operators + relroot
// is determineInternalFormat(..) is is called in the precodeGen of the copy operators
generator->initNNodes();
isCIFOn_ = FALSE;
if ((CmpCommon::getDefault(COMPRESSED_INTERNAL_FORMAT) == DF_ON )||
generator->isFastExtract() ||
generator->containsFastExtract())
{
isCIFOn_ = TRUE;
generator->setCompressedInternalFormat();
}
else
if ( CmpCommon::getDefault(COMPRESSED_INTERNAL_FORMAT) == DF_OFF )
{
generator->setExplodedInternalFormat();
}
else
{
NABoolean resize = FALSE;
NABoolean considerBufferDefrag = FALSE;
ValueIdSet vidSet = child(0)->getGroupAttr()->getCharacteristicOutputs();
ExpTupleDesc::TupleDataFormat tupleFormat =
generator->determineInternalFormat(
vidSet,
this,
resize,
RelExpr::CIF_SYSTEM,
FALSE,
considerBufferDefrag);
if (tupleFormat == ExpTupleDesc::SQLMX_ALIGNED_FORMAT)
{
generator->incNCIFNodes();
}
else
{
generator->decNCIFNodes();
}
}
//generator->setInternalFormat();
// Some operators will revert the internal format back to exploded format
// when they are directly under the root node - such as the top level ESPs,
// Sort, and HJ operators.
// This is so there is no bottleneck in the master flipping the data back
// to exploded format (required for bulk move out).
child(0)->setParentIsRoot( TRUE );
// create a list of NATypes corresponding to each entry in the
// userColumnList_ in RETDesc. Used by generator to convert to
// this type during output expr code gen.
// The value ids in userColumnList_ cannot be used as the type
// corresponding to that value id may change due to VEG transformation
// in the preCodeGen phase.
if (getRETDesc()->createNATypeForUserColumnList(CmpCommon::statementHeap()))
{
// error case.
GenAssert(FALSE, "error from createNATypeForUserColumnList.");
}
if ( (child(0)->getOperatorType() == REL_EXCHANGE) &&
(child(0)->child(0)->getOperatorType() == REL_COMPOUND_STMT) )
{
((Exchange *)((RelExpr *)child(0)))->setDP2TransactionIndicator( TRUE );
}
}
unsigned short prevNumBMOs = 0;
CostScalar prevBMOsMemoryUsage;
if (isTrueRoot())
{
if (oltOptInfo().oltAnyOpt())
{
if (treeContainsEspExchange())
{
// turn off oltQueryOptimization if the query plan contains an
// esp_exchange.
// 10-070316-3325: childOperType_ = REL_UNARY_DELETE
// 10-080118-9942: select query contains esp_exchange that is
// not directly under root.
oltOptInfo().setOltOpt(FALSE);
}
else if (childOperType() == REL_SCAN)
{
// if this was a scan query to start with but is no longer
// a scan query(which means it got transformed to join, etc),
// then turn off oltQueryOptimization.
RelExpr *childExpr = child(0)->castToRelExpr();
if (childExpr->getOperatorType() == REL_FIRST_N)
childExpr = childExpr->child(0)->castToRelExpr();
if ((childExpr->getOperatorType() != REL_EXCHANGE) &&
(childExpr->getOperatorType() != REL_HBASE_ACCESS))
oltOptInfo().setOltCliOpt(FALSE);
}
} // oltAnyOpt
*generator->oltOptInfo() = oltOptInfo();
if (generator->oltOptInfo()->oltAnyOpt())
{
// Also, PubSub streams' STREAM_TIMEOUT not handled by opt'd root
if (getGroupAttr()->isStream())
{
generator->oltOptInfo()->setOltCliOpt(FALSE);
}
if (CmpCommon::getDefault(EID_SPACE_USAGE_OPT) == DF_ON)
{
generator->setDoEidSpaceUsageOpt(TRUE);
}
else
{
generator->setDoEidSpaceUsageOpt(FALSE);
}
// olt opt not chosen if ALL stats are being collected.
// We may support this case later.
// In case of operator stats, don't disable OLT optimization
// But, when the query is OLT optimized, switch it to pertable stats
if ((generator->computeStats()) &&
((generator->collectStatsType() == ComTdb::ALL_STATS)))
generator->oltOptInfo()->setOltOpt(FALSE);
if (CmpCommon::getDefault(OLT_QUERY_OPT) == DF_OFF)
generator->oltOptInfo()->setOltOpt(FALSE);
// In the case of an embedded insert,
// do not execute the query OLT optimized.
if (getGroupAttr()->isEmbeddedInsert())
generator->oltOptInfo()->setOltMsgOpt(FALSE);
#ifdef _DEBUG
if (getenv("NO_OLT_QUERY_OPT"))
generator->oltOptInfo()->setOltOpt(FALSE);
#endif
if (generator->oltOptInfo()->oltEidOpt())
{
generator->oltOptInfo()->setOltEidLeanOpt(FALSE);
if (generator->doEidSpaceUsageOpt())
{
generator->oltOptInfo()->setOltEidLeanOpt(TRUE);
}
}
if (CmpCommon::getDefault(OLT_QUERY_OPT_LEAN) == DF_OFF)
generator->oltOptInfo()->setOltEidLeanOpt(FALSE);
} // oltAnyOpt
// mark exchange operator for maxOneRow optimization.
RelExpr *childExpr = child(0)->castToRelExpr();
NABoolean doMaxOneRowOpt = TRUE;
NABoolean doMaxOneInputRowOpt = FALSE;
NABoolean firstN = FALSE;
RelExpr *exchExpr = NULL;
if (NOT generator->doEidSpaceUsageOpt())
{
doMaxOneRowOpt = FALSE;
doMaxOneInputRowOpt = FALSE;
}
else
{
doMaxOneRowOpt = TRUE;
doMaxOneInputRowOpt = TRUE;
}
if (childExpr->getOperatorType() == REL_FIRST_N)
{
firstN = TRUE;
if (((FirstN *)childExpr)->getFirstNRows() != 1)
doMaxOneRowOpt = FALSE;
childExpr = childExpr->child(0)->castToRelExpr();
}
if ((childExpr->getOperatorType() != REL_EXCHANGE) ||
(childExpr->child(0)->castToRelExpr()->
getPhysicalProperty()->getPlanExecutionLocation() != EXECUTE_IN_DP2))
{
doMaxOneRowOpt = FALSE;
doMaxOneInputRowOpt = FALSE;
}
else
{
exchExpr = childExpr;
childExpr = childExpr->child(0)->castToRelExpr();
if (NOT childExpr->getOperator().match(REL_FORCE_ANY_SCAN))
{
doMaxOneInputRowOpt = FALSE;
}
else if (childExpr->getOperatorType() == REL_FILE_SCAN)
{
FileScan * s = (FileScan *)childExpr;
if (NOT firstN)
doMaxOneRowOpt = FALSE;
if ((s->getGroupAttr()->isStream()) ||
(s->accessOptions().accessType() == TransMode::SKIP_CONFLICT_ACCESS_))
{
//doMaxOneInputRowOpt = FALSE;
//doMaxOneRowOpt = FALSE;
}
}
}
if (doMaxOneRowOpt)
{
exchExpr->oltOptInfo().setMaxOneRowReturned(TRUE);
}
if (doMaxOneInputRowOpt)
{
exchExpr->oltOptInfo().setMaxOneInputRow(TRUE);
}
generator->setUpdErrorInternalOnError(FALSE);
if (rollbackOnError())
generator->setUpdErrorOnError(FALSE);
else
generator->setUpdErrorOnError(TRUE);
if (CmpCommon::getDefault(UPD_ABORT_ON_ERROR) == DF_ON)
generator->setUpdAbortOnError(TRUE);
else
generator->setUpdAbortOnError(FALSE);
if (CmpCommon::getDefault(UPD_PARTIAL_ON_ERROR) == DF_ON)
generator->setUpdPartialOnError(TRUE);
else
generator->setUpdPartialOnError(FALSE);
if (CmpCommon::getDefault(UPD_SAVEPOINT_ON_ERROR) == DF_ON)
generator->setUpdSavepointOnError(TRUE);
else
generator->setUpdSavepointOnError(FALSE);
generator->setSkipUnavailablePartition(FALSE);
if ((childOperType() == REL_SCAN) &&
(CmpCommon::getDefault(SKIP_UNAVAILABLE_PARTITION) == DF_ON))
generator->setSkipUnavailablePartition(TRUE);
if (avoidHalloween_)
{
// At beginning of preCodeGen, assume DP2Locks will be
// used. The NestedJoin::preCodeGen will change this
// if its left child is a sort.
generator->setHalloweenProtection(Generator::DP2LOCKS);
}
if (generator->getBindWA()->getUdrStoiList().entries () > 0)
generator->setAqrEnabled(FALSE);
// Reset the accumulated # of BMOs for the fragment
prevNumBMOs = generator->replaceNumBMOs(0);
} // true root
// propagate the need to return top sorted N rows to all sort
// nodes in the query.
if (needFirstSortedRows() == TRUE)
{
needSortedNRows(TRUE);
}
// Delete any VEGReference that appear in the Characteristic Inputs.
// The Characteristic Inputs of the root of the execution plan MUST
// only contain external dataflow inputs that are provided by the
// user. The VEGReferences may have been introduced as a side-effect
// of predicate pushdown. They are redundant in the Characteristic
// Inputs of the root.
ValueIdSet availableValues;
for (ValueId exprId = getGroupAttr()->getCharacteristicInputs().init();
getGroupAttr()->getCharacteristicInputs().next(exprId);
getGroupAttr()->getCharacteristicInputs().advance(exprId) )
{
if (exprId.getItemExpr()->getOperatorType() != ITM_VEG_REFERENCE)
availableValues += exprId;
}
getGroupAttr()->setCharacteristicInputs(availableValues);
// If this is the root for a parallel extract producer query then
// there should be an Exchange node immediately below and we need to
// set a flag in that Exchange.
if (numExtractStreams_ > 0)
{
if (child(0)->getOperatorType() == REL_EXCHANGE)
{
Exchange *e = (Exchange *) child(0)->castToRelExpr();
e->setExtractProducerFlag();
}
// fix for soln 10-090506-1407: parallel extract for a union distinct
// can sometimes have root->mapvalueidsl->exchange. It should be OK.
else if (child(0)->getOperatorType() == REL_MAP_VALUEIDS &&
child(0)->child(0)->getOperatorType() == REL_EXCHANGE)
{
Exchange *e = (Exchange *) child(0)->child(0)->castToRelExpr();
e->setExtractProducerFlag();
}
}
//
// If there is no hard requirement on #ESPs, reduce the dop based on
// the total # of rows processed per ESP. The reduction can modify
// the number of partitions attribute of the partition function stored
// in the synthesized physical property of an Exchange operator.
//
// CQD DOP_REDUCTION_ROWCOUNT_THRESHOLD set to 0.0 will disable the
// feature.
float threshold;
ActiveSchemaDB()->
getDefaults().getFloat(DOP_REDUCTION_ROWCOUNT_THRESHOLD, threshold);
if ( threshold > 0.0 && CURRSTMT_OPTDEFAULTS->getRequiredESPs() <= 0 ) {
generator->setCurrentEspFragmentPCG(NULL); // reset the 'global'
// to the current esp frag.
RelExpr::prepareDopReduction(generator);
RelExpr::doDopReduction();
}
// Now walk through the execution plan and initialize it for code generation.
child(0) = child(0)->preCodeGen(generator,
getGroupAttr()->getCharacteristicInputs(),
pulledNewInputs);
if (! child(0).getPtr())
return NULL;
if (! RelExpr::preCodeGen(
generator,
getGroupAttr()->getCharacteristicInputs(),
pulledNewInputs))
return NULL;
if ( isTrueRoot() && CmpCommon::getDefault(COMPRESSED_INTERNAL_FORMAT) == DF_SYSTEM)
{
if (generator->getNCIFNodes()>0)
{
isCIFOn_ = TRUE;
generator->setCompressedInternalFormat();
}
else
{
generator->setExplodedInternalFormat();
isCIFOn_ = FALSE;
}
}
// If the RelRoot is marked as a parallel extract producer then the
// root's child must be an Exchange and the child must also be
// marked for parallel extract. Even though we checked the type of
// the child a few lines above, we do it again here because the call
// to RelExpr::preCodeGen can potentially eliminate Exchange nodes.
NABoolean extractPlanLooksOK = TRUE;
if (numExtractStreams_ > 0)
{
if (child(0)->getOperatorType() == REL_EXCHANGE)
{
Exchange *e = (Exchange *) child(0)->castToRelExpr();
if (!e->getExtractProducerFlag())
extractPlanLooksOK = FALSE;
}
// fix for soln 10-090506-1407: parallel extract for a union distinct
// can sometimes have root->mapvalueidsl->exchange. It should be OK.
else if (child(0)->getOperatorType() == REL_MAP_VALUEIDS &&
child(0)->child(0)->getOperatorType() == REL_EXCHANGE)
{
Exchange *e = (Exchange *) child(0)->child(0)->castToRelExpr();
if (!e->getExtractProducerFlag())
extractPlanLooksOK = FALSE;
}
else
{
extractPlanLooksOK = FALSE;
}
if (!extractPlanLooksOK)
{
*CmpCommon::diags() << DgSqlCode(-7004);
GenExit();
return NULL;
}
}
// Accumulate the values that are provided as inputs by my parent
// together with the values that are produced as outputs by my
// children. Use these values for rewriting the VEG expressions.
getInputValuesFromParentAndChildren(availableValues);
// Rebuild the computable expressions using a bridge value, if possible
compExpr().replaceVEGExpressions
(availableValues, getGroupAttr()->getCharacteristicInputs());
// Rebuild the required order
reqdOrder().replaceVEGExpressions
(availableValues, getGroupAttr()->getCharacteristicInputs());
// Rebuild the pkey list
pkeyList().replaceVEGExpressions
(availableValues, getGroupAttr()->getCharacteristicInputs());
// add internally generated inputs to the input vars and make sure that
// the root isn't left with "pulled" input values that aren't "internal"
// inputs (the assert will most likely fire for leftover partition input
// variables)
inputVars().insertSet(generator->getInternalInputs());
pulledNewInputs -= (ValueIdSet) inputVars();
GenAssert(pulledNewInputs.isEmpty(),"root can't produce these values");
// Do not rollback on error for INTERNAL REFRESH commands.
if (isRootOfInternalRefresh())
{
generator->setUpdErrorInternalOnError(TRUE);
generator->setUpdAbortOnError(FALSE);
generator->setUpdPartialOnError(FALSE);
generator->setUpdSavepointOnError(FALSE);
}
// do not abort transaction for internal compiles, even if abort
// is needed for this statement.
// Catman depends on no abort for individual IUD stmts.
// It aborts the transaction when it gets an error from cli.
if ( ( CmpCommon::context()->internalCompile() == CmpContext::INTERNAL_MODULENAME ) ||
( CmpCommon::statement()->isSMDRecompile() )
)
{
generator->setUpdErrorInternalOnError(TRUE);
generator->setUpdAbortOnError(FALSE);
generator->setUpdPartialOnError(FALSE);
generator->setUpdSavepointOnError(FALSE);
}
oltOptInfo().setOltCliOpt(generator->oltOptInfo()->oltCliOpt());
if ((isTrueRoot()) &&
(CmpCommon::getDefault(LAST0_MODE) == DF_ON) &&
(child(0)))
{
OperatorTypeEnum op = child(0)->getOperatorType();
if (op != REL_DESCRIBE &&
op != REL_EXPLAIN &&
op != REL_DDL &&
op != REL_LOCK &&
op != REL_UNLOCK &&
op != REL_SET_TIMEOUT &&
op != REL_STATISTICS &&
op != REL_TRANSACTION &&
op != REL_EXE_UTIL)
{
// do not return any rows at runtime.
// Setting of -2 tells executor to simulate [last 0]
// without having to put [last 0] in the query.
setFirstNRows(-2);
}
}
if (isTrueRoot())
{
// if warnings 6008 or 6011 were raised, set missingStats indication.
if (CmpCommon::diags()->containsWarning(SINGLE_COLUMN_STATS_NEEDED) ||
CmpCommon::diags()->containsWarning(SINGLE_COLUMN_STATS_NEEDED_AUTO))
{
generator->compilerStatsInfo().setMissingStats(TRUE);
}
// change the following number(16) to whatever is considered 'large'.
//#define LARGE_NUMBER_OF_JOINS 16
//if (generator->compilerStatsInfo().totalJoins() > LARGE_NUMBER_OF_JOINS)
//generator->compilerStatsInfo().setLargeNumOfJoins(TRUE);
// set mandatoryXP indication in generator.
if (hasMandatoryXP())
generator->compilerStatsInfo().setMandatoryCrossProduct(TRUE);
// Remember # of BMOs that children's preCodeGen found for my fragment.
setNumBMOs( generator->replaceNumBMOs(prevNumBMOs) );
// Compute the total available memory quota for BMOs
NADefaults &defs = ActiveSchemaDB()->getDefaults();
// total per node
double m = defs.getAsDouble(BMO_MEMORY_LIMIT_PER_NODE_IN_MB) * (1024*1024);
generator->setBMOsMemoryLimitPerNode(m);
}
if (isTrueRoot())
{
if (generator->isAqrWnrInsert())
{
ExeUtilWnrInsert * wi = new(generator->getBindWA()->wHeap())
ExeUtilWnrInsert(generator->utilInsertTable(),
child(0)->castToRelExpr());
child(0)->markAsBound();
wi->bindNode(generator->getBindWA());
if (generator->getBindWA()->errStatus())
return NULL;
// Use the same characteristic inputs and outputs as my child
wi->setGroupAttr(new(generator->wHeap())
GroupAttributes(*(child(0)->getGroupAttr())));
//pass along some of the estimates
wi->setEstRowsUsed(child(0)->getEstRowsUsed());
wi->setMaxCardEst(child(0)->getMaxCardEst());
wi->setInputCardinality(child(0)->getInputCardinality());
wi->setPhysicalProperty(child(0)->getPhysicalProperty());
wi->setOperatorCost(0);
wi->setRollUpCost(child(0)->getRollUpCost());
if (! wi->preCodeGen(generator,
getGroupAttr()->getCharacteristicInputs(),
pulledNewInputs))
return NULL;
child(0) = wi;
}
}
// if blob values are being selected out, retrieve them and return them either in file
// or as a stream
if (isTrueRoot())
{
RETDesc * rd = getRETDesc();
const ColumnDescList * cdl = rd->getColumnList();
for (CollIndex i = 0; i < compExpr().entries(); i++)
{
ValueId val_id = compExpr()[i];
ItemExpr * expr = val_id.getItemExpr();
if ((val_id.getType().isLob()))/* &&
((expr->getOperatorType() == ITM_BASECOLUMN) ||
(expr->getOperatorType() == ITM_INDEXCOLUMN)))*/
{
LOBconvertHandle * lc = new(generator->wHeap())
LOBconvertHandle(val_id.getItemExpr(), LOBoper::STRING_);
lc->bindNode(generator->getBindWA());
lc->preCodeGen(generator);
compExpr().removeAt(i);
compExpr().insertAt(i, lc->getValueId());
ColumnDesc *cd = (*cdl)[i];
NAColumn * col = cd->getValueId().getNAColumn(TRUE);
if (col)
{
lc->lobNum() = col->lobNum();
lc->lobStorageType() = col->lobStorageType();
lc->lobStorageLocation() = col->lobStorageLocation();
}
cd->setValueId(lc->getValueId());
rd->changeNATypeForUserColumnList(i, &lc->getValueId().getType());
}
} // for
if (getPredExprTree())
{
getPredExprTree()->preCodeGen(generator);
}
} // isTrueRoot
setHdfsAccess(generator->hdfsAccess());
generator->finetuneBMOEstimates();
markAsPreCodeGenned();
#ifdef _DEBUG
if(getenv("SHOW_PLAN"))
{
NAString plan;
unparse(plan);
printf("PLAN: %s\n",convertNAString(plan,generator->wHeap()));
}
#endif
return this;
} // RelRoot::preCodeGen
RelExpr * Join::preCodeGen(Generator * generator,
const ValueIdSet & externalInputs,
ValueIdSet &pulledNewInputs)
{
if (nodeIsPreCodeGenned())
return this;
// Check if the pivs of this operator and it's child are the same.
// If they are not, make them the same.
replacePivs();
// In the case of an embedded insert,
// and there is a selection predicate,
// we need to retrieve the stored available outputs
// from the GenericUpdate group attr.
ValueIdSet availableGUOutputs;
// clear any prefix sort key
generator->clearPrefixSortKey();
if (getGroupAttr()->isEmbeddedInsert() &&
!selectionPred().isEmpty() &&
getArity() > 1)
{
if (child(1)->getArity() > 0)
child(1)->child(0)->getInputAndPotentialOutputValues(availableGUOutputs);
}
NABoolean isALeftJoin = (getOperator().match(REL_ANY_LEFT_JOIN));
NABoolean isARightJoin = (getOperator().match(REL_ANY_RIGHT_JOIN));
ValueIdSet availableValues;
ValueIdSet childPulledInputs;
if (isALeftJoin)
{
ValueId instNullId, exprId, vid;
// Prune the nullInstatiatedOutputs list.Retain only those values
// that are either:
// 1) The external dataflow inputs to the Join.
// 2) The Characteristic Outputs of the Join.
// 3) The Characteristic Outputs of the first child of the Join.
// 4) Values required for evaluating the selection expression
// on the Join.
// Discard all other values.
availableValues = getGroupAttr()->getCharacteristicInputs();
availableValues += child(0)->getGroupAttr()->getCharacteristicOutputs();
ValueIdSet discardSet;
CollIndex ne = nullInstantiatedOutput().entries();
for (CollIndex j = 0; j < ne; j++)
{
instNullId = nullInstantiatedOutput_[j];
GenAssert(instNullId.getItemExpr()->getOperatorType() == ITM_INSTANTIATE_NULL,"NOT instNullId.getItemExpr()->getOperatorType() == ITM_INSTANTIATE_NULL");
// Access the operand of the InstantiateNull
exprId = (((InstantiateNull *)(instNullId.getItemExpr()))
->getExpr()->getValueId());
if ( (NOT availableValues.contains(exprId)) AND
(NOT getGroupAttr()->getCharacteristicOutputs()
.referencesTheGivenValue(instNullId, vid)) AND
(NOT selectionPred().referencesTheGivenValue(instNullId, vid)) )
{
discardSet += nullInstantiatedOutput_[j];
}
}
// Delete all those elements that do not require null instantiation.
for (exprId = discardSet.init(); discardSet.next(exprId);
discardSet.advance(exprId))
{
nullInstantiatedOutput_.remove(exprId);
}
} // endif (getOperator().match(REL_ANY_LEFT_JOIN))
else // Null Instantiation will not be necessary.
nullInstantiatedOutput().clear(); // clear in case a LJ was transformed to an IJ
if (isARightJoin)
{
ValueId instNullIdForRightJoin, exprIdForRightJoin, vidForRightJoin;
ValueIdSet discardSetForRightJoin;
// Prune the nullInstatiatedOutputs list.Retain only those values
// that are either:
// 1) The external dataflow inputs to the Join.
// 2) The Characteristic Outputs of the Join.
// 3) The Characteristic Outputs of the second child of the Join.
// 4) Values required for evaluating the selection expression
// on the Join.
// Discard all other values.
availableValues = getGroupAttr()->getCharacteristicInputs();
availableValues += child(1)->getGroupAttr()->getCharacteristicOutputs();
CollIndex neR = nullInstantiatedForRightJoinOutput().entries();
for (CollIndex j = 0; j < neR; j++)
{
instNullIdForRightJoin = nullInstantiatedForRightJoinOutput_[j];
GenAssert(instNullIdForRightJoin.getItemExpr()->getOperatorType() == ITM_INSTANTIATE_NULL,"NOT instNullId.getItemExpr()->getOperatorType() == ITM_INSTANTIATE_NULL");
// Access the operand of the InstantiateNull
exprIdForRightJoin = (((InstantiateNull *)(instNullIdForRightJoin.getItemExpr()))
->getExpr()->getValueId());
if ( (NOT availableValues.contains(exprIdForRightJoin)) AND
(NOT getGroupAttr()->getCharacteristicOutputs()
.referencesTheGivenValue(instNullIdForRightJoin,
vidForRightJoin)) AND
(NOT selectionPred().referencesTheGivenValue(instNullIdForRightJoin,
vidForRightJoin)) )
{
discardSetForRightJoin += nullInstantiatedForRightJoinOutput_[j];
}
}
// Delete all those elements that do not require null instantiation.
for (exprIdForRightJoin = discardSetForRightJoin.init();
discardSetForRightJoin.next(exprIdForRightJoin);
discardSetForRightJoin.advance(exprIdForRightJoin))
{
nullInstantiatedForRightJoinOutput_.remove(exprIdForRightJoin);
}
} // endif (getOperator().match(REL_ANY_RIGHT_JOIN))
else // Null Instantiation will not be necessary.
nullInstantiatedForRightJoinOutput().clear(); // clear in case a LJ was transformed to an IJ
// Resolve the VEGReferences and VEGPredicates, if any, that appear
// in the Characteristic Inputs, in terms of the externalInputs.
getGroupAttr()->resolveCharacteristicInputs(externalInputs);
availableValues = getGroupAttr()->getCharacteristicInputs();
bool precodeHalloweenLHSofTSJ = false;
bool savePrecodeHalloweenLHSofTSJ = false;
if ((getHalloweenForceSort() != NO_SELF_REFERENCE) &&
(generator->getR251HalloweenPrecode()))
{
savePrecodeHalloweenLHSofTSJ =
generator->setPrecodeHalloweenLHSofTSJ(true);
precodeHalloweenLHSofTSJ = true;
if (getHalloweenForceSort() == FORCED)
generator->setHalloweenSortForced();
}
NABoolean savedOltMsgOpt = generator->oltOptInfo()->oltMsgOpt();
// My Characteristic Inputs become the external inputs for my left child.
child(0) = child(0)->preCodeGen(generator,availableValues,childPulledInputs);
if (! child(0).getPtr())
return NULL;
// For HashJoin Min/Max optimization
if (isHashJoin())
{
HashJoin *hj = (HashJoin *)this;
for(CollIndex i = hj->getStartMinMaxIndex(); i < hj->getEndMinMaxIndex(); i++)
{
// A scan may have decided to use the min/max values that
// belongs to this join, remove them from the
// childPulledInputs. We do not need to pull them from the
// parent as this Hash Join will generate them.
if(generator->getWillUseMinMaxKeys()[i] != NULL_VALUE_ID) {
childPulledInputs -= generator->getMinVals()[i];
childPulledInputs -= generator->getMaxVals()[i];
}
// Clear the candidate values generated by this HashJoin, We
// are done with the left child, so no one else can use
// these values.
generator->getMinMaxKeys()[i] = NULL_VALUE_ID;
generator->getMinVals()[i] = NULL_VALUE_ID;
generator->getMaxVals()[i] = NULL_VALUE_ID;
}
// if we have both equi join preds and a beforejoin pred
// Set a flag that will cause beforeJoinPred to be evaluated prior
// join equi pred during execution. This helps with join explosion
// if there are frequent matching values and the beforeJoinPred is
// highly selective. There is no downside to evaluating beforeJoinPred
// early, if it contains vids from outer only
if (!(getEquiJoinPredicates().isEmpty() || getJoinPred().isEmpty() ||
isAntiSemiJoin()))
{
ValueIdSet coveredPreds, dummy2, dummy3, uncoveredPreds ;
child(0)->getGroupAttr()->coverTest(getJoinPred(),
getGroupAttr()->getCharacteristicInputs(),
coveredPreds, dummy2, NULL,
&uncoveredPreds);
// set the flag only if all the non-equi-join preds are covered
if ((getJoinPred().entries() == coveredPreds.entries()) &&
uncoveredPreds.isEmpty())
setBeforeJoinPredOnOuterOnly();
}
}
if (precodeHalloweenLHSofTSJ)
{
generator->setPrecodeHalloweenLHSofTSJ(savePrecodeHalloweenLHSofTSJ);
if (generator->getUnblockedHalloweenScans() == 0)
{
// Turn off DP2_LOCKS for codeGen, using either the FORCED_SORT
// or PASSIVE values.
if (getHalloweenForceSort() == FORCED)
{
generator->setHalloweenProtection(Generator::FORCED_SORT);
}
else
generator->setHalloweenProtection(Generator::PASSIVE);
}
else if (updateSelectValueIdMap() && updateTableDesc() &&
(NOT updateTableDesc()->getNATable()->getClusteringIndex()->hasSyskey()))
{
// if the key columns of the table being inserted into are
// equal to constants or inputs then no sort is required
// to enforce Halloween blocking. Example statements are
// update tt set a = 1 ;(a is the primary key for table tt)
// insert into tt select * from tt where a = 1 ;
ValueIdList reqdOrder ;
updateSelectValueIdMap()->rewriteValueIdListDown(
updateTableDesc()->getClusteringIndex()->getOrderOfKeyValues(),
reqdOrder);
reqdOrder.removeCoveredExprs(
getGroupAttr()->getCharacteristicInputs());
if (reqdOrder.isEmpty())
{
generator->setHalloweenProtection(Generator::PASSIVE);
}
}
}
NABoolean leftMultipleRowsReturned =
generator->oltOptInfo()->multipleRowsReturned();
// if nested join and left child could return multiple rows, then
// disable olt msg opt for the right child. This is done since
// olt msg opt can only handle input and output of max 1 row.
if ((getOperatorType() == REL_NESTED_JOIN) ||
(getOperatorType() == REL_LEFT_NESTED_JOIN) ||
(getOperatorType() == REL_NESTED_SEMIJOIN) ||
(getOperatorType() == REL_NESTED_ANTI_SEMIJOIN) ||
(getOperatorType() == REL_NESTED_JOIN_FLOW))
{
if (generator->oltOptInfo()->multipleRowsReturned())
{
generator->oltOptInfo()->setOltMsgOpt(FALSE);
}
}
// process additional input value ids the child wants
// (see RelExpr::preCodeGen())
getGroupAttr()->addCharacteristicInputs(childPulledInputs);
pulledNewInputs += childPulledInputs;
availableValues += childPulledInputs;
childPulledInputs.clear();
// If this is a tuple substitution join that is implemented by the nested join
// method, then the values produced as output by my left child can be used as
// "external" inputs by my right child.
NABoolean replicatePredicates = TRUE;
ValueIdSet joinInputAndPotentialOutput;
getInputAndPotentialOutputValues(joinInputAndPotentialOutput);
if (isTSJ() || beforeJoinPredOnOuterOnly())
{
availableValues += child(0)->getGroupAttr()->getCharacteristicOutputs();
// For a TSJ the joinPred() is a predicate between the inputs
// and the first child that could not be pushed down to the first
// child because it is either a left join or an anti-semi-join
// if beforeJoinPredOnOuterOnly is true, then we have an outer join
// and an other_join_predicate that has values from the outer side alone
// We do not need the inner row to evaluate the other_join_predicate
// in this case.
// Rebuild the join predicate tree now
joinPred().replaceVEGExpressions
(availableValues,
getGroupAttr()->getCharacteristicInputs(),
FALSE, // no key predicates here
0 /* no need for idempotence here */,
replicatePredicates,
NULL /* not a groupByAgg */,
&joinInputAndPotentialOutput
);
}
bool didSetRHS = false;
bool saveSetRHS = false;
if (generator->getPrecodeHalloweenLHSofTSJ() &&
isNestedJoin())
{
saveSetRHS = generator->setPrecodeRHSofNJ(true);
didSetRHS = true;
}
// Process the right child
child(1) = child(1)->preCodeGen(generator,availableValues,childPulledInputs);
if (! child(1).getPtr())
return NULL;
if (didSetRHS)
generator->setPrecodeRHSofNJ(saveSetRHS);
NABoolean rightMultipleRowsReturned =
generator->oltOptInfo()->multipleRowsReturned();
if (leftMultipleRowsReturned || rightMultipleRowsReturned)
generator->oltOptInfo()->setMultipleRowsReturned(TRUE);
// process additional input value ids the child wants
// (see RelExpr::preCodeGen())
getGroupAttr()->addCharacteristicInputs(childPulledInputs);
pulledNewInputs += childPulledInputs;
// Accumulate the values that are provided as inputs by my parent
// together with the values that are produced as outputs by my
// children. Use these values for rewriting the VEG expressions.
getInputValuesFromParentAndChildren(availableValues);
// Rebuild the join predicate tree, for the general case where both inner
// and outer row is needed to evaluate the predicate.
if (! (isTSJ() || beforeJoinPredOnOuterOnly()))
joinPred().replaceVEGExpressions
(availableValues,
getGroupAttr()->getCharacteristicInputs(),
FALSE, // no key predicates here
0 /* no need for idempotence here */,
replicatePredicates,
NULL /* not a groupByAgg */,
&joinInputAndPotentialOutput
);
if (isALeftJoin)
{
// Replace the operands of the ITM_INSTANTIATE_NULL with values from
// the Characteristic Outputs of the right child.
// The following values are available for resolving the nullInstantiatedOuptut
// 1) The external dataflow inputs to the Join.
// 2) The Characteristic Outputs of the second (right) child of the Join.
// 3) The Characteristic Outputs of the first(left)child of the Join.
// Needed when nested_join plan is chosen.
availableValues = getGroupAttr()->getCharacteristicInputs();
availableValues += child(1)->getGroupAttr()->getCharacteristicOutputs();
availableValues += child(0)->getGroupAttr()->getCharacteristicOutputs();
nullInstantiatedOutput_.replaceOperandsOfInstantiateNull
(availableValues,
getGroupAttr()->getCharacteristicInputs());
}
if (isARightJoin)
{
// Replace the operands of the ITM_INSTANTIATE_NULL with values from
// the Characteristic Outputs of the left child.
// The following values are available for resolving the nullInstantiatedForRightJoinOutput
// 1) The external dataflow inputs to the Join.
// 2) The Characteristic Outputs of the first (left) child of the Join.
availableValues = getGroupAttr()->getCharacteristicInputs();
availableValues += child(0)->getGroupAttr()->getCharacteristicOutputs();
nullInstantiatedForRightJoinOutput_.replaceOperandsOfInstantiateNull
(availableValues,
getGroupAttr()->getCharacteristicInputs());
}
// The VEG expressions in the selection predicates and the characteristic
// outputs can reference any expression that is either a potential output
// or a characteristic input for this RelExpr. Supply these values for
// rewriting the VEG expressions.
getInputAndPotentialOutputValues(availableValues);
// If this is an embedded insert, with a selection predicate,
// add in the characteristic outputs from the generic update RelExpr
if (getGroupAttr()->isEmbeddedInsert() &&
!selectionPred().isEmpty())
{
availableValues += availableGUOutputs;
}
// Rebuild the selection predicate tree.
selectionPred().replaceVEGExpressions
(availableValues,
getGroupAttr()->getCharacteristicInputs(),
FALSE, // no need for key predicates here
0 /* no need for idempotence here */,
replicatePredicates
);
//New code was added to avoid the following situation:
//
// Query: select max(t1.a) from t1,t2 where t1.a = t2.b;
// Plan: shortcut_groupby
// |
// esp_exchange
// |
// merge_join in parallel 4 ways on
// |
// | |
// scan t2 scan T1
//
// By the time we get to precodegen merge_join has orderby
// on VEG(a,b) and characteristic output VEG(a,b)
// because scan T2 get precode gen'd first it resolves its
// orderby VEG(a,b) to t2.b this also changes orderby VEG
// in merge_join and thereafter to T2.b. Now when merge join
// resolves it characteristic output it resolves it to T1.a because
// T1 is first in the from clause and T1.a has a smaller value id and
// so the combined set of T1. and T2's characteristic output has T1.a
// in front of T2.b. Now esp_exchange during code gen time expects
// T2.b to be characteristic output of the child because it needs to
// do merge of sorted streams of its orderby value which is T2.b.
// this causes an assertion failure because merge_join produces T1.a.
// Following code counters that by making sure that if the sort key is
// part of the available values then characteristic output first gets
// resolved by sortkey then by rest of the available values.
//
ValueIdSet sortKey = getPhysicalProperty()->getSortKey();
sortKey = sortKey.simplifyOrderExpr();
sortKey.intersectSet(availableValues);
if(sortKey.entries())
{
ValueIdSet reqOutput = getGroupAttr()->getCharacteristicOutputs();
ValueIdSet copyOfSet(reqOutput);
ValueIdSet inputValues;
ValueIdSet newExpr;
ItemExpr * iePtr;
// ---------------------------------------------------------------------
// Iterate over the predicate factors in the given predicate tree.
// ---------------------------------------------------------------------
for (ValueId exprId = copyOfSet.init();
copyOfSet.next(exprId); copyOfSet.advance(exprId))
{
// -----------------------------------------------------------------
// Walk through the item expression tree and replace any
// VEGPredicates or VEGReferences that are found.
// -----------------------------------------------------------------
iePtr = exprId.getItemExpr()->replaceVEGExpressions(availableValues,
inputValues,
FALSE,
NULL,
FALSE);
if (iePtr) // expression was not discarded
{
iePtr->synthTypeAndValueId(TRUE); // redrive type synthesis
if (iePtr != exprId.getItemExpr()) // a replacement was done
{
reqOutput.subtractElement(exprId); // remove existing ValueId
reqOutput += iePtr->getValueId(); // replace with a new one
}
}
} // loop over predTree
getGroupAttr()->setCharacteristicOutputs(reqOutput);
}
// Rewrite the Characteristic Outputs.
getGroupAttr()->resolveCharacteristicOutputs
(availableValues,
getGroupAttr()->getCharacteristicInputs());
// propagate the children olt settings in case of a pushed down to dp2 NLJ
if ( !getPhysicalProperty()->executeInDP2() OR
!(generator->getBindWA()->getTopRoot()->getInliningInfo()).isUsedForMvLogging()
)
{
generator->oltOptInfo()->setOltMsgOpt(savedOltMsgOpt);
}
// In the case of an embedded insert,
// set the generator is embedded insert flag to TRUE.
if (getGroupAttr()->isEmbeddedInsert())
generator->setEmbeddedInsert(TRUE) ;
markAsPreCodeGenned();
// Done.
return this;
} // Join::preCodeGen()
RelExpr * GenericUtilExpr::preCodeGen(Generator * generator,
const ValueIdSet & externalInputs,
ValueIdSet &pulledNewInputs)
{
if (nodeIsPreCodeGenned())
return this;
if (! RelExpr::preCodeGen(generator,externalInputs,pulledNewInputs))
return NULL;
if (xnNeeded())
{
generator->setUpdSavepointOnError(FALSE);
generator->setUpdPartialOnError(FALSE);
}
markAsPreCodeGenned();
// Done.
return this;
}
RelExpr * ExeUtilExpr::preCodeGen(Generator * generator,
const ValueIdSet & externalInputs,
ValueIdSet &pulledNewInputs)
{
if (nodeIsPreCodeGenned())
return this;
if (! GenericUtilExpr::preCodeGen(generator,externalInputs,pulledNewInputs))
return NULL;
if (NOT aqrSupported())
generator->setAqrEnabled(FALSE);
markAsPreCodeGenned();
// Done.
return this;
}
// xnCanBeStarted is set to true if the whole ddl operation can run in one transaction
// It is set to false, then the DDL implementation methods manages the transaction
short DDLExpr::ddlXnsInfo(NABoolean &isDDLxn, NABoolean &xnCanBeStarted)
{
ExprNode * ddlNode = getDDLNode();
xnCanBeStarted = TRUE;
// When the DDL transaction is not turned on via CQD
if (NOT ddlXns())
{
if ((dropHbase()) ||
(purgedata()) ||
(initHbase()) ||
(createMDViews()) ||
(dropMDViews()) ||
(initAuth()) ||
(dropAuth()) ||
(createRepos()) ||
(dropRepos()) ||
(upgradeRepos()) ||
(addSchemaObjects()) ||
(updateVersion()))
{
// transaction will be started and commited in called methods.
xnCanBeStarted = FALSE;
}
if (((ddlNode) && (ddlNode->castToStmtDDLNode()) &&
(NOT ddlNode->castToStmtDDLNode()->ddlXns())) &&
((ddlNode->getOperatorType() == DDL_DROP_SCHEMA) ||
(ddlNode->getOperatorType() == DDL_CLEANUP_OBJECTS) ||
(ddlNode->getOperatorType() == DDL_ALTER_TABLE_ADD_CONSTRAINT_PRIMARY_KEY) ||
(ddlNode->getOperatorType() == DDL_ALTER_TABLE_ALTER_COLUMN_SET_SG_OPTION) ||
(ddlNode->getOperatorType() == DDL_CREATE_INDEX) ||
(ddlNode->getOperatorType() == DDL_POPULATE_INDEX) ||
(ddlNode->getOperatorType() == DDL_CREATE_TABLE) ||
(ddlNode->getOperatorType() == DDL_ALTER_TABLE_DROP_COLUMN) ||
(ddlNode->getOperatorType() == DDL_ALTER_TABLE_ALTER_COLUMN_DATATYPE) ||
(ddlNode->getOperatorType() == DDL_DROP_TABLE)))
{
// transaction will be started and commited in called methods.
xnCanBeStarted = FALSE;
}
isDDLxn = FALSE;
}
else // When the DDL transaction is turned on
{
isDDLxn = FALSE;
if (ddlNode && ddlNode->castToStmtDDLNode() &&
ddlNode->castToStmtDDLNode()->ddlXns())
isDDLxn = TRUE;
if (purgedata() || upgradeRepos())
// transaction will be started and commited in called methods.
xnCanBeStarted = FALSE;
if (initHbase() && producesOutput()) // returns status
xnCanBeStarted = FALSE;
if ((ddlNode && ddlNode->castToStmtDDLNode() &&
ddlNode->castToStmtDDLNode()->ddlXns()) &&
((ddlNode->getOperatorType() == DDL_CLEANUP_OBJECTS) ||
(ddlNode->getOperatorType() == DDL_ALTER_TABLE_DROP_COLUMN) ||
(ddlNode->getOperatorType() == DDL_ALTER_SCHEMA) ||
(ddlNode->getOperatorType() == DDL_CREATE_INDEX) ||
(ddlNode->getOperatorType() == DDL_POPULATE_INDEX) ||
(ddlNode->getOperatorType() == DDL_ALTER_TABLE_ALTER_COLUMN_DATATYPE) ||
(ddlNode->getOperatorType() == DDL_ALTER_TABLE_ADD_CONSTRAINT_PRIMARY_KEY) ||
(ddlNode->getOperatorType() == DDL_ALTER_TABLE_ALTER_HBASE_OPTIONS) ||
(ddlNode->getOperatorType() == DDL_ALTER_INDEX_ALTER_HBASE_OPTIONS) ||
(ddlNode->getOperatorType() == DDL_ALTER_TABLE_RENAME) ||
(ddlNode->getOperatorType() == DDL_ON_HIVE_OBJECTS)))
{
// transaction will be started and commited in called methods.
xnCanBeStarted = FALSE;
}
}
return 0;
}
RelExpr * DDLExpr::preCodeGen(Generator * generator,
const ValueIdSet & externalInputs,
ValueIdSet &pulledNewInputs)
{
if (nodeIsPreCodeGenned())
return this;
if (! GenericUtilExpr::preCodeGen(generator,externalInputs,pulledNewInputs))
return NULL;
if ((specialDDL()) ||
(initHbase()))
{
generator->setAqrEnabled(FALSE);
}
NABoolean startXn = FALSE;
NABoolean ddlXns = FALSE;
if (ddlXnsInfo(ddlXns, startXn))
return NULL;
if (ddlXns && startXn)
xnNeeded() = TRUE;
else
xnNeeded() = FALSE;
markAsPreCodeGenned();
// Done.
return this;
}
RelExpr * NestedJoinFlow::preCodeGen(Generator * generator,
const ValueIdSet &externalInputs,
ValueIdSet &pulledNewInputs)
{
if (nodeIsPreCodeGenned())
return this;
/* child(0) = child(0)->preCodeGen(
generator,
externalInputs,
pulledNewInputs);
if (! child(0).getPtr())
return NULL;
*/
RelExpr * nj =
NestedJoin::preCodeGen(generator, externalInputs, pulledNewInputs);
if (nj == NULL)
return NULL;
return nj;
}
RelExpr * NestedJoin::preCodeGen(Generator * generator,
const ValueIdSet &externalInputs,
ValueIdSet &pulledNewInputs)
{
if (nodeIsPreCodeGenned())
return this;
NABoolean espExchangeWithMerge = FALSE;
NABoolean childIsBlocking = FALSE;
if ((getHalloweenForceSort() != NO_SELF_REFERENCE) &&
(!generator->getR251HalloweenPrecode()))
{
GenAssert(Generator::NOT_SELF_REF != generator->getHalloweenProtection(),
"Inconsistency in Generator and NestedJoin.");
// Look for either of two patterns on the left hand side:
// sort or exchange+sort.
if (child(0)->getOperatorType() == REL_SORT)
childIsBlocking = TRUE;
else
if ((child(0)->getOperatorType() == REL_EXCHANGE) &&
(child(0)->child(0)->getOperatorType() == REL_SORT))
{
childIsBlocking = TRUE;
// The espExchangeWithMerge flag is used to conditionally
// assert that the exchange will merge. The assertion
// is deferred until after preCodeGen on the left subtree,
// because the Exchange::doesMerge() method should not be
// called until Exchange::preCodeGen is finished.
espExchangeWithMerge = TRUE;
}
if (childIsBlocking)
{
if (getHalloweenForceSort() == FORCED)
{
if (espExchangeWithMerge)
((Sort *)(child(0)->child(0).getPtr()))->
markAsHalloweenProtection();
else
((Sort *)(child(0).getPtr()))->markAsHalloweenProtection();
generator->setHalloweenProtection(Generator::FORCED_SORT);
}
else
generator->setHalloweenProtection(Generator::PASSIVE);
}
else if (updateSelectValueIdMap() && updateTableDesc() &&
(NOT updateTableDesc()->getNATable()->getClusteringIndex()->hasSyskey()))
{
// if the key columns of the table being inserted into are
// equal to constants or inputs then no sort is required
// to enforce Halloween blocking. Example statements are
// update tt set a = 1 ;(a is the primary key for table tt)
// insert into tt select * from tt where a = 1 ;
ValueIdList reqdOrder ;
updateSelectValueIdMap()->rewriteValueIdListDown(
updateTableDesc()->getClusteringIndex()->getOrderOfKeyValues(),
reqdOrder);
reqdOrder.removeCoveredExprs(
getGroupAttr()->getCharacteristicInputs());
if (reqdOrder.isEmpty())
{
generator->setHalloweenProtection(Generator::PASSIVE);
}
}
}
// Insert a probe cache above the inner table if applicable
if ( isProbeCacheApplicable(
castToRelExpr()->getPhysicalProperty()->getPlanExecutionLocation()
)
)
{
ProbeCache *probeCache =
new (generator->wHeap()) ProbeCache(
child(1), getDefault(GEN_PROBE_CACHE_NUM_ENTRIES),
generator->wHeap());
// look for an aggregate right child node
RelExpr *rightChildExpr = child(1).getPtr();
GroupByAgg *rightChildGrby = NULL;
RelExpr *rightChildExch = NULL;
MapValueIds *rightChildMvi = NULL;
ValueIdMap *optionalMap = NULL;
NABoolean done = FALSE;
while (!done)
{
if (rightChildExpr->getOperator().match(REL_ANY_GROUP))
{
rightChildGrby = (GroupByAgg *) rightChildExpr;
done = TRUE;
}
else if (rightChildExpr->getOperator() == REL_EXCHANGE)
{
if (rightChildExch == NULL)
rightChildExch = rightChildExpr;
else
done = TRUE; // can't handle more than one exchange
}
else if (rightChildExpr->getOperator() == REL_MAP_VALUEIDS)
{
if (rightChildMvi == NULL)
{
rightChildMvi = (MapValueIds *) rightChildExpr;
optionalMap = &rightChildMvi->getMap();
}
else
done = TRUE; // can't handle more than one MVI
}
else
done = TRUE;
if (!done)
rightChildExpr = rightChildExpr->child(0);
}
// Among other things, this will give the probeCache
// the characteristic inputs and outputs of the
// inner table.
probeCache->setGroupAttr(new(generator->wHeap())
GroupAttributes(*(child(1)->getGroupAttr())));
// Try to pull up predicates from the child, if that reduces
// the char. inputs sent to the child. We only try this right
// now if the child is an aggregate or groupby.
if (rightChildGrby &&
CmpCommon::getDefault(NESTED_JOIN_CACHE_PREDS) != DF_OFF &&
(// if right child exchange exists, it must have same char inputs
rightChildExch == NULL ||
rightChildExch->getGroupAttr()->getCharacteristicInputs() ==
rightChildGrby->getGroupAttr()->getCharacteristicInputs()) &&
(rightChildMvi == NULL ||
rightChildMvi->getGroupAttr()->getCharacteristicInputs() ==
rightChildGrby->getGroupAttr()->getCharacteristicInputs()))
{
ValueIdSet pcAvailableInputs(
probeCache->getGroupAttr()->getCharacteristicInputs());
// predicates can refer to both char. inputs and outputs
pcAvailableInputs +=
probeCache->getGroupAttr()->getCharacteristicOutputs();
// note that this will overwrite the ProbeCache's selection preds
rightChildGrby->tryToPullUpPredicatesInPreCodeGen(
pcAvailableInputs,
probeCache->selectionPred(),
optionalMap);
// adjust char. inputs of intervening nodes - this is not
// exactly good style, just overwriting the char. inputs, but
// hopefully we'll get away with it at this stage in the
// processing
if (rightChildExch)
rightChildExch->getGroupAttr()->setCharacteristicInputs(
rightChildGrby->getGroupAttr()->getCharacteristicInputs());
if (rightChildMvi)
rightChildMvi->getGroupAttr()->setCharacteristicInputs(
rightChildGrby->getGroupAttr()->getCharacteristicInputs());
}
// propagate estimates, physical properties, and costings
// from the child to the ProbeCache:
probeCache->setEstRowsUsed(child(1)->getEstRowsUsed());
probeCache->setMaxCardEst(child(1)->getMaxCardEst());
probeCache->setInputCardinality(child(1)->getInputCardinality());
probeCache->setPhysicalProperty(child(1)->getPhysicalProperty());
probeCache->setOperatorCost(0);
probeCache->setRollUpCost(child(1)->getRollUpCost());
// Glue the ProbeCache to the NestedJoin's right leg.
child(1) = probeCache;
}
if (isTSJForUndo())
{
Sort *sortNode = new(generator->wHeap()) Sort(child(0));
ItemExpr *sk = new (generator->wHeap()) SystemLiteral(1);
sk->synthTypeAndValueId(TRUE);
ValueIdList skey;
skey.insert(sk->getValueId());
sortNode->getSortKey() = skey;
// Use the same characteristic inputs and outputs as the left child
sortNode->setGroupAttr(new(generator->wHeap())
GroupAttributes(*(child(0)->getGroupAttr())));
//pass along some of the estimates
sortNode->setEstRowsUsed(child(0)->getEstRowsUsed());
sortNode->setMaxCardEst(child(0)->getMaxCardEst());
sortNode->setInputCardinality(child(0)->getInputCardinality());
sortNode->setPhysicalProperty(child(0)->getPhysicalProperty());
sortNode->setCollectNFErrors();
sortNode->setOperatorCost(0);
sortNode->setRollUpCost(child(0)->getRollUpCost());
child(0) = sortNode;
}
if ( childIsBlocking &&
generator->preCodeGenParallelOperator() )
{
if (espExchangeWithMerge == FALSE)
{
// A "halloween sort" needs to ensure that if it is parallel, but executes
// in the same ESP as the generic update's TSJ flow node, then the Sort
// will block until all scans are finished.
((Sort *)(child(0).getPtr()))->doCheckAccessToSelfRefTable();
}
else
{
// An ESP Exchange can be eliminated in its preCodeGen method if it is
// redundant. If this happens, then the Sort will be executing in the
// same ESP as the TSJ after all. So we set this flag now, so that the
// Exchange preCodeGen will call doCheckAccessToSelfRefTable() for the
// Sort before eliminating itself. This is part of the fix for Sol
// 10-090310-9876.
((Exchange *)(child(0).getPtr()))->markHalloweenSortIsMyChild();
}
}
RelExpr * re =
Join::preCodeGen(generator, externalInputs, pulledNewInputs);
if ( espExchangeWithMerge &&
(child(0)->getOperatorType() == REL_EXCHANGE))
GenAssert(((Exchange *)((RelExpr *)child(0)))->doesMerge(),
"Exchange operator does not block for Halloween problem.");
generator->compilerStatsInfo().nj()++;
return re;
}
RelExpr * MergeJoin::preCodeGen(Generator * generator,
const ValueIdSet & externalInputs,
ValueIdSet &pulledNewInputs)
{
if (nodeIsPreCodeGenned())
return this;
if (! Join::preCodeGen(generator, externalInputs, pulledNewInputs))
return 0;
// Accumulate the values that are provided as inputs by my parent
// together with the values that are produced as outputs by my
// children. Use these values for rewriting the VEG expressions.
ValueIdSet availableValues;
getInputValuesFromParentAndChildren(availableValues);
// find if the left child and/or the right child will have atmost
// one matching row. If so, an faster merge join implementation
// will be used at runtime.
ValueIdSet vidSet = getOrderedMJPreds();
ValueIdSet valuesUsedForPredicates;
computeValuesReqdForPredicates(vidSet,
valuesUsedForPredicates);
leftUnique() =
child(0)->getGroupAttr()->isUnique(valuesUsedForPredicates);
rightUnique() =
child(1)->getGroupAttr()->isUnique(valuesUsedForPredicates);
ValueIdList mjp(getOrderedMJPreds());
NABoolean replicatePredicates = TRUE;
/* For merge join the characteristic outputs have already been resolved
by the time the equijoin preds are resolved below. The outputs are
resolved at the very end of Join::precodegen, which was called a few
lines above. Therefore when we resolve the equijoin preds we have
only the actually resolved output values available. We do not have
all the potential output values available.
*/
ValueIdSet joinInputAndOutputValues;
joinInputAndOutputValues = getGroupAttr()->getCharacteristicInputs();
joinInputAndOutputValues += getGroupAttr()->getCharacteristicOutputs();
// Pass in the children GAs so that the equipreds can have one side
// resolved to one child and the other side resolved to the other child.
// solution 10-100722-1962
mjp.replaceVEGExpressions
(availableValues,
getGroupAttr()->getCharacteristicInputs(),
FALSE, // no key predicates here
0 /* no need for idempotence here */,
replicatePredicates,
NULL /* not a groupByAgg */,
&joinInputAndOutputValues,
NULL /* no indexDesc since we have no key preds*/,
child(0)->getGroupAttr(),
child(1)->getGroupAttr());
// must have at least 1 merge join predicate
GenAssert(!mjp.isEmpty(),"mjp.isEmpty()");
// The generator expects the merge join predicates to be in the form
// leftcol = rightcol where leftcol references a column from the left
// table and rightcol references a column from the right table. Switch
// the expression if it is the other way around. Also handle rare cases
// where a VEGPred is resolved into two equalities connected by an AND.
//
ValueIdSet dummy1;
ValueIdList newJoinPreds;
ValueIdList newLeftOrder;
ValueIdList newRightOrder;
CollIndex ne = (CollIndex)(mjp.entries());
NABoolean isANewJoinPred ;
for (CollIndex i = 0; i < ne; i++)
{
// Will store all the conjuncts under the pred mjp[i] being considered.
ValueIdSet conjuncts;
conjuncts.clear();
conjuncts.insert(mjp[i]);
ValueIdSet finerConjuncts;
do
{
finerConjuncts.clear();
// Go through the set of conjuncts, breaking down any AND seen into
// finer conjuncts.
//
for (ValueId vid = conjuncts.init();
conjuncts.next(vid);
conjuncts.advance(vid))
{
ItemExpr *pred = vid.getItemExpr();
if (pred->getOperatorType() == ITM_AND)
{
// Found another AND, break it down into finer conjuncts. Store
// them in finerConjuncts so that we can return to them later.
//
finerConjuncts.insert(pred->child(0)->getValueId());
finerConjuncts.insert(pred->child(1)->getValueId());
}
else
{
// This is the "finest" conjunct - cannot be broken down further.
// Make sure it's in the form of (leftCol = rightCol). Add the
// equality predicate to the final list of MJ predicates. leftOrder
// and rightOrder are set up correspondingly so that they match up
// with the predicates.
//
GenAssert(pred->getOperatorType() == ITM_EQUAL,
"pred->getOperatorType() != ITM_EQUAL");
ItemExpr *left = pred->child(0)->castToItemExpr();
ItemExpr *right = pred->child(1)->castToItemExpr();
isANewJoinPred = TRUE;
NABoolean child0Covered = child(0).getGroupAttr()->covers(left->getValueId(),
getGroupAttr()->getCharacteristicInputs(),
dummy1) ;
NABoolean child1Covered = child(1).getGroupAttr()->covers(right->getValueId(),
getGroupAttr()->getCharacteristicInputs(),
dummy1) ;
if (NOT (child0Covered && child1Covered))
{
//++MV - Irena
// save the pred's specialNulls_ flag before replacing the pred
BiRelat *biRelat = new(generator->wHeap()) BiRelat(ITM_EQUAL, right, left);
// restore specialNulls_
biRelat->setSpecialNulls(((BiRelat*)pred)->getSpecialNulls());
biRelat->bindNode(generator->getBindWA());
pred = biRelat;
//--MV - Irena
child0Covered = child(0).getGroupAttr()->covers(right->getValueId(),
getGroupAttr()->getCharacteristicInputs(),
dummy1) ;
child1Covered = child(1).getGroupAttr()->covers(left->getValueId(),
getGroupAttr()->getCharacteristicInputs(),
dummy1) ;
if(!(child0Covered && child1Covered))
{
if (isInnerNonSemiJoin())
{
selectionPred() += pred->getValueId();
}
else
{
// for an outer or semi join, the ON clause is stored in "joinPred"
// while the WHERE clause is stored in "selectionPred".
joinPred() += pred->getValueId();
}
isANewJoinPred = FALSE;
}
}
if (isANewJoinPred)
{
// Store the finest conjuncts in the final list of MJ predicates.
// Make sure the list is matched up with corresponding leftOrder
// and rightOrder.
//
newJoinPreds.insert(pred->getValueId());
newLeftOrder.insert(getLeftSortOrder()[i]);
newRightOrder.insert(getRightSortOrder()[i]);
}
}
} // for over conjuncts.
// Come back to process the new set of broken-down conjuncts if the set
// is non-empty.
//
conjuncts = finerConjuncts;
} while (NOT conjuncts.isEmpty());
} // for over mjp.
if (ne > 0)
GenAssert(NOT newJoinPreds.isEmpty(), "MergeJoin::PreCodeGen has no resolved join predicates");
// Count merge join as a Big Memory Operator (BMO) if use of BMO quota
// is enabled for merge join.
if (CmpCommon::getDefaultLong(MJ_BMO_QUOTA_PERCENT) != 0)
{
generator->incrNumBMOs();
}
setOrderedMJPreds(newJoinPreds);
setLeftSortOrder(newLeftOrder);
setRightSortOrder(newRightOrder);
generator->compilerStatsInfo().mj()++;
markAsPreCodeGenned();
return this;
} // MergeJoin::preCodeGen()
RelExpr * HashJoin::preCodeGen(Generator * generator,
const ValueIdSet & externalInputs,
ValueIdSet &pulledNewInputs)
{
if (nodeIsPreCodeGenned())
return this;
if ( CmpCommon::getDefault(COMPRESSED_INTERNAL_FORMAT) == DF_SYSTEM)
{
NABoolean resize = FALSE;
NABoolean defrag = FALSE;
ValueIdSet vidSet0 = child(0)->getGroupAttr()->getCharacteristicOutputs();
ValueIdSet vidSet1 = child(1)->getGroupAttr()->getCharacteristicOutputs();
ExpTupleDesc::TupleDataFormat tupleFormat =
determineInternalFormat( vidSet1,
vidSet0,
this,
resize,
generator,
FALSE,
defrag);
cacheTupleFormatAndResizeFlag(tupleFormat, resize, defrag);
if (tupleFormat == ExpTupleDesc::SQLMX_ALIGNED_FORMAT)
{
generator->incNCIFNodes();
}
else
{
generator->decNCIFNodes();
}
}
// Determine if we should attempt to use the HashJoin min/max optimization.
NABoolean useMinMaxOpt =
((CmpCommon::getDefault(GEN_HSHJ_MIN_MAX_OPT) == DF_ON) &&
! getEquiJoinPredicates().isEmpty() &&
! isLeftJoin() &&
! isRightJoin() &&
! isAntiSemiJoin());
// These indexes define the subset of min max values which belong to this HashJoin.
CollIndex startMinMaxIndex = 0;
CollIndex endMinMaxIndex = 0;
// If min/max opt is used, these lists are used to hold local copies of the
// generators min and max values. These are the min and max values
// generated by HashJoins that may be used by scans.
ValueIdList myMinVals;
ValueIdList myMaxVals;
// If min/max opt is used, this list are used to hold a local copy
// of the generators minmaxKeys. These are the values for which min
// and max values are available
ValueIdList myMinMaxKeys;
if (useMinMaxOpt) {
// This HashJoin will append to the end of the generator lists.
//
startMinMaxIndex = generator->getMinMaxKeys().entries();
// Find the candidate values from the right hand side of the join.
// For now, only consider VEGPreds.
for (ValueId valId = getEquiJoinPredicates().init();
getEquiJoinPredicates().next(valId);
getEquiJoinPredicates().advance(valId)) {
ItemExpr * itemExpr = valId.getItemExpr();
NAType *mmType = NULL;
if (itemExpr->getOperatorType() == ITM_VEG_PREDICATE) {
VEGPredicate *vPred = (VEGPredicate *)itemExpr;
VEGReference *vRef = vPred->getVEG()->getVEGReference();
mmType = vRef->getValueId().getType().newCopy(generator->wHeap());
}
// mmType is the type of the VEGRef relating a left and right value.
// We will compute the Min and Max using this type
if(mmType) {
// Min/Max are typed as nullable.
mmType->setNullable(true);
// Construct the host vars which will represent the min and
// max values for this join key.
char name[80];
sprintf(name, "_sys_MinVal%d", generator->getMinMaxKeys().entries());
ItemExpr *minVal = new(generator->wHeap())
HostVar(name,
mmType,
TRUE);
sprintf(name, "_sys_MaxVal%d", generator->getMinMaxKeys().entries());
ItemExpr *maxVal = new(generator->wHeap())
HostVar(name,
mmType,
TRUE);
minVal->synthTypeAndValueId();
maxVal->synthTypeAndValueId();
// Insert the value and min and max into generator lists to
// make the available to scans as key predicates.
generator->getMinMaxKeys().insert(itemExpr->getValueId());
generator->getMinVals().insert(minVal->getValueId());
generator->getMaxVals().insert(maxVal->getValueId());
// Initialize the 'will use' list to a NULL_VALUE_ID. A scan
// that decides to use the min max values will change this
// entry to be the same as the corresponding entry in the
// minMaxKeys list.
generator->getWillUseMinMaxKeys().insert(NULL_VALUE_ID);
}
}
// This is the end index (exclusive) for this HashJoin.
endMinMaxIndex = generator->getMinMaxKeys().entries();
// Keep local copies of the generators lists.
myMinVals = generator->getMinVals();
myMaxVals = generator->getMaxVals();
myMinMaxKeys = generator->getMinMaxKeys();
}
// Register the start and end indexes for this Hash Join
// (Join::preCodeGen() needs to have access to the indexes)
setStartMinMaxIndex(startMinMaxIndex);
setEndMinMaxIndex(endMinMaxIndex);
if (! Join::preCodeGen(generator, externalInputs, pulledNewInputs))
return NULL;
// List for min and max values that will be used by a scan and which
// will be generated by this HashJoin
minMaxVals_.clear();
minMaxCols_.clear();
{
// For each min/max value belonging to this HashJoin, check to see
// if any scan decided to use it. If so, add the min and max
// values to the list. Also, clear the 'will use' entry as no
// other HashJoin can supply this value.
for (CollIndex i = startMinMaxIndex; i < endMinMaxIndex; i++)
{
if (generator->getWillUseMinMaxKeys()[i] != NULL_VALUE_ID)
{
minMaxVals_.insert(myMinVals[i]);
minMaxVals_.insert(myMaxVals[i]);
VEGPredicate *vPred = (VEGPredicate *)myMinMaxKeys[i].getItemExpr();
VEGReference *vRef = vPred->getVEG()->getVEGReference();
minMaxCols_.insert(vRef->getValueId());
generator->getWillUseMinMaxKeys()[i] = NULL_VALUE_ID;
}
}
// If we have some minMaxCols, then replace any VEGReferences.
if (minMaxCols_.entries())
{
ValueIdSet availForMinMax;
availForMinMax += child(1)->getGroupAttr()->getCharacteristicOutputs();
availForMinMax += getGroupAttr()->getCharacteristicInputs();
minMaxCols_.replaceVEGExpressions(availForMinMax,
getGroupAttr()->getCharacteristicInputs());
}
}
// Accumulate the values that are provided as inputs by my parent
// together with the values that are produced as outputs by my
// children. Use these values for rewriting the VEG expressions.
ValueIdSet availableValues;
getInputValuesFromParentAndChildren(availableValues);
ValueIdSet hjp(getEquiJoinPredicates());
NABoolean replicatePredicates = TRUE;
/* For hash join the characteristic outputs have already been resolved
by the time the equijoin preds are resolved below. The outputs are
resolved at the very end of Join::precodegen, which was called a few
lines above. Therefore when we resolve the equijoin preds we have
only the actually resolved output values available. We do not have
all the potential output values available.
*/
ValueIdSet joinInputAndOutputValues;
joinInputAndOutputValues = getGroupAttr()->getCharacteristicInputs();
joinInputAndOutputValues += getGroupAttr()->getCharacteristicOutputs();
// Pass in the children GAs so that the equipreds can have one side
// resolved to one child and the other side resolved to the other child.
// solution 10-100722-1962
hjp.replaceVEGExpressions
(availableValues,
getGroupAttr()->getCharacteristicInputs(),
FALSE, // no key predicates here
0 /* no need for idempotence here */,
replicatePredicates,
NULL /* not a groupByAgg */,
&joinInputAndOutputValues,
NULL /* no indexDesc since we have no key preds*/,
child(0)->getGroupAttr(),
child(1)->getGroupAttr());
// Will store the rewritten hjp's which compile with the format of
// leftCol= rightCol.
//
ValueIdSet newJoinPreds;
if (hjp.isEmpty())
{
}
else
{
// The generator expects the hash join predicates to be in the form
// leftcol = rightcol where leftcol references a column from the left
// table and rightcol references a column from the right table. Switch
// the expression if it is the other way around. Also handle rare cases
// where a VEGPred is resolved into two equalities connected by an AND.
//
ValueIdSet dummy1;
NABoolean isANewJoinPred ;
do
{
ValueIdSet finerConjuncts;
finerConjuncts.clear();
for (ValueId vid = hjp.init();
hjp.next(vid);
hjp.advance(vid))
{
ItemExpr *pred = vid.getItemExpr();
// Break this up into the finer conjuncts. Store them in a separate
// set so that we can return to it later.
// of the set so that we could return
if (pred->getOperatorType() == ITM_AND)
{
finerConjuncts.insert(pred->child(0)->getValueId());
finerConjuncts.insert(pred->child(1)->getValueId());
}
else
{
GenAssert(pred->getOperatorType() == ITM_EQUAL,
"pred->getOperatorType() != ITM_EQUAL");
ItemExpr *left = pred->child(0)->castToItemExpr();
ItemExpr *right = pred->child(1)->castToItemExpr();
isANewJoinPred = TRUE;
NABoolean child0Covered = child(0).getGroupAttr()->covers(left->getValueId(),
getGroupAttr()->getCharacteristicInputs(),
dummy1) ;
NABoolean child1Covered = child(1).getGroupAttr()->covers(right->getValueId(),
getGroupAttr()->getCharacteristicInputs(),
dummy1) ;
if (NOT (child0Covered && child1Covered))
{
//++MV - Irena
// save the pred's specialNulls_ flag before replacing the pred
BiRelat *biRelat = new(generator->wHeap()) BiRelat(ITM_EQUAL, right, left);
// restore specialNulls_
biRelat->setSpecialNulls(((BiRelat*)pred)->getSpecialNulls());
biRelat->bindNode(generator->getBindWA());
pred = biRelat;
//--MV - Irena
child0Covered = child(0).getGroupAttr()->covers(right->getValueId(),
getGroupAttr()->getCharacteristicInputs(),
dummy1) ;
child1Covered = child(1).getGroupAttr()->covers(left->getValueId(),
getGroupAttr()->getCharacteristicInputs(),
dummy1) ;
if(!(child0Covered && child1Covered))
{
if (isInnerNonSemiJoin())
{
selectionPred() += pred->getValueId();
}
else
{
// for an outer or semi join, the ON clause is stored in "joinPred"
// while the WHERE clause is stored in "selectionPred".
joinPred() += pred->getValueId();
}
isANewJoinPred = FALSE;
}
}
if (isANewJoinPred)
newJoinPreds.insert(pred->getValueId());
}
} // for over hjp.
// Come back to process the new set of broken-down conjuncts if the set
// is non-empty.
//
hjp = finerConjuncts;
} while (NOT hjp.isEmpty());
GenAssert(NOT newJoinPreds.isEmpty(), "HashJoin::PreCodeGen has no resolved join predicates");
}
// Value IDs given to the right/inner child
ValueIdSet valuesGivenToRightChild =
child(1)->getGroupAttr()->getCharacteristicInputs();
if ( ! valuesGivenToRightChild.isEmpty() ) {
// Accumulate the values that are provided as inputs by my parent
// together with the values that are produced as outputs by my
// children. Use these values for rewriting the VEG expressions.
ValueIdSet availableValues;
const ValueIdSet & HJInputs = getGroupAttr()->getCharacteristicInputs();
getInputValuesFromParentAndChildren(availableValues);
valuesGivenToRightChild.replaceVEGExpressions(availableValues, HJInputs);
}
// before computing the move and check expressions, add one more
// value to "valuesGivenToRightChild": a statement execution count
// that will cause re-hashing each time the statement is
// re-executed. It is not legal to keep a hash table across
// statement executions (and possibly transactions).
ValueId execCount = generator->getOrAddStatementExecutionCount();
valuesGivenToRightChild += execCount;
pulledNewInputs += execCount;
getGroupAttr()->addCharacteristicInputs(pulledNewInputs);
// add move and search expressions
for (ValueId val_id = valuesGivenToRightChild.init();
valuesGivenToRightChild.next(val_id);
valuesGivenToRightChild.advance(val_id)) {
ItemExpr * item_expr = val_id.getItemExpr();
// add this converted value to the map table.
Convert * conv_node = new(generator->wHeap()) Convert (item_expr);
// bind/type propagate the new node
conv_node->bindNode(generator->getBindWA());
moveInputValues().insert(conv_node->getValueId());
// add the search condition
BiRelat * bi_relat = new(generator->wHeap())
BiRelat(ITM_EQUAL, item_expr, conv_node);
bi_relat->allocValueId();
checkInputValues().insert(bi_relat->getValueId());
} // for val_id
// Count this BMO and add its needed memory to the total needed
generator->incrNumBMOs();
if ((ActiveSchemaDB()->getDefaults()).getAsDouble(BMO_MEMORY_LIMIT_PER_NODE_IN_MB) > 0)
generator->incrBMOsMemory(getEstimatedRunTimeMemoryUsage(generator, TRUE));
// store the transformed predicates back into the hash join node
storeEquiJoinPredicates(newJoinPreds);
generator->compilerStatsInfo().hj()++;
//
// case of hash anti semi join optimization (NOT IN)
// add/build expression to detect inner and outer null :
// checkOuteNullexpr_ and checkInnerNullExpr_
addCheckNullExpressions(generator->wHeap());
markAsPreCodeGenned();
return this;
} // HashJoin::preCodeGen()
RelExpr * FileScan::preCodeGen(Generator * generator,
const ValueIdSet & externalInputs,
ValueIdSet &pulledNewInputs)
{
if (nodeIsPreCodeGenned())
return this;
const PartitioningFunction* myPartFunc = getPartFunc();
NABoolean usePartKeyPreds =
(isHbaseTable() &&
myPartFunc &&
myPartFunc->isPartitioned() &&
!myPartFunc->isAReplicationPartitioningFunction());
if (isRewrittenMV())
generator->setNonCacheableMVQRplan(TRUE);
if (usePartKeyPreds)
{
// partition key predicates will be applied to this file scan,
// "pull" the partition input values from the parent
pulledNewInputs += myPartFunc->getPartitionInputValues();
getGroupAttr()->addCharacteristicInputs(myPartFunc->getPartitionInputValues());
}
// Resolve the VEGReferences and VEGPredicates, if any, that appear
// in the Characteristic Inputs, in terms of the externalInputs.
getGroupAttr()->resolveCharacteristicInputs(externalInputs);
// The VEG expressions in the selection predicates and the characteristic
// outputs can reference any expression that is either a potential output
// or a characteristic input for this RelExpr. Supply these values for
// rewriting the VEG expressions.
ValueIdSet availableValues;
getInputAndPotentialOutputValues(availableValues);
sampledColumns().replaceVEGExpressions
(availableValues, getGroupAttr()->getCharacteristicInputs());
// Rewrite the partitioning function in terms of the available values.
if (getIndexDesc()->isPartitioned())
getIndexDesc()->getPartitioningFunction()->preCodeGen(availableValues);
// VEGPredicates that are key predicates but are also replicated in
// the executor predicates must be replaced with the same expression
// in both the places after they are rewritten. The VEGRewritePairs
// data structure, when passed to replaceVEGExpressions(), causes
// replaceVEGExpressions() to be idempotent.
VEGRewritePairs vegPairs(generator->wHeap());
ValueIdSet partKeyPredsHBase;
if (usePartKeyPreds)
{
// add the partitioning key predicates to this scan node,
// to make sure that each ESP reads only the part of the
// data that it is supposed to process
ValueId saltCol;
if (myPartFunc->isATableHashPartitioningFunction())
{
// find the _SALT_ column and make a partitioning key
// predicate for it
const ValueIdList &keyCols = getIndexDesc()->getIndexKey();
// the first salt column we find in the key is the one
// we are looking for
for (CollIndex i=0; i<keyCols.entries(); i++)
if (keyCols[i].isSaltColumn())
{
saltCol = keyCols[i];
break;
}
if (saltCol != NULL_VALUE_ID)
((TableHashPartitioningFunction *) myPartFunc)->
createPartitioningKeyPredicatesForSaltedTable(saltCol);
}
partKeyPredsHBase = myPartFunc->getPartitioningKeyPredicates();
}
if (getMdamKeyPtr() != NULL)
{
NABoolean replicatePredicates = TRUE;
// mdamKeyPtr()->print(); // for debugging purposes
ValueIdSet executorPredicates;
ValueIdSet augmentedPreds = getSelectionPredicates();
const LogPhysPartitioningFunction *logPhysPartFunc =
getPartFunc()->castToLogPhysPartitioningFunction();
if (!partKeyPredsHBase.isEmpty())
{
augmentedPreds += partKeyPredsHBase;
mdamKeyPtr()->setNoExePred(FALSE);
}
augmentedPreds += getComputedPredicates();
if ( logPhysPartFunc != NULL )
{
LogPhysPartitioningFunction::logPartType logPartType =
logPhysPartFunc->getLogPartType();
if ( logPartType == LogPhysPartitioningFunction::LOGICAL_SUBPARTITIONING
OR logPartType == LogPhysPartitioningFunction::HORIZONTAL_PARTITION_SLICING
)
augmentedPreds += logPhysPartFunc->getPartitioningKeyPredicates();
}
mdamKeyPtr()->preCodeGen(executorPredicates,
augmentedPreds,
availableValues,
getGroupAttr()->getCharacteristicInputs(),
&vegPairs,
replicatePredicates,
!partKeyPredsHBase.isEmpty());
setExecutorPredicates(executorPredicates);
// mdamKeyPtr()->print(); // for debugging purposes
}
else if (! isHiveTable() &&
(getSearchKey() || !partKeyPredsHBase.isEmpty()))
{
// ---------------------------------------------------
// --------------------- Rewrite preds for search key:
// ---------------------------------------------------
if (!partKeyPredsHBase.isEmpty())
{
// These predicates can compete with other key predicates;
// decide which of them to use as key preds and which as
// executor preds:
// - No search key: Use part key preds as search key
// - Search key with non-unique preds: Replace it with
// a new search key with part key preds
// - Search key with unique preds (unlikely, this shouldn't
// have been a parallel query): add part key preds as
// executor preds
ValueIdSet combinedInputs(externalInputs);
combinedInputs += pulledNewInputs;
ValueIdSet existingKeyPreds;
if (getSearchKey())
existingKeyPreds += getSearchKey()->getKeyPredicates();
// create a new search key that has the partitioning key preds
SearchKey * partKeySearchKey =
myPartFunc->createSearchKey(getIndexDesc(),
combinedInputs,
existingKeyPreds);
ValueIdSet exePreds(partKeySearchKey->getExecutorPredicates());
NABoolean replaceSearchKey = !(getSearchKey() &&
getSearchKey()->isUnique());
if (getSearchKey())
exePreds += getSearchKey()->getExecutorPredicates();
ValueId falseConst = NULL_VALUE_ID;
if (exePreds.containsFalseConstant(falseConst))
replaceSearchKey = FALSE;
// pick one search key and add the remaining
// predicates (if any) to exePreds
if (replaceSearchKey)
setSearchKey(partKeySearchKey);
else
exePreds += partKeySearchKey->getKeyPredicates();
searchKey()->setExecutorPredicates(exePreds);
}
NABoolean replicatePredicates = TRUE;
setExecutorPredicates(searchKey()->getExecutorPredicates());
// Rebuild the search key expressions
ValueIdSet& keyPred = searchKey()->keyPredicates();
keyPred.replaceVEGExpressions
(availableValues,
getGroupAttr()->getCharacteristicInputs(),
FALSE, // no need for key predicate generation here
&vegPairs,
replicatePredicates);
// Rebuild the executor predicate tree
executorPred().replaceVEGExpressions
(availableValues,
getGroupAttr()->getCharacteristicInputs(),
FALSE, // no need for key predicate generation here
&vegPairs,
replicatePredicates
);
// Generate the begin and end keys.
if ( getDoUseSearchKey() ) {
generateKeyExpr(getGroupAttr()->getCharacteristicInputs(),
getIndexDesc()->getIndexKey(),
getSearchKey()->getBeginKeyValues(),
beginKeyPred_,
generator,
replicatePredicates);
generateKeyExpr(getGroupAttr()->getCharacteristicInputs(),
getIndexDesc()->getIndexKey(),
getSearchKey()->getEndKeyValues(),
endKeyPred_,
generator,
replicatePredicates);
}
// Check to see if there are any MIN/MAX values coming from a
// HashJoin which could be used as begin/end key values for the
// leading key of this scan. Don't consider doing this if this
// is a unique scan (can't improve on that) or if the leading
// key is already unique or if both the begin and end key are
// exclusive (min max are inclusive and no easy way to mix
// them).
if (generator->getMinMaxKeys().entries() &&
(getSearchKey()->getBeginKeyValues()[0] !=
getSearchKey()->getEndKeyValues()[0]) &&
(!getSearchKey()->isBeginKeyExclusive() ||
!getSearchKey()->isEndKeyExclusive())) {
// The keys of the scan.
const ValueIdList &keys = getIndexDesc()->getIndexKey();
ValueId minMaxKeyCol = keys[0];
IndexColumn *ixCol = (IndexColumn *) (minMaxKeyCol.getItemExpr());
BaseColumn *baseCol = NULL;
ValueId underlyingCol;
NABoolean needToComputeActualMinMax = FALSE;
ItemExpr *computedColExpr = NULL;
// The candidate values for min and max.
const ValueIdList &minMaxKeys = generator->getMinMaxKeys();
CollIndex keyIdx = NULL_COLL_INDEX;
// Determine how min/max is related to begin/end. depends
// on ordering (ASC vs DESC) and scan direction (forward vs
// reverse)
NABoolean ascKey =
getIndexDesc()->getNAFileSet()->getIndexKeyColumns().isAscending(0);
if(getReverseScan())
ascKey = !ascKey;
// If the leading key column is a divisioning column, then
// look for min/max values of an underlying column
GenAssert(ixCol->getOperatorType() == ITM_INDEXCOLUMN,
"unexpected object type");
baseCol =
(BaseColumn *) (((IndexColumn *) ixCol)->getDefinition().getItemExpr());
GenAssert(baseCol->getOperatorType() == ITM_BASECOLUMN,
"unexpected object type");
if (baseCol->getNAColumn()->isDivisioningColumn()) {
ValueIdSet underlyingCols;
baseCol->getUnderlyingColumnsForCC(underlyingCols);
if (underlyingCols.entries() == 1) {
// We have a leading division column that's computed from
// 1 base column, now get the underlying column and the
// divisioning expression
needToComputeActualMinMax = TRUE;
underlyingCols.getFirst(minMaxKeyCol);
computedColExpr = baseCol->getComputedColumnExpr().getItemExpr();
BaseColumn *underlyingBaseCol =
(BaseColumn *) minMaxKeyCol.getItemExpr();
GenAssert(underlyingBaseCol->getOperatorType() == ITM_BASECOLUMN,
"unexpected object type");
// the computed column expression has been rewritten to use
// VEGRefs, so get the corresponding VEGRef for the underlying column
underlyingCol = underlyingBaseCol->getTableDesc()->
getColumnVEGList()[underlyingBaseCol->getColNumber()];
}
}
// Check all the candidate values. If any one of them matches
// the leading key of this scan, then select it for use in the
// begin/end key value of the leading key.
// Scalar min/max functions cause an exponential growth when
// combined with each other, see ItmScalarMinMax::codeGen()
Int32 limitItems = 3 ; // use at most 3
for(CollIndex i = 0; i < minMaxKeys.entries() && limitItems; i++) {
ValueId mmKeyId = minMaxKeys[i];
if(mmKeyId != NULL_VALUE_ID) {
ItemExpr *mmItem = mmKeyId.getItemExpr();
if (mmItem->getOperatorType() == ITM_VEG_PREDICATE) {
VEGPredicate *vPred = (VEGPredicate *)mmItem;
const ValueIdSet &members = vPred->getVEG()->getAllValues();
if (members.contains(minMaxKeyCol)) {
// some other operator is producing min/max values
// for our leading key column, now check whether we
// can use them
keyIdx = i;
// Indicate in the 'will use' list that we will use these
// min/max values. This will indicate to the HashJoin that
// it should produce these values.
generator->getWillUseMinMaxKeys()[keyIdx] =
generator->getMinMaxKeys()[keyIdx];
addMinMaxHJColumn(baseCol->getValueId());
limitItems-- ; // one more is used
// If we can use a min/max value for the begin key, do so...
if(!getSearchKey()->isBeginKeyExclusive()) {
ItemExpr *keyPred = getBeginKeyPred()[0].getItemExpr();
ItemExpr *currentBeg = keyPred->child(1);
// Get the proper begin key (min or max) that came from
// the HashJoin
ValueId hashJoinBeg = (ascKey ?
generator->getMinVals()[keyIdx] :
generator->getMaxVals()[keyIdx]);
// Construct an expression which determines at runtime
// which BK to use. Either the existing one or the one
// coming from HashJoin whichever is larger (smaller).
//
ItemExpr *newBeg = hashJoinBeg.getItemExpr();
if (needToComputeActualMinMax) {
ValueIdMap divExprMap;
ValueId computedBeg;
// If hashJoinBeg is :sysHV1 and the computed column
// expression is A/100, then the begin value for
// the computed column is :sysHV1/100. Do this
// rewrite by using a ValueIdMap
divExprMap.addMapEntry(underlyingCol, hashJoinBeg);
divExprMap.rewriteValueIdDown(computedColExpr->getValueId(),
computedBeg);
newBeg = computedBeg.getItemExpr();
}
newBeg = new (generator->wHeap())
ItmScalarMinMax((ascKey ? ITM_SCALAR_MAX : ITM_SCALAR_MIN),
currentBeg,
newBeg);
newBeg->synthTypeAndValueId();
// Replace the RHS of the key pred.
keyPred->child(1) = newBeg->getValueId();
// The value coming from the HashJoin must be in out inputs.
getGroupAttr()->addCharacteristicInputs(hashJoinBeg);
// And we must pull those values from the HashJoin.
pulledNewInputs += hashJoinBeg;
availableValues += hashJoinBeg;
}
// If we can use a min/max value for the end key, do so...
if(!getSearchKey()->isEndKeyExclusive()) {
ItemExpr *keyPred = getEndKeyPred()[0].getItemExpr();
ItemExpr *currentEnd = keyPred->child(1);
// Get the proper end key (max or min) that came from
// the HashJoin
ValueId hashJoinEnd = (ascKey ?
generator->getMaxVals()[keyIdx] :
generator->getMinVals()[keyIdx]);
// Construct an expression which determines at runtime
// which EK to use. Either the existing one or the one
// coming from HashJoin whichever is smaller (larger).
//
ItemExpr *newEnd = hashJoinEnd.getItemExpr();
if (needToComputeActualMinMax) {
ValueIdMap divExprMap;
ValueId computedEnd;
divExprMap.addMapEntry(underlyingCol, hashJoinEnd);
divExprMap.rewriteValueIdDown(computedColExpr->getValueId(),
computedEnd);
newEnd = computedEnd.getItemExpr();
}
newEnd = new (generator->wHeap())
ItmScalarMinMax((ascKey ? ITM_SCALAR_MIN : ITM_SCALAR_MAX),
currentEnd,
newEnd);
newEnd->synthTypeAndValueId();
// Replace the RHS of the key pred.
keyPred->child(1) = newEnd->getValueId();
// The value coming from the HashJoin must be in out inputs.
getGroupAttr()->addCharacteristicInputs(hashJoinEnd);
// And we must pull those values from the HashJoin.
pulledNewInputs += hashJoinEnd;
availableValues += hashJoinEnd;
}
}
}
}
}
}
}
else
{
// Hive table scan (HBase scan has executor preds set up already)
if (isHiveTable())
setExecutorPredicates(selectionPred());
// Rebuild the executor predicate tree
executorPred().replaceVEGExpressions
(availableValues,
getGroupAttr()->getCharacteristicInputs(),
FALSE, // no need for key predicate generation here
&vegPairs,
TRUE);
if (isHiveTable())
{
// assign individual files and blocks to each ESPs
((NodeMap *) getPartFunc()->getNodeMap())->assignScanInfos(hiveSearchKey_);
if (CmpCommon::getDefault(USE_LIBHDFS) == DF_ON)
generator->setProcessLOB(TRUE);
// flag set for HBase scan in HbaseAccess::preCodeGen
// unique scan unlikely for hive scans except
// with predicate on virtual cols.
if (!(searchKey() && searchKey()->isUnique()))
generator->oltOptInfo()->setMultipleRowsReturned(TRUE);
}
}
// Selection predicates are not needed anymore:
selectionPred().clear();
// Add the sampled columns to the set of available values. This is
// basically a kluge to get the GroupAttributes right.
availableValues += sampledColumns();
// This call also rewrites predicates
// $$$ Does it need vegPairs too? $$$
getGroupAttr()->resolveCharacteristicOutputs
(availableValues,
getGroupAttr()->getCharacteristicInputs());
generator->oltOptInfo()->mayDisableOperStats(&oltOptInfo());
markAsPreCodeGenned();
return this;
} // FileScan::preCodeGen()
RelExpr * GenericUpdate::preCodeGen(Generator * generator,
const ValueIdSet & externalInputs,
ValueIdSet &pulledNewInputs)
{
if (nodeIsPreCodeGenned())
return this;
if (! RelExpr::preCodeGen(generator,externalInputs,pulledNewInputs))
return NULL;
// Determine whether OLT optimization must be avoided.
if (getGroupAttr()->isEmbeddedUpdateOrDelete())
{
generator->oltOptInfo()->setOltMsgOpt(FALSE);
generator->setUpdAbortOnError(TRUE);
generator->setUpdSavepointOnError(FALSE);
generator->setUpdPartialOnError(FALSE);
generator->setUpdErrorOnError(FALSE);
}
if ((accessOptions().accessType() == TransMode::SKIP_CONFLICT_ACCESS_) ||
(getGroupAttr()->isStream()) ||
(newRecBeforeExprArray().entries() > 0)) // set on rollback
{
generator->oltOptInfo()->setOltEidOpt(FALSE);
oltOptInfo().setOltEidOpt(FALSE);
setExpandShortRows(FALSE);
generator->setUpdAbortOnError(TRUE);
generator->setUpdSavepointOnError(FALSE);
generator->setUpdPartialOnError(FALSE);
generator->setUpdErrorOnError(FALSE);
}
// If RI, IM, MV or triggers are being used, abort on error.
// This is because internal data consistency
// cannot be guaranteed for these cases.
if ((getInliningInfo().hasInlinedActions()) ||
(getInliningInfo().isEffectiveGU()))
{
// cannot do partial updates.
generator->setUpdPartialOnError(FALSE);
if (CmpCommon::getDefault(COMP_BOOL_206) == DF_ON)
{
if (NOT ((getInliningInfo().hasTriggers()) ||
(getInliningInfo().hasRI()) ||
(getInliningInfo().hasIM()) ||
(getInliningInfo().isMVLoggingInlined())))
{
generator->setUpdAbortOnError(TRUE);
generator->setUpdSavepointOnError(FALSE);
generator->setUpdErrorOnError(FALSE);
}
else
generator->setUpdErrorOnError(FALSE);
}
else
{
// abort on error for non-IM cases(RI,MV,Trig).
if ((NOT getInliningInfo().hasIM()) ||
(getInliningInfo().hasRI()))
{
generator->setUpdAbortOnError(TRUE);
generator->setUpdSavepointOnError(FALSE);
generator->setUpdErrorOnError(FALSE);
}
else
generator->setUpdErrorOnError(FALSE);
}
}
// If RI, MV or triggers are being used, turn off the lean optimization for
// the complete plan; all other optimizations will still apply.
if ( generator->oltOptInfo()->oltEidLeanOpt() &&
( getInliningInfo().hasTriggers() ||
getInliningInfo().hasRI() ||
getInliningInfo().isMVLoggingInlined() ) )
{
generator->oltOptInfo()->setOltEidLeanOpt(FALSE);
oltOptInfo().setOltEidLeanOpt(FALSE);
}
// Accumulate the values that are provided as inputs by my parent
// together with the values that are produced as outputs by my
// children. Use these values for rewriting the VEG expressions.
ValueIdSet availableValues;
getInputValuesFromParentAndChildren(availableValues);
newRecExpr_.replaceVEGExpressions
(availableValues,
getGroupAttr()->getCharacteristicInputs());
newRecBeforeExpr_.replaceVEGExpressions
(availableValues,
getGroupAttr()->getCharacteristicInputs());
executorPred().replaceVEGExpressions
(availableValues,
getGroupAttr()->getCharacteristicInputs());
// VEGPredicates that are key predicates but are also replicated
// in the executor predicates must be replaced with the same
// expression in both places after they are rewritten.
// Therefore, we want replaceVEGExpressions() processing to be
// idempotent. By passing the VEGRewritePairs data structure
// to replaceVEGExpressions(), we get idempotence.
VEGRewritePairs lookup(generator->wHeap()); // so replaceVEGExpressions will be idempotent
if (getSearchKey() == NULL)
{
// Begin and end key preds may already be available.
beginKeyPred_.replaceVEGExpressions
(availableValues,
getGroupAttr()->getCharacteristicInputs(),
FALSE, // no need for key predicate generation here
&lookup);
endKeyPred_.replaceVEGExpressions
(availableValues,
getGroupAttr()->getCharacteristicInputs(),
FALSE, // no need for key predicate generation here
&lookup);
// In the case of an embedded insert from VALUES,
// any predicates need to have their VEGreferences resolved.
if (getGroupAttr()->isEmbeddedInsert())
{
NABoolean replicatePredicates = TRUE;
// Rebuild the executor predicate tree
executorPred().replaceVEGExpressions
(availableValues,
getGroupAttr()->getCharacteristicInputs(),
FALSE, // no need for key predicate generation
&lookup,
replicatePredicates
);
}
}
else
{
// Build begin and end key predicates from the search key structure.
//## It *might* be a good idea to add here:
//## CMPASSERT(beginKeyPred_.isEmpty() && endKeyPred_.isEmpty());
//## as that *seems* to be the assumption here.
//## (But I haven't the time to make the change and test it.)
ValueIdSet& keyPred = getSearchKey()->keyPredicates();
NABoolean replicatePredicates = TRUE;
// Rebuild the search key expressions
keyPred.replaceVEGExpressions
(availableValues,
getGroupAttr()->getCharacteristicInputs(),
FALSE, // no need for key predicate generation
&lookup,
replicatePredicates);
// Rebuild the executor predicate tree
executorPred().replaceVEGExpressions
(availableValues,
getGroupAttr()->getCharacteristicInputs(),
FALSE, // no need for key predicate generation
&lookup,
replicatePredicates
);
// Generate the begin and end keys.
generateKeyExpr(getGroupAttr()->getCharacteristicInputs(),
getIndexDesc()->getIndexKey(),
getSearchKey()->getBeginKeyValues(),
beginKeyPred_,
generator);
generateKeyExpr(getGroupAttr()->getCharacteristicInputs(),
getIndexDesc()->getIndexKey(),
getSearchKey()->getEndKeyValues(),
endKeyPred_,
generator);
}
// ---------------------------------------------------------------------
// Rewrite the check constraint expressions.
// ---------------------------------------------------------------------
checkConstraints().replaceVEGExpressions
(availableValues,
getGroupAttr()->getCharacteristicInputs());
generator->setFoundAnUpdate(TRUE);
generator->setPartnAccessChildIUD();
#ifdef _DEBUG
// Compile in the index maintenance ... just for testing
//
if(getenv("IM_COMPILE"))
generator->imUpdateRel() = this;
#endif
if (oltOptLean() &&
((isinBlockStmt()) ||
(getTableDesc()->getNATable()->hasAddedColumn()) ||
(getTableDesc()->getNATable()->hasVarcharColumn())))
{
oltOptInfo().setOltEidLeanOpt(FALSE);
}
generator->setSkipUnavailablePartition(FALSE);
if (isMtsStatement())
generator->setEmbeddedIUDWithLast1(TRUE) ;
if (isMerge())
{
// Accumulate the values that are provided as inputs by my parent
// together with the values that are produced as outputs by my
// children. Use these values for rewriting the VEG expressions.
ValueIdSet availableValues;
getInputValuesFromParentAndChildren(availableValues);
mergeInsertRecExpr().replaceVEGExpressions
(availableValues,
getGroupAttr()->getCharacteristicInputs());
mergeUpdatePred().replaceVEGExpressions
(availableValues,
getGroupAttr()->getCharacteristicInputs());
ValueIdList tempVIDlist;
getTableDesc()->getIdentityColumn(tempVIDlist);
NAColumn *identityCol = NULL;
if (tempVIDlist.entries() > 0)
{
ValueId valId = tempVIDlist[0];
identityCol = valId.getNAColumn();
}
if (((getOperatorType() == REL_HBASE_DELETE) ||
(getOperatorType() == REL_HBASE_UPDATE)) &&
(getTableDesc()->getNATable()->getClusteringIndex()->hasSyskey()))
{
*CmpCommon::diags() << DgSqlCode(-3241)
<< DgString0(" SYSKEY not allowed.");
GenExit();
}
if ((getOperatorType() != REL_HBASE_UPDATE) &&
(mergeInsertRecExpr().entries() > 0) &&
(CmpCommon::getDefault(COMP_BOOL_175) == DF_OFF))
{
// MERGE with INSERT is limited to HBase updates unless
// the CQD is on
*CmpCommon::diags() << DgSqlCode(-3241)
<< DgString0(" This MERGE is not allowed with INSERT.");
GenExit();
}
if (oltOpt())
{
// if no update expr and only insert expr is specified for
// this MERGE stmt, turn off olt opt.
//
if (newRecExprArray().entries() == 0)
oltOptInfo().setOltEidOpt(FALSE);
oltOptInfo().setOltEidLeanOpt(FALSE);
}
generator->setUpdErrorOnError(FALSE);
generator->setUpdSavepointOnError(FALSE);
} // isMerge
generator->oltOptInfo()->mayDisableOperStats(&oltOptInfo());
// Part of the fix for Soln 10-100425-9755. Don't AQR a
// positioned update/delete because part of the recovery
// for the error that triggers the AQR is rollback transaction
// and this causes the referenced cursor to be closed. The other
// part of the fix is in compiler cache: positioned update/deletes
// will not be cached, and this should reduce the need to handle
// errors with AQR, e.g., timestamp mismatch errors.
if (updateCurrentOf())
generator->setAqrEnabled(FALSE);
if (getTableDesc()->getNATable()->hasLobColumn())
{
oltOptInfo().setOltOpt(FALSE);
generator->oltOptInfo()->setOltOpt(FALSE);
//enabling AQR to take care of the lock conflict error 8558 that
// should be retried.
// generator->setAqrEnabled(FALSE);
generator->setUpdAbortOnError(TRUE);
generator->setUpdSavepointOnError(FALSE);
}
if ((isNoRollback()) ||
(generator->getTransMode()->getRollbackMode() == TransMode::NO_ROLLBACK_))
{
generator->setWithNoRollbackUsed(isNoRollback());
if (CmpCommon::getDefault(AQR_WNR) == DF_OFF)
generator->setAqrEnabled(FALSE);
}
if (((getInliningInfo().hasInlinedActions()) ||
(getInliningInfo().isEffectiveGU())) &&
(getInliningInfo().hasRI()))
{
generator->setRIinliningForTrafIUD(TRUE);
}
if (precondition_.entries() > 0)
{
ValueIdSet availableValues;
getInputValuesFromParentAndChildren(availableValues);
precondition_.
replaceVEGExpressions(availableValues,
getGroupAttr()->getCharacteristicInputs());
}
markAsPreCodeGenned();
return this;
} // GenericUpdate::preCodeGen()
RelExpr * Update::preCodeGen(Generator * generator,
const ValueIdSet & externalInputs,
ValueIdSet &pulledNewInputs)
{
if (nodeIsPreCodeGenned())
return this;
if (! GenericUpdate::preCodeGen(generator,externalInputs,pulledNewInputs))
return NULL;
markAsPreCodeGenned();
return this;
}
RelExpr * MergeUpdate::preCodeGen(Generator * generator,
const ValueIdSet & externalInputs,
ValueIdSet &pulledNewInputs)
{
if (nodeIsPreCodeGenned())
return this;
if (! Update::preCodeGen(generator,externalInputs,pulledNewInputs))
return NULL;
markAsPreCodeGenned();
return this;
}
RelExpr * UpdateCursor::preCodeGen(Generator * generator,
const ValueIdSet & externalInputs,
ValueIdSet &pulledNewInputs)
{
if (nodeIsPreCodeGenned())
return this;
if (! Update::preCodeGen(generator,externalInputs,pulledNewInputs))
return NULL;
// primary key columns cannot be updated, yet. After RI support
// is in, they could be updated.
const NAColumnArray & key_column_array =
getTableDesc()->getNATable()->getClusteringIndex()->getIndexKeyColumns();
ValueIdSet& val_id_set = newRecExpr();
ValueId val_id;
for (val_id = val_id_set.init(); val_id_set.next(val_id); val_id_set.advance(val_id))
{
ItemExpr * item_expr = val_id.getItemExpr();
for (short i = 0; i < getTableDesc()->getNATable()->getKeyCount(); i++)
{
const char * key_colname = key_column_array[i]->getColName();
const char * upd_colname = ((BaseColumn *)
(item_expr->child(0)->castToItemExpr()))->
getColName();
if ((strcmp(key_colname, upd_colname) == 0) &&
(item_expr->getOperatorType() == ITM_ASSIGN) &&
(((Assign*)item_expr)->isUserSpecified()))
{
*CmpCommon::diags() << DgSqlCode(-4033)
<< DgColumnName(key_colname);
GenExit();
}
}
}
generator->oltOptInfo()->mayDisableOperStats(&oltOptInfo());
markAsPreCodeGenned();
return this;
} // UpdateCursor::preCodeGen()
RelExpr * Delete::preCodeGen(Generator * generator,
const ValueIdSet & externalInputs,
ValueIdSet &pulledNewInputs)
{
if (nodeIsPreCodeGenned())
return this;
if (! GenericUpdate::preCodeGen(generator,externalInputs,pulledNewInputs))
return NULL;
markAsPreCodeGenned();
return this;
}
RelExpr * MergeDelete::preCodeGen(Generator * generator,
const ValueIdSet & externalInputs,
ValueIdSet &pulledNewInputs)
{
if (nodeIsPreCodeGenned())
return this;
if (! Delete::preCodeGen(generator,externalInputs,pulledNewInputs))
return NULL;
markAsPreCodeGenned();
return this;
}
static NABoolean hasColReference(ItemExpr * ie)
{
if (! ie)
return FALSE;
if ((ie->getOperatorType() == ITM_BASECOLUMN) ||
(ie->getOperatorType() == ITM_INDEXCOLUMN) ||
(ie->getOperatorType() == ITM_REFERENCE))
return TRUE;
for (Lng32 i = 0; i < ie->getArity(); i++)
{
if (hasColReference(ie->child(i)))
return TRUE;
}
return FALSE;
}
void HbaseAccess::addReferenceFromItemExprTree(ItemExpr * ie,
NABoolean addCol, NABoolean addHBF,
ValueIdSet &colRefVIDset)
{
if (! ie)
return;
if ((ie->getOperatorType() == ITM_BASECOLUMN) ||
(ie->getOperatorType() == ITM_INDEXCOLUMN) ||
(ie->getOperatorType() == ITM_REFERENCE))
{
if (addCol)
colRefVIDset.insert(ie->getValueId());
return;
}
if (ie->getOperatorType() == ITM_HBASE_TIMESTAMP)
{
if (addHBF)
{
colRefVIDset.insert(ie->getValueId());
}
return;
}
if (ie->getOperatorType() == ITM_HBASE_VERSION)
{
if (addHBF)
{
colRefVIDset.insert(ie->getValueId());
}
return;
}
for (Lng32 i = 0; i < ie->getArity(); i++)
{
addReferenceFromItemExprTree(ie->child(i), addCol, addHBF, colRefVIDset);
}
return;
}
void HbaseAccess::addColReferenceFromVIDlist(const ValueIdList &exprList,
ValueIdSet &colRefVIDset)
{
for (CollIndex i = 0; i < exprList.entries(); i++)
{
addReferenceFromItemExprTree(exprList[i].getItemExpr(),
TRUE, FALSE, colRefVIDset);
}
}
void HbaseAccess::addReferenceFromVIDset(const ValueIdSet &exprList,
NABoolean addCol, NABoolean addHBF,
ValueIdSet &colRefVIDset)
{
for (ValueId v = exprList.init(); exprList.next(v); exprList.advance(v))
{
addReferenceFromItemExprTree(v.getItemExpr(), addCol, addHBF, colRefVIDset);
}
}
void HbaseAccess::addColReferenceFromRightChildOfVIDarray(ValueIdArray &exprList,
ValueIdSet &colRefVIDset)
{
for (CollIndex i = 0; i < exprList.entries(); i++)
{
addReferenceFromItemExprTree(exprList[i].getItemExpr()->child(1),
TRUE, FALSE, colRefVIDset);
}
}
static NABoolean isEqGetExpr(ItemExpr * ie, ValueId &vid, NABoolean &isConstParam,
const char * colName)
{
NABoolean found = FALSE;
isConstParam = FALSE;
if (ie && ie->getOperatorType() == ITM_EQUAL)
{
ItemExpr * child0 = ie->child(0)->castToItemExpr();
ItemExpr * child1 = ie->child(1)->castToItemExpr();
if ((ie->child(0)->getOperatorType() == ITM_BASECOLUMN) &&
(((BaseColumn*)ie->child(0)->castToItemExpr())->getNAColumn()->getColName() == colName) &&
(NOT hasColReference(ie->child(1))))
{
if (ie->child(1)->getOperatorType() == ITM_CONSTANT)
{
found = TRUE;
vid = ie->child(1)->getValueId();
}
else if (ie->child(1)->getOperatorType() == ITM_CACHE_PARAM)
{
found = TRUE;
isConstParam = TRUE;
vid = ie->child(1)->getValueId();
}
}
else if ((ie->child(1)->getOperatorType() == ITM_BASECOLUMN) &&
(((BaseColumn*)ie->child(1)->castToItemExpr())->getNAColumn()->getColName() == colName) &&
(NOT hasColReference(ie->child(0))))
{
if (ie->child(0)->getOperatorType() == ITM_CONSTANT)
{
found = TRUE;
vid = ie->child(0)->getValueId();
}
else if (ie->child(0)->getOperatorType() == ITM_CACHE_PARAM)
{
found = TRUE;
isConstParam = TRUE;
vid = ie->child(0)->getValueId();
}
}
else if ((ie->child(0)->getOperatorType() == ITM_INDEXCOLUMN) &&
(((IndexColumn*)ie->child(0)->castToItemExpr())->getNAColumn()->getColName() == colName) &&
(NOT hasColReference(ie->child(1))))
{
if (ie->child(1)->getOperatorType() == ITM_CONSTANT)
{
found = TRUE;
vid = ie->child(1)->getValueId();
}
else if (ie->child(1)->getOperatorType() == ITM_CACHE_PARAM)
{
found = TRUE;
isConstParam = TRUE;
vid = ie->child(1)->getValueId();
}
}
else if ((ie->child(1)->getOperatorType() == ITM_INDEXCOLUMN) &&
(((IndexColumn*)ie->child(1)->castToItemExpr())->getNAColumn()->getColName() == colName) &&
(NOT hasColReference(ie->child(0))))
{
if (ie->child(0)->getOperatorType() == ITM_CONSTANT)
{
found = TRUE;
vid = ie->child(0)->getValueId();
}
else if (ie->child(0)->getOperatorType() == ITM_CACHE_PARAM)
{
found = TRUE;
isConstParam = TRUE;
vid = ie->child(0)->getValueId();
}
}
else if ((ie->child(0)->getOperatorType() == ITM_REFERENCE) &&
(((ColReference*)ie->child(0)->castToItemExpr())->getCorrNameObj().getQualifiedNameObj().getObjectName() == colName) &&
(NOT hasColReference(ie->child(1))))
{
if (ie->child(1)->getOperatorType() == ITM_CONSTANT)
{
found = TRUE;
vid = ie->child(1)->getValueId();
}
else if (ie->child(1)->getOperatorType() == ITM_CACHE_PARAM)
{
found = TRUE;
isConstParam = TRUE;
vid = ie->child(1)->getValueId();
}
}
else if ((ie->child(1)->getOperatorType() == ITM_REFERENCE) &&
(((ColReference*)ie->child(1)->castToItemExpr())->getCorrNameObj().getQualifiedNameObj().getObjectName() == colName) &&
(NOT hasColReference(ie->child(0))))
{
if (ie->child(0)->getOperatorType() == ITM_CONSTANT)
{
found = TRUE;
vid = ie->child(0)->getValueId();
}
else if (ie->child(0)->getOperatorType() == ITM_CACHE_PARAM)
{
found = TRUE;
isConstParam = TRUE;
vid = ie->child(0)->getValueId();
}
}
}
return found;
}
RelExpr * HbaseDelete::preCodeGen(Generator * generator,
const ValueIdSet & externalInputs,
ValueIdSet &pulledNewInputs)
{
if (nodeIsPreCodeGenned())
return this;
// if a column list is specified, make sure all column names are of valid hbase
// column name format ("ColFam:ColNam")
if (csl())
{
for (Lng32 i = 0; i < csl()->entries(); i++)
{
const NAString * nas = (*csl())[i];
std::string colFam;
std::string colName;
if (nas)
{
ExFunctionHbaseColumnLookup::extractColFamilyAndName(
nas->data(), -1, FALSE, colFam, colName);
}
if (colFam.empty())
{
*CmpCommon::diags() << DgSqlCode(-1426)
<< DgString0(nas->data());
GenExit();
}
} // for
} // if
if (!processConstHBaseKeys(
generator,
this,
getSearchKey(),
getIndexDesc(),
executorPred(),
getHbaseSearchKeys(),
listOfDelUniqueRows_,
listOfDelSubsetRows_))
return NULL;
if (! Delete::preCodeGen(generator, externalInputs, pulledNewInputs))
return NULL;
if (((getTableDesc()->getNATable()->isHbaseRowTable()) ||
(getTableDesc()->getNATable()->isHbaseCellTable())) &&
(producesOutputs()))
{
*CmpCommon::diags() << DgSqlCode(-1425)
<< DgTableName(getTableDesc()->getNATable()->getTableName().
getQualifiedNameAsAnsiString())
<< DgString0("Reason: Cannot return values from an hbase insert, update or delete.");
GenExit();
}
NABoolean isAlignedFormat = getTableDesc()->getNATable()->isAlignedFormat(getIndexDesc());
if (producesOutputs())
{
retColRefSet_ = getIndexDesc()->getIndexColumns();
}
else
{
ValueIdSet colRefSet;
// create the list of columns that need to be retrieved from hbase .
// first add all columns referenced in the executor pred.
HbaseAccess::addReferenceFromVIDset(executorPred(), TRUE, TRUE, colRefSet);
if ((getTableDesc()->getNATable()->getExtendedQualName().getSpecialType() == ExtendedQualName::INDEX_TABLE))
{
for (ValueId valId = executorPred().init();
executorPred().next(valId);
executorPred().advance(valId))
{
ItemExpr * ie = valId.getItemExpr();
if (ie->getOperatorType() == ITM_EQUAL)
{
BiRelat * br = (BiRelat*)ie;
br->setSpecialNulls(TRUE);
}
}
} // index_table
if ((getTableDesc()->getNATable()->isHbaseRowTable()) ||
(getTableDesc()->getNATable()->isHbaseCellTable()) ||
isAlignedFormat)
{
for (Lng32 i = 0; i < getIndexDesc()->getIndexColumns().entries(); i++)
{
retColRefSet_.insert(getIndexDesc()->getIndexColumns()[i]);
}
}
for (ValueId valId = colRefSet.init();
colRefSet.next(valId);
colRefSet.advance(valId))
{
ValueId dummyValId;
if (NOT getGroupAttr()->getCharacteristicInputs().referencesTheGivenValue(valId, dummyValId))
{
if ((valId.getItemExpr()->getOperatorType() == ITM_HBASE_TIMESTAMP) ||
(valId.getItemExpr()->getOperatorType() == ITM_HBASE_VERSION))
{
*CmpCommon::diags() << DgSqlCode(-3242)
<< DgString0("Illegal use of Hbase Timestamp or Hbase Version function.");
GenExit();
}
retColRefSet_.insert(valId);
}
}
if (NOT ((getTableDesc()->getNATable()->isHbaseRowTable()) ||
(getTableDesc()->getNATable()->isHbaseCellTable()) ||
(isAlignedFormat)))
{
// add all the key columns. If values are missing in hbase, then atleast the key
// value is needed to retrieve a row.
HbaseAccess::addColReferenceFromVIDlist(getIndexDesc()->getIndexKey(), retColRefSet_);
}
if (getTableDesc()->getNATable()->hasLobColumn())
{
for (Lng32 i = 0; i < getIndexDesc()->getIndexColumns().entries(); i++)
{
const ValueId vid = getIndexDesc()->getIndexColumns()[i];
retColRefSet_.insert(vid);
}
}
}
NABoolean inlinedActions = FALSE;
if ((getInliningInfo().hasInlinedActions()) ||
(getInliningInfo().isEffectiveGU()))
inlinedActions = TRUE;
NABoolean isUnique = FALSE;
if (listOfDelSubsetRows_.entries() == 0)
{
if ((getSearchKey() && getSearchKey()->isUnique()) &&
(listOfDelUniqueRows_.entries() == 0))
isUnique = TRUE;
else if ((NOT (getSearchKey() && getSearchKey()->isUnique())) &&
(listOfDelUniqueRows_.entries() == 1) &&
(listOfDelUniqueRows_[0].rowIds_.entries() == 1))
isUnique = TRUE;
}
NABoolean hbaseRowsetVSBBopt =
(CmpCommon::getDefault(HBASE_ROWSET_VSBB_OPT) == DF_ON);
if ((getTableDesc()->getNATable()->isHbaseMapTable()) ||
(getTableDesc()->getNATable()->isHbaseRowTable()) ||
(getTableDesc()->getNATable()->isHbaseCellTable()))
hbaseRowsetVSBBopt = FALSE;
if (getInliningInfo().isIMGU()) {
// There is no need to do checkAndDelete for IM
canDoCheckAndUpdel() = FALSE;
uniqueHbaseOper() = FALSE;
if ((generator->oltOptInfo()->multipleRowsReturned()) &&
(hbaseRowsetVSBBopt) &&
(NOT generator->isRIinliningForTrafIUD()) &&
(NOT getTableDesc()->getNATable()->hasLobColumn()))
uniqueRowsetHbaseOper() = TRUE;
}
else
if (isUnique)
{
//If this unique delete is not part of a rowset operation ,
//don't allow it to be cancelled.
if (!generator->oltOptInfo()->multipleRowsReturned())
generator->setMayNotCancel(TRUE);
uniqueHbaseOper() = TRUE;
canDoCheckAndUpdel() = FALSE;
if ((NOT producesOutputs()) &&
(NOT inlinedActions) &&
(executorPred().isEmpty()))
{
if ((generator->oltOptInfo()->multipleRowsReturned()) &&
(hbaseRowsetVSBBopt) &&
(NOT generator->isRIinliningForTrafIUD()) &&
(NOT getTableDesc()->getNATable()->hasLobColumn()))
uniqueRowsetHbaseOper() = TRUE;
else if ((NOT generator->oltOptInfo()->multipleRowsReturned()) &&
(listOfDelUniqueRows_.entries() == 0))
{
if ((CmpCommon::getDefault(HBASE_CHECK_AND_UPDEL_OPT) == DF_ON) &&
(CmpCommon::getDefault(HBASE_SQL_IUD_SEMANTICS) == DF_ON) &&
(NOT isAlignedFormat))
canDoCheckAndUpdel() = TRUE;
}
}
}
if ((producesOutputs()) &&
((NOT isUnique) || (getUpdateCKorUniqueIndexKey())))
{
// Cannot do olt msg opt if:
// -- values are to be returned and unique operation is not being used.
// -- or this delete was transformed from an update of pkey/index key
// set an indication that multiple rows will be returned.
generator->oltOptInfo()->setMultipleRowsReturned(TRUE);
generator->oltOptInfo()->setOltCliOpt(FALSE);
}
if (getTableDesc()->getNATable()->hasLobColumn())
{
canDoCheckAndUpdel() = FALSE;
uniqueRowsetHbaseOper() = FALSE;
}
generator->setUpdSavepointOnError(FALSE);
generator->setUpdPartialOnError(FALSE);
// if unique oper with no index maintanence and autocommit is on, then
// do not require a trnsaction.
// Use hbase or region transactions.
// Hbase guarantees single row consistency.
Int64 transId = -1;
if (CmpCommon::getDefault(TRAF_NO_DTM_XN) == DF_ON)
{
// no transaction needed
noDTMxn() = TRUE;
}
else if ((uniqueHbaseOper()) &&
(NOT cursorHbaseOper()) &&
(NOT uniqueRowsetHbaseOper()) &&
(NOT inlinedActions) &&
(generator->getTransMode()->getAutoCommit() == TransMode::ON_) &&
(! NAExecTrans(0, transId)) &&
(NOT generator->oltOptInfo()->multipleRowsReturned()))
{
// no DTM transaction needed
useRegionXn() = FALSE;
if (CmpCommon::getDefault(TRAF_USE_REGION_XN) == DF_ON)
useRegionXn() = TRUE;
}
else
{
generator->setTransactionFlag(TRUE);
if ((NOT uniqueHbaseOper()) ||
(cursorHbaseOper()) ||
(uniqueRowsetHbaseOper()) ||
(inlinedActions) ||
(generator->oltOptInfo()->multipleRowsReturned()))
generator->setUpdAbortOnError(TRUE);
}
// flag for hbase tables
generator->setHdfsAccess(TRUE);
markAsPreCodeGenned();
return this;
}
RelExpr * HbaseUpdate::preCodeGen(Generator * generator,
const ValueIdSet & externalInputs,
ValueIdSet &pulledNewInputs)
{
if (nodeIsPreCodeGenned())
return this;
if (getTableDesc()->getNATable()->isHbaseMapTable())
{
*CmpCommon::diags() << DgSqlCode(-1425)
<< DgTableName(getTableDesc()->getNATable()->getTableName().
getQualifiedNameAsAnsiString())
<< DgString0("Reason: update not yet supported.");
GenExit();
}
if (!processConstHBaseKeys(
generator,
this,
getSearchKey(),
getIndexDesc(),
executorPred(),
getHbaseSearchKeys(),
listOfUpdUniqueRows_,
listOfUpdSubsetRows_))
return NULL;
// if (! GenericUpdate::preCodeGen(generator, externalInputs, pulledNewInputs))
// return NULL;
if (! UpdateCursor::preCodeGen(generator, externalInputs, pulledNewInputs))
return NULL;
CollIndex totalColCount = getTableDesc()->getColumnList().entries();
NABoolean isAlignedFormat = getTableDesc()->getNATable()->isAlignedFormat(getIndexDesc());
if (isAlignedFormat &&
(newRecExprArray().entries() > 0) &&
(newRecExprArray().entries() < totalColCount))
{
ValueIdArray holeyArray(totalColCount);
Lng32 i;
for (i = 0; i < newRecExprArray().entries(); i++)
{
ItemExpr * assign = newRecExprArray()[i].getItemExpr();
const NAColumn *nacol = assign->child(0).getNAColumn();
Lng32 colPos = nacol->getPosition();
holeyArray.insertAt(colPos, assign->getValueId());
} // for
for (i = 0; i < totalColCount; i++)
{
if (! (holeyArray.used(i)))
{
BaseColumn * bc = (BaseColumn*)getTableDesc()->getColumnList()[i].getItemExpr();
CMPASSERT(bc->getOperatorType() == ITM_BASECOLUMN);
ValueId srcId = getIndexDesc()->getIndexColumns()[i];
ItemExpr * an =
new(generator->wHeap()) Assign(bc, srcId.getItemExpr(), FALSE);
an->bindNode(generator->getBindWA());
holeyArray.insertAt(i, an->getValueId());
} // if
} // for
newRecExprArray().clear();
newRecExprArray() = holeyArray;
} // if aligned
if ((isMerge()) &&
(mergeInsertRecExpr().entries() > 0))
{
if ((listOfUpdSubsetRows_.entries() > 0) ||
(getSearchKey() && (NOT getSearchKey()->isUnique())))
{
*CmpCommon::diags() << DgSqlCode(-3241)
<< DgString0(" Non-unique ON clause not allowed with INSERT.");
GenExit();
}
}
if (((getTableDesc()->getNATable()->isHbaseRowTable()) ||
(getTableDesc()->getNATable()->isHbaseCellTable())) &&
(producesOutputs()))
{
*CmpCommon::diags() << DgSqlCode(-1425)
<< DgTableName(getTableDesc()->getNATable()->getTableName().
getQualifiedNameAsAnsiString())
<< DgString0("Reason: Cannot return values from an hbase insert, update or delete.");
GenExit();
}
NABoolean canDoRowsetOper = TRUE;
NABoolean canDoCheckAndUpdate = TRUE;
NABoolean needToGetCols = FALSE;
if (producesOutputs())
{
retColRefSet_ = getIndexDesc()->getIndexColumns();
}
else
{
ValueIdSet colRefSet;
// create the list of columns that need to be retrieved from hbase .
// first add all columns referenced in the executor pred.
HbaseAccess::addReferenceFromVIDset(executorPred(), TRUE, TRUE, colRefSet);
if ((getTableDesc()->getNATable()->getExtendedQualName().getSpecialType() == ExtendedQualName::INDEX_TABLE))
{
for (ValueId valId = executorPred().init();
executorPred().next(valId);
executorPred().advance(valId))
{
ItemExpr * ie = valId.getItemExpr();
if (ie->getOperatorType() == ITM_EQUAL)
{
BiRelat * br = (BiRelat*)ie;
br->setSpecialNulls(TRUE);
}
}
}
// add all columns referenced in the right side of the update expr.
HbaseAccess::addColReferenceFromRightChildOfVIDarray(newRecExprArray(), colRefSet);
if (isMerge())
HbaseAccess::addReferenceFromVIDset(mergeUpdatePred(), TRUE, FALSE, colRefSet);
if ((getTableDesc()->getNATable()->isHbaseRowTable()) ||
(getTableDesc()->getNATable()->isHbaseCellTable()) ||
(isAlignedFormat))
{
for (Lng32 i = 0; i < getIndexDesc()->getIndexColumns().entries(); i++)
{
retColRefSet_.insert(getIndexDesc()->getIndexColumns()[i]);
}
}
else
{
for (ValueId valId = colRefSet.init();
colRefSet.next(valId);
colRefSet.advance(valId))
{
ValueId dummyValId;
if (NOT getGroupAttr()->getCharacteristicInputs().referencesTheGivenValue(valId, dummyValId))
{
if ((valId.getItemExpr()->getOperatorType() == ITM_HBASE_TIMESTAMP) ||
(valId.getItemExpr()->getOperatorType() == ITM_HBASE_VERSION))
{
*CmpCommon::diags() << DgSqlCode(-3242)
<< DgString0("Illegal use of Hbase Timestamp or Hbase Version function.");
GenExit();
}
retColRefSet_.insert(valId);
}
}
}
if (retColRefSet_.entries() > 0)
{
needToGetCols = TRUE;
canDoRowsetOper = FALSE;
canDoCheckAndUpdate = FALSE;
}
// nullable and added columns in the row may be missing. That will cause
// a row to not be returned if those are the only columns that are being
// retrieved.
// To make sure that a row is always returned, add the key columns. These are
// guaranteed to be present in an hbase row.
HbaseAccess::addColReferenceFromVIDlist(getIndexDesc()->getIndexKey(), retColRefSet_);
}
NABoolean inlinedActions = FALSE;
if ((getInliningInfo().hasInlinedActions()) ||
(getInliningInfo().isEffectiveGU()))
inlinedActions = TRUE;
NABoolean isUnique = FALSE;
if (listOfUpdSubsetRows_.entries() == 0)
{
if ((getSearchKey() && getSearchKey()->isUnique()) &&
(listOfUpdUniqueRows_.entries() == 0))
isUnique = TRUE;
else if ((NOT (getSearchKey() && getSearchKey()->isUnique())) &&
(listOfUpdUniqueRows_.entries() == 1) &&
(listOfUpdUniqueRows_[0].rowIds_.entries() == 1))
isUnique = TRUE;
}
if (getInliningInfo().isIMGU()) {
// There is no need to checkAndPut for IM
canDoCheckAndUpdel() = FALSE;
uniqueHbaseOper() = FALSE;
if ((generator->oltOptInfo()->multipleRowsReturned()) &&
(CmpCommon::getDefault(HBASE_ROWSET_VSBB_OPT) == DF_ON) &&
(NOT generator->isRIinliningForTrafIUD()))
uniqueRowsetHbaseOper() = TRUE;
}
else
if (isUnique)
{
//If this unique delete is not part of a rowset operation ,
//don't allow it to be cancelled.
if (!generator->oltOptInfo()->multipleRowsReturned())
generator->setMayNotCancel(TRUE);
uniqueHbaseOper() = TRUE;
canDoCheckAndUpdel() = FALSE;
if ((NOT isMerge()) &&
(NOT producesOutputs()) &&
(executorPred().isEmpty()) &&
(NOT needToGetCols) &&
(NOT inlinedActions))
{
if ((generator->oltOptInfo()->multipleRowsReturned()) &&
(CmpCommon::getDefault(HBASE_ROWSET_VSBB_OPT) == DF_ON) &&
(NOT generator->isRIinliningForTrafIUD()))
uniqueRowsetHbaseOper() = TRUE;
else if ((NOT generator->oltOptInfo()->multipleRowsReturned()) &&
(listOfUpdUniqueRows_.entries() == 0))
{
if ((CmpCommon::getDefault(HBASE_CHECK_AND_UPDEL_OPT) == DF_ON) &&
(NOT isAlignedFormat))
canDoCheckAndUpdel() = TRUE;
}
}
}
else if (producesOutputs())
{
// Cannot do olt msg opt if:
// -- values are to be returned and unique operation is not being used.
// set an indication that multiple rows will be returned.
generator->oltOptInfo()->setMultipleRowsReturned(TRUE);
generator->oltOptInfo()->setOltCliOpt(FALSE);
}
generator->setUpdSavepointOnError(FALSE);
generator->setUpdPartialOnError(FALSE);
// if unique oper with no index maintanence and autocommit is on, then
// do not require a transaction.
// Use hbase or region transactions.
// Hbase guarantees single row consistency.
Int64 transId = -1;
if (CmpCommon::getDefault(TRAF_NO_DTM_XN) == DF_ON)
{
// no transaction needed
noDTMxn() = TRUE;
}
else if ((uniqueHbaseOper()) &&
(NOT isMerge()) &&
(NOT cursorHbaseOper()) &&
(NOT uniqueRowsetHbaseOper()) &&
(NOT inlinedActions) &&
(generator->getTransMode()->getAutoCommit() == TransMode::ON_) &&
(! NAExecTrans(0, transId)) &&
(NOT generator->oltOptInfo()->multipleRowsReturned()))
{
// no DTM transaction needed
useRegionXn() = FALSE;
if (CmpCommon::getDefault(TRAF_USE_REGION_XN) == DF_ON)
useRegionXn() = TRUE;
}
else
{
generator->setTransactionFlag(TRUE);
if ((NOT uniqueHbaseOper()) ||
(isMerge()) ||
(cursorHbaseOper()) ||
(uniqueRowsetHbaseOper()) ||
(inlinedActions) ||
(generator->oltOptInfo()->multipleRowsReturned()))
generator->setUpdAbortOnError(TRUE);
}
// flag for hbase tables
generator->setHdfsAccess(TRUE);
if (getTableDesc()->getNATable()->hasLobColumn())
{
for (CollIndex i = 0; i < newRecExprArray().entries(); i++)
{
NAColumn * col =
newRecExprArray()[i].getItemExpr()->child(0)->castToItemExpr()->
getValueId().getNAColumn(TRUE);
ItemExpr * val =
newRecExprArray()[i].getItemExpr()->child(1)->castToItemExpr();
if ((col->getType()->isLob()) &&
(val->getOperatorType() == ITM_LOBUPDATE))
{
LOBupdate * lu = (LOBupdate*)val;
lu->updatedTableObjectUID() =
getIndexDesc()->getPrimaryTableDesc()->
getNATable()->objectUid().castToInt64();
lu->updatedTableSchemaName() = "\"";
lu->updatedTableSchemaName() +=
getTableDesc()->getNATable()->
getTableName().getCatalogName();
lu->updatedTableSchemaName().append("\".\"");
lu->updatedTableSchemaName().
append(getTableDesc()->getNATable()->
getTableName().getSchemaName());
lu->updatedTableSchemaName() += "\"";
lu->lobSize() = col->getType()->getPrecision();
lu->lobNum() = col->lobNum();
if (lu->lobStorageType() == Lob_Empty)
{
lu->lobStorageType() = col->lobStorageType();
}
if (lu->lobStorageType() != col->lobStorageType())
{
*CmpCommon::diags() << DgSqlCode(-1432)
<< DgInt0((Int32)lu->lobStorageType())
<< DgInt1((Int32)col->lobStorageType())
<< DgString0(col->getColName());
GenExit();
}
lu->lobStorageLocation() = col->lobStorageLocation();
}
} // for
} // if
markAsPreCodeGenned();
return this;
}
RelExpr * HiveInsert::preCodeGen(Generator * generator,
const ValueIdSet & externalInputs,
ValueIdSet &pulledNewInputs)
{
if (nodeIsPreCodeGenned())
return this;
generator->setHiveAccess(TRUE);
return GenericUpdate::preCodeGen(generator, externalInputs, pulledNewInputs);
}
RelExpr * HbaseInsert::preCodeGen(Generator * generator,
const ValueIdSet & externalInputs,
ValueIdSet &pulledNewInputs)
{
if (nodeIsPreCodeGenned())
return this;
// char. outputs are set to empty after in RelExpr::genPreCode sometimes,
// after a call to resolveCharOutputs. We need to remember if a returnRow
// tdb flag should be set, even if no output columns are required
if (getIsTrafLoadPrep() && !getGroupAttr()->getCharacteristicOutputs().isEmpty())
setReturnRow(TRUE);
if (! GenericUpdate::preCodeGen(generator, externalInputs, pulledNewInputs))
return NULL;
NABoolean inlinedActions = FALSE;
if ((getInliningInfo().hasInlinedActions()) ||
(getInliningInfo().isEffectiveGU()))
inlinedActions = TRUE;
// Allow projecting rows if the upsert has IM.
if (inlinedActions && isUpsert())
setReturnRow(TRUE);
if (((getTableDesc()->getNATable()->isHbaseRowTable()) ||
(getTableDesc()->getNATable()->isHbaseCellTable())) &&
(producesOutputs()))
{
*CmpCommon::diags() << DgSqlCode(-1425)
<< DgTableName(getTableDesc()->getNATable()->getTableName().
getQualifiedNameAsAnsiString())
<< DgString0("Reason: Cannot return values from an hbase insert, update or delete.");
GenExit();
}
if ((isUpsert()) &&
((getInsertType() == Insert::VSBB_INSERT_USER) ||
(getInsertType() == Insert::UPSERT_LOAD)))
{
// Remove this restriction
/* if ((inlinedActions || producesOutputs())&& !getIsTrafLoadPrep())
setInsertType(Insert::SIMPLE_INSERT);*/
}
// if there are blob columns, use simple inserts.
if ( getTableDesc()->getNATable()->hasLobColumn())
{
setInsertType(Insert::SIMPLE_INSERT);
NAColumnArray colArray;
NAColumn *tgtCol, *srcCol;
for (CollIndex ii = 0;
ii < newRecExprArray().entries(); ii++)
{
ItemExpr *assignExpr =
newRecExprArray()[ii].getItemExpr();
ValueId tgtValueId =
assignExpr->child(0)->castToItemExpr()->getValueId();
ValueId srcValueId =
assignExpr->child(1)->castToItemExpr()->getValueId();
tgtCol = tgtValueId.getNAColumn( TRUE );
srcCol = srcValueId.getNAColumn( TRUE );
ItemExpr * child1Expr = assignExpr->child(1);
if (srcValueId.getType().isLob())
{
LOBinsert * li = NULL;
if ((child1Expr->getOperatorType() != ITM_LOBINSERT) &&
(child1Expr->getOperatorType() != ITM_LOBUPDATE))
{
li = new(generator->wHeap())
LOBinsert(child1Expr, NULL, LOBoper::LOB_);
li->insertedTableObjectUID() =
getIndexDesc()->getPrimaryTableDesc()->
getNATable()->objectUid().castToInt64();
li->insertedTableSchemaName() = "\"";
li->insertedTableSchemaName() +=
getTableDesc()->getNATable()->
getTableName().getCatalogName();
li->insertedTableSchemaName().append("\".\"");
li->insertedTableSchemaName().
append(getTableDesc()->getNATable()->
getTableName().getSchemaName());
li->insertedTableSchemaName() += "\"";
li->lobSize() = tgtValueId.getType().getPrecision();
li->lobFsType() = tgtValueId.getType().getFSDatatype();
li->lobNum() = tgtCol->lobNum();
if ((child1Expr->getOperatorType() == ITM_CONSTANT) &&
!(((ConstValue *)child1Expr)->isNull()))
{
if (srcCol->lobStorageType() != tgtCol->lobStorageType())
{
*CmpCommon::diags() << DgSqlCode(-1432)
<< DgInt0((Int32)srcCol->lobStorageType())
<< DgInt1((Int32)tgtCol->lobStorageType())
<< DgString0(tgtCol->getColName());
GenExit();
}
}
else
if ((child1Expr->getOperatorType() == ITM_BASECOLUMN)||
(child1Expr->getOperatorType() == ITM_INDEXCOLUMN))
{
if (srcCol->lobStorageType() != tgtCol->lobStorageType())
{
*CmpCommon::diags() << DgSqlCode(-1432)
<< DgInt0((Int32)srcCol->lobStorageType())
<< DgInt1((Int32)tgtCol->lobStorageType())
<< DgString0(tgtCol->getColName());
GenExit();
}
}
li->lobStorageType() = tgtCol->lobStorageType();
li->lobStorageLocation() = tgtCol->lobStorageLocation();
li->bindNode(generator->getBindWA());
child1Expr = li;
assignExpr->child(1) = child1Expr;
}
else if (child1Expr->getOperatorType() == ITM_LOBINSERT)
{
li = (LOBinsert*)child1Expr;
li->insertedTableObjectUID() =
getIndexDesc()->getPrimaryTableDesc()->
getNATable()->objectUid().castToInt64();
li->insertedTableSchemaName() = "\"";
li->insertedTableSchemaName() +=
getTableDesc()->getNATable()->
getTableName().getCatalogName();
li->insertedTableSchemaName().append("\".\"");
li->insertedTableSchemaName().
append(getTableDesc()->getNATable()->
getTableName().getSchemaName());
li->insertedTableSchemaName() += "\"";
li->lobNum() = tgtCol->lobNum();
//If we are initializing an empty_lob, assume the storage
//type of the underlying column
if (li->lobStorageType() == Lob_Empty)
{
li->lobStorageType() = tgtCol->lobStorageType();
}
if (li->lobStorageType() != tgtCol->lobStorageType())
{
*CmpCommon::diags() << DgSqlCode(-1432)
<< DgInt0((Int32)li->lobStorageType())
<< DgInt1((Int32)tgtCol->lobStorageType())
<< DgString0(tgtCol->getColName());
GenExit();
}
li->lobStorageLocation() = tgtCol->lobStorageLocation();
li->lobSize() = tgtValueId.getType().getPrecision();
if (li->lobFsType() != tgtValueId.getType().getFSDatatype())
{
// create a new LOBinsert node since fsType has changed.
ItemExpr * liChild = li->child(0);
ItemExpr * liChild1 = li->child(1);
li = new(generator->wHeap())
LOBinsert(liChild, liChild1, li->getObj(),FALSE,
li->lobAsVarchar());
li->insertedTableObjectUID() =
getIndexDesc()->getPrimaryTableDesc()->
getNATable()->objectUid().castToInt64();
li->insertedTableSchemaName() = "\"";
li->insertedTableSchemaName() +=
getTableDesc()->getNATable()->
getTableName().getCatalogName();
li->insertedTableSchemaName().append("\".\"");
li->insertedTableSchemaName().
append(getTableDesc()->getNATable()->
getTableName().getSchemaName());
li->insertedTableSchemaName() += "\"";
//li->lobSize() = srcValueId.getType().getPrecision();
li->lobSize() = tgtValueId.getType().getPrecision();
li->lobFsType() = tgtValueId.getType().getFSDatatype();
li->lobNum() = tgtCol->lobNum();
li->lobStorageLocation() = tgtCol->lobStorageLocation();
li->bindNode(generator->getBindWA());
assignExpr->child(1) = li;
}
} // lobinsert
GenAssert(li, "must have a LobInsert node");
} // lob
}
}
if ((getInsertType() == Insert::SIMPLE_INSERT) &&
(NOT getTableDesc()->getNATable()->hasLobColumn()))
uniqueHbaseOper() = TRUE;
generator->setUpdSavepointOnError(FALSE);
generator->setUpdPartialOnError(FALSE);
// if unique oper with no index maintanence and autocommit is on, then
// do not require a trnsaction.
// Use hbase or region transactions.
// Hbase guarantees single row consistency.
Int64 transId = -1;
if ((CmpCommon::getDefault(TRAF_NO_DTM_XN) == DF_ON) ||
(isNoRollback()) ||
((isUpsert()) && (insertType_ == UPSERT_LOAD)))
{
// no transaction needed
noDTMxn() = TRUE;
}
else if ((uniqueHbaseOper()) &&
(NOT uniqueRowsetHbaseOper()) &&
(NOT inlinedActions) &&
(generator->getTransMode()->getAutoCommit() == TransMode::ON_) &&
(! NAExecTrans(0, transId)) &&
(NOT generator->oltOptInfo()->multipleRowsReturned()))
{
// no DTM transaction needed
useRegionXn() = FALSE;
if (CmpCommon::getDefault(TRAF_USE_REGION_XN) == DF_ON)
useRegionXn() = TRUE;
}
else
{
generator->setTransactionFlag(TRUE);
if ((NOT uniqueHbaseOper()) ||
(uniqueRowsetHbaseOper()) ||
(inlinedActions) ||
(generator->oltOptInfo()->multipleRowsReturned()))
generator->setUpdAbortOnError(TRUE);
}
return this;
}
RelExpr * ExeUtilHiveTruncateLegacy::preCodeGen(Generator * generator,
const ValueIdSet & externalInputs,
ValueIdSet &pulledNewInputs)
{
if (nodeIsPreCodeGenned())
return this;
return ExeUtilExpr::preCodeGen(generator,externalInputs,pulledNewInputs);
}
RelExpr * ExeUtilLobExtract::preCodeGen(Generator * generator,
const ValueIdSet & externalInputs,
ValueIdSet &pulledNewInputs)
{
if (nodeIsPreCodeGenned())
return this;
if (! ExeUtilExpr::preCodeGen(generator,externalInputs,pulledNewInputs))
return NULL;
ValueIdSet availableValues;
for (ValueId exprId = getGroupAttr()->getCharacteristicInputs().init();
getGroupAttr()->getCharacteristicInputs().next(exprId);
getGroupAttr()->getCharacteristicInputs().advance(exprId) )
{
if (exprId.getItemExpr()->getOperatorType() != ITM_VEG_REFERENCE)
availableValues += exprId;
}
getGroupAttr()->setCharacteristicInputs(availableValues);
getInputValuesFromParentAndChildren(availableValues);
if (handle_)
handle_->replaceVEGExpressions
(availableValues, getGroupAttr()->getCharacteristicInputs());
markAsPreCodeGenned();
// Done.
return this;
}
RelExpr * ExeUtilLobUpdate::preCodeGen(Generator * generator,
const ValueIdSet & externalInputs,
ValueIdSet &pulledNewInputs)
{
if (nodeIsPreCodeGenned())
return this;
if (! ExeUtilExpr::preCodeGen(generator,externalInputs,pulledNewInputs))
return NULL;
ValueIdSet availableValues;
for (ValueId exprId = getGroupAttr()->getCharacteristicInputs().init();
getGroupAttr()->getCharacteristicInputs().next(exprId);
getGroupAttr()->getCharacteristicInputs().advance(exprId) )
{
if (exprId.getItemExpr()->getOperatorType() != ITM_VEG_REFERENCE)
availableValues += exprId;
}
getGroupAttr()->setCharacteristicInputs(availableValues);
getInputValuesFromParentAndChildren(availableValues);
if (handle_)
handle_->replaceVEGExpressions
(availableValues, getGroupAttr()->getCharacteristicInputs());
xnNeeded() = TRUE;
markAsPreCodeGenned();
// Done.
return this;
}
RelExpr * HashGroupBy::preCodeGen(Generator * generator,
const ValueIdSet & externalInputs,
ValueIdSet &pulledNewInputs)
{
if (nodeIsPreCodeGenned())
return this;
if ( CmpCommon::getDefault(COMPRESSED_INTERNAL_FORMAT) == DF_SYSTEM)
{
NABoolean resize = FALSE;
NABoolean defrag = FALSE;
ValueIdSet vidSet = child(0)->getGroupAttr()->getCharacteristicOutputs();
ExpTupleDesc::TupleDataFormat tupleFormat =
determineInternalFormat( vidSet,
this,
resize,
generator,
FALSE,
defrag);
cacheTupleFormatAndResizeFlag(tupleFormat, resize, defrag);
if (tupleFormat == ExpTupleDesc::SQLMX_ALIGNED_FORMAT)
{
generator->incNCIFNodes();
}
else
{
generator->decNCIFNodes();
}
}
return GroupByAgg::preCodeGen(generator, externalInputs, pulledNewInputs);
}
RelExpr * GroupByAgg::preCodeGen(Generator * generator,
const ValueIdSet & externalInputs,
ValueIdSet &pulledNewInputs)
{
if (nodeIsPreCodeGenned())
return this;
// Check if the pivs of this operator and it's child are the same.
// If they are not, make them the same.
replacePivs();
generator->clearPrefixSortKey();
// Resolve the VEGReferences and VEGPredicates, if any, that appear
// in the Characteristic Inputs, in terms of the externalInputs.
getGroupAttr()->resolveCharacteristicInputs(externalInputs);
// My Characteristic Inputs become the external inputs for my child.
child(0) = child(0)->preCodeGen(generator,
getGroupAttr()->getCharacteristicInputs(),
pulledNewInputs);
if (! child(0).getPtr())
return NULL;
if ((getOperatorType() == REL_SHORTCUT_GROUPBY)
&& (getFirstNRows() == 1))
{
RelExpr * firstnNode = new(generator->wHeap()) FirstN(child(0),
getFirstNRows(),
FALSE /* [any n] is good enough */);
firstnNode->setEstRowsUsed(getEstRowsUsed());
firstnNode->setMaxCardEst(getMaxCardEst());
firstnNode->setInputCardinality(child(0)->getInputCardinality());
firstnNode->setPhysicalProperty(child(0)->getPhysicalProperty());
firstnNode->setGroupAttr(child(0)->getGroupAttr());
//10-060516-6532 -Begin
//When FIRSTN node is created after optimization phase, the cost
//of that node does not matter.But, display_explain and explain
//show zero operator costs and rollup cost which confuses the user.
//Also, the VQP crashes when cost tab for FIRSTN node is selected.
//So, creating a cost object will fix this.
//The operator cost is zero and rollup cost is same as it childs.
Cost* firstnNodecost = new HEAP Cost();
firstnNode->setOperatorCost(firstnNodecost);
Cost* rollupcost = (Cost *)(child(0)->getRollUpCost());
*rollupcost += *firstnNodecost;
firstnNode->setRollUpCost(rollupcost);
//10-060516-6532 -End
firstnNode =
firstnNode->preCodeGen(generator,
getGroupAttr()->getCharacteristicInputs(),
pulledNewInputs);
if (! firstnNode)
return NULL;
setChild(0, firstnNode);
}
getGroupAttr()->addCharacteristicInputs(pulledNewInputs);
// Accumulate the values that are provided as inputs by my parent
// together with the values that are produced as outputs by my
// children. Use these values for rewriting the VEG expressions.
ValueIdSet availableValues;
getInputValuesFromParentAndChildren(availableValues);
NABoolean replicatePredicates = TRUE;
// Rebuild the grouping expressions tree. Use bridge values, if possible
groupExpr().replaceVEGExpressions
(availableValues,
getGroupAttr()->getCharacteristicInputs(),
FALSE, // No key predicates need to be generated here
NULL,
replicatePredicates,
&getGroupAttr()->getCharacteristicOutputs());
// Rebuild the rollup grouping expressions tree. Use bridge values, if possible
rollupGroupExprList().replaceVEGExpressions
(availableValues,
getGroupAttr()->getCharacteristicInputs(),
FALSE, // No key predicates need to be generated here
NULL,
replicatePredicates,
&getGroupAttr()->getCharacteristicOutputs());
// Rebuild the aggregate expressions tree
aggregateExpr().replaceVEGExpressions
(availableValues,
getGroupAttr()->getCharacteristicInputs());
if (CmpCommon::getDefault(COMP_BOOL_211) == DF_ON)
{
ValueIdSet constantsInGroupExpr ;
groupExpr().getConstantExprs(constantsInGroupExpr,FALSE);
if (constantsInGroupExpr.entries() > 0)
{
if (constantsInGroupExpr.entries() == groupExpr().entries())
{
ValueId vid ;
constantsInGroupExpr.getFirst(vid);
constantsInGroupExpr.remove(vid);
}
groupExpr() -= constantsInGroupExpr ;
}
}
// The VEG expressions in the selection predicates and the characteristic
// outputs can reference any expression that is either a potential output
// or a characteristic input for this RelExpr. Supply these values for
// rewriting the VEG expressions.
getInputAndPotentialOutputValues(availableValues);
selectionPred().replaceVEGExpressions
(availableValues,
getGroupAttr()->getCharacteristicInputs(),
FALSE, // No key predicates need to be generated here
NULL,
replicatePredicates);
getGroupAttr()->resolveCharacteristicOutputs
(availableValues,
getGroupAttr()->getCharacteristicInputs());
// if the grouping is executed in DP2, we don't do overflow
// handling. This also means, that it is a partial group by
// Do not do overflow handling for any partial groupby.
//
NABoolean isPartialGroupBy = (isAPartialGroupByNonLeaf() ||
isAPartialGroupByLeaf());
// The old way, only groupbys in DP2 are considered partial
//
if (CmpCommon::getDefault(COMP_BOOL_152) == DF_ON) {
isPartialGroupBy = executeInDP2();
}
if ((getOperatorType() == REL_HASHED_GROUPBY) && !isPartialGroupBy) {
// Count this BMO and add its needed memory to the total needed
generator->incrNumBMOs();
if ((ActiveSchemaDB()->getDefaults()).getAsDouble(BMO_MEMORY_LIMIT_PER_NODE_IN_MB) > 0)
generator->incrBMOsMemory(getEstimatedRunTimeMemoryUsage(generator, TRUE));
}
markAsPreCodeGenned();
// Done.
return this;
} // GroupByAgg::preCodeGen()
RelExpr * MergeUnion::preCodeGen(Generator * generator,
const ValueIdSet & externalInputs,
ValueIdSet &pulledNewInputs)
{
if (nodeIsPreCodeGenned())
return this;
// A temporary union (blocked union introduced for inlining after trigger)
// should not get here. Should be removed in optimization phase.
GenAssert(!getIsTemporary(), "Expecting this blocked union to be removed by this phase");
// Check if the pivs of this operator and it's child are the same.
// If they are not, make them the same.
replacePivs();
// clear any prefix sort key in generator work area
generator->clearPrefixSortKey();
// Resolve the VEGReferences and VEGPredicates, if any, that appear
// in the Characteristic Inputs, in terms of the externalInputs.
getGroupAttr()->resolveCharacteristicInputs(externalInputs);
// Predicate pushdown causes the Characteristic Inputs and Outputs
// of the union to be set precisely to those values that are
// required by one of its descendants or by one of its ancestors,
// respectively. However, the colMapTable_ contains all the values
// that the MergeUnion is capable of producing. The colMapTable_
// is rebuilt here to contain exactly those values that appear in
// the Characteristic Outputs.
//
// The output of the union is defined by the ValueIdUnion
// expressions that are maintained in the colMapTable_.
//
ValueIdSet charOutputs = getGroupAttr()->getCharacteristicOutputs();
colMapTable().clear();
for (ValueId v = charOutputs.init();
charOutputs.next(v); charOutputs.advance(v))
{
if (v.getItemExpr()->getOperatorType() != ITM_VALUEIDUNION)
{
// "other" available values besides the value being considered.
ValueIdSet availableValues = charOutputs;
availableValues -= v;
// -------------------------------------------------------------------
// see whether the value being considered is covered by the remaining
// values. that is, whether it is an expression in termes of the
// other vid union's.
// -------------------------------------------------------------------
ValueIdSet outputId;
outputId.insert(v);
outputId.removeUnCoveredExprs(availableValues);
// -------------------------------------------------------------------
// v removed from outputId. that means it's not covered by remaining
// vid union's. add the vid union's v is in terms of to colMapTable.
// the node needs to produce it. Instead of producing the expression,
// change the node to produce just the vid union, the expression can
// be evaluatated at the parent.
// -------------------------------------------------------------------
if (outputId.isEmpty())
{
Int32 leftIndex = getLeftMap().getTopValues().index(v);
Int32 rightIndex = getRightMap().getTopValues().index(v);
CMPASSERT((leftIndex != NULL_COLL_INDEX) &&
(rightIndex != NULL_COLL_INDEX));
ItemExpr *ptr = new(CmpCommon::statementHeap())
ValueIdUnion(getLeftMap().getBottomValues()[leftIndex],
getRightMap().getBottomValues()[rightIndex],v);
v.replaceItemExpr(ptr);
colMapTable().insert(v);
}
}
else
colMapTable().insert(v);
}
// My Characteristic Inputs become the external inputs for my children.
Lng32 nc = (Lng32)getArity();
const ValueIdSet & inputs = getGroupAttr()->getCharacteristicInputs();
for (Lng32 index = 0; index < nc; index++)
{
ValueIdSet pulledInputs;
child(index) = child(index)->preCodeGen(generator,inputs,pulledInputs);
if (child(index).getPtr() == NULL) return NULL;
pulledNewInputs += pulledInputs;
}
// Accumulate the values that are provided as inputs by my parent
// together with the values that are produced as outputs by my
// children. Use these values for rewriting the VEG expressions.
ValueIdSet availableValues;
getInputValuesFromParentAndChildren(availableValues);
// Rebuild the colMapTable
colMapTable().replaceVEGExpressions(availableValues,inputs);
// Rebuild the sortOrder.
sortOrder_.replaceVEGExpressions(availableValues,inputs);
// Rebuild the merge expression
if (mergeExpr_)
{
mergeExpr_ = mergeExpr_->replaceVEGExpressions(availableValues,inputs);
//10-061219-1283:Set the second arugment to TRUE to redrive typesynthesis of children.
mergeExpr_->synthTypeAndValueId(TRUE,TRUE);
}
// The VEG expressions in the selection predicates and the characteristic
// outputs can reference any expression that is either a potential output
// or a characteristic input for this RelExpr. Supply these values for
// rewriting the VEG expressions.
getInputAndPotentialOutputValues(availableValues);
// Rebuild the selection predicate tree.
selectionPred().replaceVEGExpressions(availableValues,inputs);
getGroupAttr()->resolveCharacteristicOutputs(availableValues,inputs);
// Rebuild the conditional expression.
condExpr().replaceVEGExpressions(availableValues,
getGroupAttr()->getCharacteristicInputs());
if (!getUnionForIF() && !getInliningInfo().isIMUnion())
generator->oltOptInfo()->setMultipleRowsReturned(TRUE);
markAsPreCodeGenned();
return this;
} // MergeUnion::preCodeGen()
RelExpr * MapValueIds::preCodeGen(Generator * generator,
const ValueIdSet & externalInputs,
ValueIdSet &pulledNewInputs)
{
const ValueIdList &upperValues = map_.getTopValues();
const ValueIdList &lowerValues = map_.getBottomValues();
if (nodeIsPreCodeGenned())
return this;
// Check if the pivs of this operator and it's child are the same.
// If they are not, make them the same.
replacePivs();
// Resolve the VEGReferences and VEGPredicates, if any, that appear
// in the Characteristic Inputs, in terms of the externalInputs.
getGroupAttr()->resolveCharacteristicInputs(externalInputs);
// My Characteristic Inputs become the external inputs for my children.
child(0) = child(0)->preCodeGen(
generator,
getGroupAttr()->getCharacteristicInputs(),
pulledNewInputs);
if (child(0).getPtr() == NULL)
return NULL;
getGroupAttr()->addCharacteristicInputs(pulledNewInputs);
if (cseRef_)
{
// -------------------------------------------------------------
// This MapValueIds represents a common subexpression.
//
// We need to take some actions here to help with VEG rewrite,
// since we eliminated some nodes from the tree, while the
// VEGies still contain all equated values, including those that
// got eliminated. Furthermore, the one tree that was chosen for
// materialization got moved and we need to make sure that the
// place where we scan the temp table produces the same ValueIds
// that were marked as "Bridge Values" when we processed the
// insert into temp statement.
// -------------------------------------------------------------
ValueIdSet cseVEGPreds;
const ValueIdList &vegCols(cseRef_->getColumnList());
ValueIdSet nonVegCols(cseRef_->getNonVEGColumns());
NABoolean isAnalyzingConsumer =
(CmpCommon::statement()->getCSEInfo(cseRef_->getName())->
getIdOfAnalyzingConsumer() == cseRef_->getId());
ValueIdSet availableValues(
getGroupAttr()->getCharacteristicInputs());
valuesNeededForVEGRewrite_ += cseRef_->getNonVEGColumns();
availableValues += valuesNeededForVEGRewrite_;
// find all the VEG predicates of the original columns that this
// common subexpression represents...
for (CollIndex v=0; v<vegCols.entries(); v++)
if (vegCols[v].getItemExpr()->getOperatorType() == ITM_VEG_REFERENCE)
{
// look at one particular VEG that is produced by this
// query tree
VEG *veg =
static_cast<VEGReference *>(vegCols[v].getItemExpr())->getVEG();
if (isAnalyzingConsumer && veg->getBridgeValues().entries() > 0)
{
// If we are looking at the analyzing consumer, then
// its child tree "C" got transformed into an
// "insert overwrite table "temp" select * from "C".
// This insert into temp statement chose some VEG
// member(s) as the "bridge value(s)". Find these bridge
// values and choose one to represent the VEG here.
const ValueIdSet &vegMembers(veg->getAllValues());
// collect all VEG members produced and subtract them
// from the values to be used for VEG rewrite
ValueIdSet subtractions(cseRef_->getNonVEGColumns());
// then add back only the bridge value
ValueIdSet additions;
// get the VEG members produced by child C
subtractions.intersectSet(vegMembers);
// augment the base columns with their index columns,
// the bridge value is likely an index column
for (ValueId v=subtractions.init();
subtractions.next(v);
subtractions.advance(v))
if (v.getItemExpr()->getOperatorType() == ITM_BASECOLUMN)
{
subtractions +=
static_cast<BaseColumn *>(v.getItemExpr())->getEIC();
}
// now find a bridge value (or values) that we can
// produce
additions = subtractions;
additions.intersectSet(veg->getBridgeValues());
// if we found it, then adjust availableValues
if (additions.entries() > 0)
{
availableValues -= subtractions;
availableValues += additions;
// do the same for valuesNeededForVEGRewrite_,
// which will be used for rewriting the char.
// outputs
valuesNeededForVEGRewrite_ -= subtractions;
valuesNeededForVEGRewrite_ += additions;
}
}
cseVEGPreds += veg->getVEGPredicate()->getValueId();
} // a VEGRef
// Replace the VEGPredicates, pretending that we still have
// the original tree below us, not the materialized temp
// table. This will hopefully keep the bookkeeping in the
// VEGies correct by setting the right referenced values
// and choosing the right bridge values.
cseVEGPreds.replaceVEGExpressions(
availableValues,
getGroupAttr()->getCharacteristicInputs());
} // this MapValueIds is for a common subexpression
// ---------------------------------------------------------------------
// The MapValueIds node describes a mapping between expressions used
// by its child tree and expressions used by its parent tree. The
// generator will make sure that the output values of the child tree
// and the input values from the parent get passed in the correct
// buffers.
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// Replacing VEGReferences in those mapped expressions is not possible
// in all cases; we have to restrict the kind of mappings that can
// be done for expressions involving VEGs. This method assumes that
// references to VEGs do not get altered during the rewrite, in other
// words it assumes mappings of the kind
//
// a) sum(VEGRef(a,b,c)) <----> VEGRef(a,b,c)
//
// and it disallows mappings of the kind
//
// b) count(VEGRef(a,b,c)) <-----> 1
// c) VEGRef(a,b,c) <-----> VEGRef(d,e,f)
//
// Mappings of type b) will still work, as long as the VEGRef is contained
// in some other mapped expression. A possible extension is to store
// in the MapValueIds node which element(s) of which VEGRef should
// be replaced in this step, but this information is hard to get
// during optimization, unless we are looking at a scan node.
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// The map contains many mappings, not all of which will have to
// be evaluated by the generator. Only those values that are either
// characteristic output values or are referenced by characteristic
// output values will actually be mapped at execution time. Therefore
// we first determine the actually needed mappings with the coverTest
// method.
// ---------------------------------------------------------------------
GroupAttributes emptyGA;
ValueIdSet coveredExpr;
ValueIdSet referencedUpperValues;
ValueIdMap newMap;
emptyGA.setCharacteristicInputs(getGroupAttr()->getCharacteristicInputs());
emptyGA.coverTest(
getGroupAttr()->getCharacteristicOutputs(), // the expressions needed
upperValues, // offer the upper values as extra inputs
coveredExpr, // doesn't matter which outputs are covered
referencedUpperValues); // get those upper values needed by the outputs
// Compute the values that are available here.
ValueIdSet lowerAvailableValues;
getOutputValuesOfMyChildren(lowerAvailableValues);
lowerAvailableValues += getGroupAttr()->getCharacteristicInputs();
// The VEGReferences that are resolved can appear as leaves of the
// expressions contained in lowerAvailableValues. These values are
// required for remapping the upperValues.
ValueIdSet leafValues;
ValueId x;
for (x = lowerAvailableValues.init();
lowerAvailableValues.next(x);
lowerAvailableValues.advance(x))
x.getItemExpr()->getLeafValueIds(leafValues);
lowerAvailableValues += leafValues;
ValueIdSet upperAvailableValues(valuesNeededForVEGRewrite_);
// The addition of the lower available values is only necessary to
// avoid an assertion failure in VEGReference::replaceVEGReference().
upperAvailableValues += lowerAvailableValues;
// ---------------------------------------------------------------------
// now walk through each needed mapping and replace wildcards in both its
// upper and lower expressions
// ---------------------------------------------------------------------
for (CollIndex i = 0; i < upperValues.entries(); i++)
{
if (referencedUpperValues.contains(upperValues[i]))
{
ItemExpr *newUpper;
ItemExpr *newLower;
// This mapping is actually required, expand wild cards for it
// We used to resolve the upper values using the
// upperAvailableValues. Note that these available values
// might not actually be available to this node. This could
// sometimes cause problems if the VEGRef was resolved to the
// 'wrong' value and the value is in a VEGPRed above. This
// would cause VEGPRed to be resolved incorrectly and
// possibly drop some join predicates.
// Don't need to replace the VEGgies in the upper since they
// will never be codeGen'ed. Just need to replace them with
// a suitable substitute.
// If it is a VEG_REF, then replace it with a surrogate
// (NATypeToItem) otherwise leave it as is. (Don't use the
// surrogate for all upper values because there are some
// MVIds that have BaseColumns in the upper values. These
// MVIds are introduced by Triggers. And these BaseColumns
// are used in other operators in other parts of the tree
// where they are expected to always be BaseColumns. So
// mapping them here will cause problems elsewhere). In any
// case, all we need to do here is to get rid of the
// VEGRefs.
//
newLower = lowerValues[i]
.getItemExpr()
->replaceVEGExpressions
(lowerAvailableValues,
getGroupAttr()->getCharacteristicInputs());
newUpper = upperValues[i].getItemExpr();
if (upperValues[i] != lowerValues[i])
{
if (newUpper->getOperatorType() == ITM_VEG_REFERENCE)
{
if (valuesNeededForVEGRewrite_.entries() > 0)
// If this node is used to map the outputs of one
// table to those of another, upperAvailableValues
// has been constructed to contain the base column a
// vegref should map to, so we use that instead of a
// created surrogate.
newUpper = newUpper->replaceVEGExpressions
(upperAvailableValues,
getGroupAttr()->getCharacteristicInputs());
else {
NAType *mapType =
upperValues[i].getType().newCopy(generator->wHeap());
// Create replacement for VEGRef
//
ItemExpr *mapping =
new(generator->wHeap()) NATypeToItem(mapType);
ValueId id = upperValues[i];
// Replace in ValueDescArray. All instances of this ID
// will now map to the surrogate.
//
id.replaceItemExpr(mapping);
newUpper = upperValues[i].getItemExpr();
}
}
} else {
// since they are the same, make upper equal to lower..
newUpper = newLower;
}
// add the mapping that may have been rewritten to the new map
newMap.addMapEntry(newUpper->getValueId(),newLower->getValueId());
}
}
// now replace the map with the recomputed mappings
map_ = newMap;
// The selectionPred() on a MapValueId should have been pushed down
// by the optimizer.
GenAssert(selectionPred().isEmpty(),"NOT selectionPred().isEmpty()");
// The VEG expressions in the selection predicates and the characteristic
// outputs can reference any expression that is either a potential output
// or a characteristic input for this RelExpr. Supply these values for
// rewriting the VEG expressions.
// Be thrifty. Reuse coveredExpr for gathering the input and output values.
getInputAndPotentialOutputValues(coveredExpr);
// Add the value that is being fabricated by the MapValueIds to the values
// that are produced by its child and flow throught the MapValueIds.
lowerAvailableValues += coveredExpr;
getGroupAttr()->resolveCharacteristicOutputs
(lowerAvailableValues,
getGroupAttr()->getCharacteristicInputs());
markAsPreCodeGenned();
return this;
} // MapValueIds::preCodeGen()
RelExpr * Sort::preCodeGen(Generator * generator,
const ValueIdSet & externalInputs,
ValueIdSet &pulledNewInputs)
{
if (nodeIsPreCodeGenned())
return this;
//else
// cerr << "Possible error..."
// Check if the pivs of this operator and it's child are the same.
// If they are not, make them the same.
replacePivs();
// Resolve the VEGReferences and VEGPredicates, if any, that appear
// in the Characteristic Inputs, in terms of the externalInputs.
getGroupAttr()->resolveCharacteristicInputs(externalInputs);
// if doing Partial Sorting, store partial sort key in generator work area
// if the split-top node is providing this underneath, protect the order
// else clear the partial sort key
ValueIdList prefixSortKey = getPrefixSortKey();
generator->clearPrefixSortKey();
if (!prefixSortKey.isEmpty())
generator->setPrefixSortKey(prefixSortKey);
PhysicalProperty* unPreCodeGendPP = NULL;
// Protect against scan of self-referencing table partitions
// completing asynchronously, thus allowing the various instances
// of SORT to start returning rows before all scans are complete.
// Let the PartitionAccess::preCodeGen and Exchange::preCodeGen
// work together to detect this. Part of the fix for solution
// 10-071204-9253.
bool doCheckUnsycHalloweenScans = false;
// solution 10-100310-8659
bool fixSolution8659 = false;
int numUnblockedHalloweenScansBefore =
generator->getUnblockedHalloweenScans();
bool needToRestoreLSH = false;
bool saveLSH = generator->getPrecodeHalloweenLHSofTSJ();
// This is the pre-R2.5.1 test that triggers the check unblocked access.
// Note that it indirectly depends on COMP_BOOL_166 OFF.
if (checkAccessToSelfRefTable_)
doCheckUnsycHalloweenScans = true;
// This is the R2.5.1 way -- see solution 10-100310-8659.
if ((generator->getPrecodeHalloweenLHSofTSJ()) &&
(!generator->getPrecodeRHSofNJ()))
{
if (generator->getHalloweenSortForced())
markAsHalloweenProtection();
if (generator->preCodeGenParallelOperator() &&
!generator->getHalloweenESPonLHS())
{
doCheckUnsycHalloweenScans = true;
fixSolution8659 = true;
}
else
{
// This serial sort is enough to block the
// scan of the target table. No need for further
// checking. Notice this serial vs. parallel sort test
// was made in NestedJoin::preCodeGen before the fix
// for 10-100310-8659.
doCheckUnsycHalloweenScans = false;
// More for 10-100310-8659 - don't call incUnblockedHalloweenScans
// below this node.
generator->setPrecodeHalloweenLHSofTSJ(false);
needToRestoreLSH = true;
GenAssert(generator->unsyncdSortFound() == FALSE,
"Unknown operator set unsyncdSortFound.");
}
}
if (doCheckUnsycHalloweenScans)
{
generator->setCheckUnsyncdSort(TRUE);
// Preserve a copy of the child's physical properties
// as it is before preCodeGen is called for the child.
// Also, in this copy of the physical properties, use
// a copy of the child's partitioning function. This
// will be used in case we need to insert an ESP for
// halloween protection.
unPreCodeGendPP = new (CmpCommon::statementHeap())
PhysicalProperty(*child(0)->getPhysicalProperty(),
child(0)->getPhysicalProperty()->getSortKey(),
child(0)->getPhysicalProperty()->getSortOrderType(),
child(0)->getPhysicalProperty()->getDp2SortOrderPartFunc(),
child(0)->getPhysicalProperty()->
getPartitioningFunction()->copy()
);
}
if ( CmpCommon::getDefault(COMPRESSED_INTERNAL_FORMAT) == DF_SYSTEM)
{
NABoolean resize = FALSE;
NABoolean defrag = FALSE;
// get the char outputs and not the child's
ValueIdSet vidSet = getGroupAttr()->getCharacteristicOutputs();
ExpTupleDesc::TupleDataFormat tupleFormat =
determineInternalFormat( vidSet,
this,
resize,
generator,
FALSE,
defrag);
cacheTupleFormatAndResizeFlag(tupleFormat, resize, defrag);
if (tupleFormat == ExpTupleDesc::SQLMX_ALIGNED_FORMAT)
{
generator->incNCIFNodes();
}
else
{
generator->decNCIFNodes();
}
}
// My Characteristic Inputs become the external inputs for my child.
child(0) = child(0)->preCodeGen(generator,
getGroupAttr()->getCharacteristicInputs(),
pulledNewInputs);
generator->clearPrefixSortKey();
if (! child(0).getPtr())
return NULL;
if (needToRestoreLSH)
generator->setPrecodeHalloweenLHSofTSJ(saveLSH);
getGroupAttr()->addCharacteristicInputs(pulledNewInputs);
// Accumulate the values that are provided as inputs by my parent
// together with the values that are produced as outputs by my
// children. Use these values for rewriting the VEG expressions.
ValueIdSet availableValues;
getInputValuesFromParentAndChildren(availableValues);
// ----------------------------------------------------------------------
// Replace VEGReferences in the order by list
// Bugfix: sol# 10-020909-1555/56: the last argument, if not explicitly
// stated, defaults to FALSE, and causes a shallow copy of the tree.
// ----------------------------------------------------------------------
sortKey_.replaceVEGExpressions
(availableValues,
getGroupAttr()->getCharacteristicInputs(),
FALSE, // default
NULL, // default
TRUE); // bugfix
// The VEG expressions in the selection predicates and the characteristic
// outputs can reference any expression that is either a potential output
// or a characteristic input for this RelExpr. Supply these values for
// rewriting the VEG expressions.
getInputAndPotentialOutputValues(availableValues);
getGroupAttr()->resolveCharacteristicOutputs
(availableValues,
getGroupAttr()->getCharacteristicInputs());
//Consider Sort as part of BMO memory participant if not partial sort.
if (prefixSortKey.entries() == 0 ||
CmpCommon::getDefault(COMP_BOOL_84) == DF_ON)
{
if (CmpCommon::getDefault(SORT_MEMORY_QUOTA_SYSTEM) != DF_OFF) {
generator->incrNumBMOs();
if ((ActiveSchemaDB()->getDefaults()).getAsDouble(BMO_MEMORY_LIMIT_PER_NODE_IN_MB) > 0)
generator->incrBMOsMemory(getEstimatedRunTimeMemoryUsage(generator, TRUE));
}
}
markAsPreCodeGenned();
// Part of the fix for solution 10-071204-9253.
// Modified for 10-100310-8659
if (doCheckUnsycHalloweenScans && generator->unsyncdSortFound())
{
RelExpr *newChild = generator->insertEspExchange(child(0),
unPreCodeGendPP);
((Exchange *)newChild)->markAsHalloweenProtection();
newChild =
newChild->preCodeGen(generator, externalInputs, pulledNewInputs);
GenAssert(newChild->getOperatorType() == REL_EXCHANGE,
"Exchange eliminated despite our best efforts.");
child(0) = newChild;
// Now that an ESP is inserted above the scans, this sort operator
// does block the scans, so we can discount them.
if (fixSolution8659)
{
generator->setUnsyncdSortFound(FALSE);
generator->setUnblockedHalloweenScans(
numUnblockedHalloweenScansBefore);
}
}
topNRows_ = generator->getTopNRows();
return this;
} // Sort::preCodeGen()
RelExpr * SortFromTop::preCodeGen(Generator * generator,
const ValueIdSet & externalInputs,
ValueIdSet &pulledNewInputs)
{
return Sort::preCodeGen(generator, externalInputs, pulledNewInputs);
}
RelExpr *ProbeCache::preCodeGen(Generator * generator,
const ValueIdSet & externalInputs,
ValueIdSet &pulledNewInputs)
{
if (nodeIsPreCodeGenned())
return this;
// Check if the pivs of this operator and it's child are the same.
// If they are not, make them the same.
replacePivs();
// Resolve the VEGReferences and VEGPredicates, if any, that appear
// in the Characteristic Inputs, in terms of the externalInputs.
getGroupAttr()->resolveCharacteristicInputs(externalInputs);
// My Characteristic Inputs become the external inputs for my child.
child(0) = child(0)->preCodeGen(generator,
getGroupAttr()->getCharacteristicInputs(),
pulledNewInputs);
if (! child(0).getPtr())
return NULL;
// add one more value to "valuesGivenToChild_": a statement execution
// count that will invalidate cache each time the statement is
// re-executed. It would be incorrect to cache across
// statement executions (and possibly transactions).
ValueId execCount = generator->getOrAddStatementExecutionCount();
pulledNewInputs += execCount;
getGroupAttr()->addCharacteristicInputs(pulledNewInputs);
// Accumulate the values that are provided as inputs by my parent
// together with the values that are produced as outputs by my
// children. Use these values for rewriting the VEG expressions.
ValueIdSet availableValues;
getInputValuesFromParentAndChildren(availableValues);
// Rewrite the selection predicates.
NABoolean replicatePredicates = TRUE;
selectionPred().replaceVEGExpressions
(availableValues,
getGroupAttr()->getCharacteristicInputs(),
FALSE, // no need to generate key predicates here
0 /* no need for idempotence here */,
replicatePredicates
);
getGroupAttr()->resolveCharacteristicOutputs
(availableValues,
getGroupAttr()->getCharacteristicInputs());
/*
TBD - maybe ProbeCache as BMO memory participant??
if(CmpCommon::getDefault(PROBE_CACHE_MEMORY_QUOTA_SYSTEM) != DF_OFF)
generator->incrNumBMOs();
if ((ActiveSchemaDB()->getDefaults()).getAsDouble(BMO_MEMORY_LIMIT_PER_NODE_IN_MB) > 0)
generator->incrNBMOsMemoryPerNode(getEstimatedRunTimeMemoryUsage(generator, TRUE));
*/
markAsPreCodeGenned();
return this;
} // ProbeCache::preCodeGen()
RelExpr * Exchange::preCodeGen(Generator * generator,
const ValueIdSet & externalInputs,
ValueIdSet &pulledNewInputs)
{
if (nodeIsPreCodeGenned())
return this;
// Set a flag if this is a parallel extract consumer. The flag for
// extract producer queries gets set earlier in RelRoot::codeGen()
if (child(0)->getOperatorType() == REL_EXTRACT_SOURCE)
{
isExtractConsumer_ = TRUE;
GenAssert(!isExtractProducer_,
"One extact query cannot be both producer and consumer");
}
const PhysicalProperty* sppOfChild = child(0)->getPhysicalProperty();
NABoolean PivsReplaced = FALSE;
if (sppOfChild->getPlanExecutionLocation() == EXECUTE_IN_DP2) {
// If this is not an ESP exchange, then check if the pivs of this op
// and it's child are the same. If they are not, make them the same.
// We don't do this for an ESP exchange because an ESP exchange
// denotes an ESP process boundary and the child's pivs
// do not have to be the same as the parent and in fact should
// not be the same.
replacePivs();
PivsReplaced = TRUE;
}
RelExpr *result = this;
// ---------------------------------------------------------------------
// copy important info from the properties into data members
// ---------------------------------------------------------------------
storePhysPropertiesInNode(generator->getPrefixSortKey());
// If this is a parallel extract producer query:
// - do a few checks to make sure the plan is valid
// - store a copy of the root's select list
if (isExtractProducer_)
{
RelRoot *root = generator->getBindWA()->getTopRoot();
// The plan is valid if this is an ESP exchange the number of
// bottom partitions matches the number of requested streams.
ComUInt32 numRequestedStreams = root->getNumExtractStreams();
ComUInt32 numBottomEsps = (ComUInt32)
getBottomPartitioningFunction()->getCountOfPartitions();
if (!isEspExchange() || (numRequestedStreams != numBottomEsps))
{
*CmpCommon::diags() << DgSqlCode(-7004);
GenExit();
return NULL;
}
// Make a copy of the root's select list
extractSelectList_ = new (generator->wHeap())
ValueIdList(root->compExpr());
// Do a coverage test to see find values in the select list that
// this operator cannot already provide
ValueIdSet valuesIDontHave(*extractSelectList_);
ValueIdSet coveredExpr;
ValueIdSet referencedUpperValues;
getGroupAttr()->coverTest(valuesIDontHave, // expressions needed
externalInputs, // extra inputs
coveredExpr, // covered exprs
referencedUpperValues); // new values needed
// Add the needed values to characteristic inputs
pulledNewInputs += referencedUpperValues;
getGroupAttr()->addCharacteristicInputs(referencedUpperValues);
}
// ---------------------------------------------------------------------
// Resolve the VEGReferences and VEGPredicates, if any, that appear
// in the Characteristic Inputs, in terms of the externalInputs.
// ---------------------------------------------------------------------
ValueIdSet saveCharInputs = getGroupAttr()->getCharacteristicInputs();
getGroupAttr()->resolveCharacteristicInputs(externalInputs);
// variables that store the result of the major decisions:
//
// makeThisExchangeAPapa: if this is a PAPA node, then make this
// node the PAPA (and add a PA below it)
// eliminateThisExchange: get rid of this node either because
// it represents a sole PA or because it is
// a redundant ESP exchange
// topPartFunc_: the partitioning function produced by
// this node after we're done with preCodeGen
// bottomPartFunc_: the partitioning function produced by
// the child of this node
// paPartFunc: the partitioning function produced by the
// PA node inserted below
// lbpf LogPhysPartitioningFunction of the child,
// if the child has such a part. function
NABoolean makeThisExchangeAPapa = FALSE;
NABoolean eliminateThisExchange = FALSE;
const PartitioningFunction *paPartFunc = topPartFunc_;
const LogPhysPartitioningFunction *lppf = NULL;
if (isDP2Exchange() AND
bottomPartFunc_->isALogPhysPartitioningFunction())
{
lppf = bottomPartFunc_->castToLogPhysPartitioningFunction();
if (lppf->getUsePapa() || getGroupAttr()->isEmbeddedUpdateOrDelete())
{
// Will a merge of sorted streams need to be done?
if (NOT sortKeyForMyOutput_.isEmpty())
{
Lng32 maxPartsPerGroup;
// Since a merge of sorted streams is needed, we must
// ensure that there is one PA for every partition in every
// process. The optimizer should already have set this up
// correctly, but sometimes, due to plan stealing, the value
// can be wrong. This code is really a patch for the plan
// stealing problem. We could try to fix the plan stealing
// problem, but that would adversely affect compile time.
// To set the number of clients (i.e. PAs) we must cast away
// the const-ness, sorry.
if (topPartFunc_->isAGroupingOf(*bottomPartFunc_,
&maxPartsPerGroup))
{
((LogPhysPartitioningFunction*)lppf)->setNumOfClients(
maxPartsPerGroup * topPartFunc_->getCountOfPartitions());
}
else
{
((LogPhysPartitioningFunction*)lppf)->setNumOfClients(
bottomPartFunc_->getCountOfPartitions() *
topPartFunc_->getCountOfPartitions());
}
}
// Keep this exchange and make it the PAPA node. The PA
// nodes below the PAPA will actually produce a partitioning
// scheme that is identical to that of the DP2 operator below,
// since the PAPA splits its requests into smaller ones that
// do not span DP2 partition boundaries.
makeThisExchangeAPapa = TRUE;
paPartFunc = bottomPartFunc_;
}
}
if (!PivsReplaced && isRedundant_)
replacePivs();
// flag to decide whether to use the characteristic inputs or outputs
// as input the to the CIF determineInternalFormatFunction
// if the the child is an insert or update then we consider the chars input
// otherwise we use the chars outputs
NABoolean useCharInputs = FALSE;
// ---------------------------------------------------------------------
// If the child of this Exchange executes in DP2, then allocate a
// PartitionAccess operator. It should have the same Group Attributes
// as its child.
// ---------------------------------------------------------------------
NABoolean savedOltMsgOpt = generator->oltOptInfo()->oltMsgOpt();
NABoolean inputOltMsgOpt = generator->oltOptInfo()->oltMsgOpt();
unsigned short prevNumBMOs = generator->replaceNumBMOs(0);
// These are used to fix solution 10-071204-9253 and for
// solution 10-100310-8659.
bool needToRestoreParallel = false;
NABoolean savedParallelSetting = FALSE;
bool needToRestoreCheckUnsync = false;
NABoolean savedCheckUnsyncdSort = FALSE;
bool needToRestoreLHS = false;
bool halloweenLHSofTSJ = generator->getPrecodeHalloweenLHSofTSJ();
bool needToRestoreESP = false;
bool halloweenESPonLHS = generator->getHalloweenESPonLHS();
if (isEspExchange() && getBottomPartitioningFunction()->isPartitioned())
{
// Tell any child NJ that its Halloween blocking operator (SORT)
// is operating in parallel.
savedParallelSetting = generator->preCodeGenParallelOperator();
generator->setPreCodeGenParallelOperator(TRUE);
needToRestoreParallel = true;
}
if (isEspExchange() && halloweenLHSofTSJ)
{
if ( !isRedundant_ )
{
// Tell any parallel SORT below that it doesn't have to check
// unsyncd access.
needToRestoreESP = true;
generator->setHalloweenESPonLHS(true);
}
savedCheckUnsyncdSort = generator->checkUnsyncdSort();
if (savedCheckUnsyncdSort == TRUE)
{
// savedCheckUnsyncdSort tells me there is a parallel SORT above this
// exchange. This ESP guarantees that all instances of the SORT will
// block until all instances of this ESP finish. So tell any child
// PARTITION ACCESS that its scan of a self-referencing is sync'd.
generator->setCheckUnsyncdSort(FALSE);
needToRestoreCheckUnsync = true;
// More for 10-100310-8659 - don't call incUnblockedHalloweenScans
// below this node.
halloweenLHSofTSJ = generator->setPrecodeHalloweenLHSofTSJ(false);
needToRestoreLHS = true;
}
}
else if (isEspExchange() &&
// this isPartitioned() condition is probably a bug, but
// to be safe I am not fixing it now.
getBottomPartitioningFunction()->isPartitioned())
{
// Tell any child PARTITION ACCESS that its scan of a self-referencing
// table is synchronized by an ESP exchange. That is, any blocking
// SORT operator above this exchange will not get any rows until all
// scans have finished.
savedCheckUnsyncdSort = generator->checkUnsyncdSort();
generator->setCheckUnsyncdSort(FALSE);
needToRestoreCheckUnsync = true;
}
if (halloweenSortIsMyChild_ && isRedundant_)
{
// Before eliminating itself, and before preCodeGen'ing the child
// tree, this Exchange will tell its child (a Sort) that it needs to
// check for unsynchronized access to the target table of a
// self-referencing update. This is part of the fix for
// solution 10-090310-9876.
((Sort *)(child(0).getPtr()))->doCheckAccessToSelfRefTable();
// Note for solution 10-100310-8659 -- the halloweenSortIsMyChild_
// flag will only be set when the COMP_BOOL_166 is used to revert
// to pre-bugfix behavior. With the fix for 10-100310-8659, the
// Sort uses the Generator's flags (precodeHalloweenLHSofTSJ and
// precodeRHSofNJ) to know if it needs check access to the target
// table. In other words, unless COMP_BOOL_166 is used, this
// is dead code.
}
if ( CmpCommon::getDefault(COMPRESSED_INTERNAL_FORMAT) == DF_SYSTEM)
{
NABoolean resize = FALSE;
NABoolean defrag = FALSE;
ValueIdSet vidSet;
if (!useCharInputs)
{
vidSet = child(0)->getGroupAttr()->getCharacteristicOutputs();
}
else
{
vidSet = saveCharInputs;
}
ExpTupleDesc::TupleDataFormat tupleFormat =
determineInternalFormat( vidSet,
this,
resize,
generator,
FALSE,
defrag);
cacheTupleFormatAndResizeFlag(tupleFormat, resize, defrag);
if (tupleFormat == ExpTupleDesc::SQLMX_ALIGNED_FORMAT)
{
generator->incNCIFNodes();
}
else
{
generator->decNCIFNodes();
}
}
// For HashJoin MIN/MAX optimization. If this is an ESP Exchange,
// block all candidate values for min/max optimization from going
// below this Exchange. Restore them upon return from
// preCodeGen'ing the child.
ValueIdList minMaxKeys, minVals, maxVals, willUseMinMaxKeys;
if(isEspExchange()) {
// Save the current values.
minMaxKeys = generator->getMinMaxKeys();
minVals = generator->getMinVals();
maxVals = generator->getMaxVals();
willUseMinMaxKeys = generator->getWillUseMinMaxKeys();
// Clear the current values.
generator->getMinMaxKeys().clear();
generator->getMinVals().clear();
generator->getMaxVals().clear();
generator->getWillUseMinMaxKeys().clear();
}
// ---------------------------------------------------------------------
// Perform preCodeGen on the child (including PA node if we created it)
// ---------------------------------------------------------------------
child(0) = child(0)->preCodeGen(
generator,
getGroupAttr()->getCharacteristicInputs(),
pulledNewInputs);
// For HashJoin MIN/MAX optimization.
if(isEspExchange()) {
// Restore the saved values.
generator->getMinMaxKeys() = minMaxKeys;
generator->getMinVals() = minVals;
generator->getMaxVals() = maxVals;
generator->getWillUseMinMaxKeys() = willUseMinMaxKeys;
}
if (needToRestoreParallel)
generator->setPreCodeGenParallelOperator(savedParallelSetting);
if (needToRestoreCheckUnsync)
generator->setCheckUnsyncdSort(savedCheckUnsyncdSort);
if (needToRestoreLHS)
generator->setPrecodeHalloweenLHSofTSJ(halloweenLHSofTSJ);
if (needToRestoreESP)
generator->setHalloweenESPonLHS(halloweenESPonLHS);
setNumBMOs( generator->replaceNumBMOs(prevNumBMOs) );
if (! child(0).getPtr())
return NULL;
generator->oltOptInfo()->setOltMsgOpt(savedOltMsgOpt);
// Decide whether this Exchange should try to eliminate itself.
if (child(0)->castToRelExpr()->getOperatorType() == REL_EXE_UTIL)
{
// No, the REL_EXE_UTIL must execute in an ESP.
}
else if (skipRedundancyCheck_)
{
// No, the ESP was inserted just to force blocking of
// data from SORT instances, to help prevent Halloween
// problem -- see Soln 10-071204-9253.
}
else
{
// Yes, perform the redundancy check.
eliminateThisExchange = (isRedundant_ OR
(isDP2Exchange() AND NOT makeThisExchangeAPapa));
}
// ---------------------------------------------------------------------
// Determine which partition input values need to be supplied by our
// parent and which are produced by this exchange node. PA or PAPA
// exchange nodes (DP2 exchange nodes) do not produce any partition
// input values themselves, just ask the parent to produce the PIVs
// needed by the child. ESP exchanges produce the PIVs for their bottom
// partition function, and this is also true for added repartitioning
// exchanges.
// ---------------------------------------------------------------------
if (isEspExchange())
{
pulledNewInputs -= bottomPartFunc_->getPartitionInputValues();
setBottomPartitionInputValues(
bottomPartFunc_->getPartitionInputValuesLayout());
}
getGroupAttr()->addCharacteristicInputs(pulledNewInputs);
// ---------------------------------------------------------------------
// The VEG expressions in the selection predicates and the characteristic
// outputs can reference any expression that is either a potential output
// or a characteristic input for this RelExpr. Supply these values for
// rewriting the VEG expressions.
// ---------------------------------------------------------------------
ValueIdSet availableValues;
getInputAndPotentialOutputValues(availableValues);
// Accumulate the values that are provided as inputs by my parent
// together with the values that are produced as outputs by my
// children. Use these values for rewriting the VEG expressions.
// ---------------------------------------------------------------------
// Rewrite the copy of the sort key which will be used for merging
// rows. The VEGRef on the column being sorted may be preceeded by
// an InverseOrder itemExpr (in case the shortcut_grby rule has fired)
// The InverseOrder itemExpr will not perform a copy of the sortKey
// before replacing VEGExpressions unless replicateExpression is set
// to TRUE below. This avoids inverse(VEGRef_60(T1.a = T2.a)) being
// resolved to T1.a in two different exchange nodes, even though T1.a
// is not available at the second exchange node.
// ---------------------------------------------------------------------
NABoolean replicateExpression = TRUE;
sortKeyForMyOutput_.replaceVEGExpressions
(availableValues,
getGroupAttr()->getCharacteristicInputs(),
FALSE, // no key predicates here
0 /* no need for idempotence here */,
replicateExpression
);
// ---------------------------------------------------------------------
// Rewrite the partitioning expression, if the repartitioning function
// contains one. A ReplicationPartitioningFunction does not contain
// a partititioning expression because it uses a broadcast for
// replicating rows to its consumers.
// ---------------------------------------------------------------------
if (isEspExchange())
{
PartitioningFunction * rpf;
// need to cast away const-ness to create partitioning expr, sorry
rpf = (PartitioningFunction *) topPartFunc_;
rpf->createPartitioningExpression();
rpf->preCodeGen(availableValues);
}
// ---------------------------------------------------------------------
// For a parallel extract producer query, rewrite our copy of the
// root's select list
// ---------------------------------------------------------------------
if (isExtractProducer_)
{
extractSelectList_->
replaceVEGExpressions(availableValues,
getGroupAttr()->getCharacteristicInputs());
}
// ---------------------------------------------------------------------
// Resolve characteristic outputs.
// ---------------------------------------------------------------------
getGroupAttr()->resolveCharacteristicOutputs
(availableValues,
getGroupAttr()->getCharacteristicInputs());
generator->oltOptInfo()->mayDisableOperStats(&oltOptInfo());
// ---------------------------------------------------------------------
// From here on we add or remove exchange nodes, but this node is
// ready and does not need to be processed again should we call
// preCodeGen for it again.
// ---------------------------------------------------------------------
markAsPreCodeGenned();
// ---------------------------------------------------------------------
// Eliminate this exchange if it simply represented the PA node or
// if it is redundant. Do not eliminate the exchange if it is a
// parallel extract producer or consumer.
// ---------------------------------------------------------------------
if (isExtractProducer_ || isExtractConsumer_)
eliminateThisExchange = FALSE;
if (eliminateThisExchange)
{
result = child(0).getPtr();
// transfer the # of BMOs to generator as
// this exchange node is to be discarded.
generator->incrNumBMOsPerFrag(getNumBMOs());
}
if ((isEspExchange()) &&
(NOT eliminateThisExchange))
{
// generator->setUpdAbortOnError(TRUE);
generator->setUpdSavepointOnError(FALSE);
generator->setUpdErrorOnError(FALSE);
generator->compilerStatsInfo().exchangeOps()++;
generator->compilerStatsInfo().dop() =
(UInt16)MAXOF(generator->compilerStatsInfo().dop(),
getBottomPartitioningFunction()->getCountOfPartitions());
if ( getNumBMOs() > 0 )
generator->incTotalESPs();
// If the exchange uses SeaMonster, set a flag in the generator
// to indicate that some part of the query does use SeaMonster
if (thisExchangeCanUseSM(generator->getBindWA()))
generator->setQueryUsesSM(TRUE);
} // isEspExchange() && !eliminateThisExchange
return result;
} // Exchange::preCodeGen()
RelExpr * Tuple::preCodeGen(Generator * generator,
const ValueIdSet & externalInputs,
ValueIdSet &pulledNewInputs)
{
if (nodeIsPreCodeGenned())
return this;
if (! RelExpr::preCodeGen(generator,externalInputs,pulledNewInputs))
return NULL;
// Accumulate the values that are provided as inputs by my parent
// together with the values that are produced as outputs by my
// children. Use these values for rewriting the VEG expressions.
ValueIdSet availableValues = getGroupAttr()->getCharacteristicInputs();
tupleExpr().replaceVEGExpressions
(availableValues,
getGroupAttr()->getCharacteristicInputs());
return this;
}
ItemExpr * BuiltinFunction::preCodeGen(Generator * generator)
{
ItemExpr * retExpr = NULL;
if (nodeIsPreCodeGenned())
return getReplacementExpr();
if (CmpCommon::getDefault(OR_PRED_KEEP_CAST_VC_UCS2) == DF_ON) {
// part of temporary workaround to yotta dp2 killer problem:
// keep cast for upper(cast name as varchar(n) char set ucs2)
switch (getOperatorType()) {
case ITM_UPPER:
case ITM_LOWER:
case ITM_SUBSTR:
case ITM_TRIM:
if (child(0)->getOperatorType() == ITM_CAST) {
Cast *arg = (Cast*)child(0)->castToItemExpr();
const NAType& typ = arg->getValueId().getType();
if (arg->matchChildType() &&
arg->child(0)->getValueId().getType() == typ &&
typ.getTypeQualifier() == NA_CHARACTER_TYPE &&
typ.isVaryingLen() &&
((CharType*)(&typ))->getCharSet() == CharInfo::UCS2) {
// don't skip codegen for the cast of
// "upper(cast name as varchar(n) char set ucs2) IN <inlist>"
arg->setMatchChildType(FALSE);
}
}
}
}
if (! ItemExpr::preCodeGen(generator))
return NULL;
switch (getOperatorType())
{
case ITM_QUERYID_EXTRACT:
{
// convert arguments to ISO88591 character set
if (child(0)->castToItemExpr()->getValueId().getType().getTypeQualifier() ==
NA_CHARACTER_TYPE)
{
const CharType &typ0 =
(const CharType &) (child(0)->castToItemExpr()->getValueId().getType());
if (typ0.getCharSet() != CharInfo::ISO88591)
{
// the executor method assumes an ASCII string for the query id, so
// convert the value to a fixed char type in the ISO88591 char set
SQLChar * newTyp0 = new(generator->wHeap())
SQLChar(generator->wHeap(), typ0.getCharLimitInUCS2or4chars(),
typ0.supportsSQLnullLogical(),
typ0.isUpshifted(),
typ0.isCaseinsensitive(),
typ0.isVaryingLen(),
CharInfo::ISO88591);
child(0) = new (generator->wHeap()) Cast(child(0), newTyp0);
child(0)->bindNode(generator->getBindWA());
child(0) = child(0)->preCodeGen(generator);
}
}
if (child(1)->castToItemExpr()->getValueId().getType().getTypeQualifier() ==
NA_CHARACTER_TYPE)
{
const CharType &typ1 =
(const CharType &) (child(1)->castToItemExpr()->getValueId().getType());
if (typ1.getCharSet() != CharInfo::ISO88591)
{
// the executor method assumes an ASCII string for the query id, so
// convert the value to a fixed char type in the ISO88591 char set
SQLChar * newTyp1 = new(generator->wHeap())
SQLChar(generator->wHeap(), typ1.getCharLimitInUCS2or4chars(),
typ1.supportsSQLnullLogical(),
typ1.isUpshifted(),
typ1.isCaseinsensitive(),
typ1.isVaryingLen(),
CharInfo::ISO88591);
child(1) = new (generator->wHeap()) Cast(child(1), newTyp1);
child(1)->bindNode(generator->getBindWA());
child(1) = child(1)->preCodeGen(generator);
}
}
}
retExpr = this;
break;
default:
{
retExpr = this;
}
break;
} // switch
setReplacementExpr(retExpr);
markAsPreCodeGenned();
return retExpr;
}
/*
ItemExpr * Abs::preCodeGen(Generator * generator)
{
// The ABS function has the distinction of being the sole BuiltinFunction
// that a) generates a new replacementExpr tree
// and b) can appear in the select-list (compExpr).
//
// What happens is that code is generated for the ABS replacement CASE
// TWICE, once in PartitionAccess eid, once in RelRoot generateOutputExpr:
// the latter fails with a GenMapTable assert failing to find info for
// the column in "SELECT ABS(col) FROM t;"
// ("SELECT ABS(-1) FROM t;" and "SELECT ABS(col),col FROM T;" work fine --
// but of course they generate twice as much code as necessary,
// however harmless/idempotent it may be...)
//
// We therefore cannot handle this one discrepant case neatly in
// preCodeGen/codeGen -- it is fixed instead by having the Binder
// upstream rewrite an ABS as the equivalent CASE.
//
// Genesis 10-980112-5942.
//
GenAssert(FALSE, "Abs::preCodeGen should be unreachable code!");
return NULL;
//if (nodeIsPreCodeGenned())
// return getReplacementExpr();
//
//ItemExpr * newExpr =
// generator->getExpGenerator()->createExprTree(
// "CASE WHEN @A1 < 0 THEN - @A1 ELSE @A1 END", 0, 1, child(0));
//
//newExpr->bindNode(generator->getBindWA());
//setReplacementExpr(newExpr->preCodeGen(generator));
//markAsPreCodeGenned();
//return getReplacementExpr();
}
*/
ItemExpr * Abs::preCodeGen(Generator * generator)
{
if (nodeIsPreCodeGenned())
return getReplacementExpr();
if (! ItemExpr::preCodeGen(generator))
return NULL;
NAType * result_type = (NAType *)(&(getValueId().getType()));
NAType * type_op1 = (NAType *)(&(child(0)->castToItemExpr()->getValueId().getType()));
if (! (*result_type == *type_op1))
{
// Insert a cast node to convert child to a result type.
child(0) = new (generator->wHeap())
Cast(child(0), result_type);
child(0)->bindNode(generator->getBindWA());
child(0) = child(0)->preCodeGen(generator);
if (! child(0).getPtr())
return NULL;
}
markAsPreCodeGenned();
return this;
} // Abs::preCodeGen()
ItemExpr * AggrMinMax::preCodeGen(Generator * generator)
{
if (nodeIsPreCodeGenned())
return getReplacementExpr();
if (! ItemExpr::preCodeGen(generator))
return NULL;
// if my child's attributes EXCEPT for nullability are not the
// same as mine, do a conversion.
NABoolean doConversion = FALSE;
const NAType &myType = getValueId().getType();
const NAType &childType = child(0)->castToItemExpr()->getValueId().getType();
if (NOT (myType == childType)) // something is different
{
if ((myType.supportsSQLnull() &&
childType.supportsSQLnull()) ||
((NOT myType.supportsSQLnull()) &&
(NOT childType.supportsSQLnull())))
doConversion = TRUE; // both nullable or not nullable,
// something else is different
else if (myType.supportsSQLnull() &&
NOT childType.supportsSQLnull())
{
// create a new my type with the same null attr as child.
NAType * newType = myType.newCopy(generator->wHeap());
newType->resetSQLnullFlag();
if (NOT(*newType == childType))
doConversion = TRUE;
delete newType;
}
else
{
// Fix for solution ID 10-031121-1505)
// I dont think we the following assert is correct
// During VEG resolution a MIN/MAX() function can have a
// NON-NULLABLE child replaced by a nullable child, consider
// as an example the following query where i2 is not null:
//
// SELECT MIN(T0.i2)
// FROM D12 T0
// WHERE
// ?pa2 = T0.i2
// GROUP BY T0.i1;
//
// In the above case i2 will be replaced by ?pa2 when the VEG
// (i2, ?pa2) is resolved. Therefore it is possible to get a
// nullable child for a non-nullable aggregate. In the above
// case the aggregate is non-nullable because i2 is non-nullable.
// In such a case MIN(?pa2) would never be executed if ?pa2 is NULL
// because predicate '?pa2 = T0.i2' will not select any rows when
// ?pa2 is NULL (I am not sure how a parameter is set to NULL, for host
// vars we can use the NULL indicator, not sure how we pass in NULL using
// parameters).
//
// Assert on the following condition
// The condition where I am not nullable and my child is nullable,
// is an error case.
//GenAssert(0, "AggrMinMax::preCodeGen::Should not reach here.");
doConversion = TRUE;
}
}
if (doConversion)
{
// Insert a cast node to convert child to a result type.
child(0) = new (generator->wHeap()) Cast(child(0), &myType);
child(0)->bindNode(generator->getBindWA());
child(0) = child(0)->preCodeGen(generator);
if (! child(0).getPtr())
return NULL;
}
markAsPreCodeGenned();
return this;
} // AggrMinMax::preCodeGen()
ItemExpr * Between::preCodeGen(Generator * generator)
{
if (nodeIsPreCodeGenned())
return getReplacementExpr();
// transform "A BETWEEN B AND C" to "A >= B AND A <= C"
ItemExpr * newExpr =
generator->getExpGenerator()->createExprTree(
"@A1 >= @A2 AND @A1 <= @A3", 0, 3, child(0), child(1), child(2));
newExpr->bindNode(generator->getBindWA());
setReplacementExpr(newExpr->preCodeGen(generator));
markAsPreCodeGenned();
return getReplacementExpr();
}
// BiArithCount::preCodeGen
//
// The BiArithCount executor clause requires that all of the operands
// be of the same type. preCodeGen introduces cast operators on the
// input operands if necessary to enforce this requirement.
//
ItemExpr * BiArithCount::preCodeGen(Generator * generator)
{
if (nodeIsPreCodeGenned())
return this;
// Get a local handle on common generator objects.
//
CollHeap *wHeap = generator->wHeap();
const NAType &resultType = getValueId().getType();
const NAType &op1Type = child(0)->castToItemExpr()->getValueId().getType();
const NAType &op2Type = child(1)->castToItemExpr()->getValueId().getType();
// If the first operand type does not match that of the result,
// cast it to the result type.
//
if(!(op1Type == resultType))
{
child(0) = new(wHeap) Cast(child(0)->castToItemExpr(),
resultType.newCopy(wHeap),
ITM_CAST);
child(0)->synthTypeAndValueId();
}
// Ditto for the second operand.
//
if(!(op2Type == resultType))
{
child(1) = new(wHeap) Cast(child(1)->castToItemExpr(),
resultType.newCopy(wHeap),
ITM_CAST);
child(1)->synthTypeAndValueId();
}
return BiArith::preCodeGen(generator);
}
// BiArithSum::preCodeGen
//
// The BiArithSum executor clause requires that all of the operands
// be of the same type. preCodeGen introduces cast operators on the
// input operands if necessary to enforce this requirement.
//
ItemExpr * BiArithSum::preCodeGen(Generator * generator)
{
if (nodeIsPreCodeGenned())
return this;
// Get a local handle on common generator objects.
//
CollHeap *wHeap = generator->wHeap();
// Get a handle on the operand types.
//
const NAType &resultType = getValueId().getType();
const NAType &op1Type = child(0)->castToItemExpr()->getValueId().getType();
const NAType &op2Type = child(1)->castToItemExpr()->getValueId().getType();
// If the first operand type does not match that of the result,
// cast it to the result type.
//
if(!(op1Type == resultType))
{
child(0) = new(wHeap) Cast(child(0)->castToItemExpr(),
resultType.newCopy(wHeap),
ITM_CAST);
child(0)->synthTypeAndValueId();
}
// Ditto for the second operand.
//
if(!(op2Type == resultType))
{
child(1) = new(wHeap) Cast(child(1)->castToItemExpr(),
resultType.newCopy(wHeap),
ITM_CAST);
child(1)->synthTypeAndValueId();
}
ItemExpr *result = BiArith::preCodeGen(generator);
if (! result)
return NULL;
ItemExpr *outExpr = NULL;
Lng32 rc = generator->getExpGenerator()->foldConstants(child(0), &outExpr);
if ((rc == 0) &&
(outExpr))
{
child(0) = outExpr->preCodeGen(generator);
}
rc = generator->getExpGenerator()->foldConstants(child(1), &outExpr);
if ((rc == 0) &&
(outExpr))
{
child(1) = outExpr->preCodeGen(generator);
}
return this;
}
ItemExpr * BiArith::preCodeGen(Generator * generator)
{
if (nodeIsPreCodeGenned())
return this;
if (! ItemExpr::preCodeGen(generator))
return NULL;
NAType * result_type = (NAType *)(&(getValueId().getType()));
NAType * type_op1 = (NAType *)(&(child(0)->castToItemExpr()->getValueId().getType()));
NAType * type_op2 = (NAType *)(&(child(1)->castToItemExpr()->getValueId().getType()));
if (result_type->isComplexType())
{
if ((getOperatorType() == ITM_PLUS) ||
(getOperatorType() == ITM_MINUS))
{
child(0) = generator->getExpGenerator()->matchScales(child(0)->getValueId(),
*result_type);
child(1) = generator->getExpGenerator()->matchScales(child(1)->getValueId(),
*result_type);
}
else
if (getOperatorType() == ITM_DIVIDE)
{
// before doing the division, the numerator has to be upscaled.
// Lets find out how much.
// NS = numerator scale
// DS = denominator scale
// RS = result scale
// Upscale = (RS - NS) + DS
// Newscale = NS + Upscale = RS + DS
Lng32 newscale
= ((NumericType *)result_type)->getScale() +
((NumericType *)type_op2)->getScale();
if (newscale != ((NumericType *)type_op1)->getScale()) {
NAType * new_type = result_type->newCopy(generator->wHeap());
((NumericType *)new_type)->setScale(newscale);
child(0) = generator->getExpGenerator()->matchScales(
child(0)->getValueId(),
*new_type);
}
}
type_op1 = (NAType *)(&(child(0)->getValueId().getType()));
type_op2 = (NAType *)(&(child(1)->getValueId().getType()));
if (result_type->getFSDatatype() == type_op1->getFSDatatype())
{
if (((getOperatorType() == ITM_PLUS) ||
(getOperatorType() == ITM_MINUS) ||
(getOperatorType() == ITM_DIVIDE)) &&
(result_type->getNominalSize() != type_op1->getNominalSize()))
{
child(0) = new(generator->wHeap()) Cast(child(0), result_type);
}
}
else
{
if ((getOperatorType() == ITM_PLUS) ||
(getOperatorType() == ITM_MINUS) ||
(getOperatorType() == ITM_DIVIDE))
{
child(0) = new(generator->wHeap()) Cast(child(0), result_type);
}
else
{
child(0) =
new(generator->wHeap()) Cast(child(0),
result_type->synthesizeType(SYNTH_RULE_PASS_THRU_NUM,
*type_op1,
*result_type,
generator->wHeap()));
}
}
if (result_type->getFSDatatype() == type_op2->getFSDatatype())
{
if (((getOperatorType() == ITM_PLUS) ||
(getOperatorType() == ITM_MINUS)) &&
(result_type->getNominalSize() != type_op2->getNominalSize()))
{
child(1) = new(generator->wHeap()) Cast(child(1), result_type);
}
}
else
{
if ((getOperatorType() == ITM_PLUS) ||
(getOperatorType() == ITM_MINUS))
{
child(1) = new(generator->wHeap()) Cast(child(1), result_type);
}
else
{
child(1) =
new(generator->wHeap()) Cast(child(1),
result_type->synthesizeType(SYNTH_RULE_PASS_THRU_NUM,
*type_op2,
*result_type,
generator->wHeap()));
}
}
child(0)->bindNode(generator->getBindWA());
child(1)->bindNode(generator->getBindWA());
child(0) = child(0)->preCodeGen(generator);
if (! child(0).getPtr())
return NULL;
child(1) = child(1)->preCodeGen(generator);
if (! child(1).getPtr())
return NULL;
markAsPreCodeGenned();
return this;
}
// following is for simple types.
SimpleType * attr_result = (SimpleType *)
(ExpGenerator::convertNATypeToAttributes
(getValueId().getType(), generator->wHeap()));
SimpleType * attr_op1 = (SimpleType *)
(ExpGenerator::convertNATypeToAttributes
(child(0)->getValueId().getType(), generator->wHeap()));
SimpleType * attr_op2 = (SimpleType *)
(ExpGenerator::convertNATypeToAttributes
(child(1)->getValueId().getType(), generator->wHeap()));
// see if conversion needed before arithmetic operation could be done.
Int32 matchScale = 0;
if (result_type->getTypeQualifier() == NA_NUMERIC_TYPE)
{
// match scales
if ((getOperatorType() == ITM_PLUS) ||
(getOperatorType() == ITM_MINUS))
{
child(0) = generator->getExpGenerator()->matchScales(child(0)->getValueId(),
*result_type);
child(1) = generator->getExpGenerator()->matchScales(child(1)->getValueId(),
*result_type);
}
else
if (getOperatorType() == ITM_DIVIDE)
{
// before doing the division, the numerator has to be upscaled.
// Lets find out how much.
// NS = numerator scale
// DS = denominator scale
// RS = result scale
// Upscale = (RS - NS) + DS
// Newscale = NS + Upscale = RS + DS
Lng32 newscale
= ((NumericType *)result_type)->getScale() +
((NumericType *)type_op2)->getScale();
if (newscale != ((NumericType *)type_op1)->getScale()) {
NAType * new_type = result_type->newCopy(generator->wHeap());
((NumericType *)new_type)->setScale(newscale);
child(0) = generator->getExpGenerator()->matchScales(
child(0)->getValueId(),
*new_type);
matchScale = 1;
}
}
}
else if (result_type->getTypeQualifier() == NA_INTERVAL_TYPE) {
switch (getOperatorType()) {
case ITM_PLUS:
case ITM_MINUS:
if (type_op1->getTypeQualifier() == NA_DATETIME_TYPE) {
Lng32 fp1 = ((DatetimeType *) type_op1)->getFractionPrecision();
Lng32 fp2 = ((DatetimeType *) type_op2)->getFractionPrecision();
if (fp1 < fp2) {
child(0) = new(generator->wHeap()) Cast(child(0), type_op2);
child(0)->bindNode(generator->getBindWA());
} else if (fp1 > fp2) {
child(1) = new(generator->wHeap()) Cast(child(1), type_op1);
child(1)->bindNode(generator->getBindWA());
}
} else {
child(0) = generator->getExpGenerator()->matchIntervalEndFields(
child(0)->getValueId(),
*result_type);
child(1) = generator->getExpGenerator()->matchIntervalEndFields(
child(1)->getValueId(),
*result_type);
child(0) = generator->getExpGenerator()->matchScales(
child(0)->getValueId(),
*result_type);
child(1) = generator->getExpGenerator()->matchScales(
child(1)->getValueId(),
*result_type);
type_op1 = (NAType *)(&(child(0)->getValueId().getType()));
type_op2 = (NAType *)(&(child(1)->getValueId().getType()));
if (result_type->getNominalSize() != type_op1->getNominalSize()) {
child(0) = new(generator->wHeap()) Cast(child(0), result_type);
child(0)->bindNode(generator->getBindWA());
}
if (result_type->getNominalSize() != type_op2->getNominalSize()) {
child(1) = new(generator->wHeap()) Cast(child(1), result_type);
child(1)->bindNode(generator->getBindWA());
}
}
break;
case ITM_TIMES: {
//
// Unfortunately, the multiply node may be the root ItemExpr node, and
// we can't change the root ItemExpr node since its ValueId has already
// been stored away in the parent RelExpr's ValueIdLists. We'll have to
// move the expression down, e.g.
//
// * <-- same root --> *
// / \ / \
// I N becomes I 1
// |
// *
// / \
// N N
// |
// I
//
if (type_op1->getTypeQualifier() == NA_INTERVAL_TYPE)
child(0) = generator->getExpGenerator()->convertIntervalToNumeric(
child(0)->getValueId());
else
child(1) = generator->getExpGenerator()->convertIntervalToNumeric(
child(1)->getValueId());
char str[20];
strcpy(str, "@A1 * @A2");
child(0) = generator->getExpGenerator()->createExprTree(str, 0,
2,
child(0),
child(1));
child(0)->bindNode(generator->getBindWA());
child(0) = generator->getExpGenerator()->convertNumericToInterval(
child(0)->getValueId(),
*result_type);
strcpy(str, "001"); // to make sure it is not a tinyint
child(1) = generator->getExpGenerator()->createExprTree(str, CharInfo::ISO88591);
child(1)->bindNode(generator->getBindWA());
type_op2 = (NAType *)(&(child(1)->getValueId().getType()));
if ((result_type->getNominalSize() != type_op2->getNominalSize()) ||
(type_op2->getFSDatatype() != REC_BIN16_SIGNED)) {
IntervalType *interval = (IntervalType *) result_type;
const Int16 DisAmbiguate = 0;
child(1) = new(generator->wHeap()) Cast(child(1),
new(generator->wHeap()) SQLNumeric(generator->wHeap(), TRUE, /* signed */
interval->getTotalPrecision(),
0,
DisAmbiguate, // added for 64bit proj.
interval->supportsSQLnull()));
child(1)->bindNode(generator->getBindWA());
}
break;
}
case ITM_DIVIDE: {
//
// Unfortunately, the divide node may be the root ItemExpr node, and
// we can't change the root ItemExpr node since its ValueId has already
// been stored away in the parent RelExpr's ValueIdLists. We'll have to
// move the expression down, e.g.
//
// div <-- same root --> div
// / \ / \
// I N becomes I 1
// |
// div
// / \
// N N
// |
// I
//
child(0) = generator->getExpGenerator()->convertIntervalToNumeric(
child(0)->getValueId());
char str[20];
strcpy(str, "@A1 / @A2");
child(0) = generator->getExpGenerator()->createExprTree(str, 0,
2,
child(0),
child(1));
child(0)->bindNode(generator->getBindWA());
child(0) = generator->getExpGenerator()->convertNumericToInterval(
child(0)->getValueId(),
*result_type);
strcpy(str, "001"); // to make sure it is not a tinyint
child(1) = generator->getExpGenerator()->createExprTree(str, CharInfo::ISO88591);
child(1)->bindNode(generator->getBindWA());
type_op2 = (NAType *)(&(child(1)->getValueId().getType()));
if ((result_type->getNominalSize() != type_op2->getNominalSize()) ||
(type_op2->getFSDatatype() != REC_BIN16_SIGNED)) {
IntervalType *interval = (IntervalType *) result_type;
const Int16 DisAmbiguate = 0;
child(1) = new(generator->wHeap()) Cast(child(1),
new(generator->wHeap()) SQLNumeric(generator->wHeap(), TRUE, /* signed */
interval->getTotalPrecision(),
0,
DisAmbiguate, // added for 64bit proj.
interval->supportsSQLnull()));
child(1)->bindNode(generator->getBindWA());
}
break;
}
default:
break;
}
} else if (result_type->getTypeQualifier() == NA_DATETIME_TYPE) {
switch (getOperatorType()) {
case ITM_PLUS:
case ITM_MINUS: {
if ((type_op1->getTypeQualifier() == NA_INTERVAL_TYPE) &&
(((IntervalType*) type_op1)->getEndField() == REC_DATE_SECOND)) {
Lng32 sourceScale = ((IntervalType *) type_op1)->getFractionPrecision();
Lng32 targetScale = ((DatetimeType *) type_op2)->getFractionPrecision();
child(0) = generator->getExpGenerator()->scaleBy10x(
child(0)->getValueId(),
targetScale - sourceScale);
} else if ((type_op2->getTypeQualifier() == NA_INTERVAL_TYPE) &&
(((IntervalType*) type_op2)->getEndField() == REC_DATE_SECOND)) {
Lng32 targetScale = ((DatetimeType *) type_op1)->getFractionPrecision();
Lng32 sourceScale = ((IntervalType *) type_op2)->getFractionPrecision();
child(1) = generator->getExpGenerator()->scaleBy10x(
child(1)->getValueId(),
targetScale - sourceScale);
}
// Extend the datetime to contain a YEAR field if needed. The
// value will need to be extended if it contains a DAY field but
// does not already contain a YEAR field. This is necessary
// since with the introduction of non-standard SQL/MP datetime
// types, it is possible to have a datetime value which has a
// DAY field but not a YEAR or not a MONTH field. In this
// situation, it is not possible to define a meaningful way to
// do the operation. Does the DAY field wrap at 30, 31, 28, or
// 29. So to make this operation meaningful, the value is
// extended to the current timestamp.
//
if (type_op1->getTypeQualifier() == NA_DATETIME_TYPE) {
if(((DatetimeType *) type_op1)->containsField(REC_DATE_DAY) &&
! ((DatetimeType *) type_op1)->containsField(REC_DATE_YEAR)) {
// Need to extend the given datetime value in order to be
// able to do the operation. Extend the value out to the
// YEAR field.
//
DatetimeType *extendedType =
DatetimeType::constructSubtype(type_op1->supportsSQLnull(),
REC_DATE_YEAR,
((DatetimeType *)type_op1)->getEndField(),
((DatetimeType *)type_op1)->getFractionPrecision(),
generator->wHeap());
// Cast the given value to the extended type.
//
child(0) = new (generator->wHeap()) Cast(child(0), extendedType);
child(0)->bindNode(generator->getBindWA());
}
} else {
if(((DatetimeType *) type_op2)->containsField(REC_DATE_DAY) &&
! ((DatetimeType *) type_op2)->containsField(REC_DATE_YEAR)) {
// Need to extend the given datetime value in order to be
// able to do the operation. Extend the value out to the
// YEAR field.
//
DatetimeType *extendedType =
DatetimeType::constructSubtype(type_op2->supportsSQLnull(),
REC_DATE_YEAR,
((DatetimeType *)type_op2)->getEndField(),
((DatetimeType *)type_op2)->getFractionPrecision(),
generator->wHeap());
// Cast the given value to the extended type.
//
child(1) = new (generator->wHeap()) Cast(child(1), extendedType);
child(1)->bindNode(generator->getBindWA());
}
}
break;
}
default:
break;
}
}
// NABoolean convertRoundedDivResult = FALSE;
// If this arith operation is supported at runtime, then no
// conversion is needed. Done for result numeric type only.
if (result_type->getTypeQualifier() == NA_NUMERIC_TYPE)
{
child(0) = child(0)->preCodeGen(generator);
if (! child(0).getPtr())
return NULL;
child(1) = child(1)->preCodeGen(generator);
if (! child(1).getPtr())
return NULL;
attr_result = (SimpleType *)
(ExpGenerator::convertNATypeToAttributes
(getValueId().getType(), generator->wHeap()));
attr_op1 = (SimpleType *)
(ExpGenerator::convertNATypeToAttributes
(child(0)->getValueId().getType(), generator->wHeap()));
attr_op2 = (SimpleType *)
(ExpGenerator::convertNATypeToAttributes
(child(1)->getValueId().getType(), generator->wHeap()));
ex_arith_clause temp_clause(getOperatorType(), NULL, NULL,
getRoundingMode(), getDivToDownscale());
if (temp_clause.isArithSupported(getOperatorType(),
attr_op1,
attr_op2,
attr_result
))
{
markAsPreCodeGenned();
return this;
}
}
// if the datatype or lengths of child and this don't match, then
// conversion is needed.
type_op1 = (NAType *)(&(child(0)->getValueId().getType()));
type_op2 = (NAType *)(&(child(1)->getValueId().getType()));
if ((result_type->getTypeQualifier() != NA_INTERVAL_TYPE) &&
(result_type->getTypeQualifier() != NA_DATETIME_TYPE) &&
((result_type->getFSDatatype() != type_op1->getFSDatatype()) ||
(result_type->getNominalSize() != type_op1->getNominalSize())))
{
// If the result type is not a float, make sure that the following
// Cast does not scale (for floats we have do do scaling). This is
// done by using the result type but changing the scale to the scale
// of the operand
NAType * new_type = result_type->newCopy(generator->wHeap());
if ((result_type->getFSDatatype() < REC_MIN_FLOAT) ||
(result_type->getFSDatatype() > REC_MAX_FLOAT)) {
((NumericType *)new_type)->
setScale(((NumericType *)type_op1)->getScale());
};
child(0) = new(generator->wHeap()) Cast(child(0), new_type,
ITM_CAST, FALSE);
}
if ((result_type->getTypeQualifier() != NA_INTERVAL_TYPE) &&
(result_type->getTypeQualifier() != NA_DATETIME_TYPE) &&
((result_type->getFSDatatype() != type_op2->getFSDatatype()) ||
(result_type->getNominalSize() != type_op2->getNominalSize())))
{
NAType * new_type = result_type->newCopy(generator->wHeap());
if ((result_type->getFSDatatype() < REC_MIN_FLOAT) ||
(result_type->getFSDatatype() > REC_MAX_FLOAT) ||
matchScale)
{
((NumericType *)new_type)->
setScale(((NumericType *)type_op2)->getScale());
};
child(1) = new(generator->wHeap()) Cast(child(1), new_type,
ITM_CAST, FALSE);
}
child(0)->bindNode(generator->getBindWA());
child(1)->bindNode(generator->getBindWA());
child(0) = child(0)->preCodeGen(generator);
if (! child(0).getPtr())
return NULL;
child(1) = child(1)->preCodeGen(generator);
if (! child(1).getPtr())
return NULL;
markAsPreCodeGenned();
return this;
} // BiArith::preCodeGen()
ItemExpr * UnArith::preCodeGen(Generator * generator)
{
if (nodeIsPreCodeGenned())
return this;
if (! ItemExpr::preCodeGen(generator))
return NULL;
return this;
}
ItemExpr * BiLogic::preCodeGen(Generator * generator)
{
if (nodeIsPreCodeGenned())
return this;
if (! ItemExpr::preCodeGen(generator))
return NULL;
ItemExpr *result = this;
ItemExpr *INlhs = NULL;
if (CmpCommon::getDefault(OR_PRED_ADD_BLOCK_TO_IN_LIST) == DF_ON
&& createdFromINlist() && (INlhs=getINlhs())!=NULL)
{
// ItmBlockFunction serves like the "C/C++ comma" expression that
// 1) evaluates its 1st operand, 2nd operand, and
// 2) returns its 2nd operand as value of that expression.
// ItmBlockFunction also has the codegen property that
// its 1st operand is evaluated (codegen'ed) only once
// even if 1st operand occurs multiple times in 2nd operand.
// So, given "UPPER(n) IN ('a', 'b')" that has been converted to
// ItmBlockFunction
// / \
// U OR
// / \
// = =
// / \ / \
// U a U b
// "UPPER(n)", represented as U, is evaluated once even if
// it's used multiple times in the OR expression.
// Trying to add ItmBlockFunction early in the parser (ie, in
// sqlparseraux.cpp convertINvaluesToOR() causes a lot of grief
// especially in cardinality estimation code. So, we resort to
// doing it late, here in precodegen.
result = new(generator->wHeap()) ItmBlockFunction(INlhs, result);
result->synthTypeAndValueId();
result->markAsPreCodeGenned();
return result;
}
markAsPreCodeGenned();
return result;
}
ItemExpr * BiRelat::preCodeGen(Generator * generator)
{
if (nodeIsPreCodeGenned())
return this;
// transform multivalue predicates to single-value comparisons.
ItemExpr * newNode = transformMultiValuePredicate();
if (newNode)
{
#ifdef _DEBUG
// NAString unp;
// unparse(unp);
// cerr << "BiRelat::preCodeGen - " << unp << " needed to be transformed!"
// << endl;
// I don't think we should ever have an untransformed MVP at this stage!
#endif
// transformMultiValuePredicate() cannot do synthTypeAndValue()
// because it is also called from the normalizer in places
// where it needs to postpone it.
newNode->synthTypeAndValueId();
return newNode->preCodeGen(generator);
}
if (! ItemExpr::preCodeGen(generator))
return NULL;
NAType * type_op1 = (NAType *)(&(child(0)->getValueId().getType()));
NAType * type_op2 = (NAType *)(&(child(1)->getValueId().getType()));
if ((type_op1->isComplexType()) || (type_op2->isComplexType()))
{
// find the 'super' type
const NAType *result_type = type_op1->synthesizeType(SYNTH_RULE_UNION,
*type_op1,
*type_op2,
generator->wHeap());
CMPASSERT(result_type);
if (result_type->getTypeQualifier() == NA_NUMERIC_TYPE)
{
// match scales
child(0) = generator->getExpGenerator()->matchScales(child(0)->getValueId(),
*result_type);
child(1) = generator->getExpGenerator()->matchScales(child(1)->getValueId(),
*result_type);
}
type_op1 = (NAType *)(&(child(0)->getValueId().getType()));
type_op2 = (NAType *)(&(child(1)->getValueId().getType()));
if ((result_type->getFSDatatype() != type_op1->getFSDatatype()) ||
(result_type->getNominalSize() != type_op1->getNominalSize()))
{
child(0) = new(generator->wHeap()) Cast(child(0), result_type);
}
if ((result_type->getFSDatatype() != type_op2->getFSDatatype()) ||
(result_type->getNominalSize() != type_op2->getNominalSize()))
{
child(1) = new(generator->wHeap()) Cast(child(1), result_type);
}
child(0)->bindNode(generator->getBindWA());
child(1)->bindNode(generator->getBindWA());
child(0) = child(0)->preCodeGen(generator);
if (! child(0).getPtr())
return NULL;
child(1) = child(1)->preCodeGen(generator);
if (! child(1).getPtr())
return NULL;
markAsPreCodeGenned();
return this;
}
const NAType &type1A =
child(0)->castToItemExpr()->getValueId().getType();
const NAType &type2A =
child(1)->castToItemExpr()->getValueId().getType();
if ((type1A.getTypeQualifier() == NA_CHARACTER_TYPE) &&
(type2A.getTypeQualifier() == NA_CHARACTER_TYPE))
{
const CharType &cType1A = (CharType&)type1A;
const CharType &cType2A = (CharType&)type2A;
CharInfo::Collation cType1A_coll = cType1A.getCollation();
CharInfo::Collation cType2A_coll = cType2A.getCollation();
//
// When Implicit Casting And Translation feature is enabled, it is
// possible for the binder to allow a comparision between an ISO88591-type
// value and a UCS2-type value to be passed through to the generator.
// If that happens, we throw in a Translate node at this point.
//
CharInfo::CharSet cType1A_CS = cType1A.getCharSet() ;
CharInfo::CharSet cType2A_CS = cType2A.getCharSet() ;
if ( ( cType1A_CS != cType2A_CS ) &&
( cType1A_CS != CharInfo::UnknownCharSet ) &&
( cType2A_CS != CharInfo::UnknownCharSet ) )
{
Int32 chld_to_trans = 0;
if ( cType1A_CS != CharInfo::ISO88591 )
{
if ( (cType1A_CS == CharInfo::UNICODE) ) chld_to_trans = 1;
if ( (cType1A_CS == CharInfo::UTF8) && (cType2A_CS != CharInfo::UNICODE) ) chld_to_trans = 1;
if ( (cType1A_CS == CharInfo::SJIS) && (cType2A_CS == CharInfo::ISO88591) ) chld_to_trans = 1;
}
Int32 tran_type = Translate::UNKNOWN_TRANSLATION;
if ( chld_to_trans == 0 )
tran_type = find_translate_type( cType1A_CS, cType2A_CS );
else
tran_type = find_translate_type( cType2A_CS, cType1A_CS );
ItemExpr * newChild = NULL;
newChild = new (generator->wHeap()) Translate(child(chld_to_trans), tran_type);
newChild = newChild->bindNode(generator->getBindWA());
newChild = newChild->preCodeGen(generator);
if (! newChild)
return NULL;
setChild(chld_to_trans, newChild);
}
else if ( cType1A_coll != cType2A_coll &&
cType1A_CS == CharInfo::ISO88591 &&
cType1A_CS == cType2A_CS &&
child(1)->getOperatorType() == ITM_CONSTANT &&
CollationInfo::isSystemCollation(cType1A_coll))
{
ItemExpr * pNewChild2 = NULL;
NAType * pNewType2 = cType2A.newCopy(generator->wHeap());
CharType * pNewCType2 = NULL;
if (pNewType2 != NULL)
pNewCType2 = (CharType*)pNewType2;
if (pNewCType2 != NULL)
pNewCType2->setCollation(cType1A_coll);
pNewChild2 = new (generator->wHeap()) Cast(child(1), pNewCType2);
pNewChild2 = pNewChild2->bindNode(generator->getBindWA());
pNewChild2 = pNewChild2->preCodeGen(generator);
if (pNewChild2 == NULL)
return NULL;
setChild(1, pNewChild2);
}
// Regenerate the types...before we continue with rest of code
type_op1 = (NAType *)(&(child(0)->getValueId().getType()));
type_op2 = (NAType *)(&(child(1)->getValueId().getType()));
ItemExpr * pChild1 = child(1)->castToItemExpr();
const NAType &type1 = pChild1->getValueId().getType();
const CharType &cType1 = (CharType&)type1;
ItemExpr * pChild2 = child(1)->castToItemExpr();
const NAType &type2 = pChild2->getValueId().getType();
const CharType &cType2 = (CharType&)type2;
CharInfo::Collation coll1 = cType1.getCollation();
CharInfo::Collation coll2 = cType2.getCollation();
CMPASSERT(coll1==coll2);
if (CollationInfo::isSystemCollation(coll1))
{
setCollationEncodeComp(TRUE);
{
ItemExpr * newIe1 = child(0);
ItemExpr * newIe2 = child(1);
if (! (cType1 == cType2))
{
NAType *resultType ;
Lng32 len = MAXOF(cType1.getMaxLenInBytesOrNAWChars(), cType2.getMaxLenInBytesOrNAWChars());
Lng32 Prec= MAXOF(cType1.getStrCharLimit(), cType2.getStrCharLimit());
if (len != cType1.getMaxLenInBytesOrNAWChars())
{
if (DFS2REC::isAnyVarChar(cType1.getFSDatatype()))
{
resultType = new (generator->wHeap())
SQLVarChar(generator->wHeap(), CharLenInfo(Prec, len),
cType1.supportsSQLnull(),
cType1.isUpshifted(),
cType1.isCaseinsensitive(),
cType1.getCharSet(),
cType1.getCollation(),
cType1.getCoercibility()
);
}
else
{
resultType = new (generator->wHeap())
SQLChar(generator->wHeap(), CharLenInfo(Prec, len),
cType1.supportsSQLnull(),
cType1.isUpshifted(),
cType1.isCaseinsensitive(),
FALSE,
cType1.getCharSet(),
cType1.getCollation(),
cType1.getCoercibility()
);
}
newIe1 = new(generator->wHeap()) Cast(newIe1,resultType);
}
if (len != cType2.getMaxLenInBytesOrNAWChars())
{
if (DFS2REC::isAnyVarChar(cType2.getFSDatatype()))
{
resultType = new (generator->wHeap())
SQLVarChar(generator->wHeap(), CharLenInfo(Prec, len),
cType2.supportsSQLnull(),
cType2.isUpshifted(),
cType2.isCaseinsensitive(),
cType2.getCharSet(),
cType2.getCollation(),
cType2.getCoercibility()
);
}
else
{
resultType = new (generator->wHeap())
SQLChar(generator->wHeap(), CharLenInfo(Prec, len),
cType2.supportsSQLnull(),
cType2.isUpshifted(),
cType2.isCaseinsensitive(),
FALSE,
cType2.getCharSet(),
cType2.getCollation(),
cType2.getCoercibility()
);
}
newIe2 = new(generator->wHeap()) Cast(newIe2,resultType);
}
}
ItemExpr * newEncode;
newEncode =
new(generator->wHeap())
CompEncode(newIe1,FALSE, -1, CollationInfo::Compare);
newEncode->bindNode(generator->getBindWA());
newEncode = newEncode->preCodeGen(generator);
if (!newEncode)
return NULL;
setChild(0, newEncode);
newEncode =
new(generator->wHeap())
CompEncode(newIe2, FALSE, -1,CollationInfo::Compare);
newEncode->bindNode(generator->getBindWA());
newEncode = newEncode->preCodeGen(generator);
if (!newEncode)
return NULL;
setChild(1, newEncode);
}
}
else
{
// update both operands if case insensitive comparions
// are to be done.
NABoolean doCIcomp =
((cType1.isCaseinsensitive()) && (cType2.isCaseinsensitive()));
ItemExpr * newChild = NULL;
if ((doCIcomp) &&
(NOT cType1.isUpshifted()))
{
newChild = child(0);
// Add UPPER except if it is NULL constant value.
if (newChild->getOperatorType() != ITM_CONSTANT || !((ConstValue *)newChild)->isNull())
newChild = new (generator->wHeap()) Upper(newChild);
newChild = newChild->bindNode(generator->getBindWA());
if (! newChild || generator->getBindWA()->errStatus())
return NULL;
newChild = newChild->preCodeGen(generator);
if (! newChild)
return NULL;
setChild(0, newChild);
}
if ((doCIcomp) &&
(NOT cType2.isUpshifted()))
{
newChild = child(1);
// Add UPPER except if it is NULL constant value.
if (newChild->getOperatorType() != ITM_CONSTANT || !((ConstValue *)newChild)->isNull())
newChild = new (generator->wHeap()) Upper(newChild);
newChild = newChild->bindNode(generator->getBindWA());
if (! newChild || generator->getBindWA()->errStatus())
return NULL;
newChild = newChild->preCodeGen(generator);
if (! newChild)
return NULL;
setChild(1, newChild);
}
}
}
// following is for simple types.
const NAType &type1B =
child(0)->castToItemExpr()->getValueId().getType();
const NAType &type2B =
child(1)->castToItemExpr()->getValueId().getType();
SimpleType * attr_op1 = (SimpleType *)
(ExpGenerator::convertNATypeToAttributes(type1B, generator->wHeap()));
SimpleType * attr_op2 = (SimpleType *)
(ExpGenerator::convertNATypeToAttributes(type2B, generator->wHeap()));
ex_comp_clause temp_clause;
temp_clause.setInstruction(getOperatorType(),
attr_op1,
attr_op2
);
if ((temp_clause.getInstruction() == COMP_NOT_SUPPORTED) &&
(type1B.getTypeQualifier() == NA_NUMERIC_TYPE) &&
(type2B.getTypeQualifier() == NA_NUMERIC_TYPE))
{
const NumericType &numOp1 = (NumericType&)type1B;
const NumericType &numOp2 = (NumericType&)type2B;
if ((numOp1.isExact() && numOp2.isExact()) &&
((numOp1.getFSDatatype() == REC_BIN64_UNSIGNED) ||
(numOp2.getFSDatatype() == REC_BIN64_UNSIGNED)))
{
if (numOp1.getFSDatatype() == REC_BIN64_UNSIGNED)
{
// add a Cast node to convert op2 to sqllargeint.
ItemExpr * newOp2 =
new (generator->wHeap())
Cast(child(1),
new (generator->wHeap())
SQLLargeInt(generator->wHeap(), numOp2.isSigned(),
numOp2.supportsSQLnull()));
newOp2 = newOp2->bindNode(generator->getBindWA());
newOp2 = newOp2->preCodeGen(generator);
if (! newOp2)
return NULL;
setChild(1, newOp2);
attr_op2 = (SimpleType *)
(ExpGenerator::convertNATypeToAttributes(
newOp2->getValueId().getType(), generator->wHeap()));
}
else
{
// add a Cast node to convert op1 to sqllargeint.
ItemExpr * newOp1 =
new (generator->wHeap())
Cast(child(0),
new (generator->wHeap())
SQLLargeInt(generator->wHeap(), numOp1.isSigned(),
numOp1.supportsSQLnull()));
newOp1 = newOp1->bindNode(generator->getBindWA());
newOp1 = newOp1->preCodeGen(generator);
if (! newOp1)
return NULL;
setChild(0, newOp1);
attr_op1 = (SimpleType *)
(ExpGenerator::convertNATypeToAttributes(
newOp1->getValueId().getType(), generator->wHeap()));
}
temp_clause.setInstruction(getOperatorType(),
attr_op1,
attr_op2
);
} // convert
}
if (temp_clause.getInstruction() != COMP_NOT_SUPPORTED)
{
NABoolean doConstFolding = FALSE;
if ((temp_clause.getInstruction() == ASCII_COMP) &&
(CmpCommon::getDefault(CONSTANT_FOLDING) == DF_ON))
{
if (((child(0)->getOperatorType() == ITM_CONSTANT) &&
(child(1)->getOperatorType() != ITM_CONSTANT)) ||
((child(1)->getOperatorType() == ITM_CONSTANT) &&
(child(0)->getOperatorType() != ITM_CONSTANT)) &&
(type_op1->getFSDatatype() == REC_BYTE_F_ASCII) &&
(type_op2->getFSDatatype() == REC_BYTE_F_ASCII))
{
if (((child(0)->getOperatorType() == ITM_CONSTANT) &&
(type_op1->getNominalSize() < type_op2->getNominalSize())) ||
((child(1)->getOperatorType() == ITM_CONSTANT) &&
(type_op2->getNominalSize() < type_op1->getNominalSize())))
{
doConstFolding = TRUE;
}
}
}
if (NOT doConstFolding)
{
markAsPreCodeGenned();
return this;
}
}
// conversion needed before comparison could be done.
// find the 'super' type
UInt32 flags =
((CmpCommon::getDefault(LIMIT_MAX_NUMERIC_PRECISION) == DF_ON)
? NAType::LIMIT_MAX_NUMERIC_PRECISION : 0);
if (CmpCommon::getDefault(ALLOW_INCOMPATIBLE_OPERATIONS) == DF_ON)
{
flags |= NAType::ALLOW_INCOMP_OPER;
}
const NAType *result_type = type_op1->synthesizeType(SYNTH_RULE_UNION,
*type_op1,
*type_op2,
generator->wHeap(),
&flags);
CMPASSERT(result_type);
if (result_type->getTypeQualifier() == NA_NUMERIC_TYPE)
{
// match scales
child(0) = generator->getExpGenerator()->matchScales(child(0)->getValueId(),
*result_type);
child(1) = generator->getExpGenerator()->matchScales(child(1)->getValueId(),
*result_type);
}
else if (result_type->getTypeQualifier() == NA_DATETIME_TYPE) {
Lng32 fp1 = ((DatetimeType *) type_op1)->getFractionPrecision();
Lng32 fp2 = ((DatetimeType *) type_op2)->getFractionPrecision();
Lng32 fpResult = ((DatetimeType *) result_type)->getFractionPrecision();
if (fp1 != fpResult) {
child(0) = new(generator->wHeap()) Cast(child(0), result_type,
ITM_CAST, FALSE);
child(0)->bindNode(generator->getBindWA());
}
if (fp2 != fpResult) {
child(1) = new(generator->wHeap()) Cast(child(1), result_type,
ITM_CAST, FALSE);
child(1)->bindNode(generator->getBindWA());
}
} else if (result_type->getTypeQualifier() == NA_INTERVAL_TYPE) {
child(0) = generator->getExpGenerator()->matchIntervalEndFields(
child(0)->getValueId(),
*result_type);
child(1) = generator->getExpGenerator()->matchIntervalEndFields(
child(1)->getValueId(),
*result_type);
child(0) = generator->getExpGenerator()->matchScales(
child(0)->getValueId(),
*result_type);
child(1) = generator->getExpGenerator()->matchScales(
child(1)->getValueId(),
*result_type);
type_op1 = (NAType *)(&(child(0)->getValueId().getType()));
type_op2 = (NAType *)(&(child(1)->getValueId().getType()));
if (result_type->getNominalSize() != type_op1->getNominalSize()) {
child(0) = new(generator->wHeap()) Cast(child(0), result_type,
ITM_CAST, FALSE);
child(0)->bindNode(generator->getBindWA());
}
if (result_type->getNominalSize() != type_op2->getNominalSize()) {
child(1) = new(generator->wHeap()) Cast(child(1), result_type,
ITM_CAST, FALSE);
child(1)->bindNode(generator->getBindWA());
}
}
// if the datatype or lengths of child and this don't match, then
// conversion is needed.
type_op1 = (NAType *)(&(child(0)->getValueId().getType()));
type_op2 = (NAType *)(&(child(1)->getValueId().getType()));
if ((result_type->getTypeQualifier() != NA_INTERVAL_TYPE) &&
((result_type->getFSDatatype() != type_op1->getFSDatatype()) ||
(result_type->getNominalSize() != type_op1->getNominalSize())))
{
child(0) = new(generator->wHeap()) Cast(child(0), result_type,
ITM_CAST, FALSE);
}
if ((result_type->getTypeQualifier() != NA_INTERVAL_TYPE) &&
((result_type->getFSDatatype() != type_op2->getFSDatatype()) ||
(result_type->getNominalSize() != type_op2->getNominalSize())))
{
child(1) = new(generator->wHeap()) Cast(child(1), result_type,
ITM_CAST, FALSE);
}
// bind/type propagate the new nodes
child(0)->bindNode(generator->getBindWA());
child(1)->bindNode(generator->getBindWA());
child(0) = child(0)->preCodeGen(generator);
if (! child(0).getPtr())
return NULL;
child(1) = child(1)->preCodeGen(generator);
if (! child(1).getPtr())
return NULL;
ItemExpr *outExpr = NULL;
Lng32 rc = generator->getExpGenerator()->foldConstants(child(0), &outExpr);
if ((rc == 0) &&
(outExpr))
{
child(0) = outExpr->preCodeGen(generator);
}
rc = generator->getExpGenerator()->foldConstants(child(1), &outExpr);
if ((rc == 0) &&
(outExpr))
{
child(1) = outExpr->preCodeGen(generator);
}
markAsPreCodeGenned();
return this;
} // BiRelat::preCodeGen()
ItemExpr * Assign::preCodeGen(Generator * generator)
{
if (nodeIsPreCodeGenned())
return this;
child(1) = generator->getExpGenerator()->matchIntervalEndFields(
child(1)->getValueId(),
getValueId().getType());
child(1) = generator->getExpGenerator()->matchScales(child(1)->getValueId(),
getValueId().getType());
child(1)->bindNode(generator->getBindWA());
child(1) = child(1)->preCodeGen(generator);
if (! child(1).getPtr())
return NULL;
markAsPreCodeGenned();
return this;
} // Assign::preCodeGen()
ItemExpr * BaseColumn::preCodeGen(Generator * generator)
{
if (nodeIsPreCodeGenned())
return this;
ItemExpr * i = convertExternalType(generator);
if (i == NULL)
return NULL;
return i;
}
ItemExpr * BitOperFunc::preCodeGen(Generator * generator)
{
if (nodeIsPreCodeGenned())
return this;
if (getOperatorType() == ITM_BITEXTRACT)
{
// convert 2nd and 3rd operands to Int32 signed.
for (Int32 i = 1; i < getArity(); i++)
{
const NAType &typ = child(i)->getValueId().getType();
if (typ.getFSDatatype() != REC_BIN32_UNSIGNED)
{
ItemExpr * newChild =
new (generator->wHeap())
Cast(child(i),
new (generator->wHeap()) SQLInt(generator->wHeap(), FALSE,
typ.supportsSQLnullLogical()));
setChild(i, newChild);
child(i)->bindNode(generator->getBindWA());
child(i) = child(i)->preCodeGen(generator);
if (! child(i).getPtr())
return NULL;
} // if
} // for
}
else
{
for (Int32 i = 0; i < getArity(); i++)
{
const NAType &typ = child(i)->getValueId().getType();
if (NOT (getValueId().getType() == typ))
{
NAType *resultType =
getValueId().getType().newCopy(generator->wHeap());
ItemExpr * newChild =
new (generator->wHeap()) Cast(child(i), resultType);
setChild(i, newChild);
}
child(i)->bindNode(generator->getBindWA());
child(i) = child(i)->preCodeGen(generator);
if (! child(i).getPtr())
return NULL;
}
}
markAsPreCodeGenned();
return this;
} // BitOperFunc::preCodeGen()
ItemExpr * Cast::preCodeGen(Generator * generator)
{
if (nodeIsPreCodeGenned())
return this;
child(0)->bindNode(generator->getBindWA());
child(0) = child(0)->preCodeGen(generator);
if (! child(0).getPtr())
return NULL;
// if a special cast node, see if my child's data attributes are
// the same as my data attributes. If they are, return pointer to
// my child.
if ((matchChildType()) &&
(child(0)->getValueId().getType() == getValueId().getType()))
{
markAsPreCodeGenned();
return child(0);
}
NABuiltInTypeEnum sourceTypeQual =
child(0)->getValueId().getType().getTypeQualifier();
NABuiltInTypeEnum targetTypeQual =
getValueId().getType().getTypeQualifier();
// If this is a NARROW operation, but it is not possible to result
// in an error, no reason to use NARROW. Convert the NARROW to the
// equivalent CAST.
if (getOperatorType() == ITM_NARROW)
{
const NAType * sourceType = &(child(0)->getValueId().getType());
const NAType * targetType = &(getValueId().getType());
if (!sourceType->errorsCanOccur(*targetType))
{
ItemExpr *c = new(generator->wHeap()) Cast(child(0), targetType);
c->bindNode(generator->getBindWA());
return c->preCodeGen(generator);
}
}
if (generator->getExpGenerator()->handleUnsupportedCast(this))
return NULL;
const NAType &srcNAType = child(0)->getValueId().getType();
const NAType &tgtNAType = getValueId().getType();
short srcFsType = srcNAType.getFSDatatype();
short tgtFsType = tgtNAType.getFSDatatype();
if ((sourceTypeQual == NA_NUMERIC_TYPE) &&
(targetTypeQual == NA_DATETIME_TYPE))
{
// binder has already verified that this is a valid conversion
// in special1 mode.
NumericType &sourceType =
(NumericType &)(child(0)->getValueId().getType());
DatetimeType &targetType =
(DatetimeType &)(getValueId().getType());
if (sourceType.getFSDatatype() != REC_BIN64_SIGNED)
{
// doing a numeric to date conversion
// convert source to largeint.
ItemExpr * newChild =
new (generator->wHeap())
Cast(child(0),
new (generator->wHeap())
SQLLargeInt(generator->wHeap(), TRUE,
child(0)->castToItemExpr()->
getValueId().getType().supportsSQLnull()));
newChild = newChild->bindNode(generator->getBindWA());
newChild = newChild->preCodeGen(generator);
if (! newChild)
return NULL;
setChild(0, newChild);
}
}
if ((sourceTypeQual == NA_DATETIME_TYPE) &&
(targetTypeQual == NA_NUMERIC_TYPE))
{
// binder has already verified that this is a valid conversion
// in special1 mode.
DatetimeType &sourceType =
(DatetimeType &)(child(0)->getValueId().getType());
NumericType &targetType =
(NumericType &)(getValueId().getType());
if (targetType.getFSDatatype() != REC_BIN64_SIGNED)
{
// doing a date to numeric conversion.
// convert source to largeint.
ItemExpr * newChild =
new (generator->wHeap())
Cast(child(0),
new (generator->wHeap())
SQLLargeInt(generator->wHeap(), TRUE,
child(0)->castToItemExpr()->
getValueId().getType().supportsSQLnull()));
newChild = newChild->bindNode(generator->getBindWA());
newChild = newChild->preCodeGen(generator);
if (! newChild)
return NULL;
setChild(0, newChild);
}
} // numeric to date conversion
if ((CmpCommon::getDefault(ALLOW_INCOMPATIBLE_OPERATIONS) == DF_ON) &&
(sourceTypeQual == NA_NUMERIC_TYPE) &&
(targetTypeQual == NA_INTERVAL_TYPE))
{
NumericType &sourceType =
(NumericType &)(child(0)->getValueId().getType());
if (NOT sourceType.isExact())
{
// doing a float numeric to interval conversion.
// convert source to corresponding exact numeric (largeint).
// This is the largest interval type that is supported.
ItemExpr * newChild =
new (generator->wHeap())
Cast(child(0),
new (generator->wHeap())
SQLLargeInt(generator->wHeap(), TRUE,
child(0)->castToItemExpr()->
getValueId().getType().supportsSQLnull()));
newChild = newChild->bindNode(generator->getBindWA());
newChild = newChild->preCodeGen(generator);
if (! newChild)
return NULL;
setChild(0, newChild);
}
} // numeric to date conversion
if ((sourceTypeQual == NA_DATETIME_TYPE) &&
(targetTypeQual == NA_DATETIME_TYPE)) {
DatetimeType &sourceType =
(DatetimeType &)(child(0)->getValueId().getType());
DatetimeType &targetType =
(DatetimeType &)(getValueId().getType());
if (targetType.getStartField() < sourceType.getStartField()) {
// Must provide some fields from the current time stamp
//
// The following code generates the current timestamp as a
// string and extracts the needed leading fields and appends to
// this the given value (child(0)) as a string. The result is a
// string which contains the given datetime value extended to
// the YEAR field with the current timestamp.
//
// Buffer to hold new expression string.
//
char str[200];
// Offset (in bytes) from the start of the current timestamp
// (represented as a char. string) to the first field needed in
// the extension.
//
// - Subtract 1 from the start field to make the value zero based.
//
// - Each field has a least 3 bytes (2 for the value and 1 for the
// delimiter)
//
// - Add 1, since the substring function is 1 based.
//
Int32 leadFieldsOffset = ((targetType.getStartField() - 1) * 3) + 1;
// - Add 2 extra for the year field if it is being skiped over
// since it has 4 bytes of value.
//
if (leadFieldsOffset > 1)
leadFieldsOffset += 2;
// Size (in bytes) of the leading fields represented as a
// character string taken from the current timestamp
//
// - Subtract 1 from the start field to make the value zero based.
//
// - Each field has a least 3 bytes (2 for the value and 1 for the
// delimiter)
//
// - Add 2 extra for the year field (which will always be one of
// the extended fields) since it has 4 bytes of value.
//
// - Subtract the leadFieldsOffset ( - 1 to make it zero based).
//
Int32 leadFieldsSize = ((((sourceType.getStartField() - 1) * 3) + 2)
- (leadFieldsOffset - 1));
// Size (in bytes) of the source value represented as a
// character string.
//
Int32 sourceFieldsSize = sourceType.getDisplayLength();
// Construct an expression (string) to concatinate the given
// value with the required fields from the current timestamp as
// a string, then cast this string as a datetime value, that can
// be cast to the desired result.
//
// Example :
//
// cast(DATETIME 'dd hh:mm:ss' DAY TO SECOND as DATETIME MONTH to MINUTE)
//
// current timestamp (as string) | "YYYY-MM-DD HH:MM:SS.FFFFFF"
// |
// leadFieldsOffset = ((2-1)*3)+1 +2 = | --6--^
// |
// leadFieldsSize = (((3-1)*3)+2) - 5 =| ^3^
// |
// result of substring(cts from 1 to 8)| "MM-"
// |
// value to be extended (as string) | "dd hh:mm:ss"
// |
// result of string concat. (as string)| "MM-dd hh:mm:ss"
// |
// Cast to a datetime MONTH TO SECOND | Mdhms
// |
// Original (this) cast to result | Mdhm
//
str_sprintf(str,
"CAST((SUBSTRING(CAST(CURRENT AS CHAR(19)) "
"FROM %d FOR %d) || CAST(@A1 AS CHAR(%d))) "
"AS DATETIME %s TO %s)",
leadFieldsOffset,
leadFieldsSize,
sourceFieldsSize,
targetType.getFieldName(targetType.getStartField()),
((sourceType.getEndField() == REC_DATE_SECOND)
? "FRACTION(6)"
: sourceType.getFieldName(sourceType.getEndField())));
GenAssert(str_len(str) < 199,"Internal Datetime Error Cast::preCodeGen");
ItemExpr * newExpr =
generator->getExpGenerator()->createExprTree(str, 0,
1, child(0));
newExpr->bindNode(generator->getBindWA());
child(0) = newExpr->preCodeGen(generator);
}
}
// Call matchScales only if both datatypes aren't intervals.
// (We make the exception for intervals because Cast is able
// to match the scales of intervals itself.)
// Also, we suppress the call to matchScales() for a narrow.
// This is because narrow will handle the scaling differently.
// Conversions from float to bignum are also not scaled here. Scaling
// is done in BigNum::castFrom method.
if (NOT ((getOperatorType() == ITM_NARROW) ||
((sourceTypeQual == NA_INTERVAL_TYPE) &&
(targetTypeQual == NA_INTERVAL_TYPE)) ||
((DFS2REC::isFloat(srcFsType)) &&
(DFS2REC::isBigNum(tgtFsType)))))
{
child(0) = generator->getExpGenerator()->matchScales(
child(0)->getValueId(),
getValueId().getType());
child(0) = child(0)->preCodeGen(generator);
if (! child(0).getPtr())
return NULL;
}
// For a numeric NARROW, check if scaling is needed.
if (targetTypeQual == NA_NUMERIC_TYPE &&
getOperatorType() == ITM_NARROW)
{
GenAssert(sourceTypeQual == NA_NUMERIC_TYPE,
"source type and target type incompatible in NARROW");
const NumericType * sourceNumType
= (const NumericType *)(&child(0)->getValueId().getType());
const NumericType * targetNumType
= (const NumericType *)(&getValueId().getType());
if (sourceNumType->getScale() != targetNumType->getScale())
{
// We need to scale the value. We don't want to use the
// usual scaling method of simply multiplying or dividing
// the result because we need to capture truncations
// and overflows at run time. The Narrow operator supports
// scaling for the BigNum-to-any-numeric type case.
// Therefore, we first cast the value to BigNum,
// then narrow it down.
// Soln 10-041105-1519
// Dont introduce the CAST operator if the target is already a BigNum
// because NARROW does not support scaling for the BigNum-to-BigNum
// case. Use the usual scaling method instead.
if (targetNumType->isBigNum())
{
child(0) = generator->getExpGenerator()->matchScales(
child(0)->getValueId(), *targetNumType);
}
else
{
Lng32 intermediatePrecision = sourceNumType->getPrecision();
Lng32 intermediateScale = sourceNumType->getScale();
// SQLBigNum takes decimal precision, so if the source
// has binary precision, we need to adjust.
if (sourceNumType->binaryPrecision())
{
// Can fit three binary digits in the space of one
// decimal digit. The '+5' in the precision calculation
// allows for an extra digit before and after the
// radix point.
intermediatePrecision = (intermediatePrecision+5)/3;
}
// If we need to cast an approximate, increase the length
// and scale so that the number can be represented now that
// it won't have an exponent.
// In each of the cases below, the formula used to calculate
// precision is:
//
// intermediatePrecision = 2 * <max exponent>
// + <# significant digits in mantissa> + 1
//
// We use 2 * <max exponent> to take into account the
// maximum positive exponent as well as the maximum
// negative exponent.
//
// The formula used to calculate scale is:
//
// intermediateScale = <max exponent> +
// <# significant digits in mantissa> - 1
//
// Here the exponent and digits are understood to be decimal,
// not binary.
//
// For the various kinds of floats we have:
//
// Kind Max exponent Decimal digits in Mantissa
// ----------- ------------ --------------------------
// IEEE 32 bit 38 7
// IEEE 64 bit 308 17
if (sourceNumType->getFSDatatype() == REC_IEEE_FLOAT32)
{
intermediatePrecision = 84; // (2 x 38) + 7 + 1 = 84
intermediateScale = 44; // 38 + 7 - 1 = 44
}
else if (sourceNumType->getFSDatatype() == REC_IEEE_FLOAT64)
{
intermediatePrecision = 634; // (2 x 308) + 17 + 1 = 634
intermediateScale = 324; // 308 + 17 - 1 = 324
}
NAType * intermediateType =
new(generator->wHeap())
SQLBigNum(generator->wHeap(), intermediatePrecision,
intermediateScale,
(sourceNumType->isBigNum() &&
((SQLBigNum*)sourceNumType)->isARealBigNum()),
TRUE, // make it signed
sourceNumType->supportsSQLnull());
child(0) = new(generator->wHeap()) Cast(child(0),intermediateType);
child(0)->bindNode(generator->getBindWA());
if (generator->getExpGenerator()->handleUnsupportedCast((Cast*)child(0)->castToItemExpr()))
return NULL;
// To suppress insertion of multiplying/dividing, mark Cast as
// already pre-code-genned.
child(0)->markAsPreCodeGenned();
}
}
}
if ((sourceTypeQual == NA_CHARACTER_TYPE) &&
((tgtFsType == REC_BLOB) ||
(tgtFsType == REC_CLOB)))
{
LOBconvertHandle * lc = new(generator->wHeap())
LOBconvertHandle(child(0), LOBoper::LOB_);
lc->bindNode(generator->getBindWA());
lc->preCodeGen(generator);
child(0) = lc;
}
if (getArity() > 1)
{
child(1)->bindNode(generator->getBindWA());
child(1) = child(1)->preCodeGen(generator);
if (! child(1).getPtr())
return NULL;
}
ItemExpr *result = this;
markAsPreCodeGenned();
return result;
} // Cast::preCodeGen()
ItemExpr * CharFunc::preCodeGen(Generator * generator)
{
if (nodeIsPreCodeGenned())
return this;
const NAType &typ1 = child(0)->getValueId().getType();
// Insert a cast node to convert child to an INT.
child(0) = new (generator->wHeap())
Cast(child(0), new (generator->wHeap()) SQLInt(generator->wHeap(), FALSE,
typ1.supportsSQLnullLogical()));
child(0)->bindNode(generator->getBindWA());
child(0) = child(0)->preCodeGen(generator);
if (! child(0).getPtr())
return NULL;
markAsPreCodeGenned();
return this;
} // CharFunc::preCodeGen()
ItemExpr * CompEncode::preCodeGen(Generator * generator)
{
if (nodeIsPreCodeGenned())
return this;
// during key encode expr generation, no need to convert external
// column types(like tandem floats) to their internal
// equivalent(ieee floats). Avoid doing preCodeGen in these cases.
// Do this only for child leaf nodes (columns, hostvar, params, literals).
//
if (NOT (child(0)->getValueId().getType().isExternalType() &&
child(0)->getArity() == 0)) {
child(0) = child(0)->preCodeGen(generator);
}
markAsPreCodeGenned();
return this;
} // CompEncode::preCodeGen()
ItemExpr * CompDecode::preCodeGen(Generator * generator)
{
if (nodeIsPreCodeGenned())
return this;
return CompEncode::preCodeGen(generator);
} // CompDecode::preCodeGen()
ItemExpr * Convert::preCodeGen(Generator * generator)
{
if (nodeIsPreCodeGenned())
return this;
// Since this CONVERT will convert its child to the original
// ExternalType, no need to ask it to first be cast to an internal
// type. So, do not call precodegen in these cases.
// Do this only for child leaf nodes (columns, hostvar, params, literals).
//
if (NOT (child(0)->getValueId().getType().isExternalType() &&
child(0)->getArity() == 0)) {
child(0) = child(0)->preCodeGen(generator);
}
markAsPreCodeGenned();
return this;
} // Convert::preCodeGen()
ItemExpr * ConvertTimestamp::preCodeGen(Generator * generator)
{
if (nodeIsPreCodeGenned())
return this;
//
// If the operand is not a largeint with a scale of 0, convert it to one.
//
NumericType *numeric = (NumericType *)(&(child(0)->getValueId().getType()));
if ((numeric->getFSDatatype() != REC_BIN64_SIGNED) ||
(numeric->getScale() != 0)) {
child(0) = new(generator->wHeap())
Cast(child(0),
new(generator->wHeap())
SQLLargeInt(generator->wHeap(), TRUE, numeric->supportsSQLnull()));
child(0)->bindNode(generator->getBindWA());
}
child(0) = child(0)->preCodeGen(generator);
if (! child(0).getPtr())
return NULL;
markAsPreCodeGenned();
return this;
} // ConvertTimestamp::preCodeGen()
ItemExpr * Extract::preCodeGen(Generator * generator)
{
if (nodeIsPreCodeGenned())
return this;
//
// If the operand is an interval and the extract field is not the end field,
// convert the interval to the units of the extract field.
// Set the dataconversionerror param to Cast so conversion error
// (truncation) could be ignored at runtime.
//
NAType * type_op1 = (NAType *)(&(child(0)->getValueId().getType()));
if ((type_op1->getTypeQualifier() == NA_INTERVAL_TYPE) &&
(getExtractField() < ((IntervalType *) type_op1)->getEndField())) {
IntervalType *interval = (IntervalType *) type_op1;
ItemExpr *dataConvError = new(generator->wHeap()) ConstValue(1234567890);
child(0) = new(generator->wHeap())
Cast(child(0), dataConvError,
new(generator->wHeap())
SQLInterval(generator->wHeap(), interval->supportsSQLnull(),
interval->getStartField(),
interval->getLeadingPrecision(),
getExtractField()),
ITM_NARROW);
child(0)->bindNode(generator->getBindWA());
}
child(0) = child(0)->preCodeGen(generator);
if (! child(0).getPtr())
return NULL;
markAsPreCodeGenned();
return this;
} // Extract::preCodeGen()
ItemExpr * Format::preCodeGen(Generator * generator)
{
return BuiltinFunction::preCodeGen(generator);
}
ItemExpr * JulianTimestamp::preCodeGen(Generator * generator)
{
if (nodeIsPreCodeGenned())
return this;
//
// If the operand is not a timestamp with a fractional precision of 6,
// convert it to one.
//
DatetimeType *dt = (DatetimeType *)(&(child(0)->getValueId().getType()));
if ((dt->getSubtype() != DatetimeType::SUBTYPE_SQLTimestamp) ||
(dt->getFractionPrecision() != 6)) {
child(0) = new(generator->wHeap())
Cast(child(0),
new(generator->wHeap())
SQLTimestamp(generator->wHeap(), dt->supportsSQLnull(), 6));
child(0)->bindNode(generator->getBindWA());
}
child(0) = child(0)->preCodeGen(generator);
if (! child(0).getPtr())
return NULL;
markAsPreCodeGenned();
return this;
} // JulianTimestamp::preCodeGen()
ItemExpr * Hash::preCodeGen(Generator * generator)
{
if (nodeIsPreCodeGenned())
return getReplacementExpr();
ItemExpr *result = this;
// ---------------------------------------------------------------------
// In the optimizer, a hash function accepts a comma-separated list
// of columns. In the executor, replace this with the HashComb of the hash
// functions of the individual list elements. NOTE: once error handling
// is in place we need to make sure that no errors are generated from
// this.
// ---------------------------------------------------------------------
if (child(0)->getOperatorType() == ITM_ITEM_LIST)
{
// child is a multi-valued expression, transform into multiple
// hash expressions
ExprValueId treePtr = child(0);
ItemExprTreeAsList hashValues(&treePtr,
ITM_ITEM_LIST,
LEFT_LINEAR_TREE);
// this expression becomes the hash operator for the first
// hash value
child(0) = hashValues[0];
const NAType &childType = child(0)->getValueId().getType();
if(childType.getTypeQualifier() == NA_CHARACTER_TYPE) {
const CharType &chType = (CharType&)childType;
CharInfo::Collation coll = chType.getCollation();
if (CollationInfo::isSystemCollation(coll))
{
child(0) = new(generator->wHeap())
CompEncode(child(0),FALSE, -1, CollationInfo::Compare);
child(0) = child(0)->bindNode(generator->getBindWA());
}
else
{
//--------------------------
if ((chType.isCaseinsensitive()) &&
(NOT casesensitiveHash()) &&
(NOT chType.isUpshifted())) {
child(0) = new (generator->wHeap()) Upper(child(0));
child(0) = child(0)->bindNode(generator->getBindWA());
}
}
}
// add hash expressions for all other hash values and HashComb
// them together
CollIndex nc = hashValues.entries();
for (CollIndex i = 1; i < nc; i++)
{
ItemExpr *hi = hashValues[i];
const NAType &childType = hi->getValueId().getType();
if(childType.getTypeQualifier() == NA_CHARACTER_TYPE) {
const CharType &chType = (CharType&)childType;
CharInfo::Collation coll = chType.getCollation();
if (CollationInfo::isSystemCollation(coll))
{
hi = new(generator->wHeap())
CompEncode(hi,FALSE, -1, CollationInfo::Compare);
hi = hi->bindNode(generator->getBindWA());
}
else
{
//-----------------------------
if ((chType.isCaseinsensitive()) &&
(NOT casesensitiveHash()) &&
(NOT chType.isUpshifted())) {
hi = new (generator->wHeap()) Upper(hi);
hi = hi->bindNode(generator->getBindWA());
}
//-----------------------
}
}
ItemExpr *hv = new(generator->wHeap()) Hash(hi);
result = new(generator->wHeap()) HashComb(result,hv);
}
result->bindNode(generator->getBindWA());
} else {
const NAType &childType = child(0)->getValueId().getType();
if(childType.getTypeQualifier() == NA_CHARACTER_TYPE) {
const CharType &chType = (CharType&)childType;
CharInfo::Collation coll = chType.getCollation();
if (CollationInfo::isSystemCollation(coll))
{
child(0) = new (generator->wHeap())
CompEncode(child(0), FALSE, -1, CollationInfo::Compare);
child(0) = child(0)->bindNode(generator->getBindWA());
}
else
{
if ((chType.isCaseinsensitive()) &&
(NOT casesensitiveHash()) &&
(NOT chType.isUpshifted())) {
child(0) = new (generator->wHeap()) Upper(child(0));
child(0) = child(0)->bindNode(generator->getBindWA());
}
}
}
}
// do generic tasks for pre-code generation (e.g. recurse to the children)
setReplacementExpr(result->ItemExpr::preCodeGen(generator));
markAsPreCodeGenned();
return getReplacementExpr();
} // Hash::preCodeGen()
ItemExpr * HiveHash::preCodeGen(Generator * generator)
{
if (nodeIsPreCodeGenned())
return getReplacementExpr();
ItemExpr *result = this;
// ---------------------------------------------------------------------
// In the optimizer, a hash function accepts a comma-separated list
// of columns. In the executor, replace this with the HashComb of the hash
// functions of the individual list elements. NOTE: once error handling
// is in place we need to make sure that no errors are generated from
// this.
// ---------------------------------------------------------------------
if (child(0)->getOperatorType() == ITM_ITEM_LIST)
{
// child is a multi-valued expression, transform into multiple
// hash expressions
ExprValueId treePtr = child(0);
ItemExprTreeAsList hivehashValues(&treePtr,
ITM_ITEM_LIST,
LEFT_LINEAR_TREE);
// this expression becomes the hash operator for the first
// hash value
child(0) = hivehashValues[0];
const NAType &childType = child(0)->getValueId().getType();
if(childType.getTypeQualifier() == NA_CHARACTER_TYPE) {
const CharType &chType = (CharType&)childType;
CharInfo::Collation coll = chType.getCollation();
if (CollationInfo::isSystemCollation(coll))
{
child(0) = new(generator->wHeap())
CompEncode(child(0),FALSE, -1, CollationInfo::Compare);
child(0) = child(0)->bindNode(generator->getBindWA());
}
else
{
//--------------------------
if ((chType.isCaseinsensitive()) &&
(NOT casesensitiveHash()) &&
(NOT chType.isUpshifted())) {
child(0) = new (generator->wHeap()) Upper(child(0));
child(0) = child(0)->bindNode(generator->getBindWA());
}
}
}
// add hash expressions for all other hash values and HiveHashComb
// them together
CollIndex nc = hivehashValues.entries();
for (CollIndex i = 1; i < nc; i++)
{
ItemExpr *hi = hivehashValues[i];
const NAType &childType = hi->getValueId().getType();
if(childType.getTypeQualifier() == NA_CHARACTER_TYPE) {
const CharType &chType = (CharType&)childType;
CharInfo::Collation coll = chType.getCollation();
if (CollationInfo::isSystemCollation(coll))
{
hi = new(generator->wHeap())
CompEncode(hi,FALSE, -1, CollationInfo::Compare);
hi = hi->bindNode(generator->getBindWA());
}
else
{
//-----------------------------
if ((chType.isCaseinsensitive()) &&
(NOT casesensitiveHash()) &&
(NOT chType.isUpshifted())) {
hi = new (generator->wHeap()) Upper(hi);
hi = hi->bindNode(generator->getBindWA());
}
//-----------------------
}
}
ItemExpr *hv = new(generator->wHeap()) HiveHash(hi);
result = new(generator->wHeap()) HiveHashComb(result,hv);
}
result->bindNode(generator->getBindWA());
} else {
const NAType &childType = child(0)->getValueId().getType();
if(childType.getTypeQualifier() == NA_CHARACTER_TYPE) {
const CharType &chType = (CharType&)childType;
CharInfo::Collation coll = chType.getCollation();
if (CollationInfo::isSystemCollation(coll))
{
child(0) = new (generator->wHeap())
CompEncode(child(0), FALSE, -1, CollationInfo::Compare);
child(0) = child(0)->bindNode(generator->getBindWA());
}
else
{
if ((chType.isCaseinsensitive()) &&
(NOT casesensitiveHash()) &&
(NOT chType.isUpshifted())) {
child(0) = new (generator->wHeap()) Upper(child(0));
child(0) = child(0)->bindNode(generator->getBindWA());
}
}
}
}
// do generic tasks for pre-code generation (e.g. recurse to the children)
setReplacementExpr(result->ItemExpr::preCodeGen(generator));
markAsPreCodeGenned();
return getReplacementExpr();
} // Hash::preCodeGen()
// --------------------------------------------------------------
// member functions for HashDistPartHash operator
// Hash Function used by Hash Partitioning. This function cannot change
// once Hash Partitioning is released! Defined for all data types,
// returns a 32 bit non-nullable hash value for the data item.
//--------------------------------------------------------------
ItemExpr * HashDistPartHash::preCodeGen(Generator * generator)
{
if (nodeIsPreCodeGenned())
return getReplacementExpr();
ItemExpr *result = this;
// ---------------------------------------------------------------------
// In the optimizer, a hash function accepts a comma-separated list
// of columns. Replace this with the HashComb of the hash functions
// of the individual list elements.
// ---------------------------------------------------------------------
if (child(0)->getOperatorType() == ITM_ITEM_LIST)
{
// child is a multi-valued expression, transform into multiple
// hash expressions
ExprValueId treePtr = child(0);
ItemExprTreeAsList hashValues(&treePtr,
ITM_ITEM_LIST,
LEFT_LINEAR_TREE);
// this expression becomes the hash operator for the first
// hash value
child(0) = hashValues[0];
const NAType &childType = child(0)->getValueId().getType();
if(childType.getTypeQualifier() == NA_CHARACTER_TYPE) {
const CharType &chType = (CharType&)childType;
CharInfo::Collation coll = chType.getCollation();
if (CollationInfo::isSystemCollation(coll))
{
if (child(0)->getOperatorType() == ITM_NARROW)
{
ItemExpr* narrowsChild = child(0)->child(0);
const NAType &narrowsChildType= narrowsChild->getValueId().getType();
CMPASSERT(narrowsChildType.getTypeQualifier() == NA_CHARACTER_TYPE);
NAType *newType= narrowsChildType.newCopy(generator->wHeap());
CharType * newCharType = (CharType *) newType;
newCharType->setDataStorageSize(chType.getDataStorageSize());
child(0)->getValueId().changeType(newCharType);
}
child(0) = new(generator->wHeap())
CompEncode(child(0),FALSE, -1, CollationInfo::Compare);
child(0) = child(0)->bindNode(generator->getBindWA());
}
else
{
if ((chType.isCaseinsensitive()) &&
(NOT chType.isUpshifted())) {
child(0) = new (generator->wHeap()) Upper(child(0));
child(0) = child(0)->bindNode(generator->getBindWA());
}
}
}
// add hash expressions for all other hash values and HashComb
// them together
CollIndex nc = hashValues.entries();
for (CollIndex i = 1; i < nc; i++)
{
ItemExpr *hi = hashValues[i];
const NAType &childType = hi->getValueId().getType();
if(childType.getTypeQualifier() == NA_CHARACTER_TYPE) {
const CharType &chType = (CharType&)childType;
CharInfo::Collation coll = chType.getCollation();
if (CollationInfo::isSystemCollation(coll))
{
//Solution 10-081216-8006
if (hi->getOperatorType() == ITM_NARROW)
{
ItemExpr* narrowsChild = hi->child(0);
const NAType &narrowsChildType= narrowsChild->getValueId().getType();
CMPASSERT(narrowsChildType.getTypeQualifier() == NA_CHARACTER_TYPE);
NAType *newType= narrowsChildType.newCopy(generator->wHeap());
CharType * newCharType = (CharType *) newType;
newCharType->setDataStorageSize(chType.getDataStorageSize());
hi->getValueId().changeType(newCharType);
}
hi = new(generator->wHeap())
CompEncode(hi,FALSE, -1, CollationInfo::Compare);
hi = hi->bindNode(generator->getBindWA());
}
else
{
if ((chType.isCaseinsensitive()) &&
(NOT chType.isUpshifted())) {
hi = new (generator->wHeap()) Upper(hi);
hi = hi->bindNode(generator->getBindWA());
}
}
}
ItemExpr *hv =
new(generator->wHeap()) HashDistPartHash(hi);
result = new(generator->wHeap())
HashDistPartHashComb(result,hv);
}
result->bindNode(generator->getBindWA());
} else {
const NAType &childType = child(0)->getValueId().getType();
if(childType.getTypeQualifier() == NA_CHARACTER_TYPE) {
const CharType &chType = (CharType&)childType;
CharInfo::Collation coll = chType.getCollation();
if (CollationInfo::isSystemCollation(coll))
{
//Solution 10-081216-8006
if (child(0)->getOperatorType() == ITM_NARROW)
{
ItemExpr* narrowsChild = child(0)->child(0);
const NAType &narrowsChildType= narrowsChild->getValueId().getType();
CMPASSERT(narrowsChildType.getTypeQualifier() == NA_CHARACTER_TYPE);
NAType *newType= narrowsChildType.newCopy(generator->wHeap());
CharType * newCharType = (CharType *) newType;
newCharType->setDataStorageSize(chType.getDataStorageSize());
child(0)->getValueId().changeType(newCharType);
}
child(0) = new(generator->wHeap())
CompEncode(child(0),FALSE, -1, CollationInfo::Compare);
child(0) = child(0)->bindNode(generator->getBindWA());
}
else
{
if ((chType.isCaseinsensitive()) &&
(NOT chType.isUpshifted())) {
child(0) = new (generator->wHeap()) Upper(child(0));
child(0) = child(0)->bindNode(generator->getBindWA());
}
}
}
}
// do generic tasks for pre-code generation (e.g. recurse to the children)
setReplacementExpr(result->ItemExpr::preCodeGen(generator));
markAsPreCodeGenned();
return getReplacementExpr();
} // HashDistPartHash::preCodeGen()
ItemExpr * HostVar::preCodeGen(Generator * generator)
{
if (nodeIsPreCodeGenned())
return this;
ItemExpr * i = convertExternalType(generator);
if (i == NULL)
return NULL;
return i;
}
ItemExpr * IndexColumn::preCodeGen(Generator * generator)
{
if (nodeIsPreCodeGenned())
return this;
ItemExpr * i = convertExternalType(generator);
if (i == NULL)
return NULL;
return i;
}
ItemExpr * Generator::addCompDecodeForDerialization(ItemExpr * ie, NABoolean isAlignedFormat)
{
if (!ie)
return NULL;
if ((ie->getOperatorType() == ITM_BASECOLUMN) ||
(ie->getOperatorType() == ITM_INDEXCOLUMN))
{
if (! isAlignedFormat && HbaseAccess::isEncodingNeededForSerialization(ie))
{
ItemExpr * newNode = new(wHeap()) CompDecode
(ie, &ie->getValueId().getType(), FALSE, TRUE);
newNode->bindNode(getBindWA());
if (getBindWA()->errStatus())
return NULL;
newNode = newNode->preCodeGen(this);
if (! newNode)
return NULL;
return newNode;
}
else
return ie;
}
for (Lng32 i = 0; i < ie->getArity(); i++)
{
ItemExpr * nie = addCompDecodeForDerialization(ie->child(i), isAlignedFormat);
if (nie)
ie->setChild(i, nie);
}
return ie;
}
ItemExpr * HbaseTimestamp::preCodeGen(Generator * generator)
{
if (nodeIsPreCodeGenned())
return getReplacementExpr();
if (! ItemExpr::preCodeGen(generator))
return NULL;
markAsPreCodeGenned();
return this;
}
ItemExpr * HbaseVersion::preCodeGen(Generator * generator)
{
if (nodeIsPreCodeGenned())
return getReplacementExpr();
if (! ItemExpr::preCodeGen(generator))
return NULL;
markAsPreCodeGenned();
return this;
}
ItemExpr * LOBoper::preCodeGen(Generator * generator)
{
generator->setProcessLOB(TRUE);
return BuiltinFunction::preCodeGen(generator);
}
ItemExpr * LOBconvert::preCodeGen(Generator * generator)
{
NAColumn * col = child(0)->getValueId().getNAColumn(TRUE);
if (col)
{
lobNum() = col->lobNum();
lobStorageType() = col->lobStorageType();
lobStorageLocation() = col->lobStorageLocation();
}
return LOBoper::preCodeGen(generator);
}
ItemExpr * LOBupdate::preCodeGen(Generator * generator)
{
return LOBoper::preCodeGen(generator);
}
ItemExpr * MathFunc::preCodeGen(Generator * generator)
{
if (nodeIsPreCodeGenned())
return this;
for (Int32 i = 0; i < getArity(); i++)
{
const NAType &typ = child(i)->getValueId().getType();
// Insert a cast node to convert child to a double precision.
child(i) = new (generator->wHeap())
Cast(child(i),
new (generator->wHeap()) SQLDoublePrecision(
generator->wHeap(), typ.supportsSQLnullLogical()));
child(i)->bindNode(generator->getBindWA());
child(i) = child(i)->preCodeGen(generator);
if (! child(i).getPtr())
return NULL;
}
markAsPreCodeGenned();
return this;
} // MathFunc::preCodeGen()
ItemExpr * Modulus::preCodeGen(Generator * generator)
{
if (nodeIsPreCodeGenned())
return this;
for (Int32 i = 0; i < 2; i++)
{
const NumericType &typ
= (NumericType&)child(i)->getValueId().getType();
if (typ.isDecimal())
{
// Insert a cast node to convert child to an LARGEINT.
child(i) = new (generator->wHeap())
Cast(child(i), new (generator->wHeap()) SQLLargeInt(generator->wHeap(), TRUE,
typ.supportsSQLnullLogical()));
}
child(i)->bindNode(generator->getBindWA());
child(i) = child(i)->preCodeGen(generator);
if (! child(i).getPtr())
return NULL;
}
markAsPreCodeGenned();
return this;
} // Modulus::preCodeGen()
ItemExpr * ItemExpr::convertExternalType(Generator * generator)
{
BindWA * bindWA = generator->getBindWA();
if (getValueId().getType().isExternalType())
{
// this type is not supported internally.
// Convert it to an equivalent internal type.
ItemExpr * c = new (bindWA->wHeap())
Cast(this, getValueId().getType().equivalentType(bindWA->wHeap()));
c->synthTypeAndValueId();
// mark 'this' as precodegenned so we don't go thru
// this path again.
markAsPreCodeGenned();
c = c->preCodeGen(generator);
unmarkAsPreCodeGenned();
return c;
}
else
return this;
}
ItemExpr * Parameter::preCodeGen(Generator * generator)
{
if (nodeIsPreCodeGenned())
return this;
ItemExpr * i = convertExternalType(generator);
if (i == NULL)
return NULL;
return i;
}
ItemExpr * PivotGroup::preCodeGen(Generator * generator)
{
if (nodeIsPreCodeGenned())
return getReplacementExpr();
if (! ItemExpr::preCodeGen(generator))
return NULL;
ItemExpr * childExpr = child(0)->castToItemExpr();
const NAType &type1 =
childExpr->getValueId().getType();
if (type1.getTypeQualifier() != NA_CHARACTER_TYPE)
{
Lng32 displayLen = type1.getDisplayLength(
type1.getFSDatatype(),
type1.getNominalSize(),
type1.getPrecision(),
type1.getScale(),
0);
NAType * newType = new(generator->getBindWA()->wHeap())
SQLVarChar(generator->getBindWA()->wHeap(), displayLen, type1.supportsSQLnull());
childExpr = new (generator->getBindWA()->wHeap()) Cast(childExpr, newType);
childExpr = childExpr->bindNode(generator->getBindWA());
if (! childExpr || generator->getBindWA()->errStatus())
return NULL;
childExpr = childExpr->preCodeGen(generator);
if (! childExpr)
return NULL;
child(0) = childExpr;
}
markAsPreCodeGenned();
return this;
}
ItemExpr * RandomNum::preCodeGen(Generator * generator)
{
if (nodeIsPreCodeGenned())
return this;
if (child(0))
{
const NAType &typ1 = child(0)->getValueId().getType();
// Insert a cast node to convert child to an INT.
child(0) = new (generator->wHeap())
Cast(child(0), new (generator->wHeap()) SQLInt(generator->wHeap(), FALSE,
typ1.supportsSQLnullLogical()));
child(0)->bindNode(generator->getBindWA());
child(0) = child(0)->preCodeGen(generator);
if (! child(0).getPtr())
return NULL;
}
markAsPreCodeGenned();
return this;
} // RandomNum::preCodeGen()
ItemExpr * Repeat::preCodeGen(Generator * generator)
{
if (nodeIsPreCodeGenned())
return this;
const NAType &typ2 = child(1)->getValueId().getType();
// Insert a cast node to convert child 2 to an INT.
child(1) = new (generator->wHeap())
Cast(child(1), new (generator->wHeap()) SQLInt(generator->wHeap(), FALSE,
typ2.supportsSQLnullLogical()));
child(1)->bindNode(generator->getBindWA());
for (Int32 i = 0; i < getArity(); i++)
{
if (child(i))
{
child(i) = child(i)->preCodeGen(generator);
if (! child(i).getPtr())
return NULL;
}
}
markAsPreCodeGenned();
return this;
} // Repeat::preCodeGen()
ItemExpr *ReplaceNull::preCodeGen(Generator *generator)
{
if (nodeIsPreCodeGenned())
return getReplacementExpr();
NAType *dstAType = getValueId().getType().newCopy(generator->wHeap());
const NAType& dstBType = getValueId().getType();
if(child(0) == child(1))
{
dstAType->setNullable(TRUE);
}
child(1) = new(generator->wHeap()) Cast(child(1), dstAType);
child(2) = new(generator->wHeap()) Cast(child(2), &dstBType);
child(1)->bindNode(generator->getBindWA());
child(2)->bindNode(generator->getBindWA());
setReplacementExpr(ItemExpr::preCodeGen(generator));
markAsPreCodeGenned();
return getReplacementExpr();
}
ItemExpr * TriRelational::preCodeGen(Generator * generator)
{
if (nodeIsPreCodeGenned())
return getReplacementExpr();
// ---------------------------------------------------------------------
// The executor does not handle tri-relational operators. It either
// handles key exclusion expressions if the operator is part or a key
// predicate, or the tri-relational operator gets converted into
// a case statement (see comment in file ItemFunc.h).
// ---------------------------------------------------------------------
NABoolean lessOrLe = (getOperatorType() == ITM_LESS_OR_LE);
BiRelat *exclusive = new(generator->wHeap()) BiRelat(
(IFX lessOrLe THENX ITM_LESS ELSEX ITM_GREATER),
child(0),
child(1));
BiRelat *inclusive = new(generator->wHeap()) BiRelat(
(IFX lessOrLe THENX ITM_LESS_EQ ELSEX ITM_GREATER_EQ),
child(0),
child(1));
exclusive->setSpecialNulls(getSpecialNulls());
inclusive->setSpecialNulls(getSpecialNulls());
ItemExpr * result = new(generator->wHeap()) Case(
NULL,
new(generator->wHeap()) IfThenElse(
child(2),
exclusive,
inclusive));
result->bindNode(generator->getBindWA());
// do generic tasks for pre-code generation (e.g. recurse to the children)
setReplacementExpr(result->preCodeGen(generator));
markAsPreCodeGenned();
return getReplacementExpr();
} // TriRelational::preCodeGen()
ItemExpr *
HashDistrib::preCodeGen(Generator * generator)
{
if (nodeIsPreCodeGenned())
return this;
if (! ItemExpr::preCodeGen(generator))
return NULL;
// Assert that the operands are unsigned int.
//
NumericType *numeric = (NumericType *)(&(child(0)->getValueId().getType()));
GenAssert(numeric->getFSDatatype()==REC_BIN32_UNSIGNED &&
numeric->getScale()==0,
"invalid first operand type to function HashDistrib");
numeric = (NumericType *)(&(child(1)->getValueId().getType()));
GenAssert(numeric->getFSDatatype()==REC_BIN32_UNSIGNED &&
numeric->getScale()==0,
"invalid second operand type to function HashDistrib");
markAsPreCodeGenned();
return this;
}
ItemExpr * ProgDistribKey::preCodeGen(Generator * generator)
{
if (nodeIsPreCodeGenned())
return this;
// Assert that all operands are of type unsigned int.
//
for (Int32 i=0; i<3; i++)
{
NumericType *numeric =
(NumericType *)(&(child(i)->getValueId().getType()));
GenAssert(numeric->getFSDatatype()==REC_BIN32_UNSIGNED &&
numeric->getScale()==0,
"invalid operand type to function ProgDistribKey");
}
markAsPreCodeGenned();
return this;
}
ItemExpr *
PAGroup::preCodeGen(Generator * generator)
{
if (nodeIsPreCodeGenned())
return this;
if (! ItemExpr::preCodeGen(generator))
return NULL;
// Assert that the operands are unsigned int.
//
NumericType *numeric = (NumericType *)(&(child(0)->getValueId().getType()));
GenAssert(numeric->getFSDatatype()==REC_BIN32_UNSIGNED &&
numeric->getScale()==0,
"invalid first operand type to function PAGroup");
numeric = (NumericType *)(&(child(1)->getValueId().getType()));
GenAssert(numeric->getFSDatatype()==REC_BIN32_UNSIGNED &&
numeric->getScale()==0,
"invalid second operand type to function PAGroup");
numeric = (NumericType *)(&(child(2)->getValueId().getType()));
GenAssert(numeric->getFSDatatype()==REC_BIN32_UNSIGNED &&
numeric->getScale()==0,
"invalid third operand type to function PAGroup");
markAsPreCodeGenned();
return this;
}
ItemExpr *
ScalarVariance::preCodeGen(Generator *generator)
{
if (nodeIsPreCodeGenned())
return this;
if (! ItemExpr::preCodeGen(generator))
return NULL;
NumericType *result_type =
(NumericType *)(&(getValueId().getType()));
NumericType *type_op1 =
(NumericType *)(&(child(0)->castToItemExpr()->getValueId().getType()));
NumericType *type_op2 =
(NumericType *)(&(child(1)->castToItemExpr()->getValueId().getType()));
NumericType *type_op3 =
(NumericType *)(&(child(1)->castToItemExpr()->getValueId().getType()));
GenAssert(result_type->getTypeQualifier() == NA_NUMERIC_TYPE &&
type_op1->getTypeQualifier() == NA_NUMERIC_TYPE &&
type_op2->getTypeQualifier() == NA_NUMERIC_TYPE &&
type_op3->getTypeQualifier() == NA_NUMERIC_TYPE &&
!result_type->isExact() &&
!type_op1->isExact() &&
!type_op2->isExact() &&
!type_op3->isExact() &&
result_type->getBinaryPrecision() == SQL_DOUBLE_PRECISION &&
type_op1->getBinaryPrecision() == SQL_DOUBLE_PRECISION &&
type_op2->getBinaryPrecision() == SQL_DOUBLE_PRECISION &&
type_op3->getBinaryPrecision() == SQL_DOUBLE_PRECISION,
"ScalarVariance: Invalid Inputs");
markAsPreCodeGenned();
return this;
}
ItemExpr * Substring::preCodeGen(Generator * generator)
{
if (nodeIsPreCodeGenned())
return this;
child(0) = child(0)->preCodeGen(generator);
if (! child(0).getPtr())
return NULL;
for (Int32 i = 1; i < getArity(); i++)
{
if (child(i))
{
const NAType &typ1 = child(i)->getValueId().getType();
// Insert a cast node to convert child to an INT.
child(i) = new (generator->wHeap())
Cast(child(i), new (generator->wHeap()) SQLInt(generator->wHeap(), TRUE,
typ1.supportsSQLnullLogical()));
child(i)->bindNode(generator->getBindWA());
child(i) = child(i)->preCodeGen(generator);
if (! child(i).getPtr())
return NULL;
}
}
markAsPreCodeGenned();
return this;
} // Substring::preCodeGen()
ItemExpr * ItemExpr::preCodeGen(Generator * generator)
{
if (nodeIsPreCodeGenned())
return this;
Lng32 nc = (Lng32)getArity();
for (Lng32 index = 0; index < nc; index++)
{
child(index) = child(index)->preCodeGen(generator);
if (! child(index).getPtr())
return NULL;
}
markAsPreCodeGenned();
return this;
} // ItemExpr::preCodeGen()
// ---------------------------------------------------------
// Methods for class VEGRewritePairs
// ---------------------------------------------------------
VEGRewritePairs::VEGRewritePairs(CollHeap* heap)
: heap_(heap),
vegRewritePairs_(&valueIdHashFunc, 1009, TRUE, heap)
{
}
ULng32 VEGRewritePairs::valueIdHashFunc(const CollIndex & v)
{
return (ULng32)v;
}
const VEGRewritePairs::VEGRewritePair *
VEGRewritePairs::getPair( const ValueId& original) const
{
CollIndex k(original);
return vegRewritePairs_.getFirstValue(&k);
} // getPair(..)
NABoolean VEGRewritePairs::
getRewritten(ValueId& rewritten, const ValueId& original) const
{
NABoolean found = FALSE;
const VEGRewritePairs::VEGRewritePair * vrPairPtr = NULL;
if (vrPairPtr = getPair(original)){
rewritten = vrPairPtr->getRewritten();
found = TRUE;
}
return found;
} // getRewritten
VEGRewritePairs::~VEGRewritePairs()
{
clear();
} // VEGRewritePairs::~VEGRewritePairs()
void
VEGRewritePairs::insert(const ValueId& original,
const ValueId& rewritten)
{
// Precondition:
// original must have not been rewritten before:
CMPASSERT(getPair(original) == NULL);
VEGRewritePairs::VEGRewritePair * vrPairPtr =
new (heap_) VEGRewritePairs::VEGRewritePair(original,rewritten);
CMPASSERT(vrPairPtr != NULL);
CollIndex* key = (CollIndex*) new (heap_) CollIndex(original);
vegRewritePairs_.insert(key, vrPairPtr);
}
void VEGRewritePairs::VEGRewritePair::print(FILE *ofd) const
{
Lng32 orId = CollIndex(original_),
reId = CollIndex(rewritten_);
fprintf(ofd,"<%d, %d>",orId,reId);
}
void VEGRewritePairs::print( FILE* ofd,
const char* indent,
const char* title) const
{
BUMP_INDENT(indent);
fprintf(ofd,"%s %s\n%s",NEW_INDENT,title,NEW_INDENT);
CollIndex *key;
VEGRewritePair *value;
NAHashDictionaryIterator<CollIndex, VEGRewritePair> iter(vegRewritePairs_);
for (CollIndex i=0; i < iter.entries(); i++)
{
iter.getNext(key, value);
value->print(ofd);
}
}
// PhysTranspose::preCodeGen() -------------------------------------------
// Perform local query rewrites such as for the creation and
// population of intermediate tables, for accessing partitioned
// data. Rewrite the value expressions after minimizing the dataflow
// using the transitive closure of equality predicates.
//
// PhysTranspose::preCodeGen() - is basically the same as the RelExpr::
// preCodeGen() except that here we replace the VEG references in the
// transUnionVals() as well as the selectionPred().
//
// Parameters:
//
// Generator *generator
// IN/OUT : A pointer to the generator object which contains the state,
// and tools (e.g. expression generator) to generate code for
// this node.
//
// ValueIdSet &externalInputs
// IN : The set of external Inputs available to this node.
//
//
RelExpr * PhysTranspose::preCodeGen(Generator * generator,
const ValueIdSet &externalInputs,
ValueIdSet &pulledNewInputs)
{
if (nodeIsPreCodeGenned())
return this;
// Check if the pivs of this operator and it's child are the same.
// If they are not, make them the same.
replacePivs();
// Resolve the VEGReferences and VEGPredicates, if any, that appear
// in the Characteristic Inputs, in terms of the externalInputs.
//
getGroupAttr()->resolveCharacteristicInputs(externalInputs);
// My Characteristic Inputs become the external inputs for my children.
//
Int32 nc = getArity();
for (Int32 index = 0; index < nc; index++)
{
ValueIdSet childPulledInputs;
child(index) = child(index)->preCodeGen(generator,
externalInputs,
pulledNewInputs);
if (! child(index).getPtr())
return NULL;
// process additional input value ids the child wants
getGroupAttr()->addCharacteristicInputs(childPulledInputs);
pulledNewInputs += childPulledInputs;
}
// The VEG expressions in the selection predicates and the characteristic
// outputs can reference any expression that is either a potential output
// or a characteristic input for this RelExpr. Supply these values for
// rewriting the VEG expressions.
//
ValueIdSet availableValues;
getInputValuesFromParentAndChildren(availableValues);
// The transUnionVals have access to only the Input Values.
// These can come from the parent of be the outputs of the child.
//
for(CollIndex v = 0; v < transUnionVectorSize(); v++) {
ValueIdList valIdList = transUnionVector()[v];
valIdList.replaceVEGExpressions
(availableValues,
getGroupAttr()->getCharacteristicInputs());
}
// The selectionPred has access to the output values generated by transpose.
// as well as any input values from the parent or child.
//
getInputAndPotentialOutputValues(availableValues);
// Rewrite the selection predicates.
//
NABoolean replicatePredicates = TRUE;
selectionPred().replaceVEGExpressions
(availableValues,
getGroupAttr()->getCharacteristicInputs(),
FALSE, // no key predicates here
0 /* no need for idempotence here */,
replicatePredicates
);
// Replace VEG references in the outputs and remove redundant
// outputs.
//
getGroupAttr()->resolveCharacteristicOutputs
(availableValues,
getGroupAttr()->getCharacteristicInputs());
generator->oltOptInfo()->setMultipleRowsReturned(TRUE);
markAsPreCodeGenned();
return this;
} // PhysTranspose::preCodeGen
// -----------------------------------------------------------------------
// PhyPack::preCodeGen() is basically the same as RelExpr::preCodeGen().
// It replaces the VEG's in its packingExpr_ as well as selectionPred_.
// -----------------------------------------------------------------------
RelExpr* PhyPack::preCodeGen(Generator* generator,
const ValueIdSet& externalInputs,
ValueIdSet &pulledNewInputs)
{
if (nodeIsPreCodeGenned())
return this;
// Check if the pivs of this operator and it's child are the same.
// If they are not, make them the same.
replacePivs();
// Resolve the VEGReferences and VEGPredicates, if any, that appear
// in the Characteristic Inputs, in terms of the externalInputs.
//
getGroupAttr()->resolveCharacteristicInputs(externalInputs);
// My Characteristic Inputs become the external inputs for my children.
//
Int32 nc = getArity();
for(Int32 index = 0; index < nc; index++)
{
ValueIdSet childPulledInputs;
child(index) = child(index)->preCodeGen(generator,
externalInputs,
pulledNewInputs);
if(! child(index).getPtr()) return NULL;
// process additional input value ids the child wants
getGroupAttr()->addCharacteristicInputs(childPulledInputs);
pulledNewInputs += childPulledInputs;
}
if (getFirstNRows() != -1)
{
RelExpr * firstn = new(generator->wHeap()) FirstN(child(0),
getFirstNRows(),
FALSE /* [any n] is good enough */);
// move my child's attributes to the firstN node.
// Estimated rows will be mine.
firstn->setEstRowsUsed(getEstRowsUsed());
firstn->setMaxCardEst(getMaxCardEst());
firstn->setInputCardinality(child(0)->getInputCardinality());
firstn->setPhysicalProperty(child(0)->getPhysicalProperty());
firstn->setGroupAttr(child(0)->getGroupAttr());
//10-060516-6532 -Begin
//When FIRSTN node is created after optimization phase, the cost
//of that node does not matter.But, display_explain and explain
//show zero operator costs and rollup cost which confuses the user.
//Also, the VQP crashes when cost tab for FIRSTN node is selected.
//So, creating a cost object will fix this.
//The operator cost is zero and rollup cost is same as it childs.
Cost* firstnNodecost = new HEAP Cost();
firstn->setOperatorCost(firstnNodecost);
Cost* rollupcost = (Cost *)(child(0)->getRollUpCost());
*rollupcost += *firstnNodecost;
firstn->setRollUpCost(rollupcost);
//10-060516-6532 -End
firstn =
firstn->preCodeGen(generator,
getGroupAttr()->getCharacteristicInputs(),
pulledNewInputs);
if (! firstn)
return NULL;
setChild(0, firstn);
}
// The VEG expressions in the selection predicates and the characteristic
// outputs can reference any expression that is either a potential output
// or a characteristic input for this RelExpr. Supply these values for
// rewriting the VEG expressions.
//
ValueIdSet availableValues;
getInputValuesFromParentAndChildren(availableValues);
const ValueIdSet& inputValues = getGroupAttr()->getCharacteristicInputs();
// Replace VEG's in both the packing expression and the packing factor.
//
packingFactor().replaceVEGExpressions(availableValues,inputValues);
packingExpr().replaceVEGExpressions(availableValues,inputValues);
// The selectionPred has access to the output values generated by Pack.
//
getInputAndPotentialOutputValues(availableValues);
// Rewrite the selection predicates.
//
NABoolean replicatePredicates = TRUE;
selectionPred().replaceVEGExpressions(availableValues,
inputValues,
FALSE, // no key predicates here
0 /* no need for idempotence here */,
replicatePredicates
);
// Replace VEG references in the outputs and remove redundant outputs.
//
getGroupAttr()->resolveCharacteristicOutputs(availableValues,inputValues);
markAsPreCodeGenned();
return this;
} // PhyPack::preCodeGen()
//
//PrecodeGen method for class PhysicalTuple list
//This was put in as a fix for cr 10-010327-1947.
//Before the fix the RelExpr was getting to the generator
//with a VEGRef still in it, because the VEGRef from the
//tupleExpr had not be removed and resolved correctly.
RelExpr * PhysicalTuple::preCodeGen(Generator * generator,
const ValueIdSet& externalInputs,
ValueIdSet& pulledNewInputs_)
{
ValueIdSet availableValues = externalInputs;
tupleExpr().replaceVEGExpressions
(availableValues, externalInputs);
return (RelExpr::preCodeGen(generator, availableValues, pulledNewInputs_));
} // PhysicalTuple::preCodeGen()
//
RelExpr * PhysicalTupleList::preCodeGen(Generator * generator,
const ValueIdSet& externalInputs,
ValueIdSet& pulledNewInputs_)
{
ValueIdSet availableValues = externalInputs;
tupleExpr().replaceVEGExpressions
(availableValues, externalInputs);
generator->oltOptInfo()->setMultipleRowsReturned(TRUE);
return (RelExpr::preCodeGen(generator, availableValues, pulledNewInputs_));
} // PhysicalTupleList::preCodeGen()
RelExpr * CompoundStmt::preCodeGen(Generator * generator,
const ValueIdSet & externalInputs,
ValueIdSet &pulledNewInputs)
{
if (nodeIsPreCodeGenned())
return this;
// Check if the pivs of this operator and it's child are the same.
// If they are not, make them the same.
replacePivs();
ValueIdSet availableValues;
ValueIdSet childPulledInputs;
// Resolve the VEGReferences and VEGPredicates, if any, that appear
// in the Characteristic Inputs, in terms of the externalInputs.
getGroupAttr()->resolveCharacteristicInputs(externalInputs);
availableValues = getGroupAttr()->getCharacteristicInputs();
// This is similar to what is done in Join::precodeGen when we have a TSJ.
// A compound statement node behaves in a similar way to a TSJ node since
// it flows values from left to right.
// My Characteristic Inputs become the external inputs for my left child.
child(0) = child(0)->preCodeGen(generator,availableValues,childPulledInputs);
if (! child(0).getPtr())
return NULL;
// process additional input value ids the child wants
// (see RelExpr::preCodeGen())
getGroupAttr()->addCharacteristicInputs(childPulledInputs);
pulledNewInputs += childPulledInputs;
availableValues += childPulledInputs;
childPulledInputs.clear();
// The values produced as output by my left child can be used as
// "external" inputs by my right child.
availableValues += child(0)->getGroupAttr()->getCharacteristicOutputs();
// Process the right child
child(1) = child(1)->preCodeGen(generator,availableValues,childPulledInputs);
if (! child(1).getPtr())
return NULL;
// process additional input value ids the child wants
// (see RelExpr::preCodeGen())
getGroupAttr()->addCharacteristicInputs(childPulledInputs);
pulledNewInputs += childPulledInputs;
// Accumulate the values that are provided as inputs by my parent
// together with the values that are produced as outputs by my
// children. Use these values for rewriting the VEG expressions.
getInputValuesFromParentAndChildren(availableValues);
// The VEG expressions in the selection predicates and the characteristic
// outputs can reference any expression that is either a potential output
// or a characteristic input for this RelExpr. Supply these values for
// rewriting the VEG expressions.
getInputAndPotentialOutputValues(availableValues);
// Rewrite the selection predicates.
NABoolean replicatePredicates = TRUE;
selectionPred().replaceVEGExpressions
(availableValues,
getGroupAttr()->getCharacteristicInputs(),
FALSE, // no need to generate key predicates here
0 /* no need for idempotence here */,
replicatePredicates
);
getGroupAttr()->resolveCharacteristicOutputs
(availableValues,
getGroupAttr()->getCharacteristicInputs());
// Xn will be aborted if there is any IUD stmt within this CS and
// an error occurs at runtime.
if (generator->foundAnUpdate())
{
//generator->setUpdAbortOnError(TRUE);
generator->setUpdSavepointOnError(FALSE);
generator->setUpdErrorOnError(FALSE);
//generator->setUpdPartialOnError(FALSE);
}
generator->setAqrEnabled(FALSE);
markAsPreCodeGenned();
return this;
} // CompoundStmt::preCodeGen
RelExpr * FirstN::preCodeGen(Generator * generator,
const ValueIdSet & externalInputs,
ValueIdSet &pulledNewInputs)
{
if (nodeIsPreCodeGenned())
return this;
if (getFirstNRows() > 0)
generator->setTopNRows(getFirstNRows());
else
generator->setTopNRows(ActiveSchemaDB()->getDefaults().getAsULong(GEN_SORT_TOPN_THRESHOLD));
if (! RelExpr::preCodeGen(generator,externalInputs,pulledNewInputs))
return NULL;
markAsPreCodeGenned();
return this;
} // FirstN::preCodeGen
RelExpr * RelRoutine::preCodeGen (Generator * generator,
const ValueIdSet &externalInputs,
ValueIdSet &pulledNewInputs)
{
if (nodeIsPreCodeGenned())
return this;
if (!RelExpr::preCodeGen(generator,externalInputs,pulledNewInputs))
return NULL;
// The VEG expressions in the selection predicates and the characteristic
// outputs can reference any expression that is either a potential output
// or a characteristic input for this RelExpr. Supply these values for
// rewriting the VEG expressions.
//
ValueIdSet availableValues;
availableValues = getGroupAttr()->getCharacteristicInputs();
const ValueIdSet &inputValues = getGroupAttr()->getCharacteristicInputs();
getProcInputParamsVids().replaceVEGExpressions(availableValues, inputValues);
generator->setAqrEnabled(FALSE);
markAsPreCodeGenned();
return this;
}
RelExpr * IsolatedNonTableUDR::preCodeGen (Generator * generator,
const ValueIdSet &externalInputs,
ValueIdSet &pulledNewInputs)
{
if (nodeIsPreCodeGenned())
return this;
if (!RelRoutine::preCodeGen(generator,externalInputs,pulledNewInputs))
return NULL;
// The VEG expressions in the selection predicates and the characteristic
// outputs can reference any expression that is either a potential output
// or a characteristic input for this RelExpr. Supply these values for
// rewriting the VEG expressions.
//
ValueIdSet availableValues;
availableValues = getGroupAttr()->getCharacteristicInputs();
const ValueIdSet &inputValues = getGroupAttr()->getCharacteristicInputs();
getNeededValueIds().replaceVEGExpressions(availableValues, inputValues);
markAsPreCodeGenned();
return this;
}
RelExpr * PhysicalTableMappingUDF::preCodeGen(Generator * generator,
const ValueIdSet &externalInputs,
ValueIdSet &pulledNewInputs)
{
if (nodeIsPreCodeGenned())
return this;
if (!RelRoutine::preCodeGen(generator,externalInputs,pulledNewInputs))
return NULL;
ValueIdSet availableValues;
getInputValuesFromParentAndChildren(availableValues);
for(Int32 i = 0; i < getArity(); i++)
{
ValueIdList &childOutputs(getChildInfo(i)->getOutputIds());
ValueIdList origChildOutputs(childOutputs);
childOutputs.replaceVEGExpressions(
availableValues,
getGroupAttr()->getCharacteristicInputs());
for (CollIndex j=0; j<childOutputs.entries(); j++)
if (NOT(childOutputs[j].getType() == origChildOutputs[j].getType()))
{
// VEG rewrite changed the type.
// Since we recorded the original type of the input
// column and exposed this type to the UDF writer, don't
// change the type now. Instead, add a cast back to the
// original type.
ItemExpr *castToOrigType = new(CmpCommon::statementHeap())
Cast(childOutputs[j].getItemExpr(),
origChildOutputs[j].getType().newCopy());
castToOrigType->synthTypeAndValueId();
childOutputs[j] = castToOrigType->getValueId();
}
}
planInfo_ = getPhysicalProperty()->getUDRPlanInfo();
if (!getDllInteraction()->finalizePlan(this, planInfo_))
return NULL;
markAsPreCodeGenned();
return this;
}
RelExpr * PhysicalFastExtract::preCodeGen (Generator * generator,
const ValueIdSet &externalInputs,
ValueIdSet &pulledNewInputs)
{
if (nodeIsPreCodeGenned())
return this;
if (getIsMainQueryOperator())
generator->setIsFastExtract(TRUE);
else
generator->setContainsFastExtract(TRUE);
if (!RelExpr::preCodeGen(generator,externalInputs,pulledNewInputs))
return NULL;
ValueIdSet availableValues;
getInputValuesFromParentAndChildren(availableValues);
getSelectListIds().replaceVEGExpressions(availableValues, externalInputs);
if (isAppend())
generator->setAqrEnabled(FALSE);
// This relation is a linear fit to cpu consumption data observed during a
// performance run, while extracting data from the LINEITEM table. CPU Usage
// can go from 0% to 50% according to this relation. CPU Usage is determined
// by 2 factors (a) bytes of data extracted and (b) % non-character
// (termed numeric below) columns in each row (computed based on total max
// row size and tol non-char column size). Both factors have equal weigth,
// i.e. they can contribute at most 25% towards Cpu usage. For upto 50 GB
// extracted data the bytes of extracted data increases linearly from 0% to
// 25%. After 50 GB (total volume across all partitions), the contribution to
// cpu usage from bytes extracted does not increase. Similarly the a table
// all non-char columns can contribute upto 25% towards cpu usage. The numeric
// factor is also weighted by the volume of data extracted.
const Int32 plateauTabSizeInGB = 50;
const float weightOfBaseExtract = 0.5;
const float weightOfNumericExpressionEval = 0.5;
const Int32 maxPossibleCpuUsage = 50 ; // in percentage units
Int32 rowLength = child(0).getGroupAttr()->getCharacteristicOutputs().getRowLength();
Int32 numericRowLength = child(0).getGroupAttr()->
getCharacteristicOutputs().getRowLengthOfNumericCols();
float numericRowLengthRatio = ((float) numericRowLength)/rowLength ;
double bytesExtractedInGB = (getEstRowsUsed().value()*rowLength)/(1024*1024*1024);
double bytesExtractedRatio = bytesExtractedInGB/plateauTabSizeInGB ;
if (bytesExtractedRatio > 1)
bytesExtractedRatio = 1;
Int32 maxCpuUsage = (Int32) (maxPossibleCpuUsage*bytesExtractedRatio*(weightOfBaseExtract +
(weightOfNumericExpressionEval*numericRowLengthRatio)));
generator->setMaxCpuUsage(maxCpuUsage);
markAsPreCodeGenned();
return this;
}
RelExpr * RelLock::preCodeGen (Generator * generator,
const ValueIdSet &externalInputs,
ValueIdSet &pulledNewInputs)
{
if (nodeIsPreCodeGenned())
return this;
// Since the newExch node is added as the parent
// to SequenceGenerator node, this method gets
// called again during the preCodeGen of t
// newExch.
if(parallelExecution_)
{
// Add an exchange node here so this could be executed in ESP.
RelExpr * exchange = new(generator->wHeap()) Exchange (this);
exchange->setPhysicalProperty(this->getPhysicalProperty());
exchange->setGroupAttr(this->getGroupAttr());
markAsPreCodeGenned();
exchange = exchange->preCodeGen(generator, externalInputs, pulledNewInputs);
// Done.
return exchange;
/*
RelExpr *newExch =
generator->insertEspExchange(this, getPhysicalProperty());
((Exchange *)newExch)->makeAnESPAccess();
markAsPreCodeGenned();
RelExpr * exch =
newExch->preCodeGen(generator, externalInputs, pulledNewInputs);
return exch;
*/
}
if (!RelExpr::preCodeGen(generator,externalInputs,pulledNewInputs))
return NULL;
markAsPreCodeGenned();
return this;
}
RelExpr * StatisticsFunc::preCodeGen(Generator * generator,
const ValueIdSet & externalInputs,
ValueIdSet &pulledNewInputs)
{
if (nodeIsPreCodeGenned())
return this;
if (! RelExpr::preCodeGen(generator,externalInputs,pulledNewInputs))
return NULL;
// don't collect stats for stats func itself
generator->setComputeStats(FALSE);
markAsPreCodeGenned();
// Done.
return this;
}
RelExpr * ExeUtilGetStatistics::preCodeGen(Generator * generator,
const ValueIdSet & externalInputs,
ValueIdSet &pulledNewInputs)
{
if (nodeIsPreCodeGenned())
return this;
if (! ExeUtilExpr::preCodeGen(generator,externalInputs,pulledNewInputs))
return NULL;
// don't collect stats for stats func itself
generator->setComputeStats(FALSE);
markAsPreCodeGenned();
// Done.
return this;
}
RelExpr * ExeUtilWnrInsert::preCodeGen(Generator * generator,
const ValueIdSet & externalInputs,
ValueIdSet &pulledNewInputs)
{
if (nodeIsPreCodeGenned())
return this;
if (! ExeUtilExpr::preCodeGen(generator,externalInputs,pulledNewInputs))
return NULL;
markAsPreCodeGenned();
return this;
}
ItemExpr * PositionFunc::preCodeGen(Generator * generator)
{
if (nodeIsPreCodeGenned())
return this;
if (! BuiltinFunction::preCodeGen(generator))
return NULL;
const NAType &type1 =
child(0)->castToItemExpr()->getValueId().getType();
const NAType &type2 =
child(1)->castToItemExpr()->getValueId().getType();
CMPASSERT(
(type1.getTypeQualifier() == NA_CHARACTER_TYPE) &&
(type2.getTypeQualifier() == NA_CHARACTER_TYPE))
const CharType &cType1 = (CharType&)type1;
const CharType &cType2 = (CharType&)type2;
CharInfo::Collation coll1 = cType1.getCollation();
CharInfo::Collation coll2 = cType2.getCollation();
CMPASSERT(coll1==coll2);
setCollation(coll1);
if (CollationInfo::isSystemCollation(coll1))
{
{
ItemExpr * newEncode =
new(generator->wHeap())
CompEncode(child(0),FALSE, -1, CollationInfo::Search);
newEncode = newEncode->bindNode(generator->getBindWA());
newEncode = newEncode->preCodeGen(generator);
if (!newEncode)
return NULL;
setChild(0, newEncode);
newEncode =
new(generator->wHeap())
CompEncode(child(1), FALSE, -1,CollationInfo::Search);
newEncode->bindNode(generator->getBindWA());
newEncode = newEncode->preCodeGen(generator);
if (!newEncode)
return NULL;
setChild(1, newEncode);
}
}
markAsPreCodeGenned();
return this;
} // PositionFunc::preCodeGen()
ItemExpr * Trim::preCodeGen(Generator * generator)
{
if (nodeIsPreCodeGenned())
return this;
if (! BuiltinFunction::preCodeGen(generator))
return NULL;
const NAType &type1 =
child(0)->castToItemExpr()->getValueId().getType();
const NAType &type2 =
child(1)->castToItemExpr()->getValueId().getType();
CMPASSERT(
(type1.getTypeQualifier() == NA_CHARACTER_TYPE) &&
(type2.getTypeQualifier() == NA_CHARACTER_TYPE))
const CharType &cType1 = (CharType&)type1;
const CharType &cType2 = (CharType&)type2;
CharInfo::Collation coll1 = cType1.getCollation();
CharInfo::Collation coll2 = cType2.getCollation();
CMPASSERT(coll1==coll2);
setCollation(coll1);
markAsPreCodeGenned();
return this;
} // Trim::preCodeGen()
ItemExpr * NotIn::preCodeGen(Generator * generator)
{
if (child(0)->getOperatorType() == ITM_ITEM_LIST)
{//Multicolumn NotIn should not reach this far
GenAssert(FALSE,"Multicolumn NotIn should not have reached this far");
return NULL;
}
if (nodeIsPreCodeGenned())
{
return getReplacementExpr();
}
// if single column NOT IN reaches pre-code generation, then replace it with
// non equi-predicate form (NE)
// An example of cases where NotIn reaches this far is a aquery like
// select * from ta where (select sum(a2) from ta) not in (select b2 from tb);
// where the NotIn predicate gets pushed down and is not caught at optimization
// time
ValueId vid = createEquivNonEquiPredicate();
ItemExpr * newPred = vid.getItemExpr();
setReplacementExpr(newPred->preCodeGen(generator));
markAsPreCodeGenned();
return getReplacementExpr();
} // NotIn::preCodeGen()
short HbaseAccess::processSQHbaseKeyPreds(Generator * generator,
NAList<HbaseSearchKey*>& searchKeys,
ListOfUniqueRows &listOfUniqueRows,
ListOfRangeRows &listOfRangeRows)
{
Int32 ct = 0;
HbaseUniqueRows getSpec;
getSpec.rowTS_ = -1;
for (CollIndex i = 0; i<searchKeys.entries(); i++ )
{
HbaseSearchKey* searchKey = searchKeys[i];
ValueIdSet newSelectionPreds;
if ( searchKey->isUnique() )
{
// Since we fill one rowId per entry, we will be using getRow() form of Get.
if ( (ct=searchKey->getCoveredLeadingKeys()) > 0 )
{
NAString result;
ValueIdList keyValues = searchKey->getBeginKeyValues();
keyValues.convertToTextKey(searchKey->getKeyColumns(), result);
getSpec.rowIds_.insert(result);
}
// getSpec.addColumnNames(searchKey->getRequiredOutputColumns());
}
else
{
// Multiple rows. Do Scan
HbaseRangeRows scanSpec;
scanSpec.beginKeyExclusive_ = FALSE;
scanSpec.endKeyExclusive_ = FALSE;
scanSpec.rowTS_ = -1;
if ((( !searchKey->areAllBeginKeysMissing() ) &&
((ct=searchKey->getCoveredLeadingKeys()) > 0 )) ||
searchKey->isFalsePred())
{
ValueIdList beginKeyValues = searchKey->getBeginKeyValues();
beginKeyValues.convertToTextKey(searchKey->getKeyColumns(),
scanSpec.beginRowId_);
scanSpec.beginKeyExclusive_ =
searchKey->isBeginKeyExclusive();
}
if ((( !searchKey->areAllEndKeysMissing() ) &&
(ct=searchKey->getCoveredLeadingKeys())) ||
searchKey->isFalsePred())
{
ValueIdList endKeyValues = searchKey->getEndKeyValues();
endKeyValues.convertToTextKey(searchKey->getKeyColumns(),
scanSpec.endRowId_);
scanSpec.endKeyExclusive_ = searchKey->isEndKeyExclusive();
}
listOfRangeRows.insertAt(listOfRangeRows.entries(), scanSpec);
}
} // for
if (getSpec.rowIds_.entries() > 0)
listOfUniqueRows.insert(getSpec);
return 0;
}
short HbaseAccess::processNonSQHbaseKeyPreds(Generator * generator,
ValueIdSet &preds,
ListOfUniqueRows &listOfUniqueRows,
ListOfRangeRows &listOfRangeRows)
{
ValueId vid;
ValueId eqRowIdValVid;
ValueId eqColNameValVid;
ItemExpr * ie = NULL;
NABoolean rowIdFound = FALSE;
NABoolean colNameFound = FALSE;
NABoolean isConstParam = FALSE;
ValueIdList newPredList;
NABoolean addToNewPredList;
HbaseUniqueRows hg;
HbaseRangeRows hs;
for (vid = preds.init();
(preds.next(vid));
preds.advance(vid))
{
ie = vid.getItemExpr();
addToNewPredList = TRUE;
ConstValue * constVal = NULL;
if ((NOT rowIdFound) && (isEqGetExpr(ie, eqRowIdValVid, isConstParam,
"ROW_ID")))
{
rowIdFound = TRUE;
if (isConstParam)
{
ConstantParameter*cp = (ConstantParameter*)eqRowIdValVid.getItemExpr();
constVal = cp->getConstVal();
}
else
constVal = (ConstValue*)eqRowIdValVid.getItemExpr();
NAString rid = *constVal->getRawText();
hg.rowIds_.insert(rid);
hg.rowTS_ = -1;
addToNewPredList = FALSE;
}
if (isEqGetExpr(ie, eqColNameValVid, isConstParam, "COL_NAME"))
{
colNameFound = TRUE;
if (isConstParam)
{
ConstantParameter*cp = (ConstantParameter*)eqColNameValVid.getItemExpr();
constVal = cp->getConstVal();
}
else
constVal = (ConstValue*)eqColNameValVid.getItemExpr();
NAString col = *constVal->getRawText();
hg.colNames_.insert(col);
hs.colNames_.insert(col);
addToNewPredList = FALSE;
}
if (addToNewPredList)
newPredList.insert(vid);
} // for
if ((rowIdFound) || (colNameFound))
{
preds.clear();
preds.insertList(newPredList);
}
if (rowIdFound)
{
listOfUniqueRows.insert(hg);
}
else
{
hs.rowTS_ = -1;
listOfRangeRows.insert(hs);
}
// markAsPreCodeGenned();
// Done.
return 0;
}
////////////////////////////////////////////////////////////////////////////
// To push down, the predicate must have the following form:
// <column> <op> <value-expr>
//
// and all of the following conditions must be met:
//
// <column>: a base table or index column which can be serialized.
// serialized: either the column doesn't need encoding, like
// an unsigned integer, or the column
// was declared with the SERIALIZED option.
// <op>: eq, ne, gt, ge, lt, le
// <value-expr>: an expression that only contains const or param values, and
// <value-expr>'s datatype is not a superset of <column>'s datatype.
//
/////////////////////////////////////////////////////////////////////////////
NABoolean HbaseAccess::isHbaseFilterPred(Generator * generator, ItemExpr * ie,
ValueId &colVID, ValueId &valueVID,
NAString &op,
NABoolean &removeFromOrigList)
{
NABoolean found = FALSE;
removeFromOrigList = FALSE;
NABoolean hbaseLookupPred = FALSE;
NABoolean flipOp = FALSE; // set to TRUE when column is child(1)
if (ie &&
((ie->getOperatorType() >= ITM_EQUAL) &&
(ie->getOperatorType() <= ITM_GREATER_EQ)))
{
ItemExpr * child0 = ie->child(0)->castToItemExpr();
ItemExpr * child1 = ie->child(1)->castToItemExpr();
if ((ie->child(0)->getOperatorType() == ITM_BASECOLUMN) &&
(NOT hasColReference(ie->child(1))))
{
found = TRUE;
colVID = ie->child(0)->getValueId();
valueVID = ie->child(1)->getValueId();
}
else if ((ie->child(1)->getOperatorType() == ITM_BASECOLUMN) &&
(NOT hasColReference(ie->child(0))))
{
found = TRUE;
flipOp = TRUE;
colVID = ie->child(1)->getValueId();
valueVID = ie->child(0)->getValueId();
}
else if ((ie->child(0)->getOperatorType() == ITM_INDEXCOLUMN) &&
(NOT hasColReference(ie->child(1))))
{
found = TRUE;
colVID = ie->child(0)->getValueId();
valueVID = ie->child(1)->getValueId();
}
else if ((ie->child(1)->getOperatorType() == ITM_INDEXCOLUMN) &&
(NOT hasColReference(ie->child(0))))
{
found = TRUE;
flipOp = TRUE;
colVID = ie->child(1)->getValueId();
valueVID = ie->child(0)->getValueId();
}
else if ((ie->child(0)->getOperatorType() == ITM_REFERENCE) &&
(NOT hasColReference(ie->child(1))))
{
found = TRUE;
colVID = ie->child(0)->getValueId();
valueVID = ie->child(1)->getValueId();
}
else if ((ie->child(1)->getOperatorType() == ITM_REFERENCE) &&
(NOT hasColReference(ie->child(0))))
{
found = TRUE;
flipOp = TRUE;
colVID = ie->child(1)->getValueId();
valueVID = ie->child(0)->getValueId();
}
else if ((ie->child(0)->getOperatorType() == ITM_HBASE_COLUMN_LOOKUP) &&
(NOT hasColReference(ie->child(1))))
{
HbaseColumnLookup * hcl = (HbaseColumnLookup*)ie->child(0)->castToItemExpr();
if (hcl->getValueId().getType().getTypeQualifier() == NA_CHARACTER_TYPE)
{
hbaseLookupPred = TRUE;
ItemExpr * newCV = new(generator->wHeap()) ConstValue(hcl->hbaseCol());
newCV = newCV->bindNode(generator->getBindWA());
newCV = newCV->preCodeGen(generator);
found = TRUE;
colVID = newCV->getValueId();
valueVID = ie->child(1)->getValueId();
}
}
else if ((ie->child(1)->getOperatorType() == ITM_HBASE_COLUMN_LOOKUP) &&
(NOT hasColReference(ie->child(0))))
{
HbaseColumnLookup * hcl = (HbaseColumnLookup*)ie->child(1)->castToItemExpr();
if (hcl->getValueId().getType().getTypeQualifier() == NA_CHARACTER_TYPE)
{
hbaseLookupPred = TRUE;
ItemExpr * newCV = new(generator->wHeap()) ConstValue(hcl->hbaseCol());
newCV = newCV->bindNode(generator->getBindWA());
newCV = newCV->preCodeGen(generator);
found = TRUE;
flipOp = TRUE;
colVID = newCV->getValueId();
valueVID = ie->child(0)->getValueId();
}
}
}
if (found)
{
const NAType &colType = colVID.getType();
const NAType &valueType = valueVID.getType();
NABoolean generateNarrow = FALSE;
if (NOT hbaseLookupPred)
{
generateNarrow = valueType.errorsCanOccur(colType);
if ((generateNarrow) || // value not a superset of column
(NOT columnEnabledForSerialization(colVID.getItemExpr())))
found = FALSE;
}
if (found)
{
if (colType.getTypeQualifier() == NA_CHARACTER_TYPE)
{
const CharType &charColType = (CharType&)colType;
const CharType &charValType = (CharType&)valueType;
if ((charColType.isCaseinsensitive() || charValType.isCaseinsensitive()) ||
(charColType.isUpshifted() || charValType.isUpshifted()))
found = FALSE;
}
else if (colType.getTypeQualifier() == NA_NUMERIC_TYPE)
{
const NumericType &numType = (NumericType&)colType;
const NumericType &valType = (NumericType&)valueType;
if (numType.isBigNum() || valType.isBigNum())
found = FALSE;
}
}
if (found)
{
if ((ie) && (((BiRelat*)ie)->addedForLikePred()) &&
(valueVID.getItemExpr()->getOperatorType() == ITM_CONSTANT))
{
// remove trailing '\0' characters since this is being pushed down to hbase.
ConstValue * cv = (ConstValue*)(valueVID.getItemExpr());
char * cvv = (char*)cv->getConstValue();
Lng32 len = cv->getStorageSize() - 1;
while ((len > 0) && (cvv[len] == '\0'))
len--;
NAString newCVV(cvv, len+1);
ItemExpr * newCV = new(generator->wHeap()) ConstValue(newCVV);
newCV = newCV->bindNode(generator->getBindWA());
newCV = newCV->preCodeGen(generator);
valueVID = newCV->getValueId();
}
ItemExpr * castValue = NULL;
if (NOT hbaseLookupPred)
castValue = new(generator->wHeap()) Cast(valueVID.getItemExpr(), &colType);
else
{
castValue = new(generator->wHeap()) Cast(valueVID.getItemExpr(), &valueVID.getType());
}
if ((NOT hbaseLookupPred) &&
(isEncodingNeededForSerialization(colVID.getItemExpr())))
{
castValue = new(generator->wHeap()) CompEncode
(castValue, FALSE, -1, CollationInfo::Sort, TRUE, FALSE);
}
castValue = castValue->bindNode(generator->getBindWA());
castValue = castValue->preCodeGen(generator);
valueVID = castValue->getValueId();
// hbase pred evaluation compares the column byte string with the
// value byte string. It doesn't have a notion of nullability.
// For a nullable value stored in database, the first byte represents
// if the value is a null value.
// During pred evaluation in hbase, a null value could either get filtered
// out due to byte string comparison, or it may get returned back.
// For ex, <col> <gt> <value>
// will return TRUE if the first byte of <col> is a null value.
// Similary, <col> <lt> <value>
// will return FALSE if the first byte of <col> is a null value.
// If the a null value gets filtered out, then that is correct semantics.
// But if the null value gets returned to executor, then it still need to be
// filtered out. To do that, the predicate need to be evaluated in executor
// with proper null semantics.
//
// Long story short, do not remove the original pred if the col or value is
// nullable.
//
if ((colType.supportsSQLnull()) ||
(valueType.supportsSQLnull()))
{
removeFromOrigList = FALSE;
}
else
{
removeFromOrigList = TRUE;
}
if (ie->getOperatorType() == ITM_EQUAL)
op = "EQUAL";
else if (ie->getOperatorType() == ITM_NOT_EQUAL)
op = "NOT_EQUAL";
else if (ie->getOperatorType() == ITM_LESS)
{
if (flipOp)
op = "GREATER";
else
op = "LESS";
}
else if (ie->getOperatorType() == ITM_LESS_EQ)
{
if (flipOp)
op = "GREATER_OR_EQUAL";
else
op = "LESS_OR_EQUAL";
}
else if (ie->getOperatorType() == ITM_GREATER)
{
if (flipOp)
op = "LESS";
else
op = "GREATER";
}
else if (ie->getOperatorType() == ITM_GREATER_EQ)
{
if (flipOp)
op = "LESS_OR_EQUAL";
else
op = "GREATER_OR_EQUAL";
}
else
op = "NO_OP";
}
}
return found;
}
short HbaseAccess::extractHbaseFilterPreds(Generator * generator,
ValueIdSet &preds, ValueIdSet &newExePreds)
{
if (CmpCommon::getDefault(HBASE_FILTER_PREDS) == DF_OFF)
return 0;
// cannot push preds for aligned format row
NABoolean isAlignedFormat = getTableDesc()->getNATable()->isAlignedFormat(getIndexDesc());
if (isAlignedFormat)
return 0;
for (ValueId vid = preds.init();
(preds.next(vid));
preds.advance(vid))
{
ItemExpr * ie = vid.getItemExpr();
ValueId colVID;
ValueId valueVID;
NABoolean removeFromOrigList = FALSE;
NAString op;
NABoolean isHFP =
isHbaseFilterPred(generator, ie, colVID, valueVID, op, removeFromOrigList);
if (isHFP)
{
hbaseFilterColVIDlist_.insert(colVID);
hbaseFilterValueVIDlist_.insert(valueVID);
opList_.insert(op);
if (NOT removeFromOrigList)
newExePreds.insert(vid);
}
else
{
newExePreds.insert(vid);
}
} // end for
return 0;
}
////////////////////////////////////////////////////////////////////////////
// To push down, the predicate must have the following form:
// xp:= <column> <op> <value-expr>
// xp:= <column> is not null (no support for hbase lookup)
// xp:= <column> is null (no support for hbase lookup)
// (xp:=<column> like <value-expr> not yet implemented)
// xp:=<xp> OR <xp> (not evaluated in isHbaseFilterPredV2, but by extractHbaseFilterPredV2)
// xp:=<xp> AND <xp>(not evaluated in isHbaseFilterPredV2, but by extractHbaseFilterPredV2)
//
// and all of the following conditions must be met:
//
// <column>: a base table or index column which can be serialized and belong to the table being scanned.
// serialized: either the column doesn't need encoding, like
// an unsigned integer, or the column
// was declared with the SERIALIZED option.
// it also must not be an added column with default non null.
// <op>: eq, ne, gt, ge, lt, le
// <value-expr>: an expression that only contains const or param values, and
// <value-expr>'s datatype is not a superset of <column>'s datatype.
//
// colVID, valueID and op are output parameters.
/////////////////////////////////////////////////////////////////////////////
NABoolean HbaseAccess::isHbaseFilterPredV2(Generator * generator, ItemExpr * ie,
ValueId &colVID, ValueId &valueVID,
NAString &op)
{
NABoolean foundBinary = FALSE;
NABoolean foundUnary = FALSE;
NABoolean hbaseLookupPred = FALSE;
NABoolean flipOp = FALSE; // set to TRUE when column is child(1)
if (ie &&
((ie->getOperatorType() >= ITM_EQUAL) &&
(ie->getOperatorType() <= ITM_GREATER_EQ))) //binary operator case
{//begin expression
ItemExpr * child0 = ie->child(0)->castToItemExpr();
ItemExpr * child1 = ie->child(1)->castToItemExpr();
if ((ie->child(0)->getOperatorType() == ITM_BASECOLUMN) &&
(NOT hasColReference(ie->child(1))))
{
foundBinary = TRUE;
colVID = ie->child(0)->getValueId();
valueVID = ie->child(1)->getValueId();
}
else if ((ie->child(1)->getOperatorType() == ITM_BASECOLUMN) &&
(NOT hasColReference(ie->child(0))))
{
foundBinary = TRUE;
flipOp = TRUE;
colVID = ie->child(1)->getValueId();
valueVID = ie->child(0)->getValueId();
}
else if ((ie->child(0)->getOperatorType() == ITM_INDEXCOLUMN) &&
(NOT hasColReference(ie->child(1))))
{
foundBinary = TRUE;
colVID = ie->child(0)->getValueId();
valueVID = ie->child(1)->getValueId();
}
else if ((ie->child(1)->getOperatorType() == ITM_INDEXCOLUMN) &&
(NOT hasColReference(ie->child(0))))
{
foundBinary = TRUE;
flipOp = TRUE;
colVID = ie->child(1)->getValueId();
valueVID = ie->child(0)->getValueId();
}
else if ((ie->child(0)->getOperatorType() == ITM_HBASE_COLUMN_LOOKUP) &&
(NOT hasColReference(ie->child(1))))
{
HbaseColumnLookup * hcl = (HbaseColumnLookup*)ie->child(0)->castToItemExpr();
if (hcl->getValueId().getType().getTypeQualifier() == NA_CHARACTER_TYPE)
{
hbaseLookupPred = TRUE;
ItemExpr * newCV = new(generator->wHeap()) ConstValue(hcl->hbaseCol());
newCV = newCV->bindNode(generator->getBindWA());
newCV = newCV->preCodeGen(generator);
foundBinary = TRUE;
colVID = newCV->getValueId();
valueVID = ie->child(1)->getValueId();
}
}
else if ((ie->child(1)->getOperatorType() == ITM_HBASE_COLUMN_LOOKUP) &&
(NOT hasColReference(ie->child(0))))
{
HbaseColumnLookup * hcl = (HbaseColumnLookup*)ie->child(1)->castToItemExpr();
if (hcl->getValueId().getType().getTypeQualifier() == NA_CHARACTER_TYPE)
{
hbaseLookupPred = TRUE;
ItemExpr * newCV = new(generator->wHeap()) ConstValue(hcl->hbaseCol());
newCV = newCV->bindNode(generator->getBindWA());
newCV = newCV->preCodeGen(generator);
foundBinary = TRUE;
flipOp = TRUE;
colVID = newCV->getValueId();
valueVID = ie->child(0)->getValueId();
}
}
}//end binary operators
else if (ie && ((ie->getOperatorType() == ITM_IS_NULL)||(ie->getOperatorType() == ITM_IS_NOT_NULL))){//check for unary operators
ItemExpr * child0 = ie->child(0)->castToItemExpr();
if ((ie->child(0)->getOperatorType() == ITM_BASECOLUMN) ||
(ie->child(0)->getOperatorType() == ITM_INDEXCOLUMN)){
foundUnary = TRUE;
colVID = ie->child(0)->getValueId();
valueVID = NULL_VALUE_ID;
}
}//end unary operators
//check if found columns belong to table being scanned (so is not an input to the scan node)
if (foundBinary || foundUnary){
ValueId dummyValueId;
if (getGroupAttr()->getCharacteristicInputs().referencesTheGivenValue(colVID,dummyValueId)){
foundBinary=FALSE;
foundUnary=FALSE;
}
}
//check if not an added column with default non null
if ((foundBinary || foundUnary)&& (NOT hbaseLookupPred)){
if (colVID.isColumnWithNonNullNonCurrentDefault()){
foundBinary=FALSE;
foundUnary=FALSE;
}
}
if (foundBinary)
{
const NAType &colType = colVID.getType();
const NAType &valueType = valueVID.getType();
NABoolean generateNarrow = FALSE;
if (NOT hbaseLookupPred)
{
generateNarrow = valueType.errorsCanOccur(colType);
if ((generateNarrow) || // value not a superset of column
(NOT columnEnabledForSerialization(colVID.getItemExpr())))
foundBinary = FALSE;
}
if (foundBinary)
{
if (colType.getTypeQualifier() == NA_CHARACTER_TYPE)
{
const CharType &charColType = (CharType&)colType;
const CharType &charValType = (CharType&)valueType;
if ((charColType.isCaseinsensitive() || charValType.isCaseinsensitive()) ||
(charColType.isUpshifted() || charValType.isUpshifted()))
foundBinary = FALSE;
}
else if (colType.getTypeQualifier() == NA_NUMERIC_TYPE)
{
const NumericType &numType = (NumericType&)colType;
const NumericType &valType = (NumericType&)valueType;
if (numType.isBigNum() || valType.isBigNum())
foundBinary = FALSE;
}
}
if (foundBinary)
{
if ((ie) && (((BiRelat*)ie)->addedForLikePred()) &&
(valueVID.getItemExpr()->getOperatorType() == ITM_CONSTANT))
{
// remove trailing '\0' characters since this is being pushed down to hbase.
ConstValue * cv = (ConstValue*)(valueVID.getItemExpr());
char * cvv = (char*)cv->getConstValue();
Lng32 len = cv->getStorageSize() - 1;
while ((len > 0) && (cvv[len] == '\0'))
len--;
NAString newCVV(cvv, len+1);
ItemExpr * newCV = new(generator->wHeap()) ConstValue(newCVV);
newCV = newCV->bindNode(generator->getBindWA());
newCV = newCV->preCodeGen(generator);
valueVID = newCV->getValueId();
}
ItemExpr * castValue = NULL;
if (NOT hbaseLookupPred)
castValue = new(generator->wHeap()) Cast(valueVID.getItemExpr(), &colType);
else
{
castValue = new(generator->wHeap()) Cast(valueVID.getItemExpr(), &valueVID.getType());
}
if ((NOT hbaseLookupPred) &&
(isEncodingNeededForSerialization(colVID.getItemExpr())))
{
castValue = new(generator->wHeap()) CompEncode
(castValue, FALSE, -1, CollationInfo::Sort, TRUE, FALSE);
}
castValue = castValue->bindNode(generator->getBindWA());
castValue = castValue->preCodeGen(generator);
valueVID = castValue->getValueId();
NAString nullType;
if ((colType.supportsSQLnull()) ||
(valueType.supportsSQLnull()))
{
nullType = "_NULL";
}
else
{
nullType = "";
}
// append -NULL to the operator to signify the java code generating pushdown filters to handle NULL semantic logic
if (ie->getOperatorType() == ITM_EQUAL)
op = "EQUAL"+nullType;
else if (ie->getOperatorType() == ITM_NOT_EQUAL)
op = "NOT_EQUAL"+nullType;
else if (ie->getOperatorType() == ITM_LESS){
if (flipOp)
op = "GREATER"+nullType;
else
op = "LESS"+nullType;
}
else if (ie->getOperatorType() == ITM_LESS_EQ){
if (flipOp)
op = "GREATER_OR_EQUAL"+nullType;
else
op = "LESS_OR_EQUAL"+nullType;
}else if (ie->getOperatorType() == ITM_GREATER){
if (flipOp)
op = "LESS"+nullType;
else
op = "GREATER"+nullType;
}else if (ie->getOperatorType() == ITM_GREATER_EQ){
if (flipOp)
op = "LESS_OR_EQUAL"+nullType;
else
op = "GREATER_OR_EQUAL"+nullType;
}else
op = "NO_OP"+nullType;
}
}
if (foundUnary){
const NAType &colType = colVID.getType();
NAString nullType;
if (colType.supportsSQLnull())
{
nullType = "_NULL";
}
else
{
nullType = "";
}
if (ie->getOperatorType() == ITM_IS_NULL)
op = "IS_NULL"+nullType;
else if (ie->getOperatorType() == ITM_IS_NOT_NULL)
op = "IS_NOT_NULL"+nullType;
}
return foundBinary || foundUnary;
}
short HbaseAccess::extractHbaseFilterPredsVX(Generator * generator,
ValueIdSet &preds, ValueIdSet &newExePreds){
//separate the code that should not belong in the recursive function
if (CmpCommon::getDefault(HBASE_FILTER_PREDS) == DF_OFF)
return 0;
// check if initial (version 1) implementation
if (CmpCommon::getDefault(HBASE_FILTER_PREDS) == DF_MINIMUM)
return extractHbaseFilterPreds(generator,preds,newExePreds);
// if here, we are DF_MEDIUM
// cannot push preds for aligned format row
NABoolean isAlignedFormat = getTableDesc()->getNATable()->isAlignedFormat(getIndexDesc());
if (isAlignedFormat)
return 0;
//recursive function call
opList_.insert("V2");//to instruct the java side that we are dealing with predicate pushdown V2 semantic, add "V2" marker
extractHbaseFilterPredsV2(generator,preds,newExePreds,FALSE);
return 0;
}
// return true if successfull push down of node
NABoolean HbaseAccess::extractHbaseFilterPredsV2(Generator * generator,
ValueIdSet &preds, ValueIdSet &newExePreds, NABoolean checkOnly)
{
// the isFirstAndLayer is used to allow detecting top level predicate that can still be pushed to executor
int addedNode=0;
for (ValueId vid = preds.init();
(preds.next(vid));
preds.advance(vid))
{
ItemExpr * ie = vid.getItemExpr();
// if it is AND operation, recurse through left and right children
if (ie->getOperatorType() == ITM_AND){
ValueIdSet leftPreds;
ValueIdSet rightPreds;
leftPreds += ie->child(0)->castToItemExpr()->getValueId();
rightPreds += ie->child(1)->castToItemExpr()->getValueId();
//cannot be first AND layer, both left and right must be pushable to get anything pushed
if(extractHbaseFilterPredsV2(generator, leftPreds, newExePreds, TRUE)&&
extractHbaseFilterPredsV2(generator, rightPreds, newExePreds, TRUE)){// both left and right child must match
if(!checkOnly){
extractHbaseFilterPredsV2(generator, leftPreds, newExePreds, FALSE);//generate tree
extractHbaseFilterPredsV2(generator, rightPreds, newExePreds, FALSE);//generate tree
opList_.insert("AND");
}
if (preds.entries()==1)
return TRUE;
}
else{
if(!checkOnly){
newExePreds.insert(vid);
}
if (preds.entries()==1)
return FALSE;
}
continue;
// the OR case is easier, as we don t have the case of top level expression that can still be pushed to executor
}//end if AND
else if(ie->getOperatorType() == ITM_OR){
ValueIdSet leftPreds;
ValueIdSet rightPreds;
leftPreds += ie->child(0)->castToItemExpr()->getValueId();
rightPreds += ie->child(1)->castToItemExpr()->getValueId();
//both left and right must be pushable to get anything pushed
if(extractHbaseFilterPredsV2(generator, leftPreds, newExePreds, TRUE)&&
extractHbaseFilterPredsV2(generator, rightPreds, newExePreds, TRUE)){// both left and right child must match
if(!checkOnly){
extractHbaseFilterPredsV2(generator, leftPreds, newExePreds, FALSE);//generate tree
extractHbaseFilterPredsV2(generator, rightPreds, newExePreds, FALSE);//generate tree
opList_.insert("OR");
if (addedNode>0)opList_.insert("AND"); // if it is not the first node add to the push down, AND it with the rest
addedNode++; // we just pushed it down, so increase the node count pushed down.
}
if (preds.entries()==1)
return TRUE;
}
else{// if predicate cannot be pushed down
if(!checkOnly){
newExePreds.insert(vid);
}
if (preds.entries()==1)
return FALSE;
}
continue;
}//end if OR
ValueId colVID;
ValueId valueVID;
NAString op;
NABoolean isHFP =
isHbaseFilterPredV2(generator, ie, colVID, valueVID, op);
if (isHFP && !checkOnly){// if pushable, push it
hbaseFilterColVIDlist_.insert(colVID);
if (valueVID != NULL_VALUE_ID) hbaseFilterValueVIDlist_.insert(valueVID);// don't insert valueID for unary operators.
opList_.insert(op);
if (addedNode>0)opList_.insert("AND"); // if it is not the first node add to the push down, AND it with the rest
addedNode++; // we just pushed it down, so increase the node count pushed down.
}else if (!checkOnly){//if not pushable, pass it for executor evaluation.
newExePreds.insert(vid);
}
if (preds.entries()==1){
return isHFP; // if we are not on the first call level, where we can have multiple preds, exit returning the pushability
}
} // end for
return TRUE;//don't really care, means we are top level.
}
void HbaseAccess::computeRetrievedCols()
{
GroupAttributes fakeGA;
ValueIdSet requiredValueIds(getGroupAttr()->
getCharacteristicOutputs());
ValueIdSet coveredExprs;
// ---------------------------------------------------------------------
// Make fake group attributes with all inputs that are available to
// the file scan node and with no "native" values.
// Then call the "coverTest" method, offering it all the index columns
// as additional inputs. "coverTest" will mark those index columns that
// it actually needs to satisfy the required value ids, and that is
// what we actually want. The actual cover test should always succeed,
// otherwise the FileScan node would have been inconsistent.
// ---------------------------------------------------------------------
fakeGA.addCharacteristicInputs(getGroupAttr()->getCharacteristicInputs());
requiredValueIds += selectionPred();
requiredValueIds += executorPred();
fakeGA.coverTest(requiredValueIds, // char outputs + preds
getIndexDesc()->getIndexColumns(), // all index columns
coveredExprs, // dummy parameter
retrievedCols()); // needed index cols
//
// *** This CMPASSERT goes off sometimes, indicating an actual problem.
// Hans has agreed to look into it (10/18/96) but I (brass) am
// commenting it out for now, for sake of my time in doing a checking.
//
// CMPASSERT(coveredExprs == requiredValueIds);
}
RelExpr * HbaseAccess::preCodeGen(Generator * generator,
const ValueIdSet & externalInputs,
ValueIdSet &pulledNewInputs)
{
if (nodeIsPreCodeGenned())
return this;
const PartitioningFunction* myPartFunc = getPartFunc();
// use const HBase keys only if we don't have to add
// partitioning key predicates
if ( myPartFunc == NULL ||
!myPartFunc->isPartitioned() ||
myPartFunc->isAReplicationPartitioningFunction())
if (!processConstHBaseKeys(
generator,
this,
getSearchKey(),
getIndexDesc(),
executorPred(),
getHbaseSearchKeys(),
listOfUniqueRows_,
listOfRangeRows_))
return NULL;
if (! FileScan::preCodeGen(generator,externalInputs,pulledNewInputs))
return NULL;
//compute isUnique:
NABoolean isUnique = FALSE;
if (listOfRangeRows_.entries() == 0)
{
if ((searchKey() && searchKey()->isUnique()) &&
(listOfUniqueRows_.entries() == 0))
isUnique = TRUE;
else if ((NOT (searchKey() && searchKey()->isUnique())) &&
(listOfUniqueRows_.entries() == 1) &&
(listOfUniqueRows_[0].rowIds_.entries() == 1))
isUnique = TRUE;
}
// executorPred() contains an ANDed list of predicates.
// if hbase filter preds are enabled, then extracts those preds from executorPred()
// which could be pushed down to hbase.
// Do this only for non-unique scan access.
ValueIdSet newExePreds;
ValueIdSet* originExePreds = new (generator->wHeap())ValueIdSet(executorPred()) ;//saved for futur nullable column check
if (CmpCommon::getDefault(HBASE_FILTER_PREDS) != DF_MINIMUM){ // the check for V2 and above is moved up before calculating retrieved columns
if ((NOT isUnique) &&
(extractHbaseFilterPredsVX(generator, executorPred(), newExePreds)))
return this;
// if some filter preds were found, then initialize executor preds with new exe preds.
// newExePreds may be empty which means that all predicates were changed into
// hbase preds. In this case, nuke existing exe preds.
if (hbaseFilterColVIDlist_.entries() > 0)
setExecutorPredicates(newExePreds);
}
ValueIdSet colRefSet;
computeRetrievedCols();
for (ValueId valId = retrievedCols().init();
retrievedCols().next(valId);
retrievedCols().advance(valId))
{
ValueId dummyValId;
if ((valId.getItemExpr()->getOperatorType() != ITM_CONSTANT) &&
(getGroupAttr()->getCharacteristicOutputs().referencesTheGivenValue(valId, dummyValId)))
colRefSet.insert(valId);
}
if (getTableDesc()->getNATable()->isHbaseCellTable())
{
for (Lng32 i = 0; i < getIndexDesc()->getIndexColumns().entries(); i++)
{
// retColRefSet_.insert(getIndexDesc()->getIndexColumns()[i]);
}
}
else if (getTableDesc()->getNATable()->isHbaseRowTable())
{
NASet<NAString> * hbaseColNameSet =
generator->getBindWA()->hbaseColUsageInfo()->hbaseColNameSet
((QualifiedName*)&getTableDesc()->getNATable()->getTableName());
NABoolean starFound = FALSE;
for (Lng32 ij = 0; ij < hbaseColNameSet->entries(); ij++)
{
NAString &colName = (*hbaseColNameSet)[ij];
retHbaseColRefSet_.insert(colName);
if (colName == "*")
starFound = TRUE;
}
if (starFound)
retHbaseColRefSet_.clear();
}
else
{
// create the list of columns that need to be retrieved from hbase .
// first add all columns referenced in the executor pred.
HbaseAccess::addReferenceFromVIDset(executorPred(), TRUE, TRUE, colRefSet);
HbaseAccess::addReferenceFromVIDset
(getGroupAttr()->getCharacteristicOutputs(), TRUE, TRUE, colRefSet);
for (ValueId valId = colRefSet.init();
colRefSet.next(valId);
colRefSet.advance(valId))
{
ValueId dummyValId;
if (NOT getGroupAttr()->getCharacteristicInputs().referencesTheGivenValue(valId, dummyValId))
{
retColRefSet_.insert(valId);
if (valId.getItemExpr()->getOperatorType() == ITM_HBASE_TIMESTAMP)
{
Lng32 colNumber = ((BaseColumn*)((HbaseTimestamp*)valId.getItemExpr())->col())->getColNumber();
ValueId colVID = getIndexDesc()->getIndexColumns()[colNumber];
retColRefSet_.insert(colVID);
}
if (valId.getItemExpr()->getOperatorType() == ITM_HBASE_VERSION)
{
Lng32 colNumber = ((BaseColumn*)((HbaseVersion*)valId.getItemExpr())->col())->getColNumber();
ValueId colVID = getIndexDesc()->getIndexColumns()[colNumber];
retColRefSet_.insert(colVID);
}
}
}
// add key columns. If values are missing in hbase, then atleast the key
// value is needed to retrieve a row.
//only if needed. If there is already a non nullable non added non nullable with default columns in the set, we should not need to add
//any other columns.
if (CmpCommon::getDefault(HBASE_FILTER_PREDS) == DF_MEDIUM && getMdamKeyPtr() == NULL){ //only enable column retrieval optimization with DF_MEDIUM and not for MDAM scan
bool needAddingNonNullableColumn = true; //assume we need to add one non nullable column
for (ValueId vid = retColRefSet_.init();// look for each column in th eresult set if one match the criteria non null non added non nullable with default
retColRefSet_.next(vid);
retColRefSet_.advance(vid))
{
if (originExePreds->isNotNullable(vid)){// it is non nullable
OperatorTypeEnum operatorType = vid.getItemExpr()->getOperatorType();
if ((operatorType == ITM_BASECOLUMN || operatorType == ITM_INDEXCOLUMN) && !vid.isColumnWithNonNullNonCurrentDefault()){//check if with non null or non current default... notgood
needAddingNonNullableColumn = false; // we found one column meeting all criteria
break;
}
}
}
if (needAddingNonNullableColumn){ // ok now we need to add one key column that is not nullable
bool foundAtLeastOneKeyColumnNotNullable = false;
for(int i=getIndexDesc()->getIndexKey().entries()-1; i>=0;i--)// doing reverse search is making sure we are trying to avoid to use _SALT_ column
// because _SALT_ is physicaly the last column therefore we don't skip columns optimally if using _SALT_ column
{
ValueId vaId = getIndexDesc()->getIndexKey()[i];
if ( (vaId.getItemExpr()->getOperatorType() == ITM_BASECOLUMN && !((BaseColumn*)vaId.getItemExpr())->getNAColumn()->getType()->supportsSQLnullPhysical())||
(vaId.getItemExpr()->getOperatorType() == ITM_INDEXCOLUMN && !((IndexColumn*)vaId.getItemExpr())->getNAColumn()->getType()->supportsSQLnullPhysical())
){ //found good key column candidate?
HbaseAccess::addReferenceFromItemExprTree(vaId.getItemExpr(),TRUE,FALSE,retColRefSet_); // add it
foundAtLeastOneKeyColumnNotNullable = true; //tag we found it
break; // no need to look further
}
}
if (!foundAtLeastOneKeyColumnNotNullable){//oh well, did not find any key column non nullable, let s add all key columns
HbaseAccess::addColReferenceFromVIDlist(getIndexDesc()->getIndexKey(), retColRefSet_);
}
}
}else //end if DF_MEDIUM
HbaseAccess::addColReferenceFromVIDlist(getIndexDesc()->getIndexKey(), retColRefSet_);
}
if ((getMdamKeyPtr()) &&
((listOfRangeRows_.entries() > 0) ||
(listOfUniqueRows_.entries() > 0)))
{
GenAssert(0, "listOfRange/Unique cannot be used if mdam is chosen.");
return NULL;
}
// flag for both hive and hbase tables
generator->setHdfsAccess(TRUE);
if (!isUnique)
generator->oltOptInfo()->setMultipleRowsReturned(TRUE) ;
// Do not allow cancel of unique queries but allow cancel of queries
// that are part of a rowset operation.
if ((isUnique) &&
(NOT generator->oltOptInfo()->multipleRowsReturned()))
{
generator->setMayNotCancel(TRUE);
uniqueHbaseOper() = TRUE;
}
else
{
generator->oltOptInfo()->setOltCliOpt(FALSE);
if (isUnique)
{
if ((CmpCommon::getDefault(HBASE_ROWSET_VSBB_OPT) == DF_ON) &&
(NOT generator->isRIinliningForTrafIUD()) &&
(searchKey() && searchKey()->isUnique()))
{
uniqueRowsetHbaseOper() = TRUE;
}
}
}
// executorPred() contains an ANDed list of predicates.
// if hbase filter preds are enabled, then extracts those preds from executorPred()
// which could be pushed down to hbase.
// Do this only for non-unique scan access.
if (CmpCommon::getDefault(HBASE_FILTER_PREDS) == DF_MINIMUM){ //keep the check for pushdown after column retrieval for pushdown V1.
if ((NOT isUnique) &&
(extractHbaseFilterPreds(generator, executorPred(), newExePreds)))
return this;
// if some filter preds were found, then initialize executor preds with new exe preds.
// newExePreds may be empty which means that all predicates were changed into
// hbase preds. In this case, nuke existing exe preds.
if (hbaseFilterColVIDlist_.entries() > 0)
setExecutorPredicates(newExePreds);
}//DF_MINIMUM
snpType_ = SNP_NONE;
DefaultToken tok = CmpCommon::getDefault(TRAF_TABLE_SNAPSHOT_SCAN);
if (tok == DF_LATEST)
//latest snapshot -- new way used with scan independent from bulk unload
snpType_= SNP_LATEST;
else if (tok == DF_SUFFIX)
//the exsiting where snapshot scan is used with bulk unload
snpType_ = SNP_SUFFIX;
markAsPreCodeGenned();
// Done.
return this;
}
RelExpr * HbaseAccessCoProcAggr::preCodeGen(Generator * generator,
const ValueIdSet & externalInputs,
ValueIdSet &pulledNewInputs)
{
if (nodeIsPreCodeGenned())
return this;
if (! HbaseAccess::preCodeGen(generator,externalInputs,pulledNewInputs))
return NULL;
// Rebuild the aggregate expressions tree
ValueIdSet availableValues;
getInputValuesFromParentAndChildren(availableValues);
aggregateExpr().replaceVEGExpressions
(availableValues,
getGroupAttr()->getCharacteristicInputs());
markAsPreCodeGenned();
// Done.
return this;
}
RelExpr * ExeUtilHbaseCoProcAggr::preCodeGen(Generator * generator,
const ValueIdSet & externalInputs,
ValueIdSet &pulledNewInputs)
{
if (nodeIsPreCodeGenned())
return this;
if (! ExeUtilExpr::preCodeGen(generator,externalInputs,pulledNewInputs))
return NULL;
// Rebuild the aggregate expressions tree
ValueIdSet availableValues;
getInputValuesFromParentAndChildren(availableValues);
aggregateExpr().replaceVEGExpressions
(availableValues,
getGroupAttr()->getCharacteristicInputs());
markAsPreCodeGenned();
// Done.
return this;
}
RelExpr * ExeUtilOrcFastAggr::preCodeGen(Generator * generator,
const ValueIdSet & externalInputs,
ValueIdSet &pulledNewInputs)
{
if (nodeIsPreCodeGenned())
return this;
if (! ExeUtilExpr::preCodeGen(generator,externalInputs,pulledNewInputs))
return NULL;
// Rebuild the aggregate expressions tree
ValueIdSet availableValues;
getInputValuesFromParentAndChildren(availableValues);
aggregateExpr().replaceVEGExpressions
(availableValues,
getGroupAttr()->getCharacteristicInputs());
markAsPreCodeGenned();
// Done.
return this;
}
ItemExpr * SplitPart::preCodeGen(Generator *generator)
{
if (nodeIsPreCodeGenned())
return this;
child(0) = child(0)->preCodeGen(generator);
if (! child(0).getPtr())
return NULL;
child(1) = child(1)->preCodeGen(generator);
if (! child(1).getPtr())
return NULL;
for (Int32 i = 2; i < getArity(); i++)
{
if (child(i))
{
const NAType &typ1 = child(i)->getValueId().getType();
//Insert a cast node to convert child to an INT.
child(i) = new (generator->wHeap())
Cast(child(i), new (generator->wHeap()) SQLInt(generator->wHeap(), TRUE,
typ1.supportsSQLnullLogical()));
child(i)->bindNode(generator->getBindWA());
child(i) = child(i)->preCodeGen(generator);
if (! child(i).getPtr())
return NULL;
}
}
markAsPreCodeGenned();
return this;
}