blob: ce6372835872af471cf3a4f93894db0d074bf160 [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: EncodedKeyValue.cpp
* Description: Functions to compute binary encoded keys that can be written
to disk for a given set of TrafDescs.
* Origin:
* Created: 10/30/2013
* Language: C++
*
*************************************************************************
*/
#include "EncodedKeyValue.h"
#include "Generator.h"
#include "GenExpGenerator.h"
#include "ExpCriDesc.h"
#include "ExpAtp.h"
#include "exp_dp2_expr.h"
#include "exp_clause_derived.h"
#include "CmpStatement.h"
#include "NATable.h"
#include "TrafDDLdesc.h"
// defined in SynthType.cpp
extern
void emitDyadicTypeSQLnameMsg(Lng32 sqlCode,
const NAType &op1,
const NAType &op2,
const char *str1 = NULL,
const char *str2 = NULL,
ComDiagsArea * diagsArea = NULL,
const Lng32 int1 = -999999);
NAString * getMinMaxValue(TrafDesc * column,
TrafDesc * key,
NABoolean highKey,
CollHeap * h)
{
NAString * minMaxValue = NULL;
NAType * type; // deleted at the end of this method
if (NAColumn::createNAType(column->columnsDesc(), NULL, type, NULL))
return NULL;
Lng32 buflen = type->getTotalSize();
Lng32 nullHdrSize = 0;
char * buf = new char[buflen]; // deleted at the end of this method
if (type->supportsSQLnullPhysical())
{
nullHdrSize = type->getSQLnullHdrSize();
for(int i = 0; i < nullHdrSize; i++)
buf[i] = '\0';
buflen-= nullHdrSize;
}
NABoolean nullValue = FALSE;
if (highKey == FALSE)
{
// low key needed
if (NOT key->keysDesc()->isDescending()) // ascending
type->minRepresentableValue(buf + nullHdrSize, &buflen,
&minMaxValue,
h) ;
else
{
if (type->supportsSQLnull())
{
minMaxValue = new (h) NAString("NULL");
nullValue = TRUE;
}
else
{
type->maxRepresentableValue(buf + nullHdrSize, &buflen,
&minMaxValue,
h) ;
}
}
}
else
{
// high key needed
if (NOT key->keysDesc()->isDescending()) // ascending
{
if (type->supportsSQLnull())
{
minMaxValue = new (h) NAString("NULL");
nullValue = TRUE;
}
else
type->maxRepresentableValue(buf + nullHdrSize, &buflen,
&minMaxValue,
h) ;
}
else
type->minRepresentableValue(buf + nullHdrSize, &buflen,
&minMaxValue,
h) ;
}
delete [] buf;
delete type;
return minMaxValue;
}
NAString ** createInArrayForLowOrHighKeys(TrafDesc * column_descs,
TrafDesc * key_descs,
Lng32 numKeys,
NABoolean highKey,
NABoolean isIndex,
CollHeap * h)
{
NAString ** inValuesArray = new (h) NAString * [numKeys];
TrafDesc * column = column_descs;
TrafDesc * key = key_descs;
Int32 i = 0;
while (key)
{
if (!isIndex) {
column = column_descs;
for (Int32 j = 0; j < key->keysDesc()->tablecolnumber; j++)
column = column->next;
}
inValuesArray[i] = getMinMaxValue(column, key, highKey, h) ;
i++;
key = key->next;
if (isIndex)
column = column->next;
}
return inValuesArray;
}
ItemExpr * buildEncodeTree(TrafDesc * column,
TrafDesc * key,
NAString * dataBuffer, //IN:contains original value
Generator * generator,
ComDiagsArea * diagsArea)
{
ExpGenerator * expGen = generator->getExpGenerator();
// values are encoded by evaluating the expression:
// encode (cast (<dataBuffer> as <datatype>))
// where <dataBuffer> points to the string representation of the
// data value to be encoded, and <datatype> contains the
// PIC repsentation of the columns's datatype.
// create the CAST part of the expression using the parser.
// if this is a nullable column and the key value passed in
// is a NULL value, then treat it as a special case. A NULL value
// is passed in as an unquoted string of characters NULL in the
// dataBuffer. This case has to be treated different since the
// parser doesn't recognize the syntax "CAST (NULL as <datatype>)".
NAString ns;
ItemExpr * itemExpr;
NABoolean nullValue = FALSE;
NABoolean caseinsensitiveEncode = FALSE;
if (column->columnsDesc()->isCaseInsensitive())
caseinsensitiveEncode = TRUE;
// cannot have a NULL source value for a non-nullable column
if (NOT column->columnsDesc()->isNullable() &&
dataBuffer->length() >= 4 &&
str_cmp(*dataBuffer, "NULL", 4) == 0)
{
return NULL;
}
if (column->columnsDesc()->isNullable() &&
dataBuffer->length() >= 4 &&
str_cmp(*dataBuffer, "NULL", 4) == 0)
{
nullValue = TRUE;
ns = "CAST ( @A1 AS ";
ns += column->columnsDesc()->pictureText;
ns += ");";
// create a NULL constant
ConstValue * nullConst = new(expGen->wHeap()) ConstValue();
nullConst->synthTypeAndValueId();
itemExpr = expGen->createExprTree(ns,
CharInfo::UTF8,
ns.length(),
1, nullConst);
}
else
{
ns = "CAST ( ";
ns += *dataBuffer;
ns += " AS ";
ns += column->columnsDesc()->pictureText;
ns += ");";
itemExpr = expGen->createExprTree(ns,
CharInfo::UTF8,
ns.length());
}
CMPASSERT(itemExpr != NULL);
ItemExpr *boundItemExpr =
itemExpr->bindNode(generator->getBindWA());
if (boundItemExpr == NULL)
return NULL;
// make sure that the source and target values have compatible type.
// Do this only if source is not a null value.
NAString srcval;
srcval = "";
srcval += *dataBuffer;
srcval += ";";
ItemExpr * srcNode = expGen->createExprTree(srcval, CharInfo::UTF8, srcval.length());
CMPASSERT(srcNode != NULL);
srcNode->synthTypeAndValueId();
if ((NOT nullValue) &&
(NOT srcNode->getValueId().getType().isCompatible(itemExpr->getValueId().getType())))
{
if (diagsArea)
{
emitDyadicTypeSQLnameMsg(-4039,
itemExpr->getValueId().getType(),
srcNode->getValueId().getType(),
column->columnsDesc()->colname,
NULL,
diagsArea);
}
return NULL;
}
if (column->columnsDesc()->isNullable())
((NAType *)&(itemExpr->getValueId().getType()))->setNullable(TRUE);
else
((NAType *)&(itemExpr->getValueId().getType()))->setNullable(FALSE);
// Explode varchars by moving them to a fixed field
// whose length is equal to the max length of varchar.
////collation??
Int16 datatype = column->columnsDesc()->datatype;
if (DFS2REC::isSQLVarChar(datatype))
{
char lenBuf[10];
NAString vc((NASize_T)100); // preallocate a big-enough buf
size_t len = column->columnsDesc()->length;
if (datatype == REC_BYTE_V_DOUBLE) len /= SQL_DBCHAR_SIZE;
vc = "CAST (@A1 as CHAR(";
vc += str_itoa(len, lenBuf);
if ( column->columnsDesc()->character_set == CharInfo::UTF8 ||
( column->columnsDesc()->character_set == CharInfo::SJIS &&
column->columnsDesc()->encoding_charset == CharInfo::SJIS ) )
{
vc += " BYTE";
if (len > 1)
vc += "S";
}
vc += ") CHARACTER SET ";
vc += CharInfo::getCharSetName(column->columnsDesc()->characterSet());
vc += ");";
itemExpr = expGen->createExprTree(vc, CharInfo::UTF8, vc.length(), 1, itemExpr);
itemExpr->synthTypeAndValueId();
((NAType *)&(itemExpr->getValueId().getType()))->
setNullable(column->columnsDesc()->isNullable());
}
// add the encode node on top of it.
short desc_flag = TRUE;
if (NOT key->keysDesc()->isDescending()) // ascending
desc_flag = FALSE;
itemExpr = new(expGen->wHeap()) CompEncode(itemExpr, desc_flag);
itemExpr->synthTypeAndValueId();
((CompEncode*)itemExpr)->setCaseinsensitiveEncode(caseinsensitiveEncode);
return itemExpr;
}
///////////////////////////////////////////////////////////////////
// This function takes as input an array of key values, where each
// key value is in ASCII string format (the way it is stored in
// catalogs). It encodes the key values and returns the encoded
// value in the encodedKeyBuffer.
// RETURNS: -1, if error. 0, if all Ok.
///////////////////////////////////////////////////////////////////
short encodeKeyValues(TrafDesc * column_descs,
TrafDesc * key_descs,
NAString * inValuesArray[], // INPUT
NABoolean isIndex,
NABoolean isMaxKey, // INPUT
char * encodedKeyBuffer, // OUTPUT
CollHeap * h,
ComDiagsArea * diagsArea)
{
short error = 0; // assume all will go well
NABoolean deleteLater = FALSE;
// set up binder/generator stuff so expressions could be generated.
InitSchemaDB();
CmpStatement cmpStatement(CmpCommon::context());
ActiveSchemaDB()->createStmtTables();
BindWA bindWA(ActiveSchemaDB(), CmpCommon::context());
Generator generator(CmpCommon::context());
ExpGenerator expGen(&generator);
generator.appendAtEnd(); // alloc a new map table
generator.setBindWA(&bindWA);
generator.setExpGenerator(&expGen);
FragmentDir * compFragDir = generator.getFragmentDir();
// create the fragment (independent code space) for this expression
CollIndex myFragmentId = compFragDir->pushFragment(FragmentDir::MASTER);
// space where RCB will be generated
Space * space = generator.getSpace();
// Let's start with a list of size 4 rather than resizing continuously
ValueIdList encodedValueIdList(4);
TrafDesc * column = column_descs;
TrafDesc * key = key_descs;
Int32 i = 0;
if (inValuesArray == NULL)
deleteLater = TRUE;
while (key)
{
// for an index, keys_desc has columns in the same order as columns_desc,
// the following for loop is not needed.
if (!isIndex) {
column = column_descs;
for (Int32 j = 0; j < key->keysDesc()->tablecolnumber; j++)
column = column->next;
}
if (inValuesArray[i] == NULL)
inValuesArray[i] = getMinMaxValue(column, key, isMaxKey, h);
ItemExpr * itemExpr = buildEncodeTree(column, key, inValuesArray[i],
&generator, diagsArea);
if (! itemExpr)
return -1;
encodedValueIdList.insert(itemExpr->getValueId());
i++;
key = key->next;
if (isIndex)
column = column->next;
}
// allocate a work cri desc to encode keys. It has
// 3 entries: 0, for consts. 1, for temps.
// 2, for the encoded key.
ex_cri_desc * workCriDesc = new(space) ex_cri_desc(3, space);
short keyAtpIndex = 2; // where the encoded key will be built
ULng32 encodedKeyLen;
ex_expr * keExpr = 0;
expGen.generateContiguousMoveExpr(encodedValueIdList,
0 /*don't add conv nodes*/,
0 /*atp*/, keyAtpIndex,
ExpTupleDesc::SQLMX_KEY_FORMAT,
encodedKeyLen,
&keExpr);
// create a DP2 expression and initialize it with the key encode expr.
ExpDP2Expr * keyEncodeExpr = new(space) ExpDP2Expr(keExpr,
workCriDesc,
space);
keyEncodeExpr->getExpr()->fixup(0,expGen.getPCodeMode(),
(ex_tcb *)space,space, h, FALSE, NULL);
atp_struct * workAtp = keyEncodeExpr->getWorkAtp();
workAtp->getTupp(keyAtpIndex).setDataPointer(encodedKeyBuffer);
workAtp->setDiagsArea(diagsArea);
if (keyEncodeExpr->getExpr()->eval(workAtp, 0, space) == ex_expr::EXPR_ERROR)
error = -1;
if (deleteLater)
delete [] inValuesArray;
generator.removeAll(NULL);
return error;
}