blob: 02f595cd53e84898315842d9c0e24447d4d1aa6d [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 @@@
**********************************************************************/
/* -*++-*-
******************************************************************************
*
* File: SqlParserAux.cpp
* Description: SQL Parser auxiliary methods: a logical progression from
* Sql Parser Gnu to Sql Parser Yacc to Sql Parser Aux.
*
* Extracted from sqlparser.y to
* work around a c89 (v2.1) internal limit that shows up as
ugen: internal: Assertion failure
in source file 'W:\parser\SqlParser.cpp', at source line NNN
detected in back end file 'eval.c', at line 4653
* when cross-compiling SQLMX for NSK
*
* Created: 4/28/94
* Language: C++
*
*
*
******************************************************************************
*/
#define SQLPARSERGLOBALS_CONTEXT_AND_DIAGS
#define SQLPARSERGLOBALS_HOSTVARS
#define SQLPARSERGLOBALS_LEX_AND_PARSE
#define SQLPARSERGLOBALS_NADEFAULTS
#define SQLPARSERGLOBALS_NAMES_AND_TOKENS
#define SQLPARSERGLOBALS_FLAGS
#include "SqlParserGlobals.h"
#include "SqlParserAux.h"
#include <errno.h>
#include "AllItemExpr.h"
#include "MiscType.h"
#include "NumericType.h"
#include "ParserMsg.h"
#include "SqlciError.h"
#include "wstr.h"
#include "nawstring.h"
// Forward references and includes for Y.tab.h (SqlParser.h)
class ExprNodePtrList;
class ForUpdateSpec;
class IntervalQualifier;
class PairOfUnsigned;
#include "CharType.h"
#include "ComSmallDefs.h"
#include "ComTransInfo.h"
#include "ComUnits.h"
#include "HvTypes.h"
#include "ElemDDLConstraintRI.h"
#include "ElemDDLPartition.h"
#include "ElemDDLParamName.h"
#include "AllElemDDL.h"
#include "AllStmtDDL.h"
#include "RelScan.h"
#include "RelUpdate.h"
#include "RelSequence.h"
#include "StmtDMLSetTransaction.h"
#include "exp_clause_derived.h"
#include <sqlparser.h> // Angled-brackets are intentional here
const UInt32 SHORT_MAX = 32767;
char *SQLTEXT() {return SqlParser_CurrentParser->inputStr();}
charBuf *SQLTEXTCHARBUF() {return SqlParser_CurrentParser->getInputcharBuf();}
Lng32 SQLTEXTCHARSET() {return SqlParser_CurrentParser->inputStrCharSet();}
NAWchar *SQLTEXTW() {return SqlParser_CurrentParser->wInputStr();}
NAWcharBuf *SQLTEXTNAWCHARBUF() {return SqlParser_CurrentParser->getInputNAWcharBuf();}
CharInfo::CharSet getStringCharSet(NAString **p)
{ return ToStringvalWithCharSet(p)->charSet_; }
CharInfo::CharSet getStringCharSet(NAWString **p)
{ return ToStringvalWithCharSet(p)->charSet_; }
NABoolean charsetMismatchError(NAString **d1, NAString **d2)
{
CharInfo::CharSet cs1 = getStringCharSet(d1);
CharInfo::CharSet cs2 = getStringCharSet(d2);
if (cs1 == cs2)
return FALSE;
NAString ns1("CHAR(n) CHARACTER SET ");
NAString ns2(ns1);
ns1 += CharInfo::getCharSetName(cs1);
ns2 += CharInfo::getCharSetName(cs2);
// 4034 The operation ($String0 $String1 $String2) is not allowed
*SqlParser_Diags << DgSqlCode(-4034)
<< DgString0(ns1)
<< DgString1("||") // string concatenation operator
<< DgString2(ns2);
return TRUE;
}
// This function prints to the user the place in the input where an
// error occurred. This error message is based on the contents of
// SqlParser_InputStr, which is what the parser scans, and the variable
// SqlParser_InputPos, which is an index that iterates over characters in that
// buffer as the Lexer tokenizes for the parser.
// However, if, on input, SqlParser_InputStr equals NULL, then the old
// behavior of printing the argument errtext is all that happens.
//
// NB: sqlcomp/parser.h's parser class keeps two versions of the SQL input stmt.
// Parser::inputStr() is the ascii/multibyte version and wInputStr() is
// the wide (unicode) version. Input/output requires multibyte strings (at
// least in NT 4.0) The wide (Unicode) versions of the C++ stream I/O
// functions do not work on Japanese or Chinese NT! Apparently, this is an
// accepted fact of life. All I/O must be multibyte. In-memory string handling
// can be in Unicode. So, you will see this duality in this parser. Strings
// destined for internal processing (eg, by the lexer) are in Unicode. But
// strings destined for I/O are first converted to multibyte. (tcr)
void yyerror(const char *errtext)
{
NAWchar *inputStr = SQLTEXTW();
#ifndef NDEBUG //## tmp code...
if (inputStr && !getenv("YYERROR_QUIET"))
{
cerr << "yyerror: ";
for (Int32 i = 0; i >= -2; i--)
{
const ParScannedTokenQueue::scannedTokenInfo &tok
= ParScannedTokens->getScannedTokenInfo(i);
if (tok.tokenStrLen)
{
NAString *ti = unicodeToChar
(&inputStr[tok.tokenStrPos], tok.tokenStrLen,
CharInfo::UTF8
,
PARSERHEAP(), TRUE);
cerr << "<" << (ti ? ti->data() : "") << "> ";
delete ti;
}
else
{
cerr << "<" << "" << "> ";
}
}
NAString *stmt = unicodeToChar
(inputStr, NAWstrlen(inputStr),
CharInfo::UTF8
,
PARSERHEAP(), TRUE);
cerr << " " << (stmt ? stmt->data() : "") << endl;
delete stmt;
}
#endif
if (inputStr || strstr(errtext, "syntax") == errtext)
{
if ((strcmp(errtext, "")) &&
(strstr(errtext, "syntax") != errtext))
*SqlParser_Diags << DgSqlCode(-SQLCI_PARSER_ERROR) << DgString0(errtext);
else
{
*SqlParser_Diags << DgSqlCode(-SQLCI_SYNTAX_ERROR);
// Point to the end of offending token,
// knowing that the Lexer has looked ahead by 2 characters
Int32 pos = SqlParser_CurrentParser->getLexer()->getInputPos();
StoreSyntaxError(inputStr,pos,*SqlParser_Diags,0,
CharInfo::UTF8
);
}
}
else
{
// Internal parser error -- we hope never to see this one
*SqlParser_Diags << DgSqlCode(-SQLCI_PARSER_ERROR) << DgString0(errtext);
}
WeAreInACreateMVStatement = FALSE;
WeAreInAnEmbeddedInsert = FALSE;
}
// THREAD_P LimitedStack *inJoinSpec = NULL; // can handle <STACK_LIMIT> nested Joins
// First emit syntax error 15001.
// Then if we're not in a parenthesized join-spec, also emit error 4101,
// "If $0~String0 is intended to be a further table reference in the
// FROM clause, the preceding join search condition must be enclosed
// in parentheses."
void checkError4101(NAString *badItemStr, ItemExpr *badPrevExpr)
{
yyerror("");
if (!((*inJoinSpec)())) return;
if (SqlParser_ParenDepth - (*inJoinSpec)() + STACKDELTA_ENSURES_NONZERO) return;
if (badPrevExpr && badPrevExpr->getOperatorType() == ITM_REFERENCE) {
const ColRefName &crn = ((ColReference *)badPrevExpr)->getColRefNameObj();
// Is it a 3-part name at most (really a tablename intended here)?
if (crn.getCorrNameObj().getQualifiedNameObj().fullyExpanded()) return;
// Prepend 1, 2, or 3-part tablename to correlation name
badItemStr->prepend(crn.getColRefAsAnsiString() + " ");
}
*SqlParser_Diags << DgSqlCode(-4101) << DgString0(*badItemStr);
}
// Emit warning 3169 if unknown collation,
// but emit it only once per stmt for each new unknown collation name.
NABoolean maybeEmitWarning3169(CharInfo::Collation co, const NAString &nam)
{
if (co != CharInfo::UNKNOWN_COLLATION) return FALSE; // no warning
for (Lng32 i = SqlParser_Diags->getNumber(); i; i--) {
const ComCondition &cond = (*SqlParser_Diags)[i];
if (cond.getSQLCODE() == +3169 &&
strcmp(cond.getOptionalString(0), nam) == 0)
return TRUE; // already emitted
}
*SqlParser_Diags << DgSqlCode(+3169) << DgString0(nam);
return TRUE;
}
NABoolean checkError3179(const NAType *na)
{
if (na->getTypeQualifier() != NA_CHARACTER_TYPE) return FALSE; // no problem
const CharType *ct = (const CharType *)na;
if (ct->isCharSetAndCollationComboOK()) return FALSE; // no problem
// 3179 Collation $0 is not defined on character set $1.
*SqlParser_Diags << DgSqlCode(-3179)
<< DgString0(CharInfo::getCollationName(ct->getCollation()))
<< DgString1(CharInfo::getCharSetName(ct->getCharSet()));
return TRUE;
}
void emitError3435( int tok_type, int cs, ParAuxCharLenSpec::ECharLenUnit parCLU )
{
char *tok_type_name = (char *)"CHAR";
if ( tok_type == TOK_BYTE )
tok_type_name = (char *)"BYTE";
if ( tok_type == TOK_VARCHAR )
tok_type_name = (char *)"VARCHAR";
if ( tok_type == TOK_LONG )
tok_type_name = (char *)"LONG VARCHAR";
const char* cs_name = CharInfo::getCharSetName( (CharInfo::CharSet) cs );
char *lenUnitName = (char *)"UNSPECIFIED";
if ( parCLU EQU ParAuxCharLenSpec::eBYTES )
lenUnitName = (char *)"BYTES";
if ( parCLU EQU ParAuxCharLenSpec::eCHARACTERS )
lenUnitName = (char *)"CHARS";
*SqlParser_Diags << DgSqlCode(-3435) << DgString0(tok_type_name)
<< DgString1(cs_name)
<< DgString2(lenUnitName);
}
// LIST(ItemExpr *) AllHostVars;
// LIST(ItemExpr *) AssignmentHostVars;
THREAD_P AllHostVarsT *AllHostVars = NULL;
THREAD_P AssignmentHostVarsT *AssignmentHostVars = NULL;
THREAD_P HVArgTypeLookup *TheProcArgTypes = NULL;
THREAD_P NABoolean intoClause = FALSE;
THREAD_P NABoolean InAssignmentSt = FALSE;
THREAD_P NABoolean ThereAreAssignments;
void resetHostVars()
{
if (AllHostVars == NULL)
AllHostVars = new AllHostVarsT(NULL);
AllHostVars->clear();
TheHostVarRoles->clear(); // SqlParserGlobals.h
// No need to "delete TheProcArgTypes;", as PARSERHEAP heap mgmt does cleanup
TheProcArgTypes = NULL;
InAssignmentSt = FALSE;
if (AssignmentHostVars == NULL)
AssignmentHostVars = new AssignmentHostVarsT(NULL);
AssignmentHostVars->clear();
intoClause = FALSE;
ThereAreAssignments = FALSE;
}
void MarkInteriorNodesAsInCompoundStmt(RelExpr *node)
{
node->setBlockStmt( TRUE );
for (Int32 i=0; i < node->getArity(); i++) {
if (node->child(i))
MarkInteriorNodesAsInCompoundStmt(node->child(i));
}
}
NAWString* localeMBStringToUnicode(NAString* localeString, Lng32 charset, CollHeap *heap)
{
charBuf cbuf((unsigned char*)(localeString->data()), localeString->length());
NAWcharBuf* wcbuf = 0;
Int32 errorcode = 0;
switch (charset) {
case SQLCHARSETCODE_ISO88591:
wcbuf = ISO88591ToUnicode(cbuf, 0, wcbuf);
break;
case SQLCHARSETCODE_SJIS:
case SQLCHARSETCODE_EUCJP:
case SQLCHARSETCODE_GB18030:
case SQLCHARSETCODE_GB2312:
case SQLCHARSETCODE_GBK:
case SQLCHARSETCODE_MB_KSC5601:
case SQLCHARSETCODE_BIG5:
case SQLCHARSETCODE_UTF8:
wcbuf = csetToUnicode(cbuf, 0, wcbuf, charset, errorcode);
break;
default:
wcbuf = ISO88591ToUnicode(cbuf, 0, wcbuf);
}
if ( wcbuf ) {
NAWString* wstr = new(heap) NAWString(wcbuf->data(), wcbuf->getStrLen(), heap);
delete wcbuf;
return wstr;
} else
return new(heap) NAWString();
}
THREAD_P Int32 in3GL_ = 0;
static Lng32 findNameInList(ItemExprList &iel, const NAString &name)
{
for (CollIndex i = 0; i < iel.entries(); i++)
{
if ((iel[i]->getOperatorType() == ITM_REFERENCE) &&
(((ColReference*)iel[i])->getColRefNameObj().getColName()
== name))
{
return i;
}
}
return -1;
}
RelRoot *finalize(RelExpr *top, NABoolean outputVarCntValid)
{
RelRoot *return_top;
if ( top == NULL )
return NULL;
// if the top node isn't already a root node (e.g. for unions, insert,
// update, delete), make a new root node to hold the hostvar information
if (top->getOperatorType() == REL_ROOT)
return_top = (RelRoot *)top;
else
return_top = new (PARSERHEAP()) RelRoot(top);
return_top->setSubRoot(TRUE);
// If called from inside a 3GL block statement, do not set the RootFlag
if (in3GL_ <= 0) {
return_top->setRootFlag(TRUE);
}
else {
// We only take care of input/output host variables when we have exited all
// 3GL blocks
return return_top;
}
// code in BindRelExpr and NormRelExpr rely on this isTrueRoot flag
// Rowset::bindNode relies (indirectly) on InputVarList, so update it.
// ---------------------------------------------------------------------
// Add all the input host vars/parameters encountered
// to the topmost RelRoot node's input variable list.
// Also keep a count of the output vars/params.
//
// NOTE: The hostvars are of course used somewhere else in
// the query. This leads to the unpleasant effect that
// we have parse tree nodes that are shared. If we don't
// want this we should consider using a LIST(ItemExpr *)
// in the RelRoot node instead of the ItemExpr *. Currently,
// the Binder does the right thing for shared ItemExprs in
// that it uses the already assigned ValueId.
//
// We iterate over the enum values in TheHostVarRoles in parallel with
// iterating over AllHostVars. We do this so as to extract *only* the
// input host variables and insert only those into the RelRoot.
// ---------------------------------------------------------------------
return_top->outputVarCnt() = 0;
HVArgType *argInfo;
CollIndex i = 0;
CollIndex j = 0;
for (i = 0; i < AllHostVars->entries(); i++)
{
HostVarRole role = (*TheHostVarRoles)[(size_t)j];
ComASSERT(role != HV_IS_INDICATOR);
if (role == HV_IS_INPUT || role == HV_IS_DYNPARAM)
{
return_top->addInputVarTree((*AllHostVars)[i]);
}
else if (role == HV_IS_OUTPUT || role == HV_IS_INPUT_OUTPUT)
{
if (role == HV_IS_INPUT_OUTPUT) {
NAString hvName = ((HostVar *)((*AllHostVars)[i]))->getName();
HostVar *hv = NULL;
if ( inCallStmt && ( 0 != hvName.compareTo ("")))
{
const NAString &str = ((HostVar *) ((*AllHostVars)[i]))->getIndName ();
if ( 0 != str.compareTo (""))
{
NAString *hvn = new (PARSERHEAP())NAString(str, PARSERHEAP ());
hv = makeHostVar(&hvName, hvn);
}
else
hv = makeHostVar (&hvName, NULL);
}
else
hv = makeHostVar(&hvName, NULL);
hv->setHVInputAssignment();
return_top->addInputVarTree(hv);
}
return_top->addOutputVarTree((*AllHostVars)[i]);
return_top->outputVarCnt()++;
argInfo = TheProcArgTypes ?
TheProcArgTypes->get(&((HostVar *)((*AllHostVars)[i]))->getName()) :
NULL;
if (argInfo) argInfo->intoCount()++;
}
//delete (*AllHostVars)[i]; // an orphan after this, so delete it
//(*AllHostVars)[i] = NULL;
// Skip past at most one following indicator
// but not beyond the end of the array please
if (++j < TheHostVarRoles->entries() &&
(*TheHostVarRoles)[(size_t)j] == HV_IS_INDICATOR)
{
j++;
if (role == HV_IS_OUTPUT)
{
if (TheProcArgTypes)
{
NAString *ivName = (NAString *)&
((HostVar *)((*AllHostVars)[i]))->getIndName();
if (!ivName->isNull())
{
argInfo = TheProcArgTypes->get(ivName);
if (!argInfo)
argInfo = new (PARSERHEAP()) HVArgType
(ivName, new (PARSERHEAP()) SQLUnknown(PARSERHEAP()));
argInfo->useCount()++;
argInfo->intoCount()++;
}
}
} // INTO :hv :iv -- lookup :iv, add if necessary, incr counts
}
} // for-AllHostVars-loop
assert(j >= TheHostVarRoles->entries()); // j should reach end when i does
// The RelRoot::outputVarCnt_ returns as
// a nonnegative count of output host variables (e.g. SELECT cols INTO :hv...)
// or of output dynamic parameters (no syntax for this currently (ever?)),
// or -1 for roots that don't get called here (non-topmost or non-true roots)
// or are called as part of a DECLARE CURSOR (as in the line following).
// Binder checks this count.
//
if (!outputVarCntValid) return_top->outputVarCnt() = -1;
if (TheProcArgTypes)
{
LIST(HVArgType *) argdump(PARSERHEAP());
TheProcArgTypes->dump(argdump);
for (i = 0; i < argdump.entries(); i++)
{
// Warnings: if procedure parameter is unused,
// or if used more than once as an INTO target.
if (!argdump[i]->useCount())
*SqlParser_Diags << DgSqlCode(+3162)
<< DgString0(*argdump[i]->getName());
// We can have an output variable appear more than once
// if we have assignment statements
if (!ThereAreAssignments) {
if (argdump[i]->intoCount() > 1)
*SqlParser_Diags << DgSqlCode(+3163)
<< DgString0(*argdump[i]->getName());
}
#ifndef NDEBUG
// Ansi 4.2.3 says that by default, hostvars are COERCIBLE,
// unless they have a COLLATE clause in which case they're EXPLICIT.
// But only column refs can be IMPLICIT.
//
const CharType *ct = (const CharType *)argdump[i]->getType();
if (ct->getTypeQualifier() == NA_CHARACTER_TYPE)
{ ComASSERT(ct->getCoercibility() != CharInfo::IMPLICIT); }
#endif
}
}
return return_top;
}
void ForUpdateSpec::finalizeUpdatability(RelExpr *top)
{
ComASSERT(top->getOperatorType() == REL_ROOT);
RelRoot *treeTop = (RelRoot *)top;
// Ansi 13.1 SR 5a -- no updatability clause specified, but ORDER BY was.
// See also RelRoot::isUpdatableCursor() in NormRelExpr.cpp.
// Genesis 10-990215-7815.
//
if (!explicitSpec_) {
if (treeTop->hasOrderBy())
forUpdate_ = FALSE;
else {
// Tandem extension, defaulting rule --
// if user has set this default attr to TRUE,
// they get FOR READ ONLY (itself a Tdm-ext syntax),
// resulting in better runtime performance.
forUpdate_ = (CmpCommon::getDefault(READONLY_CURSOR) == DF_FALSE);
}
}
treeTop->updatableSelect() = forUpdate_;
if (updateCol_) {
ComASSERT(explicitSpec_ && forUpdate_);
treeTop->addUpdateColTree(updateCol_);
}
}
NABoolean finalizeAccessOptions(RelExpr *top,
TransMode::AccessType at,
LockMode lm)
{
if (top->getOperatorType() == REL_TUPLE ||
top->getOperatorType() == REL_TUPLE_LIST) {
return (at == TransMode::ACCESS_TYPE_NOT_SPECIFIED_ && lm == LockMode::LOCK_MODE_NOT_SPECIFIED_);
}
// In case of an INSERT VALUES statement, this is a Tuple node.
if (top->getOperatorType() != REL_ROOT)
return TRUE;
RelRoot *treeTop = (RelRoot *)top;
if (at != TransMode::ACCESS_TYPE_NOT_SPECIFIED_) {
if (treeTop->accessOptions().accessType() != TransMode::ACCESS_TYPE_NOT_SPECIFIED_) {
*SqlParser_Diags << DgSqlCode(-3196);
// Access type cannot be specified more than once.
return FALSE; // error
}
treeTop->accessOptions().accessType() = at;
}
if (lm != LockMode::LOCK_MODE_NOT_SPECIFIED_) {
if (treeTop->accessOptions().lockMode() != LockMode::LOCK_MODE_NOT_SPECIFIED_) {
*SqlParser_Diags << DgSqlCode(-3197);
// Lock mode cannot be specified more than once.
return FALSE; // error
}
treeTop->accessOptions().lockMode() = lm;
}
return TRUE; // no error
}
NAString * getSqlStmtStr(CharInfo::CharSet & refparam_targetCharSet, CollHeap * heap)
{
NAWchar *inputStr = SQLTEXTW();
Int32 start_pos = 0;
if (NAWstrlen(inputStr) >= 7)
{
NAWchar temp[10];
Int32 i = 0;
while (i < 7)
{
temp[i] = na_towupper(inputStr[i]);
i++;
}
if (NAWstrncmp(temp, WIDE_("DISPLAY"), 7) == 0)
start_pos = 7;
}
// SqlParser_CurrentParser->charset_ is the encoding charset of the
// sql stmt under parsing now. Target is the charset that the stmt
// will be encoded before it is sent to catman.
// Assume the target charset is the source charset for now.
refparam_targetCharSet = SqlParser_CurrentParser->charset_;
refparam_targetCharSet = CharInfo::UTF8;
NAString *stmt = unicodeToChar ( &inputStr[start_pos] // in - const NAWchar *
, SQLTEXTNAWCHARBUF()->getStrLen() - start_pos
, CharInfo::UTF8
, heap
);
ParScannedInputCharset = CharInfo::UTF8;
return stmt;
}
// The purpose of this function is to return a pointer to a HostVar object.
// This new HostVar should have a name from the given name in the arg,
// and its type should either be SQLUnknown (if no type info is available),
// or it should be obtained from TheProcArgTypes. This depends on whether
// that list was init'd which depends on the syntax that came before the
// occurrence of the host variable for which we are making this data.
//
// If hvName is NULL, that causes an assertion failure.
// If indName is NULL, that means this host variable has no indicator
// and is not a programming error.
//
// To set the NAType of the HostVar which we return, we must
// set its own nullability flag according to whether or not there
// is an indicator in this host variable. Is this redundant since
// that info is already in the HostVar itself?
HostVar *makeHostVar(NAString *hvName, NAString *indName, NABoolean isDynamic)
{
ComASSERT(hvName && !hvName->isNull() && ((isDynamic) || (*hvName)[(StringPos)0] == ':'));
ComASSERT(!indName || (!indName->isNull() && (*indName)[(StringPos)0] == ':'));
HVArgType *argInfo = TheProcArgTypes ?
TheProcArgTypes->get(hvName) : NULL;
NAType *naType = argInfo ?
argInfo->getType() : new (PARSERHEAP()) SQLUnknown(PARSERHEAP());
ComASSERT(naType);
naType->setNullable(!!indName);
HostVar *result = indName ?
new (PARSERHEAP()) HostVar(*hvName, *indName, naType) :
new (PARSERHEAP()) HostVar(*hvName, naType);
ComASSERT(result);
if (
#ifndef NDEBUG
!getenv("HV_DEBUG") &&
#endif
(CmpCommon::context()->GetMode() == STMT_DYNAMIC) &&
(NOT isDynamic))
{
// 3049 Host variables ($string0) are not allowed in dynamic compilation.
*SqlParser_Diags << DgSqlCode(-3049) << DgString0(*hvName);
}
else if (!argInfo)
{
if (TheProcArgTypes)
{
// ANSI 12.3 SR 3
// 3161 $string0 was not declared in the procedure parameter list.
*SqlParser_Diags << DgSqlCode(-3161) << DgString0(*hvName);
// So we won't display this errmsg again for this hv in this stmt:
argInfo = new (PARSERHEAP()) HVArgType(hvName, naType);
TheProcArgTypes->insert(argInfo);
// And so we won't display warning 3162 in finalize() either:
argInfo->useCount()++;
}
else
{
// Not in a PROCEDURE, it's okay, let the Generator coerce the
// SQLUnknown type to some default flavor of signed int
// (the way it does for INDICATOR variables, which you notice
// we do *not* currently pass in the proc arg list).
}
}
else
{
argInfo->useCount()++;
}
setHVorDPvarIndex ( result, hvName );
return result;
// Do **NOT** do this (remove info from our hashtable)!
// if (argInfo)
// {
// argInfo->type_ = NULL;
// delete argInfo;
// }
}
static
NABoolean literalToNumber(NAString *strptr, char sign, NAString *cvtstr,
short &shortVal, Lng32 &longVal, Int64& i64Val,
char **bigNum, Lng32& bigNumSize, size_t &strSize)
{
NABoolean returnValue = TRUE; // assume success until proven otherwise
//
// Get the size of the absolute value first,
// *then* it's safe to modify the text --
// both the one we will store in strptr
// and the descaled one to be converted by atoxxx (cvtstr).
//
strSize = cvtstr->length();
if (sign == '-') {
strptr->prepend(sign);
if (strptr != cvtstr)
cvtstr->prepend(sign);
}
if ((! (Get_SqlParser_Flags(ALLOW_ARB_PRECISION_LITERALS))) &&
(CmpCommon::getDefaultNumeric(MAX_NUMERIC_PRECISION_ALLOWED)
> MAX_HARDWARE_SUPPORTED_SIGNED_NUMERIC_PRECISION) &&
(strSize > (ULng32)CmpCommon::getDefaultNumeric(MAX_NUMERIC_PRECISION_ALLOWED)))
{
*SqlParser_Diags << DgSqlCode(-3014) << DgInt0(strSize)
<< DgInt1((Lng32)CmpCommon::getDefaultNumeric(MAX_NUMERIC_PRECISION_ALLOWED));
returnValue = FALSE; //
return returnValue;
}
if (strSize < 5) {
shortVal = atoi(*cvtstr);
} else if (strSize < 10) {
longVal = atol(*cvtstr);
} else if (strSize < 19) {
i64Val = atoInt64(*cvtstr);
} else { // precision >= 19
if ((CmpCommon::getDefaultNumeric(MAX_NUMERIC_PRECISION_ALLOWED) >
MAX_HARDWARE_SUPPORTED_SIGNED_NUMERIC_PRECISION) ||
(Get_SqlParser_Flags(ALLOW_ARB_PRECISION_LITERALS))
// callers know what they're doing, leave them alone.
|| strSize < FLT_MAX_10_EXP) {
// LLONG_MAX & LLONG_MIN have 19 digits and must be allowed here.
// There are other places in mxcmp like GenExpGenerator.cpp's
// ExpGenerator::scaleBy10x() that do
// rettree = createExprTree(str, 0, 1, retTree);
// where str is "1000000000000000000000", a 22-digit "literal" that
// must be allowed here also.
// Also, just in case you ever want to use SQLNumeric here, be
// aware that atoInt64(LLONG_MAX) even with overflow checking can
// kill mxcmp with a signal 31!
// Prepare BCD representation of number
Lng32 largestrSize = strSize + 1; // extra byte for sign
char *largestr = new (PARSERHEAP()) char[largestrSize];
largestr[0] = sign;
size_t j = (sign == '+') ? 0 : 1;
for (size_t i = 0; i < strSize; i++) // strSize, not largestrSize
largestr[i+1] = (*cvtstr)[i+j] - '0';
// Convert BCD to Big Num representation
bigNumSize = BigNumHelper::ConvPrecisionToStorageLengthHelper(strSize);
*bigNum = new (PARSERHEAP()) char[bigNumSize];
BigNumHelper::ConvBcdToBigNumWithSignHelper(largestrSize,
bigNumSize,
largestr,
*bigNum);
NADELETEBASIC(largestr, (PARSERHEAP()));
} else { // precision >= FLT_MAX_10_EXP in a user-specified literal
// genesis 10-030220-1214 documents a cpu halt can happen here when we
// try to support digit precisions >= 77. The R2 version of this code
// has Gautam Das' BigNum code. It does not seem to cause a cpu halt
// but can quietly experience an overflow and give a wrong answer!
*SqlParser_Diags << DgSqlCode(-3165) << DgString0(*strptr);
returnValue = FALSE; //
}
}
return returnValue;
}
// In the next several procedures, we set up ConstValue's, in whose text
// we want negative numbers to end up with minus signs but positive numbers
// without plus signs. That is,
// "1",'+' -> "1"
// "2",'-' -> "-2"
// (Only digits, no signs, are passed in the strptr and cvtstr arguments.)
// (Note that we delete the strptr before returning: thus we may freely
// modify the text therein.)
ItemExpr *literalOfNumericPassingScale(NAString *strptr, char sign,
NAString *cvtstr, size_t scale)
{
short rc = 0;
ItemExpr *returnValue = NULL;
//
// Get the size of the absolute value first,
// *then* it's safe to modify the text --
// both the one we will store in ConstValue's text (strptr)
// and the descaled one to be converted by atoxxx (cvtstr).
//
size_t strSize = cvtstr->length();
NABoolean createSignedDatatype =
((CmpCommon::getDefault(TRAF_CREATE_SIGNED_NUMERIC_LITERAL)) == DF_ON);
if (sign == '-') {
createSignedDatatype = TRUE;
strptr->prepend(sign);
if (strptr != cvtstr)
cvtstr->prepend(sign);
}
if ((! (Get_SqlParser_Flags(ALLOW_ARB_PRECISION_LITERALS))) &&
(CmpCommon::getDefaultNumeric(MAX_NUMERIC_PRECISION_ALLOWED)
> MAX_HARDWARE_SUPPORTED_SIGNED_NUMERIC_PRECISION) &&
(strSize > (ULng32)CmpCommon::getDefaultNumeric(MAX_NUMERIC_PRECISION_ALLOWED)))
{
*SqlParser_Diags << DgSqlCode(-3014) << DgInt0(strSize)
<< DgInt1((Lng32)CmpCommon::getDefaultNumeric(MAX_NUMERIC_PRECISION_ALLOWED));
return NULL;
}
NABoolean createTinyLiteral =
((CmpCommon::getDefault(TRAF_CREATE_TINYINT_LITERAL)) == DF_ON);
NABoolean createLargeintUnsignedLiteral =
((CmpCommon::getDefault(TRAF_LARGEINT_UNSIGNED_IO)) == DF_ON);
char numericVal[8];
short datatype = -1;
Lng32 length = -1;
if ((createTinyLiteral) && (strSize < 3)) {
datatype = (createSignedDatatype ? REC_BIN8_SIGNED : REC_BIN8_UNSIGNED);
length = sizeof(Int8);
} else if (strSize < 5) {
datatype = (createSignedDatatype ? REC_BIN16_SIGNED : REC_BIN16_UNSIGNED);
length = sizeof(short);
} else if (strSize < 10) {
datatype = (createSignedDatatype ? REC_BIN32_SIGNED : REC_BIN32_UNSIGNED);
length = sizeof(Lng32);
} else if (strSize <= 19) {
datatype = (createSignedDatatype || !createLargeintUnsignedLiteral ? REC_BIN64_SIGNED : REC_BIN64_UNSIGNED);
length = sizeof(Int64);
} else if ((strSize == 20) && (!createSignedDatatype) && (createLargeintUnsignedLiteral)) {
datatype = REC_BIN64_UNSIGNED;
length = sizeof(Int64);
}
if (datatype != -1) {
rc = convDoIt((char*)cvtstr->data(),
(Lng32)cvtstr->length(),
REC_BYTE_F_ASCII,
0, 0,
numericVal,
length,
datatype,
0, 0,
NULL, 0,
PARSERHEAP(),
NULL,
CONV_UNKNOWN);
if (rc == 0) {
if ((CmpCommon::getDefault(LIMIT_MAX_NUMERIC_PRECISION) == DF_ON) &&
(strSize == 19))
returnValue =
new (PARSERHEAP()) ConstValue
(new (PARSERHEAP()) SQLLargeInt
(PARSERHEAP(), (Lng32)scale,
(UInt16) 0, // 64-bit
TRUE,
FALSE),
(void *)numericVal,
(UInt32) length,
strptr);
else
returnValue =
new (PARSERHEAP()) ConstValue
(new (PARSERHEAP()) SQLNumeric
(PARSERHEAP(), length,
(Lng32)strSize, // precision
(Lng32)scale,
createSignedDatatype,
FALSE),
(void *)numericVal,
(UInt32) length,
strptr);
}
else if (strSize < 19) {
*SqlParser_Diags << DgSqlCode(-8411);
return NULL;
}
else
datatype = -1;
} // datatype != -1
if (datatype == -1) {
if ((CmpCommon::getDefaultNumeric(MAX_NUMERIC_PRECISION_ALLOWED) >
MAX_HARDWARE_SUPPORTED_SIGNED_NUMERIC_PRECISION) ||
(Get_SqlParser_Flags(ALLOW_ARB_PRECISION_LITERALS))
// callers know what they're doing, leave them alone.
|| strSize < FLT_MAX_10_EXP) {
// LLONG_MAX & LLONG_MIN have 19 digits and must be allowed here.
// There are other places in mxcmp like GenExpGenerator.cpp's
// ExpGenerator::scaleBy10x() that do
// rettree = createExprTree(str, 0, 1, retTree);
// where str is "1000000000000000000000", a 22-digit "literal" that
// must be allowed here also.
// Also, just in case you ever want to use SQLNumeric here, be
// aware that atoInt64(LLONG_MAX) even with overflow checking can
// kill mxcmp with a signal 31!
// Prepare BCD representation of number
Lng32 largestrSize = strSize + 1; // extra byte for sign
char *largestr = new (PARSERHEAP()) char[largestrSize];
largestr[0] = sign;
size_t j = (sign == '+') ? 0 : 1;
for (size_t i = 0; i < strSize; i++) // strSize, not largestrSize
largestr[i+1] = (*cvtstr)[i+j] - '0';
// Convert BCD to Big Num representation
Lng32 bigNumSize = BigNumHelper::ConvPrecisionToStorageLengthHelper(strSize);
char *bigNumData = new (PARSERHEAP()) char[bigNumSize];
BigNumHelper::ConvBcdToBigNumWithSignHelper(largestrSize,
bigNumSize,
largestr,
bigNumData);
returnValue = new (PARSERHEAP()) ConstValue
(new (PARSERHEAP()) SQLBigNum(PARSERHEAP(), strSize,
scale,
TRUE,
TRUE,
FALSE),
(void *) bigNumData,
bigNumSize,
strptr);
NADELETEBASIC(largestr, (PARSERHEAP()));
NADELETEBASIC(bigNumData, (PARSERHEAP()));
} else { // precision >= FLT_MAX_10_EXP in a user-specified literal
// genesis 10-030220-1214 documents a cpu halt can happen here when we
// try to support digit precisions >= 77. The R2 version of this code
// has Gautam Das' BigNum code. It does not seem to cause a cpu halt
// but can quietly experience an overflow and give a wrong answer!
*SqlParser_Diags << DgSqlCode(-3165) << DgString0(*strptr);
}
}
//
// Delete the original string (strptr),
// but leave cvtstr alone (either a temporary, or identical to strptr)!
delete strptr;
return returnValue;
}
static Int32
removeDecimalPointReturnScale(NAString *strptr, NAString& tmpstr)
{
// remove the decimal point, if any, from tmpstr
size_t i, j, dot=0, strSize = strptr->length();
for (i=j=dot; i < strSize; i++)
if ((*strptr)[i] != '.')
tmpstr[j++] = (*strptr)[i];
else
dot = j; // Remember the position of the decimal point
tmpstr.resize(j); // adjust the size
// return number's scale
return Int32(strSize - dot -1);
}
NABoolean literalToNumeric(NAString *strptr, double &val, char sign)
{
// Create a new string after removing the decimal point, if any.
NAString tmpstr(*strptr, strptr->length());
size_t strSize;
Int32 scale = removeDecimalPointReturnScale(strptr, tmpstr);
// convert number into a double
short shortVal; Lng32 longVal = 0; Int64 i64Val;
char *bigNum = NULL; Lng32 bigNumSize;
NABoolean result = literalToNumber
(strptr, sign, &tmpstr, shortVal, longVal, i64Val, &bigNum,
bigNumSize, strSize);
if (result) {
if (strSize < 5)
val = shortVal * pow(10, -scale);
else if (strSize < 10)
val = longVal * pow(10, -scale);
else if (strSize < 19)
val = convertInt64ToDouble(i64Val) * pow(10, -scale);
else {
ComASSERT(BigNumHelper::ConvBigNumWithSignToInt64Helper
(bigNumSize, bigNum, (void*)&i64Val,FALSE) == 0);
val = convertInt64ToDouble(i64Val) * pow(10, -scale);
NADELETEBASIC(bigNum, (PARSERHEAP()));
}
}
delete strptr;
return result;
}
ItemExpr *literalOfNumericWithScale(NAString *strptr, char sign)
{
//
// Create a new string after removing the decimal point, if any.
//
size_t strSize = strptr->length();
NAString tmpstr(*strptr, strSize);
size_t i, j, dot;
for (i=j=dot=0; i < strSize; i++)
if ((*strptr)[i] != '.')
tmpstr[j++] = (*strptr)[i];
else
dot = j; // Remember the position of the decimal point
tmpstr.resize(j); // adjust the size
return literalOfNumericPassingScale(strptr, sign, &tmpstr, strSize - dot - 1);
}
NABoolean literalToDouble(NAString *strptr, double& val,
NABoolean& floatP, char sign)
{
NABoolean returnValue = FALSE; // assume failure until proven otherwise
//
// Compute the precision of the mantissa. In other words, count the number
// of significant digits in the mantissa. Leading zeroes are not
// significant.
//
UInt32 mantissaPrecision = 0;
const char* s = *strptr;
while ((*s != '\0') && (*s == '0'))
s++;
while ((*s != '\0') && (*s != 'e') && (*s != 'E')) {
if (isdigit(*s++))
mantissaPrecision++;
}
// Now get the size of the exponent
Lng32 expValue = 0;
if ((*s == 'e') || (*s == 'E')) {
s++;
expValue = atol(s);
}
// Having done that scanning, *now* it is safe to modify the text.
//
if (sign == '-') strptr->prepend(sign);
floatP = FALSE;
// check mantissa digits and exponent values to decide which type to build
// and ensure some basic values are correct.
// Overflow's and undflow's are caught at runtime in convDoIt()
// under (case CONV_FLOAT64_FLOAT32:)
if (mantissaPrecision < 8 && // real ?
expValue < FLT_MAX_10_EXP &&
expValue > -FLT_MAX_10_EXP)
{
floatP = TRUE;
}
else if (mantissaPrecision >= 20 ||
expValue > DBL_MAX_10_EXP ||
expValue < -DBL_MAX_10_EXP)
{
*SqlParser_Diags << DgSqlCode(-3165) << DgString0(*strptr);
return returnValue;
}
// now try converting the string and see if there were problems
char *endPtr;
errno=0;
val = strtod(*strptr, &endPtr);
Int32 numErr=0;
numErr = errno;
// There are some anomalies with floating point operations on
// NSK. For instance strtod can produce double values that are
// out-of-range with respect to the defines (DBL_MAX and
// DBL_MIN). Make range checking consistent with the executor
// (see exp_conv.cpp convDoit, CONV_FLOAT64_FLOAT64).
//
if((val < -DBL_MAX) ||
(val > DBL_MAX) ||
(val != 0 && val < DBL_MIN && val > -DBL_MIN)) {
numErr = ERANGE;
val = 0;
}
// check for underflow and overflow
if ( ((val == 0) &&
((numErr == ERANGE) || // underflo
(strptr->data() == endPtr))) || // not a number
(val == -HUGE_VAL) || // overflow
(val == HUGE_VAL) ) // overflow
*SqlParser_Diags << DgSqlCode(-3166) << DgString0(*strptr);
else
returnValue = TRUE; // success
return returnValue;
}
ItemExpr *literalOfApproxNumeric(NAString *strptr, char sign)
{
short rc = 0;
ItemExpr *returnValue = NULL;
if (sign == '-')
strptr->prepend(sign);
double doubleVal;
rc = convDoIt((char*)strptr->data(),
(Lng32)strptr->length(),
REC_BYTE_F_ASCII,
0, 0,
(char*)&doubleVal,
sizeof(double),
REC_FLOAT64,
0, 0,
NULL, 0,
PARSERHEAP(),
NULL,
CONV_UNKNOWN);
if (rc != 0)
{
*SqlParser_Diags << DgSqlCode(-3166) << DgString0(*strptr);;
return NULL;
}
returnValue = new (PARSERHEAP()) ConstValue (new (PARSERHEAP())
SQLDoublePrecision(PARSERHEAP(), FALSE),
(void *)&doubleVal,
sizeof(double),
strptr);
if (returnValue == NULL)
*SqlParser_Diags << DgSqlCode(-2006); // out of memory error
return returnValue;
}
ItemExpr *literalOfInterval(NAString *strptr,
IntervalQualifier *qualifier,
char sign)
{
ItemExpr *returnValue = NULL;
IntervalValue intervalValue(*strptr,
qualifier->getStartField(),
qualifier->getLeadingPrecision(),
qualifier->getEndField(),
qualifier->getFractionPrecision(),
sign);
IntervalType *intervalType = new (PARSERHEAP())
SQLInterval(PARSERHEAP(), FALSE,
qualifier->getStartField(),
qualifier->getLeadingPrecision(),
qualifier->getEndField(),
qualifier->getFractionPrecision());
strptr->prepend("'");
strptr->append ("' ");
strptr->append (intervalType->getIntervalQualifierAsString());
if (! intervalValue.isValid())
*SqlParser_Diags << DgSqlCode(-3044) << DgString0(*strptr);
else
{
if (!intervalType->isSupportedType()) // issue a warning
{
*SqlParser_Diags << DgSqlCode(3044) << DgString0(*strptr);
}
if (sign == '-')
{
strptr->prepend("-");
}
strptr->prepend("INTERVAL ");
returnValue = new (PARSERHEAP()) ConstValue(
intervalType,
(void *) intervalValue.getValue(),
intervalValue.getValueLen(),
strptr);
}
delete strptr;
//assert (returnValue); //don't assert; caller checks value for NULL
return returnValue;
}
ItemExpr *literalOfDate(NAString *strptr, NABoolean noDealloc)
{
ItemExpr *returnValue = NULL;
UInt32 fractionPrec;
DatetimeValue dtValue(*strptr, REC_DATE_YEAR, REC_DATE_DAY, fractionPrec,
(CmpCommon::getDefault(USE_OLD_DT_CONSTRUCTOR) == DF_ON));
if ((! dtValue.isValid()) &&
(CmpCommon::getDefault(MARIAQUEST_PROCESS) == DF_OFF))
*SqlParser_Diags << DgSqlCode(-3045) << DgString0(*strptr);
else
returnValue = new (PARSERHEAP()) ConstValue (new (PARSERHEAP())
SQLDate(PARSERHEAP(), FALSE),
(void *) dtValue.getValue(),
dtValue.getValueLen(),
strptr);
if (NOT noDealloc)
delete strptr;
//assert (returnValue); //don't assert; caller checks value for NULL
return returnValue;
}
ItemExpr *literalOfTime(NAString *strptr)
{
ItemExpr *returnValue = NULL;
UInt32 fractionPrec = 0;
DatetimeValue dtValue(*strptr, REC_DATE_HOUR, REC_DATE_SECOND, fractionPrec,
(CmpCommon::getDefault(USE_OLD_DT_CONSTRUCTOR) == DF_ON));
if ((! dtValue.isValid()) &&
(CmpCommon::getDefault(MARIAQUEST_PROCESS) == DF_OFF))
*SqlParser_Diags << DgSqlCode(-3046) << DgString0(*strptr);
else
returnValue = new (PARSERHEAP()) ConstValue (new (PARSERHEAP())
SQLTime(PARSERHEAP(), FALSE, fractionPrec),
(void *) dtValue.getValue(),
dtValue.getValueLen(),
strptr);
delete strptr;
//assert (returnValue); //don't assert; caller checks value for NULL
return returnValue;
}
//
// This routine handles MP-style datetime literals; some of these literals map to ANSI
// DATE, TIME or TIMESTAMP, in which case the corresponding NAType is generated.
//
ItemExpr *literalOfDateTime(NAString *strptr, DatetimeQualifier *qualifier)
{
ItemExpr *returnValue = NULL;
UInt32 fractionPrec = qualifier->getFractionPrecision();
DatetimeValue dtValue(*strptr,
qualifier->getStartField(),
qualifier->getEndField(),
fractionPrec, // returned value
(CmpCommon::getDefault(USE_OLD_DT_CONSTRUCTOR) == DF_ON));
DatetimeType *dtType = DatetimeType::constructSubtype( // This call is necessary to insure that we return
FALSE, // a standard DateTime, if possible.
qualifier->getStartField(),
qualifier->getEndField(),
fractionPrec,
PARSERHEAP()
);
if (!dtType)
{
*SqlParser_Diags << DgSqlCode(-3158) << DgString0(""); // Error - invalid datetime
}
else
{
strptr->prepend("'");
strptr->append ("'");
strptr->append (dtType->getDatetimeQualifierAsString(TRUE));
if (dtType->checkValid(SqlParser_Diags))
{
if (!dtValue.isValid())
{
*SqlParser_Diags << DgSqlCode(-3158) << DgString0(*strptr); // Error - invalid datetime
}
else
{
if (!dtType->isSupportedType())
{
*SqlParser_Diags << DgSqlCode(3158) << DgString0(*strptr); // Warning - invalid datetime
}
strptr->prepend("DATETIME");
returnValue = new (PARSERHEAP()) ConstValue(
dtType,
(void *) dtValue.getValue(),
dtValue.getValueLen(),
strptr);
}
}
}
delete strptr;
//assert (returnValue); //don't assert; caller checks value for NULL
return returnValue;
}
ItemExpr *literalOfTimestamp(NAString *strptr)
{
ItemExpr *returnValue = NULL;
UInt32 fractionPrec = 0;
DatetimeValue dtValue(*strptr, REC_DATE_YEAR, REC_DATE_SECOND, fractionPrec,
(CmpCommon::getDefault(USE_OLD_DT_CONSTRUCTOR) == DF_ON));
if ((! dtValue.isValid()) &&
(CmpCommon::getDefault(MARIAQUEST_PROCESS) == DF_OFF))
*SqlParser_Diags << DgSqlCode(-3047) << DgString0(*strptr);
else
returnValue = new (PARSERHEAP())
ConstValue (new (PARSERHEAP())
SQLTimestamp (PARSERHEAP(), FALSE, fractionPrec),
(void *) dtValue.getValue(),
dtValue.getValueLen(),
strptr);
delete strptr;
//assert (returnValue); //don't assert; caller checks value for NULL
return returnValue;
}
void parseCasedIdentifier(ParCaseIdentifierClauseType clauseType,
NAString *pClauseBuffer,
NAString &casedIdentifier)
{
assert(pClauseBuffer);
NAString workBuf(*pClauseBuffer);
StringPos invalidCharPos;
switch (clauseType)
{
case ParCALL_CASED_IDENTIFIER_CLAUSE :
{
// remove leading reserved word CALL from workBuf
StringPos firstBlankPos = 0, startPos = 0;
firstBlankPos = IndexOfFirstWhiteSpace(workBuf, startPos);
ComASSERT(firstBlankPos != NA_NPOS);
NAString firstTok(workBuf(startPos, firstBlankPos-startPos/*length*/));
casedIdentifier = workBuf;
TrimNAStringSpace(casedIdentifier,TRUE,FALSE);
break;
}
case ParGOTO_CASED_IDENTIFIER_CLAUSE :
{
// remove leading reserved word GOTO or GO TO from workBuf
StringPos firstBlankPos, startPos = 0;
firstBlankPos = IndexOfFirstWhiteSpace(workBuf, startPos);
ComASSERT(firstBlankPos != NA_NPOS);
NAString firstTok(workBuf(startPos, firstBlankPos-startPos/*length*/));
startPos = IndexOfFirstNonWhiteSpace(workBuf, firstBlankPos); // skips blank(s)
if ( firstTok == "GO" ) // either "GO TO" or "GOTO"
{ // skips GO
StringPos secondBlankPos = IndexOfFirstWhiteSpace(workBuf, startPos);
assert(secondBlankPos != NA_NPOS);
// skips TO
startPos = IndexOfFirstNonWhiteSpace(workBuf, secondBlankPos); // skips blank(s)
}
// gets cased identifier
casedIdentifier = workBuf(startPos,workBuf.length()-startPos/*length*/);
break;
}
case ParPERFORM_CASED_IDENTIFIER_CLAUSE :
{
// remove leading reserved word PERFORM from workBuf
// please note that PERFORM is not a reserved word.
StringPos firstBlankPos, startPos = 0;
firstBlankPos = IndexOfFirstWhiteSpace(workBuf, startPos);
ComASSERT(firstBlankPos != NA_NPOS);
NAString firstTok(workBuf(startPos, firstBlankPos-startPos/*length*/));
startPos = IndexOfFirstNonWhiteSpace(workBuf, firstBlankPos); // skips blank(s)
// gets cased identifier
casedIdentifier = workBuf(startPos,workBuf.length()-startPos/*length*/);
if (IdentifyMyself::GetMyName() != I_AM_COBOL_PREPROCESSOR)
{
*SqlParser_Diags << DgSqlCode(-3133); // PERFORM is valid only in COBOL
return;
}
break;
}
default :
{
*SqlParser_Diags << DgSqlCode(-4000); // internal error
return;
}
} // end of switch (clauseType)
// If the running process is the C preprocessor, checks to make sure that
// workBuf contains a valid C identifier; otherwise, issues an error message.
// Please note that workBuf may contain a delimited identifier.
// If the running process is the COBOL preprocessor, checks to make sure that
// workBuf contains a valid COBOL work; otherwise, issues an error message.
// If workBuf contains a regular identifier, makes sure that it is not a
// reserverd word.
switch (IdentifyMyself::GetMyName())
{
case I_AM_C_PREPROCESSOR:
{
if (!IsCIdentifier(casedIdentifier.data()))
{
//Illegal character in identifier $0~string0.
*SqlParser_Diags << DgSqlCode(-3127)
<< DgString0("C identifier " + casedIdentifier);
return;
}
break;
}
case I_AM_COBOL_PREPROCESSOR:
{
*SqlParser_Diags << DgSqlCode(-4222)
<< DgString0("COBOL embedded SQL");
return;
}
default:
{
// error, should not have come here
*SqlParser_Diags << DgSqlCode(-4000); // internal error
break;
}
} // end of switch
} // end of parseCasedIdentifier
// deletes all members of seq[] (recall in C++ delete NULL does nothing)
ShortStringSequence::~ShortStringSequence()
{
for (UInt32 i=0; i!=MAX_NUM_PARTS; i++)
delete seq[i];
} // destructor
// Sets up numParts_ to 0. seq[] is all NULLs.
// Cannot determine seqPos_ and seqEndPos_. Sets them to 0.
ShortStringSequence::ShortStringSequence()
: numParts_(0),
toInternalIdentifierFlags_(NASTRING_ALLOW_NSK_GUARDIAN_NAME_FORMAT),
seqPos_(0),
seqPosStartOffset_(0),
seqEndPos_(0)
{
for (UInt32 i=0; i!=MAX_NUM_PARTS; i++)
seq[i] = NULL;
} // default constructor
// numParts_ gets 1. seq[] is { arg, NULL, NULL, ... }.
ShortStringSequence::ShortStringSequence(NAString *arg,
unsigned short toInternalIdenfifierFlags)
{
toInternalIdentifierFlags_ = toInternalIdenfifierFlags;
numParts_ = 1;
seq[0] = arg;
for (UInt32 i=numParts_; i!=MAX_NUM_PARTS; i++)
seq[i] = NULL;
// initialize seqPos_, seqEndPos_ with the info from the ParScannedTokenQueue.
//
const ParScannedTokenQueue::scannedTokenInfo &tokInfo = getTokInfo(arg);
if (isValid())
{
seqPos_ = tokInfo.tokenStrPos;
seqEndPos_ = tokInfo.tokenStrPos + tokInfo.tokenStrLen - 1;
seqPosStartOffset_ = tokInfo.tokenStrOffset ;
}
} // ShortStringSequence ctor
// Increments numParts_. If numParts_ < MAX_NUM_PARTS, then sets seq[numParts_]
// to be a copy of arg. If numParts_ >= MAX_NUM_PARTS, just the
// incrementing is all we need do (if this behavior seems odd, note the
// term "Short" in the name of this class).
void ShortStringSequence::append(NAString* arg)
{
if (++numParts_ > MAX_NUM_PARTS)
{
invalidate();
delete arg;
return;
}
seq[numParts_-1] = arg;
// update seqEndPos_ with the information from the ParScannedTokenQueue.
//
const ParScannedTokenQueue::scannedTokenInfo &tokInfo = getTokInfo(arg);
if (isValid())
{
seqEndPos_ = tokInfo.tokenStrPos + tokInfo.tokenStrLen - 1;
//
// If this ShortStringSequence had a previous multi-byte component,
// then tokInfo.tokenStrPos has extra bytes added to it which should NOT
// be counted ar part of the length (in UCS2 chars) of the string, so
// we must subtract off any such extra bytes that were added.
//
seqEndPos_ -= tokInfo.tokenStrOffset - seqPosStartOffset_ ;
}
} // ShortStringSequence::append
// For i>=MAX_NUM_PARTS assertion fail.
// Otherwise return the pointer, seq[i], and set seq[i] to NULL.
NAString* ShortStringSequence::extract(UInt32 i)
{
assert(i<MAX_NUM_PARTS);
NAString *resultValue = seq[i];
seq[i] = NULL;
return resultValue;
} // ShortStringSequence::extract
void ShortStringSequence::invalidate()
{
// mark it as invalid:
numParts_ = MAX_NUM_PARTS + 1;
yyerror("Invalid SQL identifier");
/*
All callers of ShortStringSequence methods (ctor and append)
must follow with a call to isValid, and if it isn't, then a YYABORT.
This is because the following (commented out) does not work!
// emulate YYABORT here, with
// assert (*not* COMassert!)
// to break out of parser
assert(FALSE);
*/
}
const ParScannedTokenQueue::scannedTokenInfo&
ShortStringSequence::getTokInfo(NAString *arg)
{
// The parser may look ahead one or more tokens.
// So the most recently scanned token (with 0th index)
// may not relate to the string pointed to by 'arg';
// if not, then -1th indexed token (the previously scanned token) should.
const ParScannedTokenQueue::scannedTokenInfo *tokInfoPtr = NULL;
NAWchar *inputStr = SQLTEXTW();
Int32 downTo = -1;
for (Int32 i = 0; i >= downTo; i--)
{
const ParScannedTokenQueue::scannedTokenInfo &tokInfo
= ParScannedTokens->getScannedTokenInfo(i);
// is the i'th tok a valid SQL identifier?
// offset is subtracted off because in THIS case it is the Wide copy
size_t idx = tokInfo.tokenStrPos - tokInfo.tokenStrOffset;
NAString *iTok = unicodeToChar(&inputStr[idx],
tokInfo.tokenStrLen,
(Lng32) ComGetNameInterfaceCharSet(), // SQLCHARSETCODE_UTF8
PARSERHEAP());
// This identifier string needs to be converted back to
// ISO88591 because it may be passed to catman code to
// determine if the named object exists. When catman
// becomes Unicode-enabled, this ISO88591 conversion
// can go away. This comment also applies to other
// unicodeToChar() call sites here & elsewhere.
if (!iTok)
break;
TrimNAStringSpace(*iTok);
if (iTok->isNull() || tokInfo.tokenIsComment)
{
downTo--;
delete iTok;
continue;
}
if (!ToInternalIdentifier ( *iTok
, TRUE // upCase - default is TRUE
, TRUE // ^ ok? - default is FLASE
, toInternalIdentifierFlags_
)
&& *arg == *iTok)
{
delete iTok;
return tokInfo;
}
delete iTok;
tokInfoPtr = &tokInfo;
}
invalidate(); // one or both tokens is an invalid SQL identifier
return *tokInfoPtr;
} // ShortStringSequence::getTokInfo
// For error messages
NAWString badNameFromStrings(ShortStringSequence *names)
{
assert(names);
NAWString result(SQLTEXTW());
result.remove(names->getEndPosition()+1);
result.remove(0, names->getPosition()); // pos BEFORE the name began
delete names;
return result;
}
// This function is used in xxxNameFromStrings() functions below.
void getNamePart(NAString &xxxName, ShortStringSequence *names,
UInt32&index)
{
if (index>0) {
NAString namePtr (*(names->extract(--index)));
xxxName = namePtr;
}
}
static NABoolean CharHereIsaDoubleQuote(StringPos p) {
return SqlParser_CurrentParser &&
SqlParser_CurrentParser->CharHereIsDoubleQuote(p);
}
// This function knows that if there is only one
// name it is the table name, two names, table and schema, all three
// then table, schema, and catalog. And this is what it returns:
// a CorrName object whose corrName_ string is empty and whose
// QualifiedName fields are filled in with table, schema, and catalog.
//
// The input argument must not be NULL. It must also have at least
// one, and not more than three (> 3 is an assertion error), name parts.
// Each of the names is extracted and then placed into a CorrName object
// that is returned. A NULL return value implies that there was an error.
QualifiedName * qualifiedNameFromStrings(ShortStringSequence *names)
{
assert(names);
UInt32 index = names->numParts();
assert(index>0);
NAString tblName;
NAString schName;
NAString catName;
getNamePart(tblName,names,index);
getNamePart(schName,names,index);
getNamePart(catName,names,index);
if (index) {
// ~String0 is an invalid qualified name
*SqlParser_Diags << DgSqlCode(-3011)
<< DgWString0(badNameFromStrings(names));
return NULL;
}
StringPos startPos = names->getPosition();
delete names;
QualifiedName *result = new (PARSERHEAP())
QualifiedName(tblName, schName, catName, PARSERHEAP());
ComASSERT(result);
result->setNamePosition(startPos, CharHereIsaDoubleQuote(startPos));
return result;
}
// if the schemaName part inName contains volatile schema prefix, then
// return an error. Don't do this if volatile schema prefix is allowed
// for internal queries.
// If validateVolatileName is set, then validate that:
// -- volatile schema exists
// -- name is a one or 2 part name
// -- if 2-part name, then the schPart is the currentUserName.
// If updateVolatileName is set and the name is validate to be a volatile
// name, then change the schema name to be the current volatile schema
// name.
const NABoolean validateVolatileSchemaName(NAString &schName)
{
return CmpCommon::context()->sqlSession()->
validateVolatileSchemaName(schName);
}
SchemaName * processVolatileSchemaName(SchemaName *schName,
NABoolean validateVolatileName,
NABoolean updateVolatileName)
{
SchemaName *result = schName;
if (validateVolatileName)
{
//if ((!schName) ||
// (! validateVolatileSchemaName(*schName)))
//return NULL;
}
if (updateVolatileName)
{
result = CmpCommon::context()->sqlSession()->updateVolatileSchemaName();
}
return result;
}
QualifiedName * processVolatileDDLName(QualifiedName * inName,
NABoolean validateVolatileName,
NABoolean updateVolatileName)
{
QualifiedName *result = inName;
if (NOT inName->getSchemaName().isNull())
{
if (! CmpCommon::context()->sqlSession()->
validateVolatileQualifiedSchemaName(*inName))
return NULL;
}
if (validateVolatileName)
{
if (! CmpCommon::context()->sqlSession()->validateVolatileQualifiedName(*inName))
return NULL;
}
if (updateVolatileName)
{
result = CmpCommon::context()->sqlSession()->updateVolatileQualifiedName(*inName);
}
return result;
}
CorrName * corrNameFromStrings(ShortStringSequence *names)
{
QualifiedName *qn = qualifiedNameFromStrings(names);
if (!qn) return NULL;
CorrName *result;
// ##SQLMP-SYNTAX-KLUDGE##
//
// ## Temporarily ##??, till we get ANSI name mapping working for
// SQL/MP tables, this code stuffs the tablename into the corr name.
//
// MP-style queries may look like this:
// "SELECT T1.A, Y.B, T3.C FROM \Q.$R.S.T1, \N.$O.P.T2 Y, \K.$L.M.T3"
// which uses non-ANSI defaulting rules for correlation names.
//
// If in NSK nametype mode, here we supply implicit corr names AS IF
// the user had input
// "SELECT T1.A, Y.B, T3.C FROM \Q.$R.S.T1 T1, \N.$O.P.T2 Y, \K.$L.M.T3 T3"
//
// These implicit corr names are overridden if the user supplies
// an explicit corr name -- by setCorrName() in rule "table_name as_clause" --
// as in the "T2 Y" in the example here.
//
// ## See GenericUpdate::bindNode(), which UNDOES this temporary corr.
const NAString &tblName = qn->getObjectName();
result = new (PARSERHEAP()) CorrName(*qn, PARSERHEAP());
ComASSERT(result);
result->setNamePosition(qn->getNamePosition(),
CharHereIsaDoubleQuote(qn->getNamePosition()));
return result;
}
ColRefName *colRefNameFromStrings(ShortStringSequence *names)
{
assert(names);
UInt32 index = names->numParts();
assert(index>0);
NAString colName;
NAString tblName;
NAString schName;
NAString catName;
getNamePart(colName,names,index);
getNamePart(tblName,names,index);
getNamePart(schName,names,index);
getNamePart(catName,names,index);
if (index) {
// ~String0 is an invalid colref name
*SqlParser_Diags << DgSqlCode(-3002)
<< DgWString0(badNameFromStrings(names));
return NULL;
}
StringPos startPos = names->getPosition();
delete names;
ColRefName *result = new (PARSERHEAP())
ColRefName(colName, CorrName(tblName, PARSERHEAP(), schName, catName), PARSERHEAP());
ComASSERT(result);
result->setNamePosition(startPos, CharHereIsaDoubleQuote(startPos));
return result;
}
// The purpose of this function is to convert NAStrings that contain
// delimited identifiers as detected by SqlLexer
// to a format we can use internally.
//
// If a string begins with a double quote, then
// this function takes an NAString and:
// - there are supposed to be double quotes surrounding the string
// and they are removed
// - any embedded double quotes (i.e., two consecutive dquotes)
// are turned into just one dquote
//
// If the string does not begin with a double quote, then the
// only transformation is to make all the contents upper case.
//
// Efficiency: this function saves on space at the cost of some time.
// The calls to NAString.remove() probably take linear time as a function
// of string length on each call. A faster version of this function
// would establish a transformed string in a separate buffer and then
// copy it back into the original.
NABoolean transformIdentifier(NAString& delimIdent,
Int32 upCase,
NABoolean acceptCircumflex // VO: Fix genesis solution 10-040204-2957
,UInt16 toInternalIdentifierFlags
)
{
NAString origIdent(delimIdent);
Lng32 sqlcode = ToInternalIdentifier(delimIdent, upCase, acceptCircumflex, toInternalIdentifierFlags);
if (sqlcode) {
// 3004 A delimited identifier must contain at least one character.
// 3118 Identifier too long.
// 3127 Illegal character in identifier $0~string0.
// 3128 $1~string1 is a reserved word.
*SqlParser_Diags << DgSqlCode(sqlcode)
<< DgString0(origIdent)
<< DgString1(delimIdent);
if (sqlcode > 0) sqlcode = -sqlcode;
if (sqlcode != -3118) {
if (sqlcode == -3127) {
Int32 i = -delimIdent[(size_t)0]; // count of scanned chars
if (i > 0) i = -i;
i += ((Int32)origIdent.length()) - 1;
if (i > 0) {
SqlParser_CurrentParser->getLexer()->setInputPos(i);
// point to the illegal character
}
}
yyerror(""); // emit syntax error 15001
}
return TRUE;
}
return FALSE; // no error
}
void PicStream::skipPicture()
{
NAString theIdentifier;
while (isalpha(sgetc())) theIdentifier.append(toupper(sbumpc()));
skipWhite();
NAString string1 = "PIC";
NAString string2 = "PICTURE";
assert(theIdentifier == string1 || theIdentifier == string2);
}
// The skipCount function is basically a loop that on each pass
// either gets a single character, or gets a single
// character followed by some parens with an enclosed integer, and optionally
// followed by a character length unit identifier ('CHARACTERS' for R2.0).
// We assertion fail on a syntax error --- this should be okay since
// the SqlLexer.l is guaranteed (read: is supposed to) give us only
// valid strings to parse in the first place and not doing so is a
// programming error.
NABoolean PicStream::skipCount (UInt32*result, const char pattern, NABoolean isCharType)
{
assert(toupper(sgetc()) == pattern);
UInt32 total = 0;
do {
stossc();
if (sgetc() == '(') {
// Next we advance over the characters in a pattern
// of `( <unsigned-int> )' where there may be white space after
// the left parens and white space before the right parens. It is
// an assertion fail (and ostensibly a SqlLexer.l error) if this
// pattern is not encountered. The unsigned int is parsed
// and its value added to total.
stossc();
skipWhite();
assert(isdigit(sgetc()));
UInt32 val = 0;
do {
val = (val*10) + sbumpc() - '0';
if(val > SHORT_MAX)
return FALSE;
} while (isdigit(sgetc()));
skipWhite();
char ch = sbumpc();
if ( isCharType == TRUE && ( ch=='C' || ch=='c' ) ) {
char len_unit_array[11]; // len("CHARACTERS") = 10
len_unit_array[0] = ch; // store 'c'
Int32 n = sgetn(len_unit_array+1, 9); // get the rest of "haracters"
len_unit_array[10] = 0;
assert(n == 9 && strcasecmp(len_unit_array, "CHARACTERS") == 0);
skipWhite();
ch = sbumpc();
}
assert(ch==')');
total += val;
}
else
total++;
if(total > SHORT_MAX)
return FALSE;
} while ( toupper(sgetc()) == pattern);
*result = total;
return TRUE;
}
// parsePicClause() accepts a char* and either
// assertion fails (fundamentally due to a SqlLexer.l error), or,
// yields the following data about a PIC clause: string or not string,
// precision (length), scale, signed or not signed. Of course,
// scale and signedness only apply to the case of `not string.'
//
// The basic formats which this function parses are:
// PX, P9, PV9, P9V9, PS9, PSV9, PS9V9
// where P is the `PICTURE' part, X is a cobol-x's clause, 9 is a cobol-9's
// clause, and S and V are the letter S and V (upper or lower case),
// respectively.
// return TRUE if successful, FALSE if overflow
NABoolean parsePicClause(NAString *picClauseBuffer, NABoolean *isStringPtr,
UInt32*precisionPtr, UInt32*scalePtr,
NABoolean *hasSignPtr)
{
assert(picClauseBuffer);
assert(precisionPtr);
assert(isStringPtr);
UInt32 frontPart=0; // if a format is 99V999 then front=2, back=3
UInt32 backPart=0;
// We intentionally "cast away" const in the next line.
// We need the char* pointer but the NAString::data() is defined as const.
// Don't worry, we don't modify the string, and it wouldn't matter anyway.
PicStream s( (char *) picClauseBuffer->data());
s.skipPicture();
if (toupper(s.sgetc()) == 'X') {
if(s.skipCount(precisionPtr, 'X', TRUE) == FALSE)
return FALSE;
*isStringPtr = TRUE;
}
else {
assert(hasSignPtr);
assert(scalePtr);
// Syntax of just 'S' or 'V' or 'SV' with no 9's are caught
// by the picNAType() function.
if (toupper(s.sgetc()) == 'S') {
*hasSignPtr = TRUE;
s.stossc();
}
else
*hasSignPtr = FALSE;
if (s.sgetc() == '9')
if( s.skipCount(&frontPart, '9') == FALSE)
return FALSE;
if (toupper(s.sgetc()) == 'V') {
s.stossc();
if (s.sgetc() == '9')
if( s.skipCount(&backPart, '9') == FALSE)
return FALSE;
}
// We know this is the numeric case.
// Also, frontPart + backPart = precision and backPart = scale.
*precisionPtr = frontPart + backPart;
*scalePtr = backPart;
*isStringPtr = FALSE;
}
assert(s.sgetc() == EOF);
return TRUE;
}
// This function is used in the productions that handle the COBOL style
// PIC type declarations. It accepts some parameters gleaned from the
// PIC syntax and either issues an error message, returning NULL,
// or returns a pointer to a newly allocated NAType appropriate
// to the given parameters.
NAType *picNAType(const NABoolean isString,
const DISPLAY_STYLE style,
const UInt32 precision,
const UInt32 scale,
const NABoolean hasSign,
const CharInfo::CharSet charset,
const CharInfo::Collation collation,
const CharInfo::Coercibility coerc,
const NAString & picClauseBuffer,
const NABoolean isCaseinsensitive)
{
NAType *returnValue=NULL;
if (isString) {
assert(precision > 0);
switch (style) {
case STYLE_DISPLAY:
{
CharInfo::CharSet eEncodingCharSet = charset;
Int32 maxLenInBytes = precision;
Int32 characterLimit = precision;
Int32 maxBytesPerChar = CharInfo::maxBytesPerChar(charset);
returnValue = new (PARSERHEAP())
// SQLChar(precision,TRUE,FALSE,isCaseinsensitive,FALSE,charset,collation,coerc);
SQLChar(PARSERHEAP(), CharLenInfo(characterLimit, maxLenInBytes),
TRUE,FALSE,isCaseinsensitive,FALSE,
charset,collation,coerc,eEncodingCharSet);
assert(returnValue);
}
break;
case STYLE_UPSHIFT:
returnValue = new (PARSERHEAP())
SQLChar(PARSERHEAP(), precision,TRUE,TRUE,isCaseinsensitive,FALSE,charset,collation,coerc);
assert(returnValue);
break;
case STYLE_LEADING_SIGN:
// PIC X types cannot have leading signs, or any signs.
*SqlParser_Diags << DgSqlCode(-3038);
break;
case STYLE_COMP:
// PIC X types do not have any COMP representation.
*SqlParser_Diags << DgSqlCode(-3039);
break;
default: assert(FALSE);
}
}
else if (precision == 0)
// You can't have a precision of zero. Add a '9' to
// the PICTURE clause.
*SqlParser_Diags << DgSqlCode(-3040);
else
switch (style) {
case STYLE_DISPLAY:
case STYLE_LEADING_SIGN:
if (scale >= 10 && !hasSign) {
// If scale is greater than or equal to 10, UNSIGNED
// is invalid for a numeric or decimal type specification.
*SqlParser_Diags << DgSqlCode(-3041);
break;
}
else if (precision > 18)
{
// Precision of PIC 9 types, $0~string0, cannot exceed 18.
*SqlParser_Diags << DgSqlCode(-3037)
<< DgString0(picClauseBuffer.data());
break;
}
else
returnValue = new (PARSERHEAP())
SQLDecimal(PARSERHEAP(), precision,scale,hasSign);
assert(returnValue);
break;
case STYLE_UPSHIFT:
// Upshift for a numeric type is invalid.
*SqlParser_Diags << DgSqlCode(-3042);
break;
case STYLE_COMP:
if (scale >= 10 && !hasSign) {
// If scale is greater than or equal to 10, UNSIGNED
// is invalid for a numeric or decimal type specification.
*SqlParser_Diags << DgSqlCode(-3041);
break;
}
else if (precision > 18)
// Precision greater than eighteen invalid for a COMP
// numeric type.
*SqlParser_Diags << DgSqlCode(-3043);
else {
const Int16 DisAmbiguate = 0; // added for 64bit project
returnValue = new (PARSERHEAP())
SQLNumeric(PARSERHEAP(), hasSign, precision, scale, DisAmbiguate);
assert(returnValue);
}
break;
default: assert(FALSE);
}
return returnValue;
}
// : value_expression_list TOK_IN '(' value_expression_list ')'
// Convert "v IN (v1, v2 ...)" to "v=v1 OR v=v2 ...".
//
// This transformation is somewhat arbitrary (it creates a left-deep
// tree, instead of a right-deep or bushy or ...), and could easily be
// improved -- i.e., removing duplicates.
//
// NB: The left-deep nature of the resulting OR-tree is expected by
// histogram-manipulation code in /optimizer/ColStatDesc.cpp,
// CSDL::estimateCardinality(). If you change this transformation, then
// please change the code there, too. (Or at least talk to the owner of
// the histogram code.)
ItemExpr *convertINvaluesToOR(ItemExpr *lhs, ItemExpr *rhs)
{
NABoolean err = FALSE;
if (lhs->getOperatorType() == ITM_ITEM_LIST)
err = TRUE;
else if (lhs->getOperatorType() == ITM_ROW_SUBQUERY)
{
RelExpr *subq = ((RowSubquery *)lhs)->getSubquery();
CMPASSERT( subq->getOperatorType() == REL_ROOT );
RelRoot *sq = (RelRoot *)subq;
RelExpr *theChild;
theChild = sq->child(0);
if (sq->getCompExprTree() && // what if no compExprTree()?
sq->getCompExprTree()->getOperatorType() == ITM_ITEM_LIST)
err = TRUE;
else if (theChild->getOperatorType() == REL_TUPLE &&
((Tuple *)theChild)->tupleExprTree()->getOperatorType() ==
ITM_ITEM_LIST)
err = TRUE;
}
if (err)
{
// 3147 The left operand of an IN predicate whose right operand is
// a value list must be scalar (degree of one).
*SqlParser_Diags << DgSqlCode(-3147);
return NULL;
}
ExprValueId rightListId = rhs;
ItemExprTreeAsList *enl = new (PARSERHEAP())
ItemExprTreeAsList(&rightListId, ITM_ITEM_LIST);
ItemExpr *result = NULL;
CollIndex nEnlEntries = (CollIndex) enl->entries();
for (Lng32 i = 0; i < nEnlEntries ; i++)
{
BiRelat *eqpred = new (PARSERHEAP()) BiRelat(ITM_EQUAL, lhs, (*enl)[i]);
eqpred->setCreatedFromINlist(TRUE);
if (!result)
result = eqpred;
else
result = new (PARSERHEAP()) BiLogic(ITM_OR, result, eqpred);
}
if (result && result->getOperatorType() == ITM_OR)
((BiLogic*)result)->setCreatedFromINlist(TRUE);
return result;
}
// quantified_predicate : value_expression_list '=' quantifier rel_subquery
ItemExpr *makeQuantifiedComp(ItemExpr *lhs,
OperatorTypeEnum compOpType,
Int32 quantifierTok,
RelExpr *subquery)
{
assert(quantifierTok == TOK_ALL || quantifierTok == TOK_ANY);
NABoolean all = (quantifierTok == TOK_ALL);
OperatorTypeEnum resultOpType = NO_OPERATOR_TYPE;
switch (compOpType)
{
case ITM_EQUAL:
resultOpType = all ? ITM_EQUAL_ALL : ITM_EQUAL_ANY; break;
case ITM_LESS:
resultOpType = all ? ITM_LESS_ALL : ITM_LESS_ANY; break;
case ITM_GREATER:
resultOpType = all ? ITM_GREATER_ALL : ITM_GREATER_ANY; break;
case ITM_NOT_EQUAL:
resultOpType = all ? ITM_NOT_EQUAL_ALL : ITM_NOT_EQUAL_ANY; break;
case ITM_LESS_EQ:
resultOpType = all ? ITM_LESS_EQ_ALL : ITM_LESS_EQ_ANY; break;
case ITM_GREATER_EQ:
resultOpType = all ? ITM_GREATER_EQ_ALL : ITM_GREATER_EQ_ANY; break;
default:
assert(FALSE);
}
return new (PARSERHEAP()) QuantifiedComp(resultOpType, lhs, subquery, FALSE);
}
///////////////////////////////////////////////////////////////////////////
// this method converts an IN list to a VALUES subquery or
// converts it to an OR predicate.
// Conversion to VALUES SQ is done
// if all IN list elements are constants or parameters and
// the number of elements are greater than 100. For less than
// 100, it might be better to use the IN predicate so mdam could
// be chosen. The number 100 is experimental and may change.
// <value> IN (<val1>, ...., <valN>) gets converted to
// <value> IN (VALUES(<val1>), ..., (<valN))
// Otherwise, IN list is converted to ORs.
//
// IN list is also converted to ORs if all consts do not have compatible
// types. (like, all numerics, or all characters...). This is done since
// we don't (yet) support incompatible types within a TupleList.
//
// processINlist should be moved to Binder. Will do that later.
//
///////////////////////////////////////////////////////////////////////////
ItemExpr *processINlist(ItemExpr *lhs, ItemExpr *rhs)
{
ItemExpr * retItemExpr = NULL;
Lng32 defaultsLimit = ActiveSchemaDB()->getDefaults().getAsLong(COMP_INT_22);
if (rhs->castToItemExpr()->getOperatorType() == ITM_ITEM_LIST)
{
ItemList * il = (ItemList*)rhs;
if ((defaultsLimit > 0) && // if defaultsLimit == 0 the the feature is turned OFF.
(il->doesExprEvaluateToConstant(FALSE, FALSE)) &&
(il->numOfItems() > defaultsLimit)
)
{
// insert a convert node on top of each child item expr
ItemExpr * currIL = il;
NABoolean incompatibleTypes = FALSE;
NABoolean negate;
ConstValue* prevCVExpr = currIL->child(0)->castToConstValue(negate);
NABuiltInTypeEnum prevEnum = ( prevCVExpr == NULL ) ?
NA_UNKNOWN_TYPE : prevCVExpr->getType()->getTypeQualifier() ;
ConstValue* currCVExpr = NULL;
CollIndex index = 0;
while (1)
{
currCVExpr = currIL->child(index)->castToConstValue(negate);
if ( currCVExpr ) {
NABuiltInTypeEnum currEnum =
currCVExpr->getType()->getTypeQualifier();
if ( prevEnum == NA_UNKNOWN_TYPE ) {
prevEnum = currEnum;
} else {
if (currEnum != prevEnum) {
incompatibleTypes = TRUE;
break;
}
}
} else {
if (currIL->child(index)->castToItemExpr()->getOperatorType()
!= ITM_DYN_PARAM)
{
incompatibleTypes = TRUE;
break;
}
}
Convert * cnv = new(PARSERHEAP()) Convert(currIL->child(index));
currIL->setChild(index, cnv);
if ( index == 1 ) break;
if (currIL->child(1)->castToItemExpr()->getOperatorType()
== ITM_ITEM_LIST)
currIL = currIL->child(1);
else
index = 1;
}
if (NOT incompatibleTypes)
{
// convert to VALUES subq.
TupleList * tl = new(PARSERHEAP()) TupleList(il);
tl->setCreatedForInList(TRUE);
RelRoot * rr = new (PARSERHEAP()) RelRoot(tl);
retItemExpr =
makeQuantifiedComp(lhs, ITM_EQUAL, TOK_ANY, rr);
if (retItemExpr)
((QuantifiedComp*)retItemExpr)->setCreatedFromINlist(TRUE);
}
}
}
if (! retItemExpr)
retItemExpr = convertINvaluesToOR(lhs, rhs);
return retItemExpr;
}
ItemExpr *makeBetween(ItemExpr *x, ItemExpr *y, ItemExpr *z, Int32 tok)
{
ItemExpr *result = new (PARSERHEAP()) Between(x, y, z);
if (tok == TOK_NOT_BETWEEN)
result = new (PARSERHEAP()) UnLogic(ITM_NOT, result);
return result;
}
// Change the <sqltext> arg of a CQD from
// SET SCHEMA X.Y; -- unquoted: Tandem syntax extension
// SET MPLOC $V.SV; -- MPLOC: Tandem syntax extension
// SET MPLOC '$V.SV'; -- MPLOC: Tandem syntax extension
// into
// SET SCHEMA 'X.Y'; -- string literal: Ansi syntax, MX canonical fmt
// SET MP_SUBVOLUME '$V.SV'; -- string lit: Tdm ext, MX canonical format
// SET MP_SUBVOLUME '$V.SV'; -- string lit: Tdm ext, MX canonical format
//
// This needs to be called ONLY for:
// - SET cqd's (not DECLARE cqd's), and
// - the SET cqd's unquoted (non-string-literal) variants, or
// - or if we are otherwise rewriting the user input text
// (e.g. the syntactic sugar of "SET MPLOC" --
// there is no NADefaults attribute of MPLOC --
// NADefaults parses a multi-part MP_SUBVOLUME instead).
//
ControlQueryDefault *normalizeDynamicCQD(const char *attrName,
const NAString &attrValue)
{
size_t len = attrValue.length() + 2; // assume only two quotes
NAString quotedValue(len);
ToQuotedString(quotedValue, attrValue);
len += 4 + strlen(attrName) + 2;
NAString tmpSQLTEXT(len);
tmpSQLTEXT = "SET ";
tmpSQLTEXT += attrName;
tmpSQLTEXT += quotedValue + ";";
return new (PARSERHEAP())
ControlQueryDefault(
tmpSQLTEXT,
CharInfo::UTF8
, attrName, attrValue, TRUE/*dynamic*/);
}
// return the relexpr tree that evaluates an empty compound statement
RelExpr* getEmptyCSRelTree()
{
ItemExpr *tupleExpr = new (PARSERHEAP()) ConstValue();
RelExpr *tuple = new (PARSERHEAP()) Tuple(tupleExpr);
ItemExpr *predicate = new (PARSERHEAP()) BoolVal(ITM_RETURN_FALSE);
tuple->addSelPredTree(predicate);
return tuple;
}
RelExpr*
getIfRelExpr(ItemExpr* condition, RelExpr* thenBranch, RelExpr* elseBranch)
{
if ( thenBranch == NULL && elseBranch == NULL ) {
delete condition;
return NULL;
}
NABoolean thenBranchAloneIsNull = FALSE ;
NABoolean elseBranchAloneIsNull = FALSE ;
if ( thenBranch == NULL ) {
thenBranch = getEmptyCSRelTree();
thenBranchAloneIsNull = TRUE ;
}
if (elseBranch == NULL) {
elseBranch = getEmptyCSRelTree();
elseBranchAloneIsNull = TRUE ;
}
Union *u = new (PARSERHEAP())
Union(thenBranch, elseBranch, NULL, condition);
u->setUnionForIF();
if ( thenBranchAloneIsNull ) {
u->setCondEmptyIfThen();
}
if (elseBranchAloneIsNull) {
u->setCondEmptyIfElse();
}
return u;
}
// EJF L4J - dynamic CQD not allowed inside Compound Statements
NABoolean beginsWith(char *sqltext, const char *kwd)
{
// Assumes Prettify has been called, so only one space (at most) btw tokens.
// If this is called more than once, the second time in the text might begin
// with a delimiting space.
NAString sqlText(sqltext);
size_t i = 0;
size_t flen = sqlText.length();
if (!sqlText.isNull()) {
while (i < flen) {
if (isspace((unsigned char)sqlText[size_t(0)])) // For VS2003
sqlText.remove(0, 1);
else
i = flen;
}
}
size_t len = strlen(kwd);
if (sqlText.length() > len) {
NAString tmp(sqlText);
tmp.remove(len);
if (tmp == kwd)
return TRUE;
}
return FALSE;
}
void setHVorDPvarIndex(ItemExpr * expr, NAString *name)
{
// This method sets the var index value to HV or DP
// in sequential order. The duplicate HV or DP will be eliminated
// during 'bind' phase.
OperatorTypeEnum opType = expr->getOperatorType();
if (inCallStmt)
{
if (ITM_HOSTVAR == opType)
((HostVar *)expr)->setPMOrdPosAndIndex(COM_INPUT_COLUMN,
1,
currVarIndex++);
else
((DynamicParam *)expr)->setPMOrdPosAndIndex(COM_INPUT_COLUMN,
1,
currVarIndex++);
}
}
//ct-bug-10-030102-3803 Begin
void conditionalDelimit(NAString &tmpName, const NAString &tmp)
{
if(tmp.contains(".",NAString::exact) || tmp.contains("*",NAString::exact))
{
tmpName.append("\"",1);
tmpName.append(tmp.data(),tmp.length());
tmpName.append("\"",1);
}
else
{
tmpName.append(tmp.data(),tmp.length());
}
}
//ct-bug-10-030102-3803 End
Lng32 getDefaultMaxLengthForLongVarChar(CharInfo::CharSet cs)
{
if (IdentifyMyself::IsPreprocessor() == TRUE )
return INT_MAX;
switch(cs) {
case CharInfo::UNICODE:
return (Lng32)CmpCommon::getDefaultNumeric(MAX_LONG_WVARCHAR_DEFAULT_SIZE);
break;
default:
return (Lng32)CmpCommon::getDefaultNumeric(MAX_LONG_VARCHAR_DEFAULT_SIZE);
break;
}
}
Lng32 getDefaultMinLengthForLongVarChar(CharInfo::CharSet cs)
{
if (IdentifyMyself::IsPreprocessor() == TRUE )
return 0;
switch(cs) {
case CharInfo::UNICODE:
return (Lng32)CmpCommon::getDefaultNumeric(MIN_LONG_WVARCHAR_DEFAULT_SIZE);
break;
default:
return (Lng32)CmpCommon::getDefaultNumeric(MIN_LONG_VARCHAR_DEFAULT_SIZE);
break;
}
}
NABoolean getCharSetInferenceSetting(NAString& defVal)
{
if (IdentifyMyself::IsPreprocessor() == TRUE )
return FALSE;
return (CmpCommon::getDefault(INFER_CHARSET, defVal) == DF_ON);
}
NABoolean allowRandFunction()
{
if (IdentifyMyself::IsPreprocessor() == TRUE
)
return TRUE;
else
return CmpCommon::getDefault(ALLOW_RAND_FUNCTION) == DF_ON;
}
RelExpr * getTableExpressionRelExpr(
RelExpr *fromClause,
ItemExpr *whereClause,
RelExpr *sampleClause,
RelExpr *transClause,
ItemExpr *seqByClause,
ItemExpr *groupByClause,
ItemExpr *havingClause,
ItemExpr *qualifyClause,
NABoolean hasTDFunctions,
NABoolean hasOlapFunctions)
{
if (qualifyClause && seqByClause)
{
*SqlParser_Diags << DgSqlCode(-4360);
return NULL;
}
if(hasOlapFunctions && seqByClause)
{
*SqlParser_Diags << DgSqlCode(-4345);
return NULL;
}
// childPtr is the current child node
// as the tree is built. At the end
// it is the root of the table expression tree.
//
RelExpr *childPtr = fromClause;
NABoolean groupByClauseProcessed = FALSE;
// add where clause as a selection pred
//
if (whereClause)
childPtr->addSelPredTree(whereClause);
if(groupByClause || havingClause)
{
// we are making this change so that for the following query the groupby is
// associlated with the last select statement and not the entire unioned result.
// select a from t union select a from t order by 1 group by 1;
if ((CmpCommon::getDefault(MODE_SPECIAL_1) == DF_ON) &&
((childPtr->getOperatorType() == REL_GROUPBY) && // group by for union distinct
childPtr->child(0) && (childPtr->child(0)->getOperatorType() == REL_ROOT) &&
childPtr->child(0)->child(0) &&
(childPtr->child(0)->child(0)->getOperatorType() == REL_UNION) &&
childPtr->child(0)->child(0)->child(1) &&
(childPtr->child(0)->child(0)->child(1)->getOperatorType() == REL_ROOT) &&
childPtr->child(0)->child(0)->child(1)->child(0))
|| // union all case is below
((childPtr->getOperatorType() == REL_UNION) &&
childPtr->child(1) && (childPtr->child(1)->getOperatorType() == REL_ROOT) &&
childPtr->child(1)->child(0)))
{
RelExpr * unionGroupByChild ;
RelExpr * unionChild;
if (childPtr->getOperatorType() == REL_GROUPBY)
{
unionGroupByChild = childPtr->child(0)->child(0)->child(1)->child(0);
unionChild = childPtr->child(0)->child(0)->child(1);
}
else
{
unionGroupByChild = childPtr->child(1)->child(0);
unionChild = childPtr->child(1);
}
RelExpr * unionGrby ;
if(unionGroupByChild->getOperatorType() != REL_GROUPBY)
{
unionGrby = new (PARSERHEAP())
GroupByAgg(unionGroupByChild,
REL_GROUPBY,
groupByClause);
// add having clause as a selection pred
unionGrby->addSelPredTree(havingClause);
if (childPtr->getOperatorType() == REL_GROUPBY)
childPtr->child(0)->child(0)->child(1)->child(0) = unionGrby;
else
childPtr->child(1)->child(0) = unionGrby;
}
else
{
yyerror("");
}
groupByClauseProcessed = TRUE;
}
}
// Add the optional sample clause.
//
if(sampleClause) {
sampleClause->setChild(0, childPtr);
childPtr = sampleClause;
}
// Add chain of transpose clauses
//
if(transClause) {
RelExpr *chain = transClause;
while(chain->child(0))
chain = chain->child(0);
chain->setChild(0,childPtr);
childPtr = transClause;
}
// Add the optional sequence by clause.
// Used with sequence functions.
//
if(seqByClause) {
childPtr = new (PARSERHEAP())
RelSequence(childPtr, seqByClause);
}
else if( hasTDFunctions ) {
RelSequence *seqNode = new (PARSERHEAP()) RelSequence(childPtr,NULL);
seqNode->setHasTDFunctions(hasTDFunctions);
childPtr = seqNode;
}
if (!hasTDFunctions)
{
if (qualifyClause)
{ //Using Qualify clause without using rank function in the query is not allowed.
*SqlParser_Diags << DgSqlCode(-4363);
return NULL;
}
if((groupByClause || havingClause) && (NOT groupByClauseProcessed))
{
childPtr = new (PARSERHEAP())
GroupByAgg(childPtr,
REL_GROUPBY,
groupByClause);
// add having clause as a selection pred
childPtr->addSelPredTree(havingClause);
if (groupByClause)
((GroupByAgg*)childPtr)->setIsRollup(groupByClause->isGroupByRollup());
}
// sequence node goes right below rel root
if( !seqByClause && hasOlapFunctions )
{
RelSequence *seqNode = new (PARSERHEAP()) RelSequence(childPtr,NULL);
seqNode->setHasOlapFunctions(hasOlapFunctions);
childPtr = seqNode;
}
} // !hasTDFunctions
else
{
if (!seqByClause)
{
// for TD rank, the group by becomes the partition by
if (groupByClause)
{
((RelSequence *)childPtr)->
setPartitionBy(groupByClause->copyTree(CmpCommon::statementHeap()));
}
if (qualifyClause)
{
childPtr->addSelPredTree(qualifyClause);
}
}
}
return childPtr;
}
RelExpr * processReturningClause(RelExpr * re, UInt32 returningType)
{
// disable returning clause if in a compound statement.
if (in3GL_)
return NULL;
Insert * insert = (Insert *)re;
NAString nas("SYSKEY");
ColRefName * newColRefName = new(PARSERHEAP())
ColRefName(nas, PARSERHEAP());
if (newColRefName == NULL)
return NULL;
ColReference * cr = new (PARSERHEAP()) ColReference(newColRefName);
// this flag sets up the insert operator to return rows.
// Maybe it should be renamed to indicate that inserted rows are
// being returned.
insert->setMtsStatement(TRUE);
RelRoot * root = new (PARSERHEAP())
RelRoot(insert,
TransMode::ACCESS_TYPE_NOT_SPECIFIED_,
LockMode::LOCK_MODE_NOT_SPECIFIED_,
REL_ROOT, cr);
if ((insert->child(0)) &&
(insert->child(0)->getOperatorType() != REL_TUPLE))
{
// indicate that [last 1] is needed. LAST 1 is indicated by -3.
if (root)
root->setFirstNRows(-3);
}
return root;
}
// Process the sequence generator options.
// Ensure that the number is not negative and not larger than
// the maximum allowed for an Int64
NABoolean validateSGOption(NABoolean positive,
NABoolean negAllowed,
char * numStr,
const char * optionName,
const char * objectType)
{
Lng32 strLen = (Lng32)(strlen(numStr));
Int64 theValue;
Lng32 convErrFlag = ex_conv_clause::CONV_RESULT_OK;
// if the option is a negative number and negative numbers are not allowed,
// prepare a diagnostic and return.
// In the future, negatives may be allowed.
if (!positive && !negAllowed)
{
*SqlParser_Diags << DgSqlCode(-1572)
<< DgString0(optionName)
<< DgString1(objectType);
return FALSE;
}
// Mark the diagnostics.
// Any possible diagnostics added by convDoIt
// will be removed.
Lng32 markValue = SqlParser_Diags->mark();
/* for char(n), we limit the value of n to be no more than
2^63-1, so convert it to 64bit signed first, then check
to make sure the converted value is not negative. */
ex_expr::exp_return_type result =
convDoIt(numStr, /*source*/
strLen, /*sourceLen*/
REC_BYTE_F_ASCII, /*sourceType*/
0, /*sourcePrecision*/
0, /*sourceScale*/
(char *)&theValue, /*target*/
sizeof(theValue), /*targetLen*/
REC_BIN64_SIGNED, /*targetType*/
0, /*targetPrecision*/
0, /*targetScale*/
NULL, /*varCharLen*/
0, /*varCharLenSize*/
PARSERHEAP(), /*heap*/
&SqlParser_Diags, /*diagsArea*/
CONV_ASCII_BIN64S, /*index*/
&convErrFlag, /*dataConversionErrorFlag*/
0 /*flags*/);
switch(convErrFlag){
case ex_conv_clause::CONV_RESULT_ROUNDED_DOWN:
case ex_conv_clause::CONV_RESULT_ROUNDED_DOWN_TO_MAX:
case ex_conv_clause::CONV_RESULT_ROUNDED_UP:
case ex_conv_clause::CONV_RESULT_ROUNDED_UP_TO_MIN:
case ex_conv_clause::CONV_RESULT_ROUNDED:
case ex_conv_clause::CONV_RESULT_FAILED:
result = ex_expr::EXPR_ERROR;
break;
default:
break;
}
if (result == ex_expr::EXPR_ERROR)
{
// Rewind any errors added by convDoIt
SqlParser_Diags->rewind(markValue);
// Set the correct error
*SqlParser_Diags << DgSqlCode(-1576)
<< DgString0(optionName)
<< DgString1(objectType);
return FALSE;
}
return TRUE;
}
/*
// INSERT2000 COLUMN FIX STARTS HERE
// Please refer to SqlParserAux.h for class details and comments.
RearrangeValueExprList * RearrangeValueExprList::tail = NULL;
RearrangeValueExprList::RearrangeValueExprList()
{
next = NULL;
prev = NULL;
value = NULL;
}
// start to return the value_expression_list in reverse order.
ItemExpr* RearrangeValueExprList::Return_ValueExprList()
{
RearrangeValueExprList *tmp;
tmp = new (PARSERHEAP()) RearrangeValueExprList();
if(tmp->tail->prev != NULL) { // redundant check
tmp->value = new (PARSERHEAP()) ItemList(tmp->tail->prev->value, tmp->tail->value);
(tmp->tail->prev->prev != NULL) ?
(tmp->tail = tmp->tail->prev->prev) : (tmp->tail = NULL);
}
while (tmp->tail != NULL) {
tmp->value = new (PARSERHEAP()) ItemList(tmp->tail->value, tmp->value);
(tmp->tail->prev != NULL) ?
(tmp->tail = tmp->tail->prev) : (tmp->tail = NULL);
}
tmp->tail = NULL;
return tmp->value;
}
// ( ItemExpr -2, ItemExpr 0 )
ItemExpr* RearrangeValueExprList::Store_ValueExprList(ItemExpr *j, ItemExpr *i)
{
if (j != NULL) {
RearrangeValueExprList *newNode;
newNode = new (PARSERHEAP()) RearrangeValueExprList();
if (newNode->tail != NULL) {
// this condition will be TRUE only for the statement like follow:
// insert into table_name values (1,2,3),(4,5,6),(7,8,9);
// call the code to store call the Return_ValueExprList
// Store the final tmp->value in a list.
MultiValueExprList *tmp;
tmp = new (PARSERHEAP()) MultiValueExprList();
tmp->value = newNode->Return_ValueExprList();
tmp->Store_MultiValueExprList(tmp->value);
// this would be the next new row
newNode->value = j;
newNode->tail = newNode;
}
else { // the first node.
newNode->value = j;
newNode->tail = newNode;
}
}
if (i != NULL) {
RearrangeValueExprList *newNode2;
newNode2 = new (PARSERHEAP()) RearrangeValueExprList();
if (newNode2->tail != NULL) {
newNode2->tail->next = newNode2;
newNode2->prev = newNode2->tail;
newNode2->value = i;
newNode2->tail = newNode2;
}
else {
// this code should not be executed since
// "i" will never be the first node.. failsafe.
newNode2->value = i;
newNode2->tail = newNode2;
}
}
// Expecting some value.
// Correct value assigned later.
return i;
}
RearrangeValueExprList::~RearrangeValueExprList()
{
// setting free resources gives fragmentation error. remove this?
}
MultiValueExprList * MultiValueExprList::tail = NULL;
MultiValueExprList::MultiValueExprList()
{
next = NULL;
prev = NULL;
value = NULL;
}
void MultiValueExprList::Store_MultiValueExprList(ItemExpr *i)
{
if (i != NULL) {
MultiValueExprList *newNode;
newNode = new (PARSERHEAP()) MultiValueExprList();
if (newNode->tail != NULL) {
newNode->tail->next = newNode;
newNode->prev = newNode->tail;
newNode->value = i;
newNode->tail = newNode;
}
else {
newNode->value = i;
newNode->tail = newNode;
}
}
}
ItemExpr* MultiValueExprList::Return_MultiValueExprList()
{
MultiValueExprList *tmpNode;
tmpNode = new (PARSERHEAP()) MultiValueExprList();
if (tmpNode->tail != NULL) {
tmpNode = tmpNode->tail;
(tmpNode->tail->prev != NULL) ?
(tmpNode->tail = tmpNode->tail->prev) : (tmpNode->tail = NULL) ;
}
return tmpNode->value;
}
MultiValueExprList::~MultiValueExprList()
{
// setting free resources gives fragmentation error.
}
*/
// INSERT2000 COLUMN FIX ENDS HERE
ItemExpr *buildUdfExpr(NAString *udfName,
NAString *fixedInput,
ItemExpr *valueList)
{
NAString udfNameUpper = *udfName;
udfNameUpper.toUpper();
// Construct a 3-part UDF name
QualifiedName *qualifiedName = new (PARSERHEAP())
QualifiedName(udfNameUpper,
"", // schema name
"", // catalog name
PARSERHEAP());
// Create a RelExpr
IsolatedScalarUDF *udfFunc = new (PARSERHEAP())
IsolatedScalarUDF(*qualifiedName, PARSERHEAP());
// Give the RelExpr a pointer to the input and output
// parameters. The output datatype is not known yet but
// still a dummy ItemExpr is required.
ConstValue *dummyOutVal = new (PARSERHEAP()) ConstValue(0);
if (valueList)
{
ItemList *inputsAndReturn = new (PARSERHEAP())
ItemList(valueList, dummyOutVal);
udfFunc->setProcAllParamsTree(inputsAndReturn);
}
else
{
udfFunc->setProcAllParamsTree(dummyOutVal);
}
// Give the RelExpr a pointer to the fixed input string
//if (fixedInput)
// XXX Ignoring this for now
//udfFunc->setFixedInput(*fixedInput);
// Package the RelExpr as a scalar subquery
RelRoot *root = new (PARSERHEAP()) RelRoot(udfFunc);
ItemExpr *result = new (PARSERHEAP()) RowSubquery(root);
return result;
}
// -----------------------------------------------------------------------
// SqlParserAux_buildUdfOptimizationHint
// -----------------------------------------------------------------------
ElemDDLNode *
SqlParserAux_buildUdfOptimizationHint ( Int32 tokvalStage // in
, Int32 tokvalResource // in
, ComSInt32 cost // in
)
{
ElemDDLUdfOptimizationHint * pNode = NULL;
ComUdfOptimizationHintKind kind = COM_UDF_INITIAL_CPU_COST;
if (tokvalStage EQU TOK_INITIAL)
{
switch (tokvalResource)
{
case TOK_CPU: kind = COM_UDF_INITIAL_CPU_COST; break;
case TOK_IO: kind = COM_UDF_INITIAL_IO_COST; break;
case TOK_MESSAGE: kind = COM_UDF_INITIAL_MESSAGE_COST; break;
default: return pNode; break;
}
}
else if (tokvalStage EQU TOK_NORMAL)
{
switch (tokvalResource)
{
case TOK_CPU: kind = COM_UDF_NORMAL_CPU_COST; break;
case TOK_IO: kind = COM_UDF_NORMAL_IO_COST; break;
case TOK_MESSAGE: kind = COM_UDF_NORMAL_MESSAGE_COST; break;
default: return pNode; break;
}
}
else
return pNode;
pNode = new (PARSERHEAP()) ElemDDLUdfOptimizationHint(kind);
pNode->setCost(cost);
return pNode;
}
// -----------------------------------------------------------------------
// SqlParserAux_buildAlterFunction
// -----------------------------------------------------------------------
StmtDDLNode *
SqlParserAux_buildAlterFunction ( QualifiedName * ddl_qualified_name_of_udf // in - deep copy
, ElemDDLNode * optional_alter_passthrough_inputs_clause // in
, ElemDDLNode * optional_add_passthrough_inputs_clause // in
, ElemDDLNode * optional_function_attribute_list // in
)
{
QualifiedName noRoutineActionName(PARSERHEAP());
StmtDDLAlterRoutine *pNode66 = new (PARSERHEAP())
StmtDDLAlterRoutine
( COM_UDF_NAME // in - function name space
, *ddl_qualified_name_of_udf // in - deep copy
, noRoutineActionName // in - deep copy
, COM_UNKNOWN_ROUTINE_TYPE // in - either scalar or universal function
, optional_alter_passthrough_inputs_clause // in - shallow copy
, optional_add_passthrough_inputs_clause // in - shallow copy
, optional_function_attribute_list // in - shallow copy
, PARSERHEAP()
);
pNode66->synthesize();
return pNode66;
}
// -----------------------------------------------------------------------
// SqlParserAux_buildAlterAction
// -----------------------------------------------------------------------
StmtDDLNode *
SqlParserAux_buildAlterAction ( QualifiedName * ddl_qualified_name_of_uudf // in - deep copy
, QualifiedName * ddl_qualified_name_of_action // in - deep copy
, ElemDDLNode * optional_alter_passthrough_inputs_clause // in
, ElemDDLNode * optional_add_passthrough_inputs_clause // in
, ElemDDLNode * optional_function_attribute_list // in
)
{
StmtDDLAlterRoutine *pNode77 = new (PARSERHEAP()) StmtDDLAlterRoutine
( COM_UUDF_ACTION_NAME // in - routine action name space
, *ddl_qualified_name_of_uudf // in - deep copy
, *ddl_qualified_name_of_action // in - deep copy
, COM_ACTION_UDF_TYPE // in - routine action type
, optional_alter_passthrough_inputs_clause // in - shallow copy
, optional_add_passthrough_inputs_clause // in - shallow copy
, optional_function_attribute_list // in - shallow copy
, PARSERHEAP()
);
pNode77->synthesize();
return pNode77;
}
// -----------------------------------------------------------------------
// SqlParserAux_buildAlterTableMappingFunction
// -----------------------------------------------------------------------
StmtDDLNode *
SqlParserAux_buildAlterTableMappingFunction
( QualifiedName * ddl_qualified_name_of_table_mapping_udf // in - deep copy
, ElemDDLNode * optional_alter_passthrough_inputs_clause // in
, ElemDDLNode * optional_add_passthrough_inputs_clause // in
, ElemDDLNode * optional_function_attribute_list // in
)
{
QualifiedName noRoutineActionName(PARSERHEAP());
StmtDDLAlterRoutine *pNode88 = new (PARSERHEAP()) StmtDDLAlterRoutine
( COM_TABLE_NAME // table mapping function name belongs to table name space
, *ddl_qualified_name_of_table_mapping_udf // in - deep copy
, noRoutineActionName // in - deep copy
, COM_TABLE_UDF_TYPE // in - ComRoutineType
, optional_alter_passthrough_inputs_clause // in - shallow copy
, optional_add_passthrough_inputs_clause // in - shallow copy
, optional_function_attribute_list // in - shallow copy
, PARSERHEAP()
);
pNode88->synthesize();
return pNode88;
}
// -----------------------------------------------------------------------
// SqlParserAux_buildDropAction
// -----------------------------------------------------------------------
StmtDDLNode *
SqlParserAux_buildDropAction ( QualifiedName * ddl_qualified_name_of_uudf // in - deep copy
, QualifiedName * ddl_qualified_name_of_action // in - deep copy
, NABoolean optional_cleanup // in
, ComDropBehavior optional_drop_behavior // in
, NABoolean optional_validate // in
, NAString * optional_logfile // in - deep copy
)
{
// If CLEANUP, VALIDATE, or LOG option specified,
// ALLOW_SPECIALTABLETYPE must also be specified
if ( (optional_cleanup OR optional_validate OR optional_logfile) AND
NOT Get_SqlParser_Flags(ALLOW_SPECIALTABLETYPE) )
{
return NULL; // Error: internal syntax only!
}
NAString *pLogFile = NULL;
if (optional_logfile NEQ NULL) // logfile specified
{
pLogFile = new (PARSERHEAP()) NAString ( optional_logfile->data()
, PARSERHEAP()
);
}
ddl_qualified_name_of_uudf->setObjectNameSpace(COM_UDF_NAME);
ddl_qualified_name_of_action->setObjectNameSpace(COM_UUDF_ACTION_NAME);
return new (PARSERHEAP()) StmtDDLDropRoutine
( COM_ACTION_UDF_TYPE // in - ComRoutineType
, *ddl_qualified_name_of_uudf // in - QualifiedName * - deep copy
, *ddl_qualified_name_of_action // in - QualifiedName * - deep copy
, optional_drop_behavior // in - ComDropBehavior
, optional_cleanup // in - NABoolean - for CLEANUP mode set to TRUE
, optional_validate // in - NABoolean - for VALIDATE mode set to FALSE (?)
, pLogFile // in - NAString * - shallow copy
, PARSERHEAP()
);
// Do not delete pLogFile because we did a shallow copy
}
// -----------------------------------------------------------------------
// SqlParserAux_buildDropRoutine
// -----------------------------------------------------------------------
StmtDDLNode *
SqlParserAux_buildDropRoutine ( ComRoutineType drop_routine_type_tokens // in
, QualifiedName * ddl_qualified_name_of_udf // in - deep copy
, NABoolean optional_cleanup // in
, ComDropBehavior optional_drop_behavior // in
, NABoolean optional_validate // in
, NAString * optional_logfile // in - deep copy
, NABoolean optional_if_exists // in
)
{
// If CLEANUP, VALIDATE, or LOG option specified,
// ALLOW_SPECIALTABLETYPE must also be specified
if ( (optional_cleanup OR optional_validate OR optional_logfile) AND
NOT Get_SqlParser_Flags(ALLOW_SPECIALTABLETYPE) )
{
return NULL; // Error: internal syntax only!
}
if (drop_routine_type_tokens EQU COM_ACTION_UDF_TYPE)
{
return NULL; // Error: illegal syntax!
}
QualifiedName noRoutineActionName(PARSERHEAP());
NAString *pLogFile = NULL;
if (optional_logfile NEQ NULL) // logfile specified
{
pLogFile = new (PARSERHEAP()) NAString ( optional_logfile->data()
, PARSERHEAP()
);
}
switch (drop_routine_type_tokens)
{
case COM_PROCEDURE_TYPE:
case COM_TABLE_UDF_TYPE:
ddl_qualified_name_of_udf->setObjectNameSpace(COM_TABLE_NAME);
break;
case COM_ACTION_UDF_TYPE:
ComASSERT(FALSE);
break;
case COM_UNKNOWN_ROUTINE_TYPE: // either scalar or universal
default:
ddl_qualified_name_of_udf->setObjectNameSpace(COM_UDF_NAME);
break;
} // switch
StmtDDLNode *pNode99 = new (PARSERHEAP()) StmtDDLDropRoutine
( drop_routine_type_tokens // in - ComRoutineType
, *ddl_qualified_name_of_udf // in - QualifiedName & - deep copy
, noRoutineActionName // in - QualifiedName & - deep copy
, optional_drop_behavior // in - ComDropBehavior
, optional_cleanup // in - NABoolean - for CLEANUP mode set to TRUE
, optional_validate // in - NABoolean - for VALIDATE mode set to FALSE (?)
, pLogFile // in - NAString * - shallow copy
, PARSERHEAP()
);
pNode99->castToStmtDDLDropRoutine()->setDropIfExists(optional_if_exists);
// Do not delete pLogFile because we did a shallow copy
return pNode99;
}
// -----------------------------------------------------------------------
// SqlParserAux_buildAlterPassThroughParamDef
// -----------------------------------------------------------------------
ElemDDLNode *
SqlParserAux_buildAlterPassThroughParamDef
( UInt32 passthrough_param_position // in
, ElemDDLNode * passthrough_input_value // in - shallow copy
, ComRoutinePassThroughInputType optional_passthrough_input_type // in
)
{
ElemDDLPassThroughParamDef *pNode3 =
passthrough_input_value->castToElemDDLPassThroughParamDef();
pNode3->setParamPosition(passthrough_param_position);
pNode3->setPassThroughInputType(optional_passthrough_input_type);
pNode3->setPassThroughParamDefKind(ElemDDLPassThroughParamDef::eALTER_PASS_THROUGH_INPUT);
return pNode3;
}
// -----------------------------------------------------------------------
// SqlParserAux_buildDescribeForFunctionAndAction
// -----------------------------------------------------------------------
RelExpr *
SqlParserAux_buildDescribeForFunctionAndAction
( CorrName * actual_routine_name_of_udf_or_uudf // in - deep copy
, CorrName * optional_showddl_action_name_clause // in - deep copy
, Lng32 optional_showddlroutine_options // in
)
{
actual_routine_name_of_udf_or_uudf->getQualifiedNameObj().setObjectNameSpace(COM_UDF_NAME);
Describe * pDescribe = NULL;
if (optional_showddl_action_name_clause NEQ NULL) // routine action name
{
optional_showddl_action_name_clause->getQualifiedNameObj().setObjectNameSpace(COM_UUDF_ACTION_NAME);
pDescribe = new (PARSERHEAP())
Describe ( SQLTEXT()
, *optional_showddl_action_name_clause // in - const CorrName & - deep copy
, Describe::SHOWDDL_
, COM_UUDF_ACTION_NAME // in - ComAnsiNameSpace labelAnsiNameSpace_
, optional_showddlroutine_options // in - long optional_showddlroutine_options
);
pDescribe->setUudfQualName(actual_routine_name_of_udf_or_uudf->getQualifiedNameObj()); // deep copy
}
else // function name
{
pDescribe = new (PARSERHEAP())
Describe ( SQLTEXT()
, *actual_routine_name_of_udf_or_uudf // in - const CorrName & - deep copy
, Describe::SHOWDDL_
, COM_UDF_NAME // in - ComAnsiNameSpace labelAnsiNameSpace_
, optional_showddlroutine_options // in - long optional_showddlroutine_options
);
}
return new (PARSERHEAP()) RelRoot ( pDescribe
, REL_ROOT
, new (PARSERHEAP()) ColReference ( new (PARSERHEAP())
ColRefName ( TRUE
, PARSERHEAP()
)
)
);
}
// ----------------------------------------------------------------------------
// method:: TableTokens::setTableTokens
//
// Method that sets appropriate values in the createTableNode parser tree
// based on options described in this class.
//
// in: StmtDDLCreateTable *pNode - pointer to the create table parse tress
//
// ----------------------------------------------------------------------------
void
TableTokens::setTableTokens(StmtDDLCreateTable *pNode)
{
pNode->setCreateIfNotExists(ifNotExistsSet());
switch (type_)
{
case TableTokens::TYPE_REGULAR_TABLE:
case TableTokens::TYPE_GHOST_TABLE:
break;
case TableTokens::TYPE_EXTERNAL_TABLE:
pNode->setIsExternal(TRUE);
break;
case TableTokens::TYPE_IMPLICIT_EXTERNAL_TABLE:
pNode->setIsExternal(TRUE);
pNode->setIsImplicitExternal(TRUE);
break;
case TableTokens::TYPE_SET_TABLE:
pNode->setInsertMode(COM_SET_TABLE_INSERT_MODE);
break;
case TableTokens::TYPE_MULTISET_TABLE:
pNode->setInsertMode(COM_MULTISET_TABLE_INSERT_MODE);
break;
case TableTokens::TYPE_VOLATILE_TABLE:
pNode->setIsVolatile(TRUE);
pNode->setProcessAsExeUtil(TRUE);
break;
case TableTokens::TYPE_VOLATILE_TABLE_MODE_SPECIAL1:
case TableTokens::TYPE_VOLATILE_SET_TABLE:
pNode->setIsVolatile(TRUE);
pNode->setProcessAsExeUtil(TRUE);
pNode->setInsertMode(COM_SET_TABLE_INSERT_MODE);
break;
case TableTokens::TYPE_VOLATILE_MULTISET_TABLE:
pNode->setProcessAsExeUtil(TRUE);
pNode->setInsertMode(COM_MULTISET_TABLE_INSERT_MODE);
break;
default:
NAAbort("TableTokens - TypeAttr", __LINE__, "internal logic error");
break;
}
switch (options_)
{
case TableTokens::OPT_NONE:
break;
case TableTokens::OPT_LOAD:
pNode->setLoadIfExists(TRUE);
break;
case TableTokens::OPT_NO_LOAD:
pNode->setNoLoad(TRUE);
break;
case TableTokens::OPT_IN_MEM:
pNode->setNoLoad(TRUE);
pNode->setInMemoryObjectDefn(TRUE);
break;
case TableTokens::OPT_LOAD_WITH_DELETE:
pNode->setLoadIfExists(TRUE);
pNode->setDeleteData(TRUE);
break;
default:
NAAbort("TableTokens - LoadAttr", __LINE__, "internal logic error");
break;
}
}
//
// End of File
//