blob: 44fcb041a9b66fd80fd62b305c2e3d3c040c2863 [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: GenKey.C
* Description: Generate code for key expressions
*
* Created: 11/25/96
* Language: C++
*
*
******************************************************************************
*/
#include "Sqlcomp.h"
#include "SearchKey.h"
#include "Generator.h"
#include "GenExpGenerator.h"
//#include "ex_stdh.h"
#include "ComTdb.h"
//#include "ex_tcb.h"
#include "ComKeySingleSubset.h"
#include "ComKeyMDAM.h" // generator Mdam classes
#include "mdamkey.h" // optimizer Mdam classes
#include "ExpCriDesc.h"
// the next include file is here solely to make the horrible kludge work
#include "NAFileSet.h"
/////////////////////////////////////////////////////////////////////
//
// Contents:
//
// ExpGenerator::buildKeyInfo()
// This method is used to generate the key information for both the
// subset/range operators and unique operators.
//
//////////////////////////////////////////////////////////////////////
short ExpGenerator::buildKeyInfo(keyRangeGen ** keyInfo, // out -- generated object
Generator * generator,
const NAColumnArray & keyColumns,
const ValueIdList & listOfKeyColumns,
const ValueIdList & beginKeyPred,
const ValueIdList & endKeyPred,
const SearchKey * searchKey,
const MdamKey * mdamKeyPtr,
const NABoolean reverseScan,
const ExpTupleDesc::TupleDataFormat tf
)
{
Space * space = generator->getSpace();
const Int32 work_atp = 1;
const Int32 key_atp_index = 2;
const Int32 exclude_flag_atp_index = 3;
const Int32 data_conv_error_atp_index = 4;
const Int32 key_column_atp_index = 5; // used only for Mdam
const Int32 key_column2_atp_index = 6; // used only for Mdam MDAM_BETWEEN pred;
// code in BiLogic::mdamPredGenSubrange
// and MdamColumn::buildDisjunct
// requires this to be 1 more than
// key_column_atp_index
ULng32 keyLen;
// add an entry to the map table for work Atp
MapTable *keyBufferPartMapTable = generator->appendAtEnd();
// generate a temporary variable, which will be used for handling
// data conversion errors during key building
ValueIdList temp_varb_list;
ItemExpr * dataConversionErrorFlag = new(generator->wHeap())
HostVar("_sys_dataConversionErrorFlag",
new(generator->wHeap()) SQLInt(generator->wHeap(), TRUE,FALSE), // int not null
TRUE);
ULng32 temp_varb_tupp_len;
dataConversionErrorFlag->bindNode(generator->getBindWA());
temp_varb_list.insert(dataConversionErrorFlag->getValueId());
processValIdList(temp_varb_list,
ExpTupleDesc::SQLARK_EXPLODED_FORMAT,
temp_varb_tupp_len, // out
work_atp,
data_conv_error_atp_index);
NABoolean doEquiKeyPredOpt = FALSE;
#ifdef _DEBUG
if (getenv("DO_EQUI_KEY_PRED_OPT"))
doEquiKeyPredOpt
= (searchKey ? searchKey->areAllChosenPredsEqualPreds() : FALSE);
#endif
if (mdamKeyPtr == NULL)
{
// check to see if there is a begin key expression; if there
// isn't, don't generate a key object
if (beginKeyPred.entries() == 0)
*keyInfo = 0;
else
{
// For subset and range operators, generate the begin key
// expression, end key expression, begin key exclusion expression
// and end key exclusion expression. For unique operators,
// generate only the begin key exppression.
ex_expr *bk_expr = 0;
ex_expr *ek_expr = 0;
ex_expr *bk_excluded_expr = 0;
ex_expr *ek_excluded_expr = 0;
short bkey_excluded = 0;
short ekey_excluded = 0;
generateKeyExpr(keyColumns,
beginKeyPred,
work_atp,
key_atp_index,
dataConversionErrorFlag,
tf,
keyLen, // out
&bk_expr, // out
doEquiKeyPredOpt);
if (&endKeyPred)
generateKeyExpr(keyColumns,
endKeyPred,
work_atp,
key_atp_index,
dataConversionErrorFlag,
tf,
keyLen, // out -- should be the same as above
&ek_expr, // out
doEquiKeyPredOpt);
if (reverseScan)
{
// reverse scan - swap the begin and end key predicates
// Note: evidently, the Optimizer has already switched
// the key predicates in this case, so what we are
// really doing is switching them back.
ex_expr *temp = bk_expr;
bk_expr = ek_expr;
ek_expr = temp;
}
if (searchKey)
{
generateExclusionExpr(searchKey->getBeginKeyExclusionExpr(),
work_atp,
exclude_flag_atp_index,
&bk_excluded_expr); // out
bkey_excluded = (short) searchKey->isBeginKeyExclusive();
generateExclusionExpr(searchKey->getEndKeyExclusionExpr(),
work_atp,
exclude_flag_atp_index,
&ek_excluded_expr); // out
ekey_excluded = (short) searchKey->isEndKeyExclusive();
if (reverseScan)
{
NABoolean x = bkey_excluded;
bkey_excluded = ekey_excluded;
ekey_excluded = x;
ex_expr* temp = bk_excluded_expr;
bk_excluded_expr = ek_excluded_expr;
bk_excluded_expr = temp;
}
} // if searchKey
// Build key info
// the work cri desc is used to build key values (entry 2) and
// to compute the exclusion flag (entry 3) to monitor for data
// conversion errors (entry 4) and to compute values on a column
// basis (entry 5 - Mdam only)
ex_cri_desc * work_cri_desc
= new(space) ex_cri_desc(6, space);
*keyInfo = new(space) keySingleSubsetGen(
keyLen,
work_cri_desc,
key_atp_index,
exclude_flag_atp_index,
data_conv_error_atp_index,
bk_expr,
ek_expr,
bk_excluded_expr,
ek_excluded_expr,
// static exclude flags (if exprs are NULL)
bkey_excluded,
ekey_excluded);
}
} // end of non-mdam case
else // Mdam case
{
// the work cri desc is used to build key values (entry 2) and
// to compute the exclusion flag (entry 3) to monitor for data
// conversion errors (entry 4) and to compute values on a column
// basis (entry 5 - Mdam only, and entry 6 - Mdam only, and only
// for MDAM_BETWEEN predtype)
ex_cri_desc * work_cri_desc
= new(space) ex_cri_desc(7, space);
// compute the format of the key buffer -- We need this
// so that Mdam will know, for each column, where in the buffer
// to move a value and how many bytes that value takes. The
// next few lines of code result in this information being stored
// in the attrs array.
// Some words on the technique: We create expressions whose
// result datatype matches the key buffer datatypes for each key
// column. Then we use the datatypes of these expressions to
// compute buffer format. The expressions themselves are not
// used any further; they do not result in compiled expressions
// in the plan. At run time we use string moves to move key
// values instead.
const CollIndex keyCount = listOfKeyColumns.entries();
CollIndex i;
// assert at least one column
GenAssert(keyCount > 0,"MDAM: at least one key column required.");
Attributes ** attrs = new(generator->wHeap()) Attributes * [keyCount];
for (i = 0; i < keyCount; i++)
{
ItemExpr * col_node =
listOfKeyColumns[i].getItemExpr();
ItemExpr *enode = col_node;
if ((tf == ExpTupleDesc::SQLMX_KEY_FORMAT) &&
(enode->getValueId().getType().getVarLenHdrSize() > 0))
{
// varchar keys in SQL/MP tables are converted to
// fixed length chars in key buffers
const CharType& char_type =
(CharType&)(enode->getValueId().getType());
if (!CollationInfo::isSystemCollation(char_type.getCollation()))
{
enode = new(generator->wHeap())
Cast(enode,
(new (generator->wHeap())
SQLChar(generator->wHeap(), CharLenInfo(char_type.getStrCharLimit(),
char_type.getDataStorageSize()),
char_type.supportsSQLnull(),
FALSE, FALSE, FALSE,
char_type.getCharSet(),
char_type.getCollation(),
char_type.getCoercibility())));
}
}
NABoolean desc_flag;
if (keyColumns.isAscending(i))
desc_flag = reverseScan;
else
desc_flag = !reverseScan;
enode = new(generator->wHeap()) CompEncode(enode,desc_flag);
enode->bindNode(generator->getBindWA());
attrs[i] =
(generator->
addMapInfoToThis(keyBufferPartMapTable, enode->getValueId(), 0))->getAttr();
} // for, over keyCount
// Compute offsets, lengths, etc. and assign them to the right
// atp and atp index
processAttributes((ULng32)keyCount,
attrs, tf,
keyLen,
work_atp, key_atp_index);
// Now we have key column offsets and lengths stored in attrs.
// Next, for each column, generate expressions to compute hi,
// lo, non-null hi and non-null lo values, and create
// MdamColumnGen structures.
// Notes: In the Mdam network itself, all key values are
// encoded. Hence, we generate CompEncode nodes in all of the
// expressions, regardless of tuple format. In the Simulator
// case, we must at run-time decode the encoded values when
// moving them to the key buffer. $$$ We need an expression to
// do this. This decoding work has not yet been done, so the
// simulator only works correctly for columns that happen to be
// correctly aligned and whose encoding function does not change
// the value. $$$
MdamColumnGen * first = 0;
MdamColumnGen * last = 0;
LIST(NAType *) keyTypeList(generator->wHeap());//to keep the type of the keys for later
for (i = 0; i < keyCount; i++)
{
// generate expressions to compute hi, lo, non-null hi, non-null lo
NAType * targetType = (keyColumns[i]->getType())->newCopy(generator->wHeap());
// Genesis case 10-971031-9814 fix: desc_flag must take into account
// both the ASC/DESC attribute of the key column and the reverseScan
// attribute. Before this fix, it only took into account the first of
// these.
NABoolean desc_flag;
if (keyColumns.isAscending(i))
desc_flag = reverseScan;
else
desc_flag = !reverseScan;
// End Genesis case 10-971031-9814 fix.
if ((tf == ExpTupleDesc::SQLMX_KEY_FORMAT) &&
(targetType->getVarLenHdrSize() > 0))
{
// 5/9/98: add support for VARNCHAR
const CharType* char_type = (CharType*)(targetType);
if (!CollationInfo::isSystemCollation(char_type->getCollation()))
{
targetType = new(generator->wHeap())
SQLChar(generator->wHeap(), CharLenInfo(char_type->getStrCharLimit(),
char_type->getDataStorageSize()),
char_type -> supportsSQLnull(),
FALSE, FALSE, FALSE,
char_type -> getCharSet(),
char_type -> getCollation(),
char_type -> getCoercibility());
/*
targetType->getNominalSize(),
targetType->supportsSQLnull()
*/
}
}
keyTypeList.insert(targetType); // save in ith position for later
// don't need to make copy of targetType in next call
ItemExpr * lo = new(generator->wHeap()) ConstValue(targetType,
!desc_flag,
TRUE /* allow NULL */);
lo = new(generator->wHeap()) CompEncode(lo,desc_flag);
lo->bindNode(generator->getBindWA());
ValueIdList loList;
loList.insert(lo->getValueId());
ex_expr *loExpr = 0;
ULng32 dataLen = 0;
generateContiguousMoveExpr(loList,
0, // don't add convert nodes
work_atp,
key_column_atp_index,
tf,
dataLen,
&loExpr);
ItemExpr * hi = new(generator->wHeap()) ConstValue(targetType->newCopy(generator->wHeap()),
desc_flag,
TRUE /* allow NULL */);
hi = new(generator->wHeap()) CompEncode(hi,desc_flag);
hi->bindNode(generator->getBindWA());
ValueIdList hiList;
hiList.insert(hi->getValueId());
ex_expr *hiExpr = 0;
generateContiguousMoveExpr(hiList,
0, // don't add convert nodes
work_atp,
key_column_atp_index,
tf,
dataLen,
&hiExpr);
ex_expr *nonNullLoExpr = loExpr;
ex_expr *nonNullHiExpr = hiExpr;
if (targetType->supportsSQLnull())
{
if (desc_flag)
{
ItemExpr * nonNullLo = new(generator->wHeap())
ConstValue(targetType->newCopy(generator->wHeap()),
!desc_flag,
FALSE /* don't allow NULL */);
nonNullLo = new(generator->wHeap()) CompEncode(nonNullLo,desc_flag);
nonNullLo->bindNode(generator->getBindWA());
ValueIdList nonNullLoList;
nonNullLoList.insert(nonNullLo->getValueId());
nonNullLoExpr = 0; // so we will get an expression back
generateContiguousMoveExpr(nonNullLoList,
0, // don't add convert nodes
work_atp,
key_column_atp_index,
tf,
dataLen,
&nonNullLoExpr);
}
else
{
ItemExpr * nonNullHi = new(generator->wHeap())
ConstValue(targetType->newCopy(generator->wHeap()),
desc_flag,
FALSE /* don't allow NULL */);
nonNullHi = new(generator->wHeap()) CompEncode(nonNullHi,desc_flag);
nonNullHi->bindNode(generator->getBindWA());
ValueIdList nonNullHiList;
nonNullHiList.insert(nonNullHi->getValueId());
nonNullHiExpr = 0; // so we will get an expression back
generateContiguousMoveExpr(nonNullHiList,
0, // don't add convert nodes
work_atp,
key_column_atp_index,
tf,
dataLen,
&nonNullHiExpr);
}
}
NABoolean useSparseProbes = mdamKeyPtr->isColumnSparse(i);
// calculate offset to the beginning of the column value
// (including the null indicator and the varchar length
// indicator if present)
ULng32 column_offset = attrs[i]->getOffset();
if (attrs[i]->getNullFlag())
column_offset = attrs[i]->getNullIndOffset();
else if (attrs[i]->getVCIndicatorLength() > 0)
column_offset = attrs[i]->getVCLenIndOffset();
last = new(space) MdamColumnGen(last,
dataLen,
column_offset,
useSparseProbes,
loExpr,
hiExpr,
nonNullLoExpr,
nonNullHiExpr);
if (first == 0)
first = last;
} // for over keyCount
// generate MdamPred's and attach to MdamColumnGen's
const ColumnOrderListPtrArray &columnOrderListPtrArray =
mdamKeyPtr->getColumnOrderListPtrArray();
#ifdef _DEBUG
// Debug print stataments below depend on this
// variable:
char *ev = getenv("MDAM_PRINT");
const NABoolean mdamPrintOn = (ev != NULL AND strcmp(ev,"ON")==0);
#endif
#ifdef _DEBUG
if (mdamPrintOn)
{
fprintf(stdout, "\n\n***Generating the MDAM key for table with index"
" columns: ");
listOfKeyColumns.display();
}
#endif
for (CollIndex n = 0; n < columnOrderListPtrArray.entries(); n++)
{
// get the list of key predicates associated with the n disjunct:
const ColumnOrderList &columnOrderList = *columnOrderListPtrArray[n];
#ifdef _DEBUG
if (mdamPrintOn)
{
fprintf(stdout,"\nDisjunct[%d]:----------------\n",n);
columnOrderList.print();
}
#endif
MdamColumnGen * cc = first;
CMPASSERT(keyCount == columnOrderList.entries());
const ValueIdSet *predsPtr = NULL;
for (i = 0; i < keyCount; i++)
{
#ifdef _DEBUG
if (mdamPrintOn)
{
fprintf(stdout, "Column(%d) using: ", i);
if ( mdamKeyPtr->isColumnSparse(i) )
fprintf(stdout,"SPARSE probes\n");
else
fprintf(stdout, "DENSE probes\n");
}
#endif
// get predicates for column order i:
predsPtr = columnOrderList[i];
NAType * keyType = keyTypeList[i];
NABoolean descending;
if (keyColumns.isAscending(i))
descending = reverseScan;
else
descending = !reverseScan;
ValueId keyColumn = listOfKeyColumns[i];
MdamCodeGenHelper mdamHelper(
n,
keyType,
descending,
work_atp,
key_column_atp_index,
tf,
dataConversionErrorFlag,
keyColumn);
MdamPred * lastPred = cc->getLastPred();
if (predsPtr != NULL)
{
for (ValueId predId = predsPtr->init();
predsPtr->next(predId); predsPtr->advance(predId))
{
MdamPred * head = 0; // head of generated MdamPred's
MdamPred * tail = 0;
ItemExpr * orGroup = predId.getItemExpr();
orGroup->mdamPredGen(generator,&head,&tail,mdamHelper,NULL);
if (lastPred)
{
if ( CmpCommon::getDefault(RANGESPEC_TRANSFORMATION) == DF_ON )
{
MdamPred* curr = lastPred;
while(curr->getNext() != NULL)
curr=curr->getNext();
curr->setNext(head);
}
else
lastPred->setNext(head);
}
cc->setLastPred(tail);
lastPred = tail; //@ZXmdam if 1st pred has head != tail, head is lost
} // for over preds
} // if (predsPtr != NULL)
cc = cc->getNext();
} // for every order...
} // for every column order list in the array (of disjuncts)
// build the Mdam key info
*keyInfo = new(space) keyMdamGen(keyLen,
work_cri_desc,
key_atp_index,
exclude_flag_atp_index,
data_conv_error_atp_index,
key_column_atp_index,
first,
last,
reverseScan,
generator->wHeap());
} // end of mdam case
// reset map table to forget about the key object's work Atp
// aside: this logic is more bloody than it should be because the
// map table implementation doesn't accurately reflect the map table
// abstraction
generator->removeAll(keyBufferPartMapTable); // deletes anything that might have been
// added after keyBufferPartMapTable (at
// this writing we don't expect there to
// be anything, but we want to be safe)
// at this point keyBufferPartMapTable should be the last map table in the
// global map table chain
generator->removeLast(); // unlinks keyBufferPartMapTable and deletes it
return 0;
};