| /********************************************************************** |
| // @@@ START COPYRIGHT @@@ |
| // |
| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| // |
| // @@@ END COPYRIGHT @@@ |
| **********************************************************************/ |
| /* -*-C++-*- |
| ****************************************************************************** |
| * |
| * File: SynthType.C |
| * Description: Methods for synthesizing a type |
| * Created: 3/15/95 |
| * Language: C++ |
| * |
| * |
| * |
| * |
| ****************************************************************************** |
| */ |
| |
| #define SQLPARSERGLOBALS_NADEFAULTS |
| |
| #include "Sqlcomp.h" |
| #include "AllItemExpr.h" |
| #include "BindWA.h" |
| #include "CmpStatement.h" |
| #include "CmpErrors.h" |
| #include "ComSqlId.h" |
| #include "OptimizerSimulator.h" |
| #include "exp_datetime.h" |
| |
| #include "ComSSL.h" |
| // For TRIGGERS_STATUS_VECTOR_SIZE and SIZEOF_UNIQUE_EXECUTE_ID |
| #include "Triggers.h" |
| #include "TriggerEnable.h" |
| #ifndef NDEBUG |
| static Int32 NCHAR_DEBUG = -1; // note that, for perf, we call getenv only once |
| #endif |
| |
| #include "SqlParserGlobalsCmn.h" |
| //#define getDefaultCharSet CharInfo::getCharSetEnum(ActiveSchemaDB()->getDefaults().getValue(DEFAULT_CHARSET)) |
| #define getDefaultCharSet SqlParser_DEFAULT_CHARSET |
| |
| // ----------------------------------------------------------------------- |
| // utility functions -- cosmetics of error message arguments |
| // ----------------------------------------------------------------------- |
| |
| // These just shorten error messages by removing irrelevant junk |
| static void shortenTypeSQLname(NAString &typStr) |
| { |
| size_t i = typStr.index('('); |
| if (i && i != NA_NPOS) |
| typStr.remove(i); // remove from lparen onward |
| i = typStr.index(' '); |
| if (i && i != NA_NPOS) |
| typStr.remove(i); // remove from space onward |
| } |
| |
| static void shortenTypeSQLname(const NAType &op, |
| NABuiltInTypeEnum typEnum, |
| NAString &typStr) |
| { |
| if (op.getTypeQualifier() == typEnum) { |
| size_t i = typStr.index(')'); |
| if (i != NA_NPOS) |
| i = typStr.index(" ", i); // find space AFTER the parens |
| else |
| i = typStr.index(' '); // find first space (no parens) |
| if (i && i != NA_NPOS) |
| typStr.remove(i); // remove from space onward |
| } |
| } |
| |
| void emitDyadicTypeSQLnameMsg(Lng32 sqlCode, |
| const NAType &op1, |
| const NAType &op2, |
| const char *str1 = NULL, |
| const char *str2 = NULL, |
| ComDiagsArea * diagsArea = NULL, |
| const Lng32 int1 = -999999) |
| { |
| NAString tsn1(op1.closestEquivalentExternalType( HEAP )->getTypeSQLname(TRUE /*terse*/)); |
| NAString tsn2(op2.closestEquivalentExternalType( HEAP )->getTypeSQLname(TRUE /*terse*/)); |
| NABoolean charShorten = TRUE, numShorten = TRUE; |
| |
| if (op1.getTypeQualifier() == NA_CHARACTER_TYPE && |
| op2.getTypeQualifier() == NA_CHARACTER_TYPE) { |
| |
| const CharType &ct1 = (CharType&)op1; |
| const CharType &ct2 = (CharType&)op2; |
| NABoolean csDiff = (ct1.getCharSet() != ct2.getCharSet() || |
| ct1.getCharSet() == CharInfo::UnknownCharSet); |
| NABoolean coDiff = (ct1.getCollation() != ct2.getCollation() || |
| ct1.getCollation() == CharInfo::UNKNOWN_COLLATION); |
| |
| if (csDiff) |
| charShorten = FALSE; // leave the CHARACTER SET info as is in tsnX |
| else if (coDiff) { // add COLLATE info to the type-texts |
| tsn1 += NAString(" ") + ct1.getCollateClause(ct1.getCollation()); |
| tsn2 += NAString(" ") + ct2.getCollateClause(ct2.getCollation()); |
| charShorten = FALSE; |
| } |
| // else, do do the charShorten thing |
| |
| numShorten = FALSE; // both args are character: shorten is a no-op |
| } |
| else if (op1.getTypeQualifier() == NA_NUMERIC_TYPE && |
| op2.getTypeQualifier() == NA_NUMERIC_TYPE) { |
| if (!str1 || strcmp(str1, "||")) // If a CONCAT, we do shorten; else: |
| numShorten = FALSE; // do not shorten, we need distinguishing info |
| charShorten = FALSE; // both args are numeric: shorten is a no-op |
| } |
| |
| if (charShorten) { // "CHAR(8) CHARACTER SET UNICODE" => "CHAR(8)" |
| shortenTypeSQLname(op1, NA_CHARACTER_TYPE, tsn1); |
| shortenTypeSQLname(op2, NA_CHARACTER_TYPE, tsn2); |
| } |
| if (numShorten) { // "NUMERIC(8, 2) SIGNED" => "NUMERIC(8, 2)" |
| shortenTypeSQLname(op1, NA_NUMERIC_TYPE, tsn1); |
| shortenTypeSQLname(op2, NA_NUMERIC_TYPE, tsn2); |
| } |
| |
| ComDiagsArea * da = (diagsArea ? diagsArea : CmpCommon::diags()); |
| *da << DgSqlCode(sqlCode); |
| switch (sqlCode) { |
| case -4034: |
| CMPASSERT(str1); |
| *da |
| << DgString0(tsn1) |
| << DgString1(str1) |
| << DgString2(tsn2) |
| << DgString3(str2 ? str2 : ""); |
| break; |
| case -4039: |
| CMPASSERT(str1); |
| *da |
| << DgColumnName(str1) |
| << DgString0(tsn1) |
| << DgString1(tsn2); |
| break; |
| case arkcmpErrorISPWrongDataType: // -19016 |
| CMPASSERT(str1); |
| CMPASSERT(int1 != -999999); |
| *da |
| << DgString0(str1) |
| << DgString1(tsn1) |
| << DgInt0(int1) |
| << DgString2(tsn2); |
| break; |
| default: |
| *da |
| << DgString0(tsn1) |
| << DgString1(tsn2); |
| } |
| |
| } // emitDyadicTypeSQLnameMsg |
| |
| |
| // ----------------------------------------------------------------------- |
| // helper functions for *Type::isComparable() methods |
| // ----------------------------------------------------------------------- |
| |
| static inline NABoolean involvesEQorNE(const OperatorType &opType) |
| { |
| return opType.match(ITM_WILDCARD_EQ_NE) || |
| OperatorTypeEnum(opType) == ITM_LIKE || |
| OperatorTypeEnum(opType) == ITM_LIKE_DOUBLEBYTE || |
| OperatorTypeEnum(opType) == ITM_POSITION || |
| OperatorTypeEnum(opType) == ITM_REPLACE || |
| OperatorTypeEnum(opType) == ITM_REPLACE_UNICODE || |
| OperatorTypeEnum(opType) == ITM_TRIM || // == ' ' (space char) |
| OperatorTypeEnum(opType) == ITM_VALUEIDUNION; // when DISTINCT flag |
| } |
| |
| static ItemExpr * propagateCoAndCoToItem(ItemExpr *ie, |
| CharInfo::Collation co, |
| CharInfo::Coercibility ce) |
| { |
| // The special check is here for isAUserSuppliedInput() |
| // because we cannot directly mutate the type of a |
| // ConstValue, HostVar, DynamicParam, or AnsiUSERFunction |
| // because their bindNode's call ItemExpr::bindUserInput() |
| // which makes multiple refs to an input all map to |
| // (all share) the SAME valueId. |
| // |
| // Here we only want to mutate THIS ref's type, |
| // or, equivalently, CAST a leaf type to the new type -- |
| // where leaves are basic items the Binder does lookup on -- |
| // ColRefs (Base/IndexColumns), ConstValues, HostVars, DynamicParams. |
| // In these lookups we do NOT want to change the leaf's original ValueId |
| // NOR its original type -- hence, we CAST. |
| // It is safe not to do an additional bindNode (we have no bindWA anyway!) |
| // because we KNOW the new type is okay! |
| |
| CharType *ct = (CharType *)&ie->getValueId().getType(); |
| CMPASSERT(ct->getTypeQualifier() == NA_CHARACTER_TYPE); |
| if (ct->getCollation() != co) { |
| |
| #ifndef NDEBUG |
| if (NCHAR_DEBUG > 0) |
| cerr << "CMP--:\t" |
| << ct->getCollation() << ',' << ct->getCoercibility() << ' ' |
| << co << ',' << ce |
| << '\t' << ie->getText() |
| << '\t' << ie->getValueId(); |
| |
| CMPASSERT(co != CharInfo::UNKNOWN_COLLATION); // sanity check |
| CMPASSERT(ce != CharInfo::NO_COLLATING_SEQUENCE); // sanity check |
| #endif |
| |
| ct = (CharType *)ct->newCopy(HEAP); |
| ct->setCoAndCo(co, ce); |
| |
| // add a cast to the right type |
| // (since the child may be shared by other ItemExprs we can't |
| // simply change it, but we might someday be able to do an |
| // optimization similar to what ICAT does) |
| ie = new HEAP Cast(ie, ct); |
| ie->synthTypeAndValueId(); |
| |
| #ifndef NDEBUG |
| if (NCHAR_DEBUG > 0) |
| cerr << '\t' << ie->getValueId() |
| << endl; |
| #endif |
| |
| } // collation of operand overridden |
| |
| return ie; |
| } |
| |
| static void propagateCoAndCoToChildren(ItemExpr *parentOp, |
| CharInfo::Collation co, |
| CharInfo::Coercibility ce) |
| { |
| // Propagate the new co & ce to the immediate operands -- |
| // yes, a shallow propagation is what we want. |
| |
| #ifndef NDEBUG |
| // Just double-check that Cast::synthType() is NOT calling here... |
| // If it did, we would rewrite its child and the Cast would get optimized |
| // away as a no-op. |
| CMPASSERT(!parentOp->getOperator().match(ITM_ANY_CAST)); |
| #endif |
| |
| for (Int32 i = 0; i < parentOp->getArity(); i++) { |
| ItemExpr *ie = parentOp->child(i); |
| if (ie && |
| ie->getValueId().getType().getTypeQualifier() == NA_CHARACTER_TYPE) |
| parentOp->child(i) = propagateCoAndCoToItem(ie, co, ce); |
| } |
| } |
| |
| static Int32 getNumCHARACTERArgs(ItemExpr *parentOp) |
| { |
| Int32 n = 0; |
| for (Int32 i = 0; i < parentOp->getArity(); i++) { |
| ItemExpr *ie = parentOp->child(i); |
| if (ie && |
| ie->getValueId().getType().getTypeQualifier() == NA_CHARACTER_TYPE) |
| n++; |
| } |
| return n; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // The virtual NAType::isComparable() methods -- implemented here rather than |
| // ../common/*Type.cpp because they're called only from here, and here |
| // we've embedded stuff like ../optimizer/ItemExpr methods and |
| // the ../arkcmp/CmpCommon global-diags-area. |
| // ----------------------------------------------------------------------- |
| |
| NABoolean NAType::isComparable(const NAType &other, |
| ItemExpr *parentOp, |
| Int32 emitErr, |
| UInt32 * flags) const |
| { |
| #ifndef NDEBUG |
| CMPASSERT(parentOp); //## reserved for future errmsg 4034 w/ unparse, |
| #endif // for CoAndCo propagation and for errmsgs! |
| |
| if (isCompatible(other, flags)) |
| return TRUE; |
| NAString defVal; |
| NABoolean charsetInference = |
| (CmpCommon::getDefault(INFER_CHARSET, defVal) == DF_ON); |
| if(charsetInference && |
| getTypeQualifier() == NA_CHARACTER_TYPE && |
| other.getTypeQualifier() == NA_CHARACTER_TYPE){ // do not reject matches of UNKNOWN_CHARSET with UNKNOWN_CHARSET |
| CharType *ct = (CharType *)this; |
| if (ct->isCompatibleAllowUnknownCharset(other)) |
| return TRUE; |
| } |
| |
| if (emitErr == EmitErrIfAnyChar) |
| if (getTypeQualifier() != NA_CHARACTER_TYPE && |
| other.getTypeQualifier() != NA_CHARACTER_TYPE) |
| emitErr = FALSE; |
| |
| if (emitErr) { |
| // 4041 Type $1 cannot be compared with type $2. |
| //10-070228-2913 -Begin |
| |
| //When MODE_SPECIAL_1 'ON', UNICODE CHARSET, and UPPER |
| //function is involved data type is converted to VARCHAR(dataStorageSize). |
| //dataSotragesize = getMaxCharLen() * bytesPerChar. |
| //see in Upper::synthesizeType() method. |
| //When generating error condition we convert it back to original size. |
| if ( getTypeQualifier() == NA_CHARACTER_TYPE) |
| { |
| CharType &ct1 = (CharType&)*this; |
| if((ct1.isCaseinsensitive()) && |
| (ct1.getCharSet() == CharInfo::UNICODE) && |
| (parentOp->child(0)->castToItemExpr()->getOperatorType() == ITM_UPPER)) |
| { |
| ct1.setDataStorageSize(ct1.getDataStorageSize()/3); |
| } |
| //10-070228-2913 -End |
| } |
| |
| emitDyadicTypeSQLnameMsg(-4041, *this, other); |
| } |
| return FALSE; |
| } |
| |
| NABoolean CharType::isComparable(const NAType &otherNA, |
| ItemExpr *parentOp, |
| Int32 emitErr, |
| UInt32 * flags) const |
| { |
| if (NOT NAType::isComparable(otherNA, parentOp, emitErr, flags)) |
| return FALSE; |
| |
| const CharType &other = (const CharType &)otherNA; |
| |
| CharInfo::Collation co; |
| CharInfo::Coercibility ce; |
| computeCoAndCo(other, co, ce); |
| NABoolean cmpOK = (co != CharInfo::UNKNOWN_COLLATION); |
| |
| if (emitErr) emitErr = +1; // for fall-thru msg suppression |
| if (cmpOK) { |
| |
| // a "mini-cache" to avoid proc call, for perf |
| static THREAD_P CharInfo::Collation cachedCO = CharInfo::UNKNOWN_COLLATION; |
| static THREAD_P Int32 cachedFlags = CollationInfo::ALL_NEGATIVE_SYNTAX_FLAGS; |
| |
| if (cachedCO != co) { // use the mini-cache |
| cachedCO = co; |
| cachedFlags = CharInfo::getCollationFlags(co); |
| } |
| |
| if (involvesEQorNE(parentOp->getOperator())) { |
| if (cachedFlags & CollationInfo::EQ_NE_CMP_ILLEGAL) |
| cmpOK = FALSE; |
| } |
| else if (cachedFlags & CollationInfo::ORDERED_CMP_ILLEGAL) |
| cmpOK = FALSE; |
| |
| if (!cmpOK && emitErr > 0) { |
| // 4044 Collation $0~String0 does not support the $1~String1 operator. |
| *CmpCommon::diags() << DgSqlCode(-4044) |
| << DgString0(CharInfo::getCollationName(co)) |
| << DgString1(parentOp->getTextUpper()); |
| |
| emitErr = -1; // We fall thru but do not also emit error 4041. |
| } |
| } // additional collation flag checks |
| |
| if (cmpOK) |
| propagateCoAndCoToChildren(parentOp, co, ce); // type-synth/propagate! |
| else { |
| if (emitErr > 0) |
| // 4041 Type $1 cannot be compared with type $2. |
| emitDyadicTypeSQLnameMsg(-4041, *this, other); |
| |
| // if (emitErr) // +1 OR -1 |
| // //## also emit errmsg 4034 w/ unparse? |
| } |
| |
| #ifndef NDEBUG |
| if (NCHAR_DEBUG < 0) NCHAR_DEBUG = getenv("NCHAR_DEBUG") ? +1 : 0; |
| if (NCHAR_DEBUG > 0) { |
| NAString p(CmpCommon::statementHeap()); |
| parentOp->unparse(p); |
| NAString s(getTypeSQLname(TRUE /*terse*/)); |
| s += NAString(" ") + getCollateClause(getCollation()); |
| cerr << "CMP" << (cmpOK ? "==" : "<>") << ":\t" |
| << (Int32)parentOp->getOperatorType() << '\t' |
| << p << endl |
| << s << '\t' << getCoercibilityText(getCoercibility()) |
| << endl; |
| |
| s = other.getTypeSQLname(TRUE /*terse*/); |
| s += NAString(" ") + other.getCollateClause(other.getCollation()); |
| cerr << s << '\t' << other.getCoercibilityText(other.getCoercibility()) |
| << endl; |
| |
| cerr << CharInfo::getCollationName(co) << '\t' |
| << CharType::getCoercibilityText(ce) |
| << endl; |
| if (!cmpOK) |
| cerr << endl; |
| } |
| #endif |
| |
| return cmpOK; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // additional, miscellaneous helper functions |
| // ----------------------------------------------------------------------- |
| |
| // Called by BiRelat and QuantifiedComp comparison predicates. |
| static NABoolean synthItemExprLists(ItemExprList &exprList1, |
| ItemExprList &exprList2, |
| NABoolean allowIncompatibleComparison, |
| NABoolean &allowsUnknown, |
| ItemExpr *parentOp) |
| { |
| if (exprList1.entries() != exprList2.entries()) { |
| // 4042 The operands of a comparison predicate must be of equal degree. |
| *CmpCommon::diags() << DgSqlCode(-4042); |
| return FALSE; |
| } |
| |
| NABoolean ODBC = (CmpCommon::getDefault(ODBC_PROCESS) == DF_ON); |
| NABoolean JDBC = (CmpCommon::getDefault(JDBC_PROCESS) == DF_ON); |
| allowsUnknown = FALSE; |
| for (CollIndex i = 0; i < exprList1.entries(); i++) { |
| // |
| // Type cast any params. |
| // |
| ValueId vid1 = exprList1[i]->getValueId(); |
| ValueId vid2 = exprList2[i]->getValueId(); |
| |
| NABoolean dummy; |
| if (vid1.getType().getTypeQualifier() != NA_UNKNOWN_TYPE && |
| vid2.getType().getTypeQualifier() == NA_UNKNOWN_TYPE && |
| vid2.getItemExpr()->getOperatorType() == ITM_CONSTANT && |
| (vid2.getItemExpr()->castToConstValue(dummy))->isNull()) |
| { |
| vid2.coerceType(vid1.getType()); |
| } |
| else |
| if (vid2.getType().getTypeQualifier() != NA_UNKNOWN_TYPE && |
| vid1.getType().getTypeQualifier() == NA_UNKNOWN_TYPE && |
| vid1.getItemExpr()->getOperatorType() == ITM_CONSTANT && |
| (vid1.getItemExpr()->castToConstValue(dummy))->isNull()) |
| { |
| vid1.coerceType(vid2.getType()); |
| } |
| |
| // if this query is being processed for ODBC, then type cast param |
| // to have the same type as the other side of birelat op. Otherwise, |
| // give param the default type if the other side is an exact numeric. |
| if ((NOT ODBC) && (NOT JDBC)) { |
| // give param the default type if the other side is an exact numeric. |
| if (vid1.getType().getTypeQualifier() == NA_UNKNOWN_TYPE && |
| vid2.getType().getTypeQualifier() == NA_NUMERIC_TYPE) { |
| // if op1 is a param with unknown type and |
| // op2 is an exact numeric, type cast op1 to the default numeric type |
| const NumericType& op2 = (NumericType&)vid2.getType(); |
| if (op2.isExact()) |
| vid1.coerceType(NA_NUMERIC_TYPE); |
| } |
| else if (vid2.getType().getTypeQualifier() == NA_UNKNOWN_TYPE && |
| vid1.getType().getTypeQualifier() == NA_NUMERIC_TYPE) { |
| const NumericType& op1 = (NumericType&)vid1.getType(); |
| if (op1.isExact()) |
| vid2.coerceType(NA_NUMERIC_TYPE); |
| }; |
| |
| vid1.coerceType(vid2.getType(), NA_NUMERIC_TYPE); |
| } |
| else { |
| // coerce to default character type(VARCHAR(32)) for ODBC. |
| vid1.coerceType(vid2.getType(), NA_CHARACTER_TYPE); |
| } |
| |
| vid2.coerceType(vid1.getType()); |
| // |
| // Check that the operands are comparable. |
| // |
| const NAType *operand1 = &vid1.getType(); |
| const NAType *operand2 = &vid2.getType(); |
| |
| NABoolean DoCompatibilityTest = TRUE; |
| |
| NAString defVal; |
| |
| if ( operand1->getTypeQualifier() == NA_CHARACTER_TYPE && |
| operand2->getTypeQualifier() == NA_CHARACTER_TYPE |
| ) |
| { |
| if ( CmpCommon::getDefault(ALLOW_IMPLICIT_CHAR_CASTING) == DF_ON ) |
| { |
| DoCompatibilityTest = FALSE; |
| // |
| // NOTE: The Generator has code to throw in a Translate node if an |
| // incompatible character set comparison is attempted. |
| // |
| } |
| const CharType *charOp1 = (CharType*)&(vid1.getType()); |
| const CharType *charOp2 = (CharType*)&(vid2.getType()); |
| |
| NABoolean charsetInference = |
| (CmpCommon::getDefault(INFER_CHARSET, defVal) == DF_ON); |
| |
| if ( charsetInference ) { |
| |
| const CharType* desiredType = |
| CharType::findPushDownCharType(getDefaultCharSet, |
| charOp1, charOp2, 0); |
| |
| if ( desiredType ) |
| { |
| // just push down the charset field. All other fields are |
| // meaningless. |
| vid1.coerceType((NAType&)*desiredType, NA_CHARACTER_TYPE); |
| vid2.coerceType((NAType&)*desiredType, NA_CHARACTER_TYPE); |
| |
| } |
| } |
| |
| // get the newly pushed-down/relaxed types |
| operand1 = &vid1.getType(); |
| operand2 = &vid2.getType(); |
| |
| if ( DoCompatibilityTest && NOT operand1->isCompatible(*operand2) ) { |
| // 4041 comparison between these two types is not allowed |
| emitDyadicTypeSQLnameMsg(-4041, *operand1, *operand2); |
| return FALSE; |
| } |
| } |
| |
| |
| allowsUnknown = allowsUnknown OR |
| operand1->supportsSQLnullLogical() OR |
| operand2->supportsSQLnullLogical(); |
| |
| if (allowIncompatibleComparison) |
| { |
| // incompatible conversion is only allowed between: |
| // 1. char and numeric types. |
| // 2. char literal and date types |
| |
| // Or for special_1 mode: |
| // 3. DATE and numeric. Date is an interval from year 1900. |
| // 4. interval and numeric. |
| |
| // Check if this is char and numeric comparison |
| if (((operand1->getTypeQualifier() == NA_CHARACTER_TYPE) && |
| (operand2->getTypeQualifier() == NA_NUMERIC_TYPE) && |
| ((((CharType*)operand1)->getCharSet() == CharInfo::ISO88591) || |
| (((CharType*)operand1)->getCharSet() == CharInfo::UTF8))) || |
| ((operand1->getTypeQualifier() == NA_NUMERIC_TYPE) && |
| (operand2->getTypeQualifier() == NA_CHARACTER_TYPE) && |
| ((((CharType*)operand2)->getCharSet() == CharInfo::ISO88591) || |
| (((CharType*)operand2)->getCharSet() == CharInfo::UTF8)))) |
| { |
| return TRUE; |
| } |
| |
| // Check if this is char and date comparison |
| if (((operand1->getTypeQualifier() == NA_CHARACTER_TYPE) && |
| (operand2->getTypeQualifier() == NA_DATETIME_TYPE) && |
| ((((CharType*)operand1)->getCharSet() == CharInfo::ISO88591) || |
| (((CharType*)operand1)->getCharSet() == CharInfo::UTF8))) || |
| ((operand1->getTypeQualifier() == NA_DATETIME_TYPE) && |
| (operand2->getTypeQualifier() == NA_CHARACTER_TYPE) && |
| ((((CharType*)operand2)->getCharSet() == CharInfo::ISO88591) || |
| (((CharType*)operand2)->getCharSet() == CharInfo::UTF8)))) |
| |
| { |
| return TRUE; |
| } |
| |
| if (CmpCommon::getDefault(MODE_SPECIAL_1) == DF_ON) |
| { |
| // Check if this is numeric literal and date comparison |
| if (((operand1->getTypeQualifier() == NA_NUMERIC_TYPE) && |
| (operand2->getTypeQualifier() == NA_DATETIME_TYPE)) || |
| ((operand1->getTypeQualifier() == NA_DATETIME_TYPE) && |
| (operand2->getTypeQualifier() == NA_NUMERIC_TYPE))) |
| { |
| NumericType *numOper; |
| DatetimeType *dtOper; |
| if (operand1->getTypeQualifier() == NA_NUMERIC_TYPE) |
| { |
| numOper = &(NumericType&)vid1.getType(); |
| dtOper = &(DatetimeType&)vid2.getType(); |
| } |
| else |
| { |
| numOper = &(NumericType&)vid2.getType(); |
| dtOper = &(DatetimeType&)vid1.getType(); |
| } |
| |
| // make sure it is DATE to exact NUMERIC with scale |
| // of 0 comparison. |
| if ((numOper->isExact()) && |
| (numOper->getScale() == 0) && |
| (dtOper->getPrecision() == SQLDTCODE_DATE)) |
| return TRUE; |
| } |
| |
| // Check if this is numeric literal and interval comparison |
| if (((operand1->getTypeQualifier() == NA_NUMERIC_TYPE) && |
| (operand2->getTypeQualifier() == NA_INTERVAL_TYPE) && |
| (vid1.getItemExpr()->getOperatorType() == ITM_CONSTANT)) || |
| ((operand1->getTypeQualifier() == NA_INTERVAL_TYPE) && |
| (operand2->getTypeQualifier() == NA_NUMERIC_TYPE) && |
| (vid2.getItemExpr()->getOperatorType() == ITM_CONSTANT))) |
| { |
| IntervalType* interval; |
| const NumericType* numeric; |
| if (operand1->getTypeQualifier() == NA_NUMERIC_TYPE) |
| { |
| numeric = &(NumericType&)vid1.getType(); |
| interval = &(IntervalType&)vid2.getType(); |
| } |
| else |
| { |
| numeric = &(NumericType&)vid2.getType(); |
| interval = &(IntervalType&)vid1.getType(); |
| } |
| |
| // make sure it is exact NUMERIC with scale |
| // of 0 comparison. |
| if ((numeric->isExact()) && |
| (numeric->getScale() == 0) && |
| (interval->getFractionPrecision() == 0)) |
| return TRUE; |
| } |
| } |
| } |
| |
| UInt32 flags = 0; |
| if (CmpCommon::getDefault(ALLOW_INCOMPATIBLE_OPERATIONS) == DF_ON) |
| { |
| flags |= NAType::ALLOW_INCOMP_OPER; |
| } |
| |
| //## errmsg 4034 w/ unparse? |
| if ( DoCompatibilityTest && |
| NOT operand1->isComparable(*operand2, parentOp, |
| NAType::EmitErrAlways, &flags) ) |
| return FALSE; |
| } |
| return TRUE; // success |
| |
| } // synthItemExprLists |
| |
| static const NAType *synthAvgSum(const NAType& operand, |
| NABoolean inScalarGroup) |
| { |
| |
| NABoolean aggNeedsToBeNullable; |
| aggNeedsToBeNullable = operand.supportsSQLnullPhysical() || inScalarGroup; |
| |
| switch (operand.getTypeQualifier()) { |
| case NA_NUMERIC_TYPE: |
| { |
| NumericType * const type = (NumericType * const ) &operand; |
| if(type->isExact()) |
| { |
| ///////////////////////////////////////////////////////////////////// |
| // Rules to compute datatype, precision and scale of SUM/AVG for |
| // EXACT numerics. |
| // |
| // Precision and Datatype computation: |
| // |
| // If precision is less than 19, make it LargeInt (Int64). |
| // |
| // If precision is > 19, make result precision = operand precision |
| // + 10 and result to be BigNum datatype. |
| // |
| // Scale computation: |
| // Result scale is always equal to operand's scale. |
| // |
| // Result is also signed if operand is signed. |
| // |
| ///////////////////////////////////////////////////////////////////// |
| |
| Lng32 precision = (type->getMagnitude() + 9) / 10 + type->getScale(); |
| Lng32 scale = type->getScale(); |
| NABoolean isARealBigNum = FALSE; |
| if (type->isBigNum()) |
| { |
| // make the max precision to be the precision of operand + 10. |
| // Just a nice, round number. |
| // |
| // It could also be always made to |
| // be MAX_NUMERIC_PRECISION_ALLOWED. But that would mean that |
| // the result is always NUMERIC(128) which may be too much |
| // for all aggregates. We can think about it. |
| precision = MINOF(precision + 10, |
| (Lng32)CmpCommon::getDefaultNumeric(MAX_NUMERIC_PRECISION_ALLOWED)); |
| |
| isARealBigNum = ((SQLBigNum*)type)->isARealBigNum(); |
| } |
| else |
| { |
| NABoolean limitPrecision = |
| (CmpCommon::getDefault(LIMIT_MAX_NUMERIC_PRECISION) == DF_ON); |
| |
| if (precision <= MAX_NUMERIC_PRECISION) |
| precision = MAX_NUMERIC_PRECISION; |
| |
| if (limitPrecision) |
| { |
| if (precision > MAX_NUMERIC_PRECISION) |
| { |
| precision = MAX_NUMERIC_PRECISION; |
| } |
| } else { |
| |
| if ( precision >= MAX_NUMERIC_PRECISION + 1 ) |
| precision = MINOF(precision + 10, |
| (Lng32)CmpCommon::getDefaultNumeric(MAX_NUMERIC_PRECISION_ALLOWED)); |
| } |
| } |
| |
| if ((NOT type->isBigNum()) && |
| (precision <= MAX_NUMERIC_PRECISION)) |
| { |
| if (scale > 0) |
| return new HEAP |
| SQLNumeric(HEAP, 8, // length = 8 bytes |
| precision, |
| scale, |
| (precision > 9 ? TRUE : type->isSigned()), |
| aggNeedsToBeNullable); |
| |
| else |
| return new HEAP SQLLargeInt(HEAP, TRUE, aggNeedsToBeNullable); |
| |
| } |
| else |
| { |
| return new HEAP |
| SQLBigNum(HEAP, precision, |
| scale, |
| isARealBigNum, |
| type->isSigned(), |
| aggNeedsToBeNullable); |
| } |
| } |
| else |
| { |
| return new HEAP SQLDoublePrecision(HEAP, aggNeedsToBeNullable); |
| } |
| } |
| break; |
| |
| case NA_INTERVAL_TYPE: |
| { |
| IntervalType * const type = (IntervalType * const ) &operand; |
| if (type->isSupportedType()) |
| { |
| return new HEAP |
| SQLInterval(HEAP, aggNeedsToBeNullable, |
| type->getStartField(), |
| type->computeLeadingPrecision(type->getStartField(), |
| MAX_NUMERIC_PRECISION, |
| type->getEndField(), |
| type->getFractionPrecision()), |
| type->getEndField(), |
| type->getFractionPrecision()); |
| } |
| // else fall through to error |
| } |
| break; |
| } |
| |
| // 4038 The operand of an AVG or SUM function must be numeric or interval. |
| *CmpCommon::diags() << DgSqlCode(-4038); |
| return NULL; |
| } |
| |
| // |
| // getFirstKnownCharSet() - get CharSet of first vid that has one. |
| // |
| CharInfo::CharSet getFirstKnownCharSet( ValueId vid1, ValueId vid2, ValueId vid3) |
| { |
| CharInfo::CharSet first_cs = CharInfo::ISO88591; // Default to ISO88591 |
| |
| if ( CmpCommon::getDefault(ALLOW_IMPLICIT_CHAR_CASTING) == DF_ON ) |
| { |
| const NAType *otyp1 = &(vid1.getType()); |
| const NAType *otyp2 = &(vid2.getType()); |
| const NAType *otyp3 = &(vid3.getType()); |
| if (otyp1->getTypeQualifier() == NA_CHARACTER_TYPE) |
| first_cs = ((CharType *)otyp1)->getCharSet(); |
| else if (otyp2->getTypeQualifier() == NA_CHARACTER_TYPE) |
| first_cs = ((CharType *)otyp2)->getCharSet(); |
| else if (otyp3->getTypeQualifier() == NA_CHARACTER_TYPE) |
| first_cs = ((CharType *)otyp3)->getCharSet(); |
| } |
| return first_cs; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class ItemExpr |
| // ----------------------------------------------------------------------- |
| |
| const NAType *ItemExpr::synthTypeWithCollateClause(BindWA *bindWA, |
| const NAType *type) |
| { |
| // First, call the VIRTUAL method, if we were not passed a type to use |
| if (!type) // e.g. ColReference::bindNode passes a type |
| type = synthesizeType(); |
| |
| if (type && collateClause()) { |
| CharInfo::Collation co = collateClause()->collation_; |
| CharInfo::Coercibility ce = collateClause()->coercibility_; |
| collateClause() = NULL; |
| CMPASSERT(ce == CharInfo::EXPLICIT); // else, Parser screwed up? |
| |
| const ColumnDescList *cols = NULL; |
| if (isASubquery()) |
| cols = ((Subquery *)this)->getRETDesc()->getColumnList(); |
| |
| if (type->getTypeQualifier() != NA_CHARACTER_TYPE) { |
| // 4034 The operation (operand COLLATE coll-name) is not allowed. |
| NAString optext(CmpCommon::statementHeap()); |
| unparse(optext, DEFAULT_PHASE, USER_FORMAT_DELUXE); |
| if (isASubquery()) { |
| // Cosmetics: convert 'SCAN C.S.T' to '(SELECT a,b FROM C.S.T)' |
| NAString x(optext, CmpCommon::statementHeap()); |
| x.remove(5); //offset of "SCAN " |
| x.toUpper(); |
| if (x == "SCAN " || x == "SCAN(") { |
| x = NAString("(SELECT ") + cols->getColumnDescListAsString() + |
| " FROM "; |
| optext.remove(0,5); |
| optext.prepend(x); |
| optext += ")"; |
| } |
| } |
| *CmpCommon::diags() << DgSqlCode(-4034) |
| << DgString0(optext) |
| << DgString1("COLLATE") |
| << DgString2(CharInfo::getCollationName(co)); |
| |
| // 4073 COLLATE may not appear after a $string0 type expression. |
| NAString typnam(type->getTypeSQLname(TRUE/*terse*/)); |
| shortenTypeSQLname(typnam); |
| if (typnam.length() == 9) { //len("SQLRecord") |
| // Cosmetics: convert 'SQLRecord' to 'NON-SCALAR' |
| NAString x(typnam, CmpCommon::statementHeap()); |
| x.toUpper(); |
| if (x == "SQLRECORD") typnam="NON-SCALAR"; |
| } |
| *CmpCommon::diags() << DgSqlCode(-4073) |
| << DgString0(typnam); |
| |
| type = NULL; |
| } |
| else if (co == CharInfo::UNKNOWN_COLLATION && |
| bindWA->getCurrentScope()->context()->inOrderBy()) { |
| // 3169 $0~string0 is not a known collation. |
| *CmpCommon::diags() << DgSqlCode(-3169) |
| << DgString0(CharInfo::getCollationName(co)); |
| type = NULL; |
| } |
| else { |
| // Consider |
| // ('a' COLLATE AAA || 'b') COLLATE BBB |
| // 'a' is AAA/EXPLICIT, so the concat within the parens is too -- |
| // but ANSI 6.13 SR 4(a)(i) says that outside the parens we override |
| // the inner EXPLICIT, so the expression outside the parens |
| // is BBB/EXPLICIT. No computeCoAndCo() needed, simply setCoAndCo(). |
| // |
| type = type->newCopy(HEAP); |
| CharType *ct = (CharType *)type; |
| ct->setCoAndCo(co, ce); |
| if (!ct->isCharSetAndCollationComboOK()) { |
| // 3179 Collation $0 is not defined on character set $1. |
| *CmpCommon::diags() << DgSqlCode(-3179) |
| << DgString0(CharInfo::getCollationName(ct->getCollation())) |
| << DgString1(CharInfo::getCharSetName(ct->getCharSet())); |
| type = NULL; |
| } |
| |
| if (isASubquery()) { |
| CMPASSERT(cols->entries() == 1); |
| ColumnDesc *col = cols->at(0); |
| ItemExpr *ie = propagateCoAndCoToItem( |
| col->getValueId().getItemExpr(), co, ce); |
| col->setValueId(ie->getValueId()); |
| } |
| } |
| } |
| |
| if (!type) bindWA->setErrStatus(); |
| return type; |
| } |
| |
| // This is the virtual method for ItemExpr's that do not define their own |
| const NAType *ItemExpr::synthesizeType() |
| { |
| if (getArity() > 0) |
| return &child(0)->castToItemExpr()->getValueId().getType(); |
| return new HEAP SQLUnknown(HEAP); |
| } |
| |
| // Propagate type information down the ItemExpr tree. |
| // Called by coerceType(). The default implementation |
| // does nothing. Currently is only redefined by ValueIdUnion |
| // to propagate the desired type to the sources of the ValueIdUnion. |
| // |
| const NAType * |
| ItemExpr::pushDownType(NAType& desiredType, |
| enum NABuiltInTypeEnum defaultQualifier) |
| { |
| for(CollIndex i = 0; i < getArity(); i++) { |
| child(i) -> getValueId().coerceType(desiredType, defaultQualifier); |
| } |
| |
| return (NAType *)synthesizeType(); |
| //return &desiredType; |
| } |
| |
| const NAType * |
| Cast::pushDownType(NAType& desiredType, |
| enum NABuiltInTypeEnum defaultQualifier) |
| { |
| for(CollIndex i = 0; i < getArity(); i++) { |
| child(i) -> getValueId().coerceType(desiredType, defaultQualifier); |
| } |
| |
| if (getType()->getTypeQualifier() == NA_UNKNOWN_TYPE && |
| desiredType.getTypeQualifier() != NA_UNKNOWN_TYPE) |
| { |
| type_ = desiredType.newCopy(HEAP); |
| } |
| |
| return (NAType *)synthesizeType(); |
| } |
| |
| void ItemExpr::coerceChildType(NAType& desiredType, |
| enum NABuiltInTypeEnum defaultQualifier) |
| { |
| for(CollIndex i = 0; i < getArity(); i++) { |
| child(i) -> getValueId().coerceType(desiredType, defaultQualifier); |
| } |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class BuiltinFunction. |
| // This methods is for those functions which are not defined as a |
| // derived class or do not have a derived synthesizeType method. |
| // This method should not be called from any derived class's |
| // synthesizeType method. |
| // ----------------------------------------------------------------------- |
| const NAType *BuiltinFunction::synthesizeType() |
| { |
| NAType * retType = NULL; |
| switch (getOperatorType()) |
| { |
| case ITM_CONVERTTOBITS: |
| { |
| ValueId vid1 = child(0)->getValueId(); |
| |
| // untyped param operands are typed as Int32 Unsigned. |
| SQLInt si(NULL, FALSE); |
| vid1.coerceType(si, NA_NUMERIC_TYPE); |
| |
| const NAType &typ1 = vid1.getType(); |
| |
| // one byte of display size for each bit. |
| // 8 bits per byte. |
| Int32 maxLength = typ1.getNominalSize() * 8; |
| |
| if ( typ1.getTypeQualifier() == NA_CHARACTER_TYPE && |
| typ1.isVaryingLen() == TRUE ) |
| retType = new HEAP |
| SQLVarChar(HEAP, maxLength, typ1.supportsSQLnull()); |
| else |
| retType = new HEAP |
| SQLChar(HEAP, maxLength, typ1.supportsSQLnull()); |
| } |
| break; |
| case ITM_SHA1: |
| { |
| // type cast any params |
| ValueId vid1 = child(0)->getValueId(); |
| SQLChar c1(NULL, ComSqlId::MAX_QUERY_ID_LEN); |
| vid1.coerceType(c1, NA_CHARACTER_TYPE); |
| //input type must be string |
| const NAType &typ1 = child(0)->getValueId().getType(); |
| |
| if (typ1.getTypeQualifier() != NA_CHARACTER_TYPE) |
| { |
| *CmpCommon::diags() << DgSqlCode(-4067) << DgString0("SHA"); |
| return NULL; |
| } |
| |
| retType = new HEAP |
| SQLChar(HEAP, 128, FALSE); |
| if (typ1.supportsSQLnull()) |
| { |
| retType->setNullable(TRUE); |
| } |
| } |
| break; |
| |
| case ITM_SHA2_256: |
| case ITM_SHA2_224: |
| case ITM_SHA2_384: |
| case ITM_SHA2_512: |
| { |
| ValueId vid1 = child(0)->getValueId(); |
| SQLChar c1(NULL, ComSqlId::MAX_QUERY_ID_LEN); |
| vid1.coerceType(c1, NA_CHARACTER_TYPE); |
| |
| const NAType &typ1 = child(0)->getValueId().getType(); |
| |
| if (typ1.getTypeQualifier() != NA_CHARACTER_TYPE) |
| { |
| *CmpCommon::diags() << DgSqlCode(-4067) << DgString0("SHA2"); |
| return NULL; |
| } |
| |
| Lng32 resultLen = 0; |
| switch (getOperatorType()) { |
| case ITM_SHA2_224: |
| resultLen = (224 * 2) / 8; |
| break; |
| case ITM_SHA2_256: |
| resultLen = (256 * 2) / 8; |
| break; |
| case ITM_SHA2_384: |
| resultLen = (384 * 2) / 8; |
| break; |
| case ITM_SHA2_512: |
| resultLen = (512 * 2) / 8; |
| break; |
| default: |
| break; |
| } |
| retType = new HEAP |
| SQLChar(HEAP, resultLen, typ1.supportsSQLnull()); |
| } |
| break; |
| |
| case ITM_MD5: |
| { |
| // type cast any params |
| ValueId vid1 = child(0)->getValueId(); |
| SQLChar c1(NULL, ComSqlId::MAX_QUERY_ID_LEN); |
| vid1.coerceType(c1, NA_CHARACTER_TYPE); |
| //input type must be string |
| const NAType &typ1 = child(0)->getValueId().getType(); |
| |
| if (typ1.getTypeQualifier() != NA_CHARACTER_TYPE) |
| { |
| *CmpCommon::diags() << DgSqlCode(-4067) << DgString0("MD5"); |
| return NULL; |
| } |
| |
| retType = new HEAP |
| SQLChar(HEAP, 32, FALSE); |
| if (typ1.supportsSQLnull()) |
| { |
| retType->setNullable(TRUE); |
| } |
| } |
| break; |
| case ITM_CRC32: |
| { |
| const NAType &typ1 = child(0)->getValueId().getType(); |
| retType = new HEAP |
| SQLInt(HEAP, FALSE, FALSE); //unsigned int |
| if (typ1.supportsSQLnull()) |
| { |
| retType->setNullable(TRUE); |
| } |
| } |
| break; |
| case ITM_ISIPV4: |
| case ITM_ISIPV6: |
| { |
| // type cast any params |
| ValueId vid1 = child(0)->getValueId(); |
| SQLChar c1(NULL, ComSqlId::MAX_QUERY_ID_LEN); |
| vid1.coerceType(c1, NA_CHARACTER_TYPE); |
| //input type must be string |
| const NAType &typ1 = child(0)->getValueId().getType(); |
| |
| if (typ1.getTypeQualifier() != NA_CHARACTER_TYPE) |
| { |
| *CmpCommon::diags() << DgSqlCode(-4067) << DgString0("IS_IP"); |
| return NULL; |
| } |
| retType = new HEAP |
| SQLSmall(HEAP, TRUE, FALSE); |
| if (typ1.supportsSQLnull()) |
| { |
| retType->setNullable(TRUE); |
| } |
| } |
| break; |
| |
| case ITM_INET_ATON: |
| { |
| // type cast any params |
| ValueId vid1 = child(0)->getValueId(); |
| SQLChar c1(NULL, ComSqlId::MAX_QUERY_ID_LEN); |
| vid1.coerceType(c1, NA_CHARACTER_TYPE); |
| |
| //input type must be string |
| const NAType &typ1 = child(0)->getValueId().getType(); |
| |
| if (typ1.getTypeQualifier() != NA_CHARACTER_TYPE) |
| { |
| *CmpCommon::diags() << DgSqlCode(-4067) << DgString0("INET_ATON"); |
| return NULL; |
| } |
| retType = new HEAP |
| SQLInt(HEAP, FALSE, FALSE); |
| if (typ1.supportsSQLnull()) |
| { |
| retType->setNullable(TRUE); |
| } |
| } |
| break; |
| case ITM_INET_NTOA: |
| { |
| // type cast any params |
| ValueId vid = child(0)->getValueId(); |
| vid.coerceType(NA_NUMERIC_TYPE); |
| |
| const NAType &typ1 = child(0)->getValueId().getType(); |
| if (typ1.getTypeQualifier() != NA_NUMERIC_TYPE) |
| { |
| *CmpCommon::diags() << DgSqlCode(-4045) << DgString0("INET_NTOA"); |
| return NULL; |
| } |
| const NumericType &ntyp1 = (NumericType &) typ1; |
| if (NOT ntyp1.isExact() || ntyp1.getScale() != 0) |
| { |
| *CmpCommon::diags() << DgSqlCode(-4046) << DgString0("INET_NTOA"); |
| return NULL; |
| } |
| |
| retType = new HEAP |
| SQLVarChar(HEAP, 15, FALSE); |
| |
| if (typ1.supportsSQLnull()) |
| { |
| retType->setNullable(TRUE); |
| } |
| } |
| break; |
| case ITM_NULLIFZERO: |
| { |
| // type cast any params |
| ValueId vid = child(0)->getValueId(); |
| vid.coerceType(NA_NUMERIC_TYPE); |
| |
| const NAType &typ1 = child(0)->getValueId().getType(); |
| if (typ1.getTypeQualifier() != NA_NUMERIC_TYPE) |
| { |
| // 4045 nullifzero function is only defined for numeric types. |
| *CmpCommon::diags() << DgSqlCode(-4045) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| |
| // returned type is the same as child's type but always nullable. |
| retType = typ1.newCopy(HEAP); |
| if (NOT typ1.supportsSQLnull()) |
| { |
| retType->setNullable(TRUE); |
| } |
| } |
| break; |
| |
| case ITM_NVL: |
| { |
| // type cast any params |
| ValueId vid1 = child(0)->getValueId(); |
| vid1.coerceType(NA_NUMERIC_TYPE); |
| |
| const NAType &typ1 = vid1.getType(); |
| ValueId vid2 = child(1)->getValueId(); |
| vid2.coerceType(typ1); |
| |
| |
| const NAType &typ2 = vid2.getType(); |
| |
| // |
| // Synthesize the result. |
| // |
| UInt32 flags = |
| ((CmpCommon::getDefault(LIMIT_MAX_NUMERIC_PRECISION) == DF_ON) |
| ? NAType::LIMIT_MAX_NUMERIC_PRECISION : 0); |
| |
| retType = (NAType*)typ1.synthesizeType(SYNTH_RULE_UNION, |
| typ1, |
| typ2, |
| HEAP, |
| &flags); |
| if (retType == NULL) |
| { |
| // 4049 CASE can't have result types that are mixed |
| emitDyadicTypeSQLnameMsg(-4049, typ1, typ2); |
| return NULL; |
| } |
| |
| if (NOT typ1.supportsSQLnull()) |
| { |
| retType = typ1.newCopy(HEAP); |
| } |
| } |
| break; |
| |
| case ITM_JSONOBJECTFIELDTEXT: |
| { |
| ValueId vid1 = child(0)->getValueId(); |
| ValueId vid2 = child(1)->getValueId(); |
| |
| // untyped param operands are typed as CHAR |
| vid2.coerceType(NA_CHARACTER_TYPE); |
| |
| const NAType &typ1 = vid1.getType(); |
| const NAType &typ2 = vid2.getType(); |
| |
| if ((typ1.getTypeQualifier() != NA_CHARACTER_TYPE) || |
| (typ2.getTypeQualifier() != NA_CHARACTER_TYPE)) |
| { |
| // 4043 The operand of a $0~String0 function must be character. |
| *CmpCommon::diags() << DgSqlCode(-4043) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| |
| retType = new HEAP |
| SQLVarChar(HEAP, typ1.getNominalSize(), typ1.supportsSQLnull()); |
| } |
| break; |
| |
| case ITM_QUERYID_EXTRACT: |
| { |
| // type cast any params |
| ValueId vid1 = child(0)->getValueId(); |
| SQLChar c1(NULL, ComSqlId::MAX_QUERY_ID_LEN); |
| vid1.coerceType(c1, NA_CHARACTER_TYPE); |
| |
| ValueId vid2 = child(1)->getValueId(); |
| SQLChar c2(NULL, 40, FALSE); |
| vid2.coerceType(c2, NA_CHARACTER_TYPE); |
| |
| const CharType &typ1 = (CharType&)child(0)->getValueId().getType(); |
| if (typ1.getTypeQualifier() != NA_CHARACTER_TYPE) |
| { |
| // 4043 The operand of a $0~String0 function must be character. |
| *CmpCommon::diags() << DgSqlCode(-4043) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| |
| const CharType &typ2 = (CharType&)child(1)->getValueId().getType(); |
| if (typ2.getTypeQualifier() != NA_CHARACTER_TYPE) |
| { |
| // 4043 The operand of a $0~String0 function must be character. |
| *CmpCommon::diags() << DgSqlCode(-4043) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| |
| retType = new HEAP |
| SQLVarChar(HEAP, ComSqlId::MAX_QUERY_ID_LEN, |
| (typ1.supportsSQLnull() || typ2.supportsSQLnull()), |
| FALSE, // not upshifted |
| FALSE, // not case-insensitive |
| CharInfo::ISO88591); |
| } |
| break; |
| |
| case ITM_TOKENSTR: |
| { |
| // tokenstr('token', 'string'); |
| // First param is a quoted_string and is typed as char during |
| // parsing phase. |
| ValueId vid1 = child(0)->getValueId(); |
| ValueId vid2 = child(1)->getValueId(); |
| |
| // untyped param operands are typed as CHAR |
| vid2.coerceType(NA_CHARACTER_TYPE); |
| |
| const NAType &typ1 = vid1.getType(); |
| const NAType &typ2 = vid2.getType(); |
| |
| if ((typ1.getTypeQualifier() != NA_CHARACTER_TYPE) || |
| (typ2.getTypeQualifier() != NA_CHARACTER_TYPE)) |
| { |
| // 4043 The operand of a $0~String0 function must be character. |
| *CmpCommon::diags() << DgSqlCode(-4043) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| |
| retType = new HEAP |
| SQLVarChar(HEAP, typ2.getNominalSize(), typ2.supportsSQLnull()); |
| } |
| break; |
| |
| case ITM_REVERSE: |
| { |
| // reserve(<value>); |
| ValueId vid1 = child(0)->getValueId(); |
| |
| // untyped param operands are typed as CHAR |
| vid1.coerceType(NA_CHARACTER_TYPE); |
| |
| const NAType &typ1 = vid1.getType(); |
| |
| if (typ1.getTypeQualifier() != NA_CHARACTER_TYPE) |
| { |
| // 4043 The operand of a $0~String0 function must be character. |
| *CmpCommon::diags() << DgSqlCode(-4043) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| |
| // return type same as child type |
| retType = typ1.newCopy(HEAP); |
| } |
| break; |
| |
| case ITM_UNIQUE_ID: |
| { |
| //please check the ExFunctionUniqueId::eval if the size is changed |
| retType = new HEAP SQLChar(HEAP, 36, FALSE); |
| } |
| break; |
| case ITM_UNIQUE_SHORT_ID: |
| { |
| //please check the ExFunctionUniqueId::eval if the size is changed |
| retType = new HEAP SQLChar(HEAP, 21, FALSE); |
| } |
| break; |
| |
| case ITM_SOUNDEX: |
| { |
| // type cast any params |
| ValueId vid1 = child(0)->getValueId(); |
| SQLChar c1(NULL, ComSqlId::MAX_QUERY_ID_LEN); |
| vid1.coerceType(c1, NA_CHARACTER_TYPE); |
| |
| //input type must be string |
| const NAType &typ1 = vid1.getType(); |
| |
| if (typ1.getTypeQualifier() != NA_CHARACTER_TYPE) |
| { |
| *CmpCommon::diags() << DgSqlCode(-4067) << DgString0("SOUNDEX"); |
| return NULL; |
| } |
| |
| retType = new HEAP SQLChar(HEAP, 4, FALSE); |
| if (typ1.supportsSQLnull()) |
| { |
| retType->setNullable(TRUE); |
| } |
| } |
| break; |
| |
| case ITM_AES_ENCRYPT: |
| case ITM_AES_DECRYPT: |
| { |
| const NAType &typ1 = child(0)->getValueId().getType(); |
| const NAType &typ2 = child(1)->getValueId().getType(); |
| |
| if (typ1.getTypeQualifier() != NA_CHARACTER_TYPE || |
| typ2.getTypeQualifier() != NA_CHARACTER_TYPE) |
| { |
| *CmpCommon::diags() << DgSqlCode(-4043) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| |
| if (getArity() == 3) |
| { |
| // check the optional init_vector argument |
| const NAType &typ3 = child(0)->getValueId().getType(); |
| if (typ3.getTypeQualifier() != NA_CHARACTER_TYPE) |
| { |
| *CmpCommon::diags() << DgSqlCode(-4043) << DgString0(getTextUpper()); |
| } |
| } |
| |
| Int32 source_len = typ1.getNominalSize(); |
| |
| Int32 maxLength = source_len; |
| |
| // the origin string is short than encrypted string, so for descrypt process, |
| // the length of source string is enough. |
| // When encrypting a string, we need a formula to calculate the length |
| if (getOperatorType() == ITM_AES_ENCRYPT) |
| { |
| // the length of crypt_str can be calculated by |
| // block_size * (trunc(string_length / block_size) + 1) |
| // |
| // the block_size should be get using EVP_CIPHER_block_size(), but in some Algorithm |
| // type, it return 1 in OpenSSL 1.0.1e . So using EVP_MAX_BLOCK_LENGTH instead of it, |
| // which can make sure longer then block size. |
| //Int32 aes_mode = CmpCommon::getDefaultNumeric(BLOCK_ENCRYPTION_MODE); |
| //size_t block_size = EVP_CIPHER_block_size(aes_algorithm_type[aes_mode]); |
| |
| Int32 block_size = EVP_MAX_IV_LENGTH; |
| if (block_size > 1) { |
| maxLength = block_size * (source_len / block_size) + block_size; |
| } |
| } |
| |
| retType = new HEAP |
| SQLVarChar(HEAP, maxLength, TRUE); |
| } |
| break; |
| |
| default: |
| { |
| retType = (NAType *)ItemExpr::synthesizeType(); |
| } |
| |
| } // switch |
| |
| return retType; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class UDFunction. |
| // ----------------------------------------------------------------------- |
| const NAType *UDFunction::synthesizeType() |
| { |
| const NAType * retType = NULL; |
| ValueId outVarId; |
| |
| // We assosiate the type of the UDFunction ItemExpr to that of the |
| // first output of the Function. If the function has more than one output |
| // that gets hadled when we flatten the MVF out and use the ValueIdProxies |
| // to represent those outputs. See bindRowValues(). |
| |
| if (udfDesc_ != NULL) |
| { |
| outVarId = udfDesc_->getOutputColumnList()[0]; |
| const NAType &funcType = outVarId.getType(); |
| retType = &funcType; |
| } |
| |
| return retType; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class Abs |
| // ----------------------------------------------------------------------- |
| |
| const NAType *Abs::synthesizeType() |
| { |
| // The expression is ABS(<value>) |
| // The result is the absolute value of the operand. |
| |
| // type cast any params |
| ValueId vid = child(0)->getValueId(); |
| SQLDoublePrecision dp(NULL, TRUE); |
| vid.coerceType(dp, NA_NUMERIC_TYPE); |
| |
| const NAType &typ1 = child(0)->getValueId().getType(); |
| if (typ1.getTypeQualifier() != NA_NUMERIC_TYPE) |
| { |
| // 4045 Absolute function is only defined for numeric types. |
| *CmpCommon::diags() << DgSqlCode(-4045) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| |
| // now it's safe to cast the types to numeric type |
| const NumericType &ntyp1 = (NumericType &) typ1; |
| NAType *result = NULL; |
| |
| if (ntyp1.isExact()) |
| { |
| Lng32 precision = (ntyp1.getMagnitude() + 9) / 10 + ntyp1.getScale(); |
| |
| if (precision <= MAX_NUMERIC_PRECISION) |
| { |
| Int32 length; |
| if (precision < 5) |
| length = 2; |
| else if (precision < 10) |
| length = 4; |
| else |
| length = 8; |
| |
| result = new HEAP SQLNumeric(HEAP, length, |
| precision, |
| ntyp1.getScale(), |
| ntyp1.isSigned()); |
| } |
| else if (NOT ntyp1.isBigNum() && (ntyp1.getScale()==0) ) // this must be LargeInt |
| result = new HEAP SQLLargeInt(HEAP, ntyp1.isSigned()); |
| else |
| result = new HEAP SQLDoublePrecision(HEAP); |
| } |
| else |
| { |
| result = new HEAP SQLDoublePrecision(HEAP); |
| } |
| |
| if (ntyp1.supportsSQLnullLogical()) result->setNullable(TRUE); |
| |
| return result; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class CodeVal |
| // ----------------------------------------------------------------------- |
| |
| const NAType *CodeVal::synthesizeType() |
| { |
| // The expression is ASCII(<value>)/CODE_VALUE(<value>) |
| // The result is the ASCII or UNICODE value of the first character in <value>. |
| |
| // |
| // Type cast any params. |
| // |
| ValueId vid1 = child(0)->getValueId(); |
| vid1.coerceType(NA_CHARACTER_TYPE); |
| |
| const CharType &typ1 = (CharType&)child(0)->getValueId().getType(); |
| if (typ1.getTypeQualifier() != NA_CHARACTER_TYPE) |
| { |
| // 4043 The operand of a $0~String0 function must be character. |
| *CmpCommon::diags() << DgSqlCode(-4043) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| |
| switch (getOperatorType()) |
| { |
| case ITM_NCHAR_MP_CODE_VALUE: |
| if ( CharInfo::is_NCHAR_MP(typ1.getCharSet()) != TRUE ) |
| { |
| // 4000: internal binder error. This should not happen because we set the |
| // operator type according to the charset of the operand in NO_OPERATOR_TYPE |
| // case first. If we get to here, then type code/operand has been changed |
| // such that they do no match. |
| *CmpCommon::diags() << DgSqlCode(-4000); |
| return NULL; |
| } |
| break; |
| |
| case ITM_UNICODE_CODE_VALUE: |
| if ( typ1.getCharSet() != CharInfo::UNICODE ) { |
| // 4000: internal binder error. This should not happen because we set the |
| // operator type according to the charset of the operand in NO_OPERATOR_TYPE |
| // case first. If we get to here, then type code/operand has been changed |
| // such that they do no match. |
| *CmpCommon::diags() << DgSqlCode(-4000); |
| return NULL; |
| } |
| break; |
| |
| case ITM_ASCII: |
| { |
| CharInfo::CharSet cs = typ1.getCharSet(); |
| if ( CharInfo::maxBytesPerChar( cs ) != 1 ) { |
| if ( cs == CharInfo::UNICODE ) { |
| // 4106 The character set for the operand of string function |
| // ascii/code_value must be $1~String1. |
| *CmpCommon::diags() << DgSqlCode(-4106) << |
| DgString0(getTextUpper()) << |
| DgString1(SQLCHARSETSTRING_ISO88591); |
| |
| return NULL; |
| } |
| } |
| } |
| break; |
| case ITM_CODE_VALUE: |
| // Before R2.4, code_value and ASCII functions returned the same result. |
| // In R2.4, code_value will return the code value of the first |
| // character. ASCII will return an error if the first character in the |
| // buffer is not an ASCII character, for example, SJIS or UTF8 |
| // character. Add one case for code_value. |
| |
| break; |
| |
| case NO_OPERATOR_TYPE: |
| { |
| switch ( typ1.getCharSet() ) |
| { |
| case CharInfo::KANJI_MP: |
| case CharInfo::KSC5601_MP: |
| setOperatorType(ITM_NCHAR_MP_CODE_VALUE); |
| break; |
| |
| case CharInfo::UNICODE: |
| setOperatorType(ITM_UNICODE_CODE_VALUE); |
| break; |
| |
| case CharInfo::ISO88591: |
| default: |
| setOperatorType(ITM_CODE_VALUE); |
| break; |
| } |
| break; |
| // fall through |
| } |
| default: |
| // Character set $0~string0 is not supported for function $1~string1 |
| *CmpCommon::diags() << DgSqlCode(-3403) << |
| DgString0(getTextUpper()) << |
| DgString1(CharInfo::getCharSetName(typ1.getCharSet())); |
| return NULL; |
| } |
| |
| NAType *result = new (HEAP) SQLInt(HEAP, FALSE, typ1.supportsSQLnullLogical()); |
| |
| return result; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class Aggregate |
| // ----------------------------------------------------------------------- |
| |
| const NAType *Aggregate::synthesizeType() |
| { |
| const NAType *result; |
| switch (getEffectiveOperatorType()) { |
| case ITM_COUNT: |
| case ITM_COUNT_NONULL: |
| result = new HEAP |
| SQLLargeInt(HEAP, TRUE /* 'long long' on NSK can't be unsigned */, |
| FALSE /*not null*/); |
| break; |
| case ITM_AVG: |
| case ITM_SUM: { |
| ValueId vid = child(0)->getValueId(); |
| vid.coerceType(NA_NUMERIC_TYPE); |
| |
| const NAType& operand = child(0)->castToItemExpr()->getValueId().getType(); |
| |
| // If Top of a split aggregate, use the data type of the child |
| // aggregate. |
| if(topPartOfAggr()) { |
| |
| // If this is in a scalar groupby, it can potentially return NULL. |
| // Make sure that the type is nullable. |
| if (inScalarGroupBy()) |
| result = operand.synthesizeNullableType(HEAP); |
| else |
| result = operand.newCopy(HEAP); |
| |
| } else { |
| result = synthAvgSum(operand, inScalarGroupBy()); |
| } |
| break; |
| } |
| case ITM_MAX: |
| case ITM_MIN: { |
| ValueId vid = child(0)->getValueId(); |
| vid.coerceType(NA_CHARACTER_TYPE); |
| |
| const NAType& operand = child(0)->castToItemExpr()->getValueId().getType(); |
| |
| if ( operand.getTypeQualifier() == NA_CHARACTER_TYPE ) { |
| |
| if (CmpCommon::wantCharSetInference()) { |
| |
| const CharType *charOp = (CharType*)&(vid.getType()); |
| |
| const CharType* desiredType = |
| CharType::findPushDownCharType(getDefaultCharSet, charOp, 0); |
| |
| if ( desiredType ) { |
| // just push down the charset field. All other fields are |
| // meaningless. |
| vid.coerceType((NAType&)*desiredType, NA_CHARACTER_TYPE); |
| } |
| } |
| } |
| |
| if (!operand.isSupportedType()) |
| { |
| emitDyadicTypeSQLnameMsg(-4041, operand, operand); |
| return NULL; |
| } |
| |
| if (inScalarGroupBy()) |
| result = operand.synthesizeNullableType(HEAP); |
| else |
| result = operand.newCopy(HEAP); |
| break; |
| } |
| |
| case ITM_GROUPING: |
| { |
| // grouping result is an unsigned int (32 bit) |
| result = new HEAP |
| SQLInt(HEAP, FALSE /*unsigned*/, |
| FALSE /*not null*/); |
| } |
| break; |
| |
| case ITM_ONE_ROW: |
| case ITM_ONEROW: |
| { |
| const NAType& operand = child(0)->castToItemExpr()->getValueId().getType(); |
| result = operand.synthesizeNullableType(HEAP); |
| break; |
| } |
| case ITM_ONE_TRUE: |
| case ITM_ANY_TRUE_MAX: |
| case ITM_ANY_TRUE: |
| { |
| const SQLBooleanRelat& operand = (const SQLBooleanRelat &) |
| child(0)->castToItemExpr()->getValueId().getType(); |
| |
| // The argument of a ONE/ANY TRUE must be of type SQLBoolean |
| CMPASSERT(operand.getTypeQualifier() == NA_BOOLEAN_TYPE); |
| |
| result = new HEAP SQLBooleanRelat(HEAP, operand.canBeSQLUnknown()); |
| break; |
| } |
| default: |
| result = ItemExpr::synthesizeType(); |
| break; |
| } |
| return result; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class AggMinMax |
| // ----------------------------------------------------------------------- |
| const NAType *AggrMinMax::synthesizeType() |
| { |
| const NAType *result; |
| |
| const NAType& operand = child(0)->castToItemExpr()->getValueId().getType(); |
| result = operand.newCopy(HEAP); |
| |
| return result; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class AggGrouping |
| // ----------------------------------------------------------------------- |
| const NAType *AggrGrouping::synthesizeType() |
| { |
| // result unsigned 32 bit integer |
| const NAType *result = new HEAP SQLInt(HEAP, FALSE, FALSE); |
| |
| return result; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class PivotGroup |
| // ----------------------------------------------------------------------- |
| const NAType *PivotGroup::synthesizeType() |
| { |
| //for Character type, need to consider the charset |
| //the output charset should be same as input child 0 |
| const NAType &operand = child(0)->getValueId().getType(); |
| if(operand.getTypeQualifier() == NA_CHARACTER_TYPE) |
| { |
| CharType & origType = (CharType &) operand; |
| return new HEAP SQLVarChar(HEAP, maxLen_, TRUE, origType.isUpshifted(), FALSE, operand.getCharSet()); |
| } |
| else |
| return new HEAP SQLVarChar(HEAP, maxLen_, TRUE); |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class AnsiUSERFunction |
| // ----------------------------------------------------------------------- |
| |
| static const Lng32 MAX_NT_DOMAIN_NAME_LEN = 30; |
| static const Lng32 MAX_NT_USERNAME_LEN = 20; |
| //the ldap username needs to fit into this field, so make them equal |
| static const Lng32 OPT_MAX_USERNAME_LEN = ComSqlId::MAX_LDAP_USER_NAME_LEN+1; |
| |
| const NAType *AnsiUSERFunction::synthesizeType() |
| { |
| return new HEAP SQLVarChar(HEAP, OPT_MAX_USERNAME_LEN, FALSE); |
| } |
| |
| const NAType *MonadicUSERFunction::synthesizeType() |
| { |
| // |
| // Type cast any params. |
| // |
| ValueId vid = child(0)->getValueId(); |
| |
| SQLInt si(NULL, TRUE); |
| vid.coerceType(si, NA_NUMERIC_TYPE); |
| |
| // |
| // Check that the operands are compatible. |
| // |
| const NAType& typ1 = vid.getType(); |
| |
| if (typ1.getTypeQualifier() != NA_NUMERIC_TYPE) { |
| // 4043 The operand of a USER function must be character. |
| *CmpCommon::diags() << DgSqlCode(-4045) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| |
| // now it's safe to cast the types to numeric type |
| const NumericType &ntyp1 = (NumericType &) typ1; |
| if (NOT ntyp1.isExact()) |
| { |
| // 4046 USER function is only defined for exact numeric types. |
| *CmpCommon::diags() << DgSqlCode(-4046) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| |
| if (ntyp1.getScale() != 0) |
| { |
| // 4047 Arguments of USER function must have a scale of 0. |
| *CmpCommon::diags() << DgSqlCode(-4047) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| |
| // |
| // Return the result. |
| // |
| return new HEAP SQLVarChar(HEAP, OPT_MAX_USERNAME_LEN, typ1.supportsSQLnullLogical()); |
| } |
| |
| const NAType *MonadicUSERIDFunction::synthesizeType() |
| { |
| // |
| // Type cast any params. |
| // |
| ValueId vid = child(0)->getValueId(); |
| vid.coerceType(NA_CHARACTER_TYPE); |
| // |
| // Check that the operands are compatible. |
| // |
| const NAType& operand = vid.getType(); |
| if (operand.getTypeQualifier() != NA_CHARACTER_TYPE) { |
| // 4043 The operand of a USERID function must be character. |
| *CmpCommon::diags() << DgSqlCode(-4043) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| |
| // |
| // Return the result. |
| // |
| return new HEAP SQLVarChar(HEAP, OPT_MAX_USERNAME_LEN, operand.supportsSQLnullLogical()); |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class Assign |
| // ----------------------------------------------------------------------- |
| |
| const NAType *Assign::doSynthesizeType(ValueId & targetId, ValueId & sourceId) |
| { |
| NABoolean ODBC = (CmpCommon::getDefault(ODBC_PROCESS) == DF_ON); |
| NABoolean JDBC = (CmpCommon::getDefault(JDBC_PROCESS) == DF_ON); |
| NABoolean isSourceNullConst = FALSE; |
| NABoolean forceSourceParamToBeNullable = |
| (CmpCommon::getDefault(COMP_BOOL_173) == DF_ON); |
| |
| // |
| // Type cast any params. |
| // |
| targetId = child(0)->castToItemExpr()->getValueId(); |
| sourceId = child(1)->castToItemExpr()->getValueId(); |
| |
| const NAType& targetType = targetId.getType(); |
| |
| NABoolean sourceIsUntypedParam = |
| (sourceId.getType().getTypeQualifier() == NA_UNKNOWN_TYPE); |
| |
| // Charset inference. |
| const NAType& sourceType = sourceId.getType(); |
| targetId.coerceType(sourceType); |
| |
| sourceId.coerceType(targetType); |
| |
| // if this param is the source of an insert/update stmt coming in |
| // from odbc/jdbc interface and is not nullable, then make it nullable |
| // if the user has asked for it. |
| if ((NOT sourceId.getType().supportsSQLnull()) && |
| (ODBC || JDBC) && (forceSourceParamToBeNullable) && |
| (sourceIsUntypedParam)) |
| { |
| NAType &sourceType = (NAType&)(sourceId.getType()); |
| sourceType.setNullable(TRUE); |
| |
| // Propagate (pushDowntype()) this type to the children of this valueid |
| // in case one of the children could not be typed. |
| // |
| const NAType* synthesizedNewType = |
| sourceId.getItemExpr()->pushDownType(sourceType); |
| |
| sourceId.changeType(synthesizedNewType); |
| } |
| |
| // |
| // Check that the operands are compatible. |
| // |
| if (NOT targetId.getType().isCompatible(sourceId.getType())) { |
| if ((CmpCommon::getDefault(ALLOW_INCOMPATIBLE_OPERATIONS) == DF_ON) && |
| (sourceId.getType().getTypeQualifier() != NA_RECORD_TYPE ) && |
| ((child(1)->getOperatorType() != ITM_CONSTANT) || |
| (NOT ((ConstValue *) child(1).getPtr() )->isNull()))) |
| { |
| // target type is not the same as source type. |
| // Assignment allowed in special_1 mode. |
| // bindNode will add an explicit CAST node. |
| // All supported incompatible conversions will be handled by CAST. |
| return &targetType; |
| } |
| |
| return NULL; |
| } |
| |
| // |
| // Return the result. |
| // |
| return &targetType; |
| } |
| const NAType *Assign::synthesizeType(const char * str1, const Lng32 int1) |
| { |
| ValueId targetId, sourceId; |
| const NAType * result = doSynthesizeType(targetId, sourceId); |
| if (result == NULL) |
| { |
| emitDyadicTypeSQLnameMsg(arkcmpErrorISPWrongDataType, |
| targetId.getType(), |
| sourceId.getType(), |
| str1, |
| NULL, // No str2 value |
| NULL, // Default diags area |
| int1); |
| return NULL; |
| } |
| |
| // |
| // Return the result. |
| // |
| return result; |
| } |
| |
| const NAType *Assign::synthesizeType() |
| { |
| ValueId targetId, sourceId; |
| const NAType * result = doSynthesizeType(targetId, sourceId); |
| if (result == NULL) |
| { |
| emitDyadicTypeSQLnameMsg(-4039, |
| targetId.getType(), |
| sourceId.getType(), |
| ToAnsiIdentifier(targetId.getNAColumn()->getColName())); |
| return NULL; |
| } |
| // |
| // Return the result. |
| // |
| return result; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class BaseColumn |
| // ----------------------------------------------------------------------- |
| |
| const NAType *BaseColumn::synthesizeType() |
| { |
| return &getType(); |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class IndexColumn |
| // ----------------------------------------------------------------------- |
| |
| const NAType * IndexColumn::synthesizeType() |
| { |
| return &indexColDefinition_.getType(); |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class Between |
| // ----------------------------------------------------------------------- |
| |
| const NAType *Between::synthesizeType() |
| { |
| ItemExprList exprList1(child(0).getPtr(), HEAP); |
| ItemExprList exprList2(child(1).getPtr(), HEAP); |
| ItemExprList exprList3(child(2).getPtr(), HEAP); |
| if (exprList1.entries() != exprList2.entries() OR |
| exprList1.entries() != exprList3.entries()) { |
| // 4040 The operands of a between predicate must be of equal degree. |
| *CmpCommon::diags() << DgSqlCode(-4040); |
| return NULL; |
| } |
| |
| NABoolean allowsUnknown = FALSE; |
| |
| NABoolean allowIncompatibleComparison = |
| ((CmpCommon::getDefault(ALLOW_INCOMPATIBLE_OPERATIONS) == DF_ON) && |
| (child(0)->castToItemExpr()->getOperatorType() != ITM_ONE_ROW) && |
| (child(1)->castToItemExpr()->getOperatorType() != ITM_ONE_ROW) && |
| (child(2)->castToItemExpr()->getOperatorType() != ITM_ONE_ROW) && |
| (child(0)->castToItemExpr()->getOperatorType() != ITM_ONEROW) && |
| (child(1)->castToItemExpr()->getOperatorType() != ITM_ONEROW) && |
| (child(2)->castToItemExpr()->getOperatorType() != ITM_ONEROW)); |
| |
| for (CollIndex i = 0; i < exprList1.entries(); i++) { |
| // |
| // Type cast any params. |
| // |
| ValueId vid1 = exprList1[i]->getValueId(); |
| ValueId vid2 = exprList2[i]->getValueId(); |
| ValueId vid3 = exprList3[i]->getValueId(); |
| vid1.coerceType(vid2.getType()); |
| vid1.coerceType(vid3.getType(), NA_NUMERIC_TYPE); |
| vid2.coerceType(vid1.getType()); |
| vid3.coerceType(vid1.getType()); |
| // |
| // Check that the operands are comparable. |
| // |
| const NAType& op1 = vid1.getType(); |
| const NAType& op2 = vid2.getType(); |
| const NAType& op3 = vid3.getType(); |
| |
| NABoolean compareOp2 = TRUE; |
| NABoolean compareOp3 = TRUE; |
| |
| if (allowIncompatibleComparison) |
| { |
| if(((op1.getTypeQualifier() == NA_DATETIME_TYPE) && |
| (op2.getTypeQualifier() == NA_CHARACTER_TYPE) && |
| (vid2.getItemExpr()->getOperatorType() == ITM_CONSTANT)) || |
| ((op2.getTypeQualifier() == NA_DATETIME_TYPE) && |
| (op1.getTypeQualifier() == NA_CHARACTER_TYPE) && |
| (vid1.getItemExpr()->getOperatorType() == ITM_CONSTANT))) |
| compareOp2 = FALSE; |
| |
| if(((op1.getTypeQualifier() == NA_DATETIME_TYPE) && |
| (op3.getTypeQualifier() == NA_CHARACTER_TYPE) && |
| (vid3.getItemExpr()->getOperatorType() == ITM_CONSTANT)) || |
| ((op3.getTypeQualifier() == NA_DATETIME_TYPE) && |
| (op1.getTypeQualifier() == NA_CHARACTER_TYPE) && |
| (vid1.getItemExpr()->getOperatorType() == ITM_CONSTANT))) |
| compareOp3 = FALSE; |
| } |
| |
| if (op1.getTypeQualifier() == NA_CHARACTER_TYPE && |
| op2.getTypeQualifier() == NA_CHARACTER_TYPE && |
| op3.getTypeQualifier() == NA_CHARACTER_TYPE) |
| { |
| if ( CmpCommon::getDefault(ALLOW_IMPLICIT_CHAR_CASTING) == DF_ON ) |
| { |
| compareOp2 = FALSE; |
| compareOp3 = FALSE; |
| } |
| } |
| |
| if ((compareOp2) && (NOT op1.isComparable(op2, this))) //## errmsg 4034 w/ unparse? |
| return FALSE; |
| if ((compareOp3) && (NOT op1.isComparable(op3, this))) //## errmsg 4034 w/ unparse? |
| return FALSE; |
| |
| // If any of the operands is nullable the result could be unknown |
| allowsUnknown = allowsUnknown OR |
| op1.supportsSQLnullLogical() OR |
| op2.supportsSQLnullLogical() OR |
| op3.supportsSQLnullLogical(); |
| } |
| // |
| // Return the result. |
| // |
| return new HEAP SQLBooleanRelat(HEAP, allowsUnknown); |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class BiArith |
| // ----------------------------------------------------------------------- |
| |
| const NAType *BiArith::synthesizeType() |
| { |
| // |
| // Type cast any params. |
| // |
| ValueId vid1 = child(0)->getValueId(); |
| ValueId vid2 = child(1)->getValueId(); |
| |
| if (vid1.getType().getTypeQualifier() == NA_UNKNOWN_TYPE && |
| vid2.getType().getTypeQualifier() == NA_NUMERIC_TYPE) { |
| // if op1 is a param with unknown type and op2 |
| // is an exact numeric, type cast op1 to the default |
| // numeric type |
| const NumericType& op2 = (NumericType&)vid2.getType(); |
| if (op2.isExact()) |
| vid1.coerceType(NA_NUMERIC_TYPE); |
| } |
| else if (vid2.getType().getTypeQualifier() == NA_UNKNOWN_TYPE && |
| vid1.getType().getTypeQualifier() == NA_NUMERIC_TYPE) { |
| // if op2 is a param with unknown type and op1 |
| // is an exact numeric, type cast op2 to the default |
| // numeric type |
| const NumericType& op1 = (NumericType&)vid1.getType(); |
| if (op1.isExact()) |
| vid2.coerceType(NA_NUMERIC_TYPE); |
| }; |
| |
| vid1.coerceType(vid2.getType(), NA_NUMERIC_TYPE); |
| vid2.coerceType(vid1.getType()); |
| |
| UInt32 flags = |
| ((CmpCommon::getDefault(LIMIT_MAX_NUMERIC_PRECISION) == DF_ON) |
| ? NAType::LIMIT_MAX_NUMERIC_PRECISION : 0); |
| |
| if (CmpCommon::getDefault(MODE_SPECIAL_1) == DF_ON) |
| { |
| flags |= NAType::MODE_SPECIAL_1; |
| } |
| |
| if (CmpCommon::getDefault(MODE_SPECIAL_4) == DF_ON) |
| { |
| flags |= NAType::MODE_SPECIAL_4; |
| } |
| |
| NABoolean limitPrecision = |
| ((flags & NAType::LIMIT_MAX_NUMERIC_PRECISION) != 0); |
| |
| // |
| // Synthesize the result. |
| // |
| const NAType& operand1 = vid1.getType(); |
| const NAType& operand2 = vid2.getType(); |
| const NAType *result; |
| switch (getOperatorType()) { |
| case ITM_PLUS: |
| result = operand1.synthesizeType(SYNTH_RULE_ADD, operand1, operand2, HEAP, |
| &flags); |
| break; |
| case ITM_MINUS: |
| result = (getIntervalQualifier() == NULL) |
| ? operand1.synthesizeType(SYNTH_RULE_SUB, operand1, operand2, HEAP, |
| &flags) |
| : operand1.synthesizeTernary(SYNTH_RULE_SUB, |
| operand1, |
| operand2, |
| *getIntervalQualifier(), |
| HEAP); |
| break; |
| case ITM_TIMES: |
| result = operand1.synthesizeType(SYNTH_RULE_MUL, operand1, operand2, HEAP, |
| &flags); |
| break; |
| case ITM_DIVIDE: |
| { |
| // if roundingMode is already set in this node, use it. |
| // ignoreSpecialRounding() == TRUE indicates rounding disabled, in |
| // which case follow default roundingMode = 0. |
| |
| short roundingMode = getRoundingMode(); |
| if (roundingMode == 0 && ! ignoreSpecialRounding() ) |
| roundingMode = (short)CmpCommon::getDefaultLong(ROUNDING_MODE); |
| |
| if (roundingMode != 0) |
| { |
| flags |= NAType::ROUND_RESULT; |
| |
| // also limit precision, if rounding is to be done. |
| // Rounding is only supported using division rounding mechanism |
| // for exact and simple (no BigNums) numerics. |
| flags |= NAType::LIMIT_MAX_NUMERIC_PRECISION; |
| } |
| |
| result = |
| operand1.synthesizeType(SYNTH_RULE_DIV, operand1, operand2, HEAP, |
| &flags); |
| if ((roundingMode != 0) && (result) && |
| ((flags & NAType::RESULT_ROUNDED) != 0)) |
| { |
| // if rounding was requested and done, set that info in |
| // the BiArith node. |
| setRoundingMode(roundingMode); |
| } |
| else |
| { |
| setRoundingMode(0); |
| } |
| } |
| break; |
| case ITM_EXPONENT: |
| result = operand1.synthesizeType(SYNTH_RULE_EXP, operand1, operand2, HEAP, |
| &flags); |
| break; |
| default: |
| result = ItemExpr::synthesizeType(); |
| break; |
| } |
| if (!result) { |
| if (operand1.getTypeQualifier() == NA_RECORD_TYPE || |
| operand2.getTypeQualifier() == NA_RECORD_TYPE) { |
| // 4020 arith operation not allowed on row-value-constructor. |
| *CmpCommon::diags() << DgSqlCode(-4020); |
| } |
| else { |
| const char *intervalQ; |
| if (getIntervalQualifier()) |
| intervalQ = getIntervalQualifier()->getTypeSQLname(TRUE /*terse*/); |
| else |
| intervalQ = ""; |
| // 4034 The operation (~op1 ~operator ~op2) ~iq is not allowed. |
| emitDyadicTypeSQLnameMsg(-4034, operand1, operand2, |
| getTextUpper(), intervalQ); |
| } |
| } |
| return result; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class UnArith |
| // ----------------------------------------------------------------------- |
| |
| const NAType *UnArith::synthesizeType() |
| { |
| // |
| // Type cast any params. |
| // |
| ValueId vid1 = child(0)->getValueId(); |
| |
| if (vid1.getType().getTypeQualifier() == NA_UNKNOWN_TYPE) |
| { |
| vid1.coerceType(NA_BOOLEAN_TYPE); |
| } |
| |
| const NAType& operand1 = vid1.getType(); |
| if (operand1.getFSDatatype() != REC_BOOLEAN) |
| { |
| *CmpCommon::diags() << DgSqlCode(-4034) |
| << DgString0("!") |
| << DgString1(child(0)->getText()) |
| << DgString2(""); |
| |
| return NULL; |
| } |
| |
| NAType * retType = new HEAP SQLBooleanNative(HEAP, operand1.supportsSQLnull()); |
| |
| return retType; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class BiLogic |
| // ----------------------------------------------------------------------- |
| |
| const NAType *BiLogic::synthesizeType() |
| { |
| const SQLBooleanRelat& operand0 = (SQLBooleanRelat&) child(0).getValueId().getType(); |
| const SQLBooleanRelat& operand1 = (SQLBooleanRelat&) child(1).getValueId().getType(); |
| |
| NABoolean allowsUnknown = operand0.canBeSQLUnknown() OR |
| operand1.canBeSQLUnknown(); |
| |
| return new HEAP SQLBooleanRelat(HEAP, allowsUnknown); |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class BiRelat |
| // ----------------------------------------------------------------------- |
| const NAType *BiRelat::synthesizeType() |
| { |
| ItemExpr *ie1 = child(0); |
| ItemExpr *ie2 = child(1); |
| |
| if (ie1->getOperatorType() == ITM_ONE_ROW) ie1 = ie1->child(0); |
| if (ie2->getOperatorType() == ITM_ONE_ROW) ie2 = ie2->child(0); |
| ItemExprList exprList1(ie1, HEAP); |
| ItemExprList exprList2(ie2, HEAP); |
| |
| // in some cases, we allow comparisons between 'incompatible' datatypes. |
| // This is allowed if CQD is set, and it is a single valued scaler |
| // predicate (a <op> b), and the comparison is done between a char/varhar |
| // and numeric type. |
| // In these conditions, the char type is converted to numeric by putting |
| // a CAST node on top of it. |
| // This incompatible comparison is not allowed if the statement is a DDL |
| NABoolean allowIncompatibleComparison = FALSE; |
| |
| if ((CmpCommon::getDefault(ALLOW_INCOMPATIBLE_OPERATIONS) == DF_ON) && |
| (child(0)->castToItemExpr()->getOperatorType() != ITM_ONE_ROW) && |
| (child(1)->castToItemExpr()->getOperatorType() != ITM_ONE_ROW) && |
| (exprList1.entries() == 1) && |
| (exprList2.entries() == 1)) |
| allowIncompatibleComparison = TRUE; |
| |
| NABoolean allowsUnknown; |
| if (!synthItemExprLists(exprList1, exprList2, allowIncompatibleComparison, |
| allowsUnknown, this)) |
| return NULL; |
| return new HEAP SQLBooleanRelat(HEAP, allowsUnknown); |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class BoolResult |
| // ----------------------------------------------------------------------- |
| |
| const NAType *BoolResult::synthesizeType() |
| { |
| return new HEAP SQLBooleanRelat(HEAP, getOperatorType() == ITM_RETURN_NULL); |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class BoolVal |
| // ----------------------------------------------------------------------- |
| |
| const NAType *BoolVal::synthesizeType() |
| { |
| return new HEAP SQLBooleanRelat(HEAP, getOperatorType() == ITM_RETURN_NULL); |
| } |
| |
| //------------------------------------------------------------------ |
| // member functions for class RaiseError |
| //------------------------------------------------------------------ |
| const NAType *RaiseError::synthesizeType() |
| { |
| // -- Triggers |
| if (getArity() == 1) |
| { // Verify the string expression is of character type. |
| if (child(0)->getValueId().getType().getTypeQualifier() != NA_CHARACTER_TYPE) |
| { |
| // parameter 3 must be of type string. |
| *CmpCommon::diags() << DgSqlCode(-3185); |
| return NULL; |
| } |
| } |
| return new HEAP SQLBooleanRelat(FALSE); // can be overridden in IfThenElse |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class IfThenElse |
| // ----------------------------------------------------------------------- |
| |
| const NAType *IfThenElse::synthesizeType() |
| { |
| // |
| // The ELSE clause may be a NULL pointer if this is part of a CASE statement |
| // created by the generator. |
| // |
| ValueId thenId = child(1)->getValueId(); |
| if (child(2).getPtr() == NULL) |
| return &thenId.getType(); |
| ValueId elseId = child(2)->getValueId(); |
| // |
| // Type cast any params. |
| // |
| thenId.coerceType(elseId.getType(), NA_NUMERIC_TYPE); |
| elseId.coerceType(thenId.getType()); |
| |
| // infer the charset if unknown. |
| if ( thenId.getType().getTypeQualifier() == NA_CHARACTER_TYPE && |
| elseId.getType().getTypeQualifier() == NA_CHARACTER_TYPE |
| ) |
| { |
| const CharType *thenCharType = (CharType*)&thenId.getType(); |
| const CharType *elseCharType = (CharType*)&elseId.getType(); |
| |
| if (CmpCommon::wantCharSetInference()) { |
| const CharType* desiredType = |
| CharType::findPushDownCharType(getDefaultCharSet, |
| thenCharType, elseCharType, 0); |
| |
| if ( desiredType ) { |
| // just push down the charset field. All other fields are |
| // meaningless. |
| thenId.coerceType((NAType&)*desiredType, NA_CHARACTER_TYPE); |
| elseId.coerceType((NAType&)*desiredType, NA_CHARACTER_TYPE); |
| } |
| } |
| } |
| |
| // |
| // Synthesize the result. |
| // |
| UInt32 flags = |
| ((CmpCommon::getDefault(LIMIT_MAX_NUMERIC_PRECISION) == DF_ON) |
| ? NAType::LIMIT_MAX_NUMERIC_PRECISION : 0); |
| |
| if (CmpCommon::getDefault(MODE_SPECIAL_1) == DF_ON) |
| { |
| flags |= NAType::MODE_SPECIAL_1; |
| } |
| |
| if (CmpCommon::getDefault(TYPE_UNIONED_CHAR_AS_VARCHAR) == DF_ON) |
| { |
| flags |= NAType::MAKE_RESULT_VARCHAR; |
| } |
| |
| const NAType& thenType = thenId.getType(); |
| const NAType& elseType = elseId.getType(); |
| |
| const NAType *result = thenType.synthesizeType(SYNTH_RULE_UNION, |
| thenType, |
| elseType, |
| HEAP, |
| &flags); |
| if (result == NULL) { |
| |
| // Ignore the RaiseError's type and pass thru the other operand's type |
| if (thenId.getItemExpr()->getOperatorType() == ITM_RAISE_ERROR) |
| return &elseType; |
| if (elseId.getItemExpr()->getOperatorType() == ITM_RAISE_ERROR) |
| return &thenType; |
| |
| // 4049 CASE can't have result types that are mixed |
| emitDyadicTypeSQLnameMsg(-4049, thenType, elseType); |
| } |
| return result; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class Cast |
| // ----------------------------------------------------------------------- |
| |
| // Exact numeric can be cast to a single-field interval, and vice versa. |
| // In special_1 mode, numerics can be cast to multi-field intervals. |
| static NABoolean numericCastIsCompatible(const NAType &src, const NAType &tgt) |
| { |
| if (src.getTypeQualifier() == NA_NUMERIC_TYPE && |
| tgt.getTypeQualifier() == NA_INTERVAL_TYPE && |
| tgt.isSupportedType()) |
| { |
| if (CmpCommon::getDefault(ALLOW_INCOMPATIBLE_OPERATIONS) == DF_ON) |
| return TRUE; |
| |
| NumericType& numeric = (NumericType&)src; |
| IntervalType& interval = (IntervalType&)tgt; |
| if (numeric.isExact()) |
| { |
| if (CmpCommon::getDefault(MODE_SPECIAL_1) == DF_ON) |
| return TRUE; |
| else if (interval.getStartField() == interval.getEndField()) |
| return TRUE; |
| } |
| } |
| //check for numeric to date conversion |
| else if ((CmpCommon::getDefault(MODE_SPECIAL_1) == DF_ON) && |
| (tgt.getTypeQualifier() == NA_DATETIME_TYPE) && |
| (src.getTypeQualifier() == NA_NUMERIC_TYPE)) |
| { |
| DatetimeType &dtType = (DatetimeType&)tgt; |
| NumericType &numeric = (NumericType&)src; |
| if ((dtType.getPrecision() == SQLDTCODE_DATE) && |
| (numeric.isExact()) && |
| (NOT numeric.isBigNum()) && |
| (numeric.getScale() == 0)) |
| { |
| return TRUE; |
| } |
| } |
| |
| return FALSE; |
| } |
| |
| // Begin_Fix 10-040114-2431 |
| // 02/18/2004 |
| // Added as part of above mentioned fix |
| // synthesizeType for Narrow ensures that |
| // if we match the nullability of the child |
| // if that is required. This is done by setting |
| // Cast::matchChildType_ flag, if it is not already |
| // set. If Cast::matchChildType_ is not set, we set |
| // it and then unset it after Cast::synthesizeType(). |
| // Setting Cast::matchChildType_ does more than just |
| // matching my child's nullability (please see class |
| // Cast in ItemFunc.h), therefore if it is not set |
| // initially we just unset it after calling |
| // Cast::synthesizeType(). |
| const NAType *Narrow::synthesizeType() |
| { |
| //check if we Cast::matchChildType_ is set |
| NABoolean matchChildType = Cast::matchChildType(); |
| |
| //if Cast::matchChildType_ is not set and we |
| //want to force our nullability to be the same |
| //as the child's nullability, set Cast::matchChildType_ |
| if ((!matchChildType) && (matchChildNullability_)) |
| { |
| //setting this flag will force our nullability |
| //to be the same as the child's nullability |
| Cast::setMatchChildType(TRUE); |
| } |
| |
| //call Cast::synthesizeType() to do the real type synthesis |
| const NAType * result = Cast::synthesizeType(); |
| |
| //if Cast::matchChildType_ was not initially set |
| //then just unset it again. |
| if ((!matchChildType) && (matchChildNullability_)) |
| { |
| Cast::setMatchChildType(FALSE); |
| } |
| |
| return result; |
| } |
| // End_Fix 10-040114-2431 |
| |
| const NAType *Cast::synthesizeType() |
| { |
| // |
| // Type cast any params. |
| // Assert that we are bound, or created by Generator, so we have type info. |
| // |
| ValueId vid = child(0)->getValueId(); |
| CMPASSERT(vid != NULL_VALUE_ID); |
| NABoolean untypedParam = |
| ((child(0)->getOperatorType() == ITM_DYN_PARAM) && |
| (vid.getType().getTypeQualifier() == NA_UNKNOWN_TYPE)); |
| NAType * result = NULL; |
| NABoolean typeChanged = FALSE; |
| NABoolean sensitiveChanged = FALSE; |
| NABoolean charsetChanged = FALSE; |
| vid.coerceType(*getType()); |
| if (untypedParam) |
| { |
| // an untyped param is being typed using CAST. |
| if (vid.getType().supportsSQLnull() != getType()->supportsSQLnull()) |
| { |
| // Set the null attribute to be the same as that of the cast node. |
| NAType * newType = vid.getType().newCopy(HEAP); |
| newType->setNullable(getType()->supportsSQLnull()); |
| vid.changeType(newType); |
| } |
| |
| // mark this cast node so code for it is not generated at code |
| // generation time. |
| setMatchChildType(TRUE); |
| } |
| |
| // Fix for CR 10-010426-2464: If its child supports NULL but itself |
| // does not AND the node's nullability is changable (i.e. not specified |
| // explicitly in the application), set it same as its child. |
| // NOTE: the new copy is necessary only because that the type_ is a const |
| // member. If the const is ever removed, setNullable can be called |
| // directly |
| else if (vid.getType().supportsSQLnull() != getType()->supportsSQLnull()) |
| { |
| if (matchChildType()) |
| // NOT NULL phrase not specified |
| { |
| result = getType()->newCopy(HEAP); |
| result->setNullable(vid.getType()); |
| typeChanged = TRUE; |
| } |
| } |
| |
| const NAType &src = vid.getType(); |
| const NAType &tgt = (typeChanged)? *result: *getType(); |
| NABuiltInTypeEnum srcQual = src.getTypeQualifier(); |
| NABuiltInTypeEnum tgtQual = tgt.getTypeQualifier(); |
| |
| if ((src.getTypeQualifier() == NA_CHARACTER_TYPE) && |
| (tgt.getTypeQualifier() == NA_CHARACTER_TYPE)) |
| { |
| const CharType &cSrc = (CharType&)src; |
| CharType &cTgt = (CharType&)tgt; |
| if (cSrc.isCaseinsensitive() && (NOT cTgt.isCaseinsensitive())) |
| sensitiveChanged = TRUE; |
| if ( cSrc.getCharSet() != CharInfo::UnknownCharSet && |
| cTgt.getCharSet() == CharInfo::UnknownCharSet) |
| charsetChanged = TRUE; |
| if (sensitiveChanged || charsetChanged) |
| { |
| result = tgt.newCopy(HEAP); |
| typeChanged = TRUE; |
| if (sensitiveChanged) |
| ((CharType*)result)->setCaseinsensitive(TRUE); |
| if (charsetChanged) |
| ((CharType*)result)->setCharSet(cSrc.getCharSet()); |
| } |
| } |
| const NAType &res = (typeChanged)? *result: *getType(); |
| // |
| // The NULL constant can be cast to any type. |
| // |
| if (getExpr()->getOperatorType() == ITM_CONSTANT) |
| if (((ConstValue*)getExpr())->isNull()) |
| return (typeChanged)? result: getType(); |
| // |
| // See the chart in ANSI 6.10, a rather symmetrical piece of work. |
| // Currently the "M" (Maybe) general subrules are being interpreted |
| // as "Y" (Yes, legally castable). |
| // Also, the Bitstring datatypes are not currently supported. |
| // Internally, we use SQLBooleans for some predicate results (=ANY, e.g.). |
| // |
| // The diagonal of compatible types is fine. |
| // Character types can be cast from or to with impunity. |
| // Numeric can be cast to our internal SQLBoolean. |
| // Exact numeric can be cast to a single-field interval, and vice versa. |
| // Timestamp can be cast to time or date; date or time can cast to timestamp. |
| // |
| |
| NABoolean legal = FALSE; |
| |
| // If both operands are char, they must be compatible (i.e., same charset); |
| // they do NOT have to be comparable (i.e., collation/coercibility ok)! |
| // |
| // The result type takes the charset from the target, and: |
| // - if target is a standard Ansi data type -- i.e., no COLLATE-clause -- |
| // then DEFAULT collation and COERCIBLE coercibility are used, |
| // per Ansi 6.10 SR 8; |
| // - if target is a Tandem-extension data type declaration -- |
| // i.e., with an explicit COLLATE-clause, such as |
| // CAST(a AS CHAR(n) COLLATE SJIS) -- |
| // then the specified collation and coercibility (EXPLICIT) are used. |
| // |
| // Note that both of these come "for free": |
| // - if no COLLATE-clause was specified, |
| // SqlParser.y and the CharType-ctor-defaults will give |
| // DEFAULT/COERCIBLE to the unadorned data type; |
| // - if a COLLATE-clause was specified by user |
| // or if we are doing internal-expr casts -- |
| // e.g., if our caller is |
| // propagateCoAndCoToXXX(), or CodeGen, or ColReference::bindNode -- |
| // we simply use that collate/coerc. |
| // |
| // In fact, if these DIDN'T come for free, we would break INTERNAL casts: |
| // if (srcQual == NA_CHARACTER_TYPE && tgtQ == NA_CHARACTER_TYPE)[ |
| // if (((const CharType&)src).getCharSet() == tgt.getCharSet())[ |
| // CharType* newType = (CharType*)(tgtCT.newCopy(HEAP)); |
| // newType->setCoAndCo(CharInfo::DefaultCollation, COERCIBLE); |
| // return newType; |
| // |
| // But if in future we support Ansi "domains", |
| // then need to revisit this, per Ansi 6.10 SR 1a + 8. |
| // |
| // In other words, a) use isCompatible(), not isComparable(), |
| // and b) just pass the tgt's collation/coercibility along! |
| // |
| if ((srcQual == NA_LOB_TYPE) && (tgtQual != NA_LOB_TYPE)) |
| legal = FALSE; |
| else if (charsetChanged && src.isCompatible(res)) |
| legal = TRUE; |
| else if (src.isCompatible(tgt)) |
| legal = TRUE; |
| else if (srcQual == NA_CHARACTER_TYPE || tgtQual == NA_CHARACTER_TYPE) |
| { |
| legal = (srcQual != tgtQual); // if BOTH are CHAR: isCompatible() failed |
| |
| // disable casting KANJI/KSC5601 from/to any other data types. Same behavior as MP. |
| if ( (srcQual == NA_CHARACTER_TYPE && CharInfo::is_NCHAR_MP(((const CharType&)src).getCharSet())) || |
| (tgtQual == NA_CHARACTER_TYPE && CharInfo::is_NCHAR_MP(((const CharType&)tgt).getCharSet())) |
| ) |
| legal = FALSE; |
| // if BOTH are CHAR: make legal if both unknown charset |
| if ( (srcQual == NA_CHARACTER_TYPE && (((const CharType&)src).getCharSet())==CharInfo::UnknownCharSet) && |
| (tgtQual == NA_CHARACTER_TYPE && (((const CharType&)tgt).getCharSet())==CharInfo::UnknownCharSet) |
| ) |
| legal = TRUE; |
| |
| if ( srcQual == tgtQual ) // if BOTH are CHAR |
| { |
| if ( CmpCommon::getDefault(ALLOW_IMPLICIT_CHAR_CASTING) == DF_ON ) |
| { |
| legal = TRUE; |
| // |
| // NOTE: The Generator has code to throw in a Translate node if an |
| // incompatible character set comparison is attempted. |
| // |
| } |
| } |
| } |
| else if (srcQual == NA_NUMERIC_TYPE) |
| legal = numericCastIsCompatible(src, tgt); |
| else if (srcQual == NA_INTERVAL_TYPE) |
| legal = numericCastIsCompatible(tgt, src); |
| else if (srcQual == NA_DATETIME_TYPE && tgtQual == NA_NUMERIC_TYPE) |
| { |
| legal = |
| (((DatetimeType&)src).getSubtype() == DatetimeType::SUBTYPE_SQLDate); |
| } |
| else if (srcQual == NA_DATETIME_TYPE && tgtQual == NA_DATETIME_TYPE) { |
| legal = |
| ((DatetimeType&)src).getSubtype() == DatetimeType::SUBTYPE_SQLTimestamp |
| || |
| ((DatetimeType&)tgt).getSubtype() == DatetimeType::SUBTYPE_SQLTimestamp |
| || |
| ((DatetimeType&)tgt).fieldsOverlap((DatetimeType &)src); |
| } |
| |
| if (!src.isSupportedType() || !tgt.isSupportedType()) |
| { |
| if (src == tgt) |
| { |
| legal = TRUE; |
| } |
| else |
| { |
| legal = FALSE; |
| } |
| } |
| if (legal) |
| return (typeChanged)? result: getType(); |
| |
| // 4035 can't cast type from src to tgt |
| emitDyadicTypeSQLnameMsg(-4035, src, tgt); |
| return NULL; |
| } |
| |
| const NAType *CastConvert::synthesizeType() |
| { |
| const NAType * type = Cast::synthesizeType(); |
| if (type == NULL) |
| return NULL; |
| |
| NABuiltInTypeEnum qual = type->getTypeQualifier(); |
| if (qual != NA_CHARACTER_TYPE) |
| return type; |
| |
| // return a char type that is large enough to hold the ascii |
| // representation of the operand. |
| const NAType &childType = |
| child(0)->castToItemExpr()->getValueId().getType(); |
| Lng32 maxLength = childType.getDisplayLength(childType.getFSDatatype(), |
| childType.getNominalSize(), |
| childType.getPrecision(), |
| childType.getScale(), |
| 0); |
| CharType * origType = (CharType *)getType(); |
| if (DFS2REC::isAnyVarChar(origType->getFSDatatype()) == FALSE) |
| type = new HEAP |
| SQLChar(HEAP, maxLength, childType.supportsSQLnull(), |
| origType->isUpshifted()); |
| else |
| type = new HEAP |
| SQLVarChar(HEAP, maxLength, childType.supportsSQLnull(), |
| origType->isUpshifted()); |
| |
| return type; |
| } |
| |
| const NAType *CastType::synthesizeType() |
| { |
| if (getType()) |
| return getType(); |
| |
| ValueId childVid = child(0)->getValueId(); |
| NAType *newType = childVid.getType().newCopy(HEAP); |
| if (makeNullable_ && (NOT newType->supportsSQLnull())) |
| { |
| newType->setNullable(TRUE); |
| } |
| changeType(newType); |
| |
| return getType(); |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class CharFunc |
| // ----------------------------------------------------------------------- |
| |
| const NAType *CharFunc::synthesizeType() |
| { |
| // The expression is CHAR(<num>) or UNICODE_CHAR(<num>) or NCHAR(<num>) |
| // The result is the character that has the |
| // ASCII or UNICODE or <NATIONAL_CHARSET> code of <num>. |
| |
| // |
| // Type cast any params. |
| // |
| SQLInt nType(NULL, FALSE); |
| ValueId vid1 = child(0)->getValueId(); |
| vid1.coerceType(nType, NA_NUMERIC_TYPE); |
| |
| const NAType &typ1 = child(0)->getValueId().getType(); |
| if (typ1.getTypeQualifier() != NA_NUMERIC_TYPE) |
| { |
| // 4045 Operand must be numeric. |
| *CmpCommon::diags() << DgSqlCode(-4045) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| |
| // now it's safe to cast the type to numeric type |
| const NumericType &ntyp1 = (NumericType &) typ1; |
| |
| if (! ntyp1.isExact()) |
| { |
| // 4046 Operand must be exact. |
| *CmpCommon::diags() << DgSqlCode(-4046) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| |
| if (typ1.getScale() != 0) |
| { |
| // 4047 Operand must be not have scale. |
| *CmpCommon::diags() << DgSqlCode(-4047) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| |
| CharInfo::CharSet cs_to_use = charSet_ ; |
| |
| |
| CharType *result; |
| |
| if(charSet_ == CharInfo::UCS2 || charSet_ < 0) // UCS2, kanji and KSC5601_MP |
| result = new (HEAP) SQLChar ( HEAP, 1, |
| typ1.supportsSQLnullLogical(), |
| FALSE/*not upshift*/, |
| FALSE/*case sensitive*/, |
| FALSE/*not varchar*/, |
| charSet_); |
| else |
| result = new (HEAP) SQLVarChar(HEAP, CharInfo::maxBytesPerChar( cs_to_use ) |
| , typ1.supportsSQLnullLogical() |
| , FALSE /*not upshift*/ |
| , FALSE /*case sensitive*/ |
| , cs_to_use |
| , CharInfo::DefaultCollation |
| , CharInfo::COERCIBLE |
| ); |
| |
| return result; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class ConvertHex |
| // ----------------------------------------------------------------------- |
| |
| const NAType *ConvertHex::synthesizeType() |
| { |
| // |
| // Type cast any params. |
| // |
| ValueId vid = child(0)->getValueId(); |
| vid.coerceType(NA_CHARACTER_TYPE); |
| // |
| // Check that the operands are compatible. |
| // |
| const NAType* operand = &vid.getType(); |
| if (getOperatorType() == ITM_CONVERTFROMHEX) |
| { |
| if (operand->getTypeQualifier() != NA_CHARACTER_TYPE) { |
| // 4043 The operand of an ConvertHex function must be character. |
| *CmpCommon::diags() << DgSqlCode(-4043) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| |
| const CharType* charType = (CharType*)operand; |
| if ( charType->getCharSet() == CharInfo::UnknownCharSet ) { |
| const CharType* desiredType = |
| CharType::desiredCharType(CharInfo::ISO88591); |
| vid.coerceType(*desiredType, NA_CHARACTER_TYPE); |
| operand = &vid.getType(); |
| } |
| |
| // operand's size must be an even number since two hex characters make |
| // up one result byte. |
| const CharType* chartype1 = (CharType*)operand; |
| if (NOT chartype1->sizeIsEven()) { |
| *CmpCommon::diags() << DgSqlCode(-4068) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| } |
| |
| Int32 maxLength; |
| if (getOperatorType() == ITM_CONVERTTOHEX) |
| maxLength = operand->getNominalSize() * 2; |
| else |
| maxLength = operand->getNominalSize() / 2; |
| |
| NAType * type; |
| |
| if ( operand -> getTypeQualifier() == NA_CHARACTER_TYPE && |
| ( (operand -> isVaryingLen() == TRUE) || |
| ( (const CharType*)operand)->getCharSet()==CharInfo::UTF8 ) |
| ) |
| type = new HEAP |
| SQLVarChar(HEAP, maxLength, operand->supportsSQLnull()); |
| else |
| type = new HEAP |
| SQLChar(HEAP, maxLength, operand->supportsSQLnull()); |
| |
| // |
| // Return the result. |
| // |
| return type; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class CharLength |
| // ----------------------------------------------------------------------- |
| |
| const NAType *CharLength::synthesizeType() |
| { |
| // |
| // Type cast any params. |
| // |
| ValueId vid = child(0)->getValueId(); |
| vid.coerceType(NA_CHARACTER_TYPE); |
| // |
| // Check that the operands are compatible. |
| // |
| const NAType& operand = vid.getType(); |
| if (operand.getTypeQualifier() != NA_CHARACTER_TYPE) { |
| // 4043 The operand of a CHAR_LENGTH function must be character. |
| *CmpCommon::diags() << DgSqlCode(-4043) << DgString0(getTextForError()); |
| return NULL; |
| } |
| |
| const CharType* charOperand = (CharType*)&(vid.getType()); |
| |
| NAString defVal; |
| NABoolean charsetInference = |
| (CmpCommon::getDefault(INFER_CHARSET, defVal) == DF_ON); |
| if(charsetInference) |
| { |
| const CharType* desiredType = |
| CharType::findPushDownCharType(getDefaultCharSet, charOperand, 0); |
| |
| if ( desiredType ) |
| { |
| // just push down the charset field. All other fields are |
| // ignored. |
| |
| //coerceChildType((NAType&)*desiredType, NA_CHARACTER_TYPE); |
| vid.coerceType(*desiredType, NA_CHARACTER_TYPE); |
| |
| // get the newly pushed-down types |
| charOperand = (CharType*)&(vid.getType()); |
| } |
| } |
| |
| if ( charOperand -> getCharSet() == CharInfo::UnknownCharSet ) { |
| *CmpCommon::diags() << DgSqlCode(-4127); |
| return NULL; |
| } |
| |
| // |
| // Return the result. |
| // |
| return new HEAP |
| SQLInt(HEAP, FALSE // unsigned |
| ,operand.supportsSQLnullLogical() |
| ); |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class Concat |
| // ----------------------------------------------------------------------- |
| |
| const NAType *Concat::synthesizeType() |
| { |
| // |
| // Type cast any params. |
| // |
| ValueId vid1 = child(0)->getValueId(); |
| ValueId vid2 = child(1)->getValueId(); |
| // these first two extra calls handle any parameters |
| // operands must be gotten twice because they could change types. |
| |
| CharInfo::CharSet new_cs = getFirstKnownCharSet(vid1, vid2, vid2); |
| |
| // If vid not aleady of NA_CHARACTER_TYPE, make varchar(255) of character set = new_cs |
| vid1.coerceType(NA_CHARACTER_TYPE, new_cs); |
| vid2.coerceType(NA_CHARACTER_TYPE, new_cs); |
| |
| vid1.coerceType(vid2.getType(), NA_CHARACTER_TYPE); |
| vid2.coerceType(vid1.getType()); |
| // |
| // Synthesize the result. |
| // |
| const NAType* operand1 = &vid1.getType(); |
| const NAType* operand2 = &vid2.getType(); |
| |
| NABoolean isCaseInsensitive = FALSE; |
| |
| if ( operand1 -> getTypeQualifier() == NA_CHARACTER_TYPE && |
| operand2 -> getTypeQualifier() == NA_CHARACTER_TYPE |
| ) |
| { |
| const CharType *op1 = (CharType *)operand1; |
| const CharType *op2 = (CharType *)operand2; |
| |
| if (CmpCommon::wantCharSetInference()) { |
| |
| const CharType* desiredType = |
| CharType::findPushDownCharType(getDefaultCharSet, op1, op2, 0); |
| |
| if ( desiredType ) |
| { |
| // just push down the charset field. All other fields are |
| // meaningless. |
| vid1.coerceType((NAType&)*desiredType, NA_CHARACTER_TYPE); |
| vid2.coerceType((NAType&)*desiredType, NA_CHARACTER_TYPE); |
| } |
| } |
| |
| if (op1->isCaseinsensitive() || op2->isCaseinsensitive()) |
| { |
| isCaseInsensitive = TRUE; |
| } |
| } |
| |
| const NAType *result = operand1->synthesizeType(SYNTH_RULE_CONCAT, |
| *operand1, |
| *operand2, |
| HEAP); |
| if (result == NULL) |
| { |
| // 4034 The operation (~op1 ~operator ~op2) is not allowed. |
| emitDyadicTypeSQLnameMsg(-4034, *operand1, *operand2, getTextUpper()); |
| return result; |
| } |
| |
| if ((result->getTypeQualifier() == NA_CHARACTER_TYPE) && |
| (isCaseInsensitive)) |
| { |
| CharType *ct = (CharType *)result; |
| ct->setCaseinsensitive(TRUE); |
| } |
| |
| return result; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class ConstValue |
| // ----------------------------------------------------------------------- |
| |
| const NAType * ConstValue::synthesizeType() |
| { |
| return getType(); |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class ConvertTimestamp |
| // ----------------------------------------------------------------------- |
| |
| const NAType *ConvertTimestamp::synthesizeType() |
| { |
| // |
| // Type cast any params. |
| // |
| ValueId vid = child(0)->getValueId(); |
| SQLLargeInt largeintType(NULL); |
| vid.coerceType(largeintType); |
| // |
| // Check that the operands are compatible. |
| // |
| const NAType& operand = vid.getType(); |
| if (operand.getTypeQualifier() != NA_NUMERIC_TYPE OR |
| NOT ((NumericType&) operand).isExact()) { |
| // 4070 The operand of a CONVERTTIMESTAMP function must be exact numeric. |
| *CmpCommon::diags() << DgSqlCode(-4070) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| // |
| // Return the result. |
| // |
| return new HEAP SQLTimestamp (HEAP, operand.supportsSQLnullLogical(), |
| SQLTimestamp::DEFAULT_FRACTION_PRECISION); |
| |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class SleepFunction |
| // ----------------------------------------------------------------------- |
| const NAType *SleepFunction::synthesizeType() |
| { |
| return new HEAP SQLInt(HEAP, TRUE, TRUE); |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class UnixTimestamp |
| // ----------------------------------------------------------------------- |
| const NAType *UnixTimestamp::synthesizeType() |
| { |
| return new HEAP SQLLargeInt(HEAP, FALSE,FALSE); |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class CurrentTimestamp |
| // ----------------------------------------------------------------------- |
| |
| const NAType *CurrentTimestamp::synthesizeType() |
| { |
| return new HEAP SQLTimestamp (HEAP, FALSE, |
| SQLTimestamp::DEFAULT_FRACTION_PRECISION); |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class InternalTimestamp |
| // ----------------------------------------------------------------------- |
| |
| const NAType *InternalTimestamp::synthesizeType() |
| { |
| return new HEAP SQLTimestamp(HEAP, FALSE); |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class CurrentTimestampRunning |
| // ----------------------------------------------------------------------- |
| |
| const NAType *CurrentTimestampRunning::synthesizeType() |
| { |
| return new HEAP SQLTimestamp(HEAP, FALSE); |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class DateFormat |
| // ----------------------------------------------------------------------- |
| const NAType *DateFormat::synthesizeType() |
| { |
| // |
| // Type cast any params. |
| // |
| ValueId vid = child(0)->getValueId(); |
| SQLTimestamp timestampType(NULL); |
| vid.coerceType(timestampType); |
| // |
| // Check that the operands are compatible. |
| // |
| if (!vid.getType().isSupportedType()) { |
| // 4071 The operand of a DATEFORMAT function must be a datetime. |
| *CmpCommon::diags() << DgSqlCode(-4071) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| |
| if ((getDateFormat() == TIME_FORMAT_STR) && |
| ((vid.getType().getTypeQualifier() != NA_NUMERIC_TYPE) && |
| (vid.getType().getTypeQualifier() != NA_CHARACTER_TYPE) && |
| (vid.getType().getTypeQualifier() != NA_DATETIME_TYPE))) { |
| // 4071 The operand of a DATEFORMAT function must be a datetime. |
| *CmpCommon::diags() << DgSqlCode(-4071) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| |
| Lng32 length = 0; |
| NABoolean formatAsDate = FALSE; |
| NABoolean formatAsTimestamp = FALSE; |
| NABoolean formatAsTime = FALSE; |
| |
| if (vid.getType().getTypeQualifier() == NA_DATETIME_TYPE) |
| { |
| const DatetimeType& operand = (DatetimeType &)vid.getType(); |
| Lng32 frmt = ExpDatetime::getDatetimeFormat(formatStr_.data()); |
| |
| if (wasDateformat_) |
| { |
| length = operand.getDisplayLength(); |
| if(operand.containsField(REC_DATE_HOUR) && |
| ((frmt == ExpDatetime::DATETIME_FORMAT_USA) || |
| (frmt == ExpDatetime::DATETIME_FORMAT_TS7))) |
| length += 3; // add 3 for a blank and "am" or "pm" |
| } |
| else |
| { |
| length = ExpDatetime::getDatetimeFormatMaxLen(frmt); |
| } |
| } |
| else if (vid.getType().getTypeQualifier() == NA_CHARACTER_TYPE) |
| { |
| length = formatStr_.length(); |
| if (getDateFormat() == DATE_FORMAT_STR) |
| { |
| formatAsDate = TRUE; |
| } |
| else if (getDateFormat() == TIMESTAMP_FORMAT_STR) |
| { |
| formatAsTimestamp = TRUE; |
| } |
| else if (getDateFormat() == TIME_FORMAT_STR) |
| { |
| formatAsTime = TRUE; |
| } |
| } |
| else if (vid.getType().getTypeQualifier() == NA_NUMERIC_TYPE) |
| { |
| length = formatStr_.length(); |
| } |
| |
| if (formatAsDate) |
| return new HEAP SQLDate(HEAP, vid.getType().supportsSQLnullLogical()); |
| else if (formatAsTimestamp) |
| return new HEAP SQLTimestamp(HEAP, vid.getType().supportsSQLnullLogical()); |
| else if (formatAsTime) |
| return new HEAP SQLTime(HEAP, vid.getType().supportsSQLnullLogical()); |
| else |
| return new HEAP SQLChar(HEAP, length, vid.getType().supportsSQLnullLogical()); |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class DayOfWeek |
| // ----------------------------------------------------------------------- |
| |
| const NAType *DayOfWeek::synthesizeType() |
| { |
| // |
| // Type cast any params. |
| // |
| ValueId vid = child(0)->getValueId(); |
| SQLTimestamp timestampType(NULL); |
| vid.coerceType(timestampType); |
| // |
| // Check that the operand contains a DAY field |
| // |
| const NAType& operand = vid.getType(); |
| if ((operand.getTypeQualifier() != NA_DATETIME_TYPE) || |
| (!((DatetimeType&) operand).containsField (REC_DATE_YEAR)) || |
| (!((DatetimeType&) operand).containsField (REC_DATE_MONTH)) || |
| (!((DatetimeType&) operand).containsField (REC_DATE_DAY))) { |
| // Need to reword: |
| // 4072 The operand of function DAYOFWEEK must be a Datetime containing a DAY field. |
| *CmpCommon::diags() << DgSqlCode(-4072) << DgString0(getTextUpper()) << DgString1("YEAR, MONTH and DAY"); |
| return NULL; |
| } |
| // |
| // Return the result. |
| // |
| const Int16 DisAmbiguate = 0; // added for 64bit project |
| return new HEAP SQLNumeric(HEAP, FALSE, 1, 0, DisAmbiguate, |
| operand.supportsSQLnullLogical()); |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class DynamicParam |
| // ----------------------------------------------------------------------- |
| |
| const NAType *DynamicParam::synthesizeType() |
| { |
| // dynamic params are always nullable. |
| return new HEAP SQLUnknown(HEAP, TRUE); |
| } |
| |
| |
| const NAType *ExplodeVarchar::synthesizeType() |
| { |
| return getType(); |
| } |
| |
| const NAType *Format::synthesizeType() |
| { |
| NAType * retType = NULL; |
| |
| retType = (NAType *)ItemExpr::synthesizeType(); |
| |
| return retType; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class RoutineParam |
| // ----------------------------------------------------------------------- |
| |
| const NAType *RoutineParam::synthesizeType() |
| { |
| return getType(); |
| } |
| |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class Function -- a catchall for those funx which |
| // don't have their own virtual synthType() |
| // ----------------------------------------------------------------------- |
| |
| const NAType *Function::synthesizeType() |
| { |
| // Function derives directly from ItemExpr, so safe to do this |
| const NAType *result = ItemExpr::synthesizeType(); |
| |
| if (0) |
| { |
| if (result->getTypeQualifier() == NA_CHARACTER_TYPE) { |
| Int32 n = getNumCHARACTERArgs(this); |
| if (n > 1) { |
| #ifndef NDEBUG |
| // if (NCHAR_DEBUG > 0) |
| { |
| NAString unparsed(CmpCommon::statementHeap()); |
| unparse(unparsed, DEFAULT_PHASE, USER_FORMAT_DELUXE); |
| cerr << "## FUNCTION " << (Int32)getOperatorType() |
| << " (" << n << " char-type args)\t" |
| << unparsed |
| << "\t *might* not be computing its result collation/coercibility properly and/or pushing co/co back down to its children..." << endl; |
| |
| // Also emit a warning so you can catch this in regression results |
| unparsed.prepend("## FUNCTION, co/co issue: "); |
| *CmpCommon::diags() << DgSqlCode(+1110) << DgString0(unparsed); |
| } |
| #endif |
| } |
| |
| // WE DO *NOT* SYNTHESIZE A RESULT CoAndCo here |
| // nor push it back down to children via propagateCoAndCoToChildren, |
| // because that might not be the right thing. |
| // If the above CERR msg appears and you see a problem, |
| // you should add a synthesizeType method for that particular |
| // Function-derived class. |
| // CharType *ct = (CharType *)result; |
| // propagateCoAndCoToChildren(this, |
| // ct->getCollation(), ct->getCoercibility()); |
| } |
| } |
| |
| return result; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class Hash |
| // ----------------------------------------------------------------------- |
| |
| const NAType *Hash::synthesizeType() |
| { |
| // result of hash function is always a non-nullable, unsigned 32 bit integer |
| return new HEAP SQLInt(HEAP, FALSE, FALSE); |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class HashComb |
| // ----------------------------------------------------------------------- |
| |
| NABoolean HashCommon::areChildrenExactNumeric(Lng32 left, Lng32 right) |
| { |
| const NAType &typ1 = child(left)->getValueId().getType(); |
| const NAType &typ2 = child(right)->getValueId().getType(); |
| |
| if (typ1.getTypeQualifier() != NA_NUMERIC_TYPE || |
| typ2.getTypeQualifier() != NA_NUMERIC_TYPE) |
| return FALSE; |
| |
| // now it's safe to cast the types to numeric type |
| const NumericType &ntyp1 = (NumericType &) typ1; |
| const NumericType &ntyp2 = (NumericType &) typ2; |
| |
| return (ntyp1.isExact() AND ntyp2.isExact() AND |
| ntyp1.binaryPrecision() AND ntyp2.binaryPrecision() AND |
| ntyp1.getPrecision() == ntyp2.getPrecision() AND |
| ntyp1.isUnsigned() AND ntyp2.isUnsigned()); |
| } |
| |
| const NAType *HashComb::synthesizeType() |
| { |
| // Both dividend and divisor must be exact numeric with scale 0. |
| // The result has values from 0 to <divisor> - 1 and therefore |
| // can always fit into the data type of the divisor. |
| |
| // HashComb is an internal operator and errors are fatal |
| CMPASSERT(areChildrenExactNumeric(0, 1)); |
| |
| // result of hashcomb function is always a non-nullable, |
| // unsigned 32 bit integer |
| return new HEAP SQLInt(HEAP, FALSE, FALSE); |
| } |
| |
| const NAType *HiveHashComb::synthesizeType() |
| { |
| // Both dividend and divisor must be exact numeric with scale 0. |
| // The result has values from 0 to <divisor> - 1 and therefore |
| // can always fit into the data type of the divisor. |
| |
| // HashComb is an internal operator and errors are fatal |
| CMPASSERT(areChildrenExactNumeric(0, 1)); |
| |
| // result of hashcomb function is always a non-nullable, |
| // unsigned 32 bit integer |
| return new HEAP SQLInt(HEAP, FALSE, FALSE); |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class HashDistHash |
| // Hash Function used by Hash Partitioning. This function cannot change |
| // once Hash Partitioning is released! Defined for all data types, |
| // returns a 32 bit non-nullable hash value for the data item. |
| // ----------------------------------------------------------------------- |
| |
| const NAType *HashDistPartHash::synthesizeType() |
| { |
| // result of hash function is always a non-nullable, unsigned 32 bit integer |
| return new HEAP SQLInt(HEAP, FALSE, FALSE); |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class HiveHash |
| // ----------------------------------------------------------------------- |
| const NAType *HiveHash::synthesizeType() |
| { |
| // result of hivehash function is always a non-nullable, unsigned 32 bit integer |
| return new HEAP SQLInt(HEAP, FALSE, FALSE); |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class HashDistHashComb |
| // This function is used to combine two hash values to produce a new |
| // hash value. Used by Hash Partitioning. This function cannot change |
| // once Hash Partitioning is released! Defined for all data types, |
| // returns a 32 bit non-nullable hash value for the data item. |
| // ----------------------------------------------------------------------- |
| |
| const NAType *HashDistPartHashComb::synthesizeType() |
| { |
| // Both dividend and divisor must be exact numeric with scale 0. |
| // The result has values from 0 to <divisor> - 1 and therefore |
| // can always fit into the data type of the divisor. |
| |
| const NAType &typ1 = child(0)->getValueId().getType(); |
| const NAType &typ2 = child(1)->getValueId().getType(); |
| |
| // HashDistHashComb is an internal operator and errors are fatal |
| CMPASSERT(typ1.getTypeQualifier() == NA_NUMERIC_TYPE AND |
| typ2.getTypeQualifier() == NA_NUMERIC_TYPE); |
| |
| // now it's safe to cast the types to numeric type |
| const NumericType &ntyp1 = (NumericType &) typ1; |
| const NumericType &ntyp2 = (NumericType &) typ2; |
| |
| // Make sure both operands are SQLInt. |
| // |
| CMPASSERT(ntyp1.getFSDatatype() == REC_BIN32_UNSIGNED AND |
| ntyp2.getFSDatatype() == REC_BIN32_UNSIGNED AND |
| ntyp1.isAnyUnsignedInt() AND |
| ntyp2.isAnyUnsignedInt() AND |
| ntyp1.getPrecision() == SQL_UINT_PRECISION AND |
| ntyp2.getPrecision() == SQL_UINT_PRECISION); |
| |
| // result of hashcomb function is always a non-nullable, |
| // unsigned 32 bit integer |
| return new HEAP SQLInt(HEAP, FALSE, FALSE); |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class ReplaceNull |
| // ----------------------------------------------------------------------- |
| |
| const NAType *ReplaceNull::synthesizeType() |
| { |
| // result of ReplaceNull is always the same as the second argument |
| // except it is non nullable. |
| ValueId childId = child(1)->getValueId(); |
| NAType *newType = childId.getType().newCopy(HEAP); |
| //newType->setNullable(FALSE); |
| return newType; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class JulianTimestamp |
| // ----------------------------------------------------------------------- |
| |
| const NAType *JulianTimestamp::synthesizeType() |
| { |
| // |
| // Type cast any params. |
| // |
| ValueId vid = child(0)->getValueId(); |
| SQLTimestamp timestampType(NULL); |
| vid.coerceType(timestampType); |
| // |
| // Check that the operands are compatible. |
| // |
| const NAType& operand = vid.getType(); |
| if (operand.getTypeQualifier() != NA_DATETIME_TYPE) { |
| // 4071 The operand of a JULIANTIMESTAMP function must be a datetime. |
| *CmpCommon::diags() << DgSqlCode(-4071) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| // |
| // Return the result. |
| // |
| return new HEAP SQLLargeInt(HEAP, TRUE, operand.supportsSQLnullLogical()); |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class StatementExecutionCount |
| // ----------------------------------------------------------------------- |
| const NAType * StatementExecutionCount::synthesizeType() |
| { |
| return new HEAP SQLLargeInt(HEAP, TRUE,FALSE); |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class CurrentTransId |
| // ----------------------------------------------------------------------- |
| const NAType * CurrentTransId::synthesizeType() |
| { |
| return new HEAP SQLLargeInt(HEAP, TRUE,FALSE); |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class BitOperFunc |
| // ----------------------------------------------------------------------- |
| const NAType *BitOperFunc::synthesizeType() |
| { |
| NABoolean nullable = FALSE; |
| for (Int32 i = 0; i < getArity(); i++) |
| { |
| // type cast any params |
| ValueId vid = child(i)->getValueId(); |
| // untyped param operands are typed as Int32 Unsigned. |
| SQLInt dp(NULL, FALSE); |
| vid.coerceType(dp, NA_NUMERIC_TYPE); |
| |
| const NAType &typ = vid.getType(); |
| |
| if (typ.getTypeQualifier() != NA_NUMERIC_TYPE) |
| { |
| // 4045 operand must be numeric. |
| // 4052 2nd operand must be numeric. |
| // 4059 1st operand must be numeric. |
| if (getArity() == 1) |
| *CmpCommon::diags() |
| << DgSqlCode(-4045) << DgString0(getTextUpper()); |
| else |
| { |
| if (i == 0) |
| *CmpCommon::diags() |
| << DgSqlCode(-4059) << DgString0(getTextUpper()); |
| else if (i == 1) |
| *CmpCommon::diags() |
| << DgSqlCode(-4052) << DgString0(getTextUpper()); |
| else |
| *CmpCommon::diags() |
| << DgSqlCode(-4053) << DgString0(getTextUpper()); |
| } |
| |
| return NULL; |
| } |
| |
| if (typ.supportsSQLnullLogical()) |
| nullable = TRUE; |
| } |
| |
| const NAType *result = NULL; |
| |
| switch (getOperatorType()) |
| { |
| case ITM_BITAND: |
| case ITM_BITOR: |
| case ITM_BITXOR: |
| { |
| CMPASSERT(getArity() == 2); |
| |
| // now it's safe to cast the types to numeric type |
| const NumericType &ntyp1 = (NumericType &) child(0)->getValueId().getType(); |
| const NumericType &ntyp2 = (NumericType &) child(1)->getValueId().getType(); |
| |
| if (NOT ntyp1.isExact() OR NOT ntyp2.isExact() OR |
| ntyp1.isBigNum() OR ntyp2.isBigNum()) |
| { |
| // 4046 BIT operation is only defined for exact numeric types. |
| *CmpCommon::diags() << DgSqlCode(-4046) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| |
| if (ntyp1.getScale() != 0 OR |
| ntyp2.getScale() != 0) |
| { |
| // 4047 Arguments of BIT operation must both have a scale of 0. |
| *CmpCommon::diags() << DgSqlCode(-4047) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| |
| UInt32 flags = NAType::MAKE_UNION_RESULT_BINARY; |
| |
| result = ntyp1.synthesizeType( |
| SYNTH_RULE_UNION, |
| ntyp1, |
| ntyp2, |
| HEAP, |
| &flags); |
| } |
| break; |
| |
| case ITM_BITNOT: |
| { |
| CMPASSERT(getArity() == 1); |
| |
| // now it's safe to cast the types to numeric type |
| const NumericType &ntyp1 = (NumericType &) child(0)->getValueId().getType(); |
| |
| if (NOT ntyp1.isExact() OR ntyp1.isBigNum()) |
| { |
| // 4046 BIT operation is only defined for exact numeric types. |
| *CmpCommon::diags() << DgSqlCode(-4046) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| |
| if (ntyp1.getScale() != 0) |
| { |
| // 4047 Arguments of BIT operation must both have a scale of 0. |
| *CmpCommon::diags() << DgSqlCode(-4047) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| |
| // result of BITNOT is the same type as the operand, if the |
| // operand is binary. |
| // If operand is decimal, then convert it to equivalent binary. |
| NAType * result1 = NULL; |
| if (ntyp1.binaryPrecision()) |
| result1 = (NumericType*)ntyp1.newCopy(HEAP); |
| else |
| { |
| const Int16 DisAmbiguate = 0; |
| result1 = new HEAP SQLNumeric(HEAP, NOT ntyp1.isUnsigned(), |
| ntyp1.getPrecision(), |
| ntyp1.getScale(), |
| DisAmbiguate); // added for 64bit proj. |
| } |
| |
| result1->setNullable(nullable); |
| result = result1; |
| } |
| break; |
| |
| case ITM_BITEXTRACT: |
| { |
| CMPASSERT(getArity() == 3); |
| |
| // now it's safe to cast the types to numeric type |
| const NumericType &ntyp1 = (NumericType &) child(0)->getValueId().getType(); |
| const NumericType &ntyp2 = (NumericType &) child(1)->getValueId().getType(); |
| const NumericType &ntyp3 = (NumericType &) child(2)->getValueId().getType(); |
| |
| if (ntyp1.isBigNum() || |
| ntyp1.isDecimal()) |
| { |
| // 4046 BIT operation is only defined for exact numeric types. |
| *CmpCommon::diags() << DgSqlCode(-4046) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| |
| if ((NOT ntyp2.isExact()) || |
| (NOT ntyp3.isExact())) |
| { |
| // 4046 BIT operation is only defined for exact numeric types. |
| *CmpCommon::diags() << DgSqlCode(-4046) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| |
| if ((ntyp2.getScale() != 0) || |
| (ntyp3.getScale() != 0)) |
| { |
| // 4047 Arguments of BIT operation must both have a scale of 0. |
| *CmpCommon::diags() << DgSqlCode(-4047) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| |
| // result can contain as many bits as the length of the operand. |
| // Make the result an Int32 or Int64. |
| NAType * result1 = NULL; |
| if (ntyp1.getNominalSize() <= 9) |
| result = new HEAP SQLInt(HEAP, TRUE, nullable); |
| else |
| result = new HEAP SQLLargeInt(HEAP, TRUE, nullable); |
| } |
| break; |
| |
| default: |
| { |
| // 4000 Internal Error. This function not supported. |
| *CmpCommon::diags() << DgSqlCode(-4000); |
| result = NULL; |
| } |
| break; |
| } |
| |
| return result; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class MathFunc |
| // ----------------------------------------------------------------------- |
| |
| NAType* MathFunc::findReturnTypeForFloorCeil(NABoolean nullable) |
| { |
| assert (getArity() == 1); |
| |
| for (Int32 i = 0; i < getArity(); i++) { |
| ValueId vid = child(i)->getValueId(); |
| const NAType &typ = vid.getType(); |
| |
| if (typ.getTypeQualifier() != NA_NUMERIC_TYPE) |
| break; |
| |
| const NumericType& nuTyp = (NumericType&)(typ); |
| |
| // If the child is a SQL integer, just return the same type |
| // for the function. |
| if ( nuTyp.isInteger() ) |
| return nuTyp.newCopy(HEAP); |
| |
| // Here we only return a modified data type for SQLDecimal |
| // or SQLNumeric. For all others, we return SQLDoublePrecision. |
| if ( !nuTyp.decimalPrecision() ) |
| break; |
| |
| switch ( nuTyp.getFSDatatype() ) { |
| |
| // for SQLDecimal |
| case REC_DECIMAL_UNSIGNED: |
| case REC_DECIMAL_LSE: |
| return new HEAP SQLDecimal(HEAP, nuTyp.getNominalSize() |
| ,0 |
| ,!nuTyp.isUnsigned() |
| ,nuTyp.supportsSQLnull() |
| ); |
| break; |
| |
| // for SQLNumeric |
| case REC_BIN8_UNSIGNED: |
| case REC_BIN16_UNSIGNED: |
| case REC_BIN32_UNSIGNED: |
| case REC_BIN64_UNSIGNED: |
| case REC_BIN8_SIGNED: |
| case REC_BIN16_SIGNED: |
| case REC_BIN32_SIGNED: |
| case REC_BIN64_SIGNED: |
| return new HEAP SQLNumeric(HEAP, nuTyp.getNominalSize() |
| ,nuTyp.getPrecision() |
| ,0 |
| ,!nuTyp.isUnsigned() |
| ,nuTyp.supportsSQLnull() |
| ); |
| |
| default: |
| break; |
| } |
| } |
| return new HEAP SQLDoublePrecision(HEAP, nullable); |
| } |
| |
| const NAType *MathFunc::synthesizeType() |
| { |
| CMPASSERT(getArity() <= 2); |
| |
| NABoolean nullable = FALSE; |
| |
| for (Int32 i = 0; i < getArity(); i++) |
| { |
| // type cast any params |
| ValueId vid = child(i)->getValueId(); |
| |
| SQLDoublePrecision dp(NULL, TRUE); |
| vid.coerceType(dp, NA_NUMERIC_TYPE); |
| |
| const NAType &typ = vid.getType(); |
| |
| if (typ.getTypeQualifier() != NA_NUMERIC_TYPE) |
| { |
| // 4045 operand must be numeric. |
| // 4052 2nd operand must be numeric. |
| // 4059 1st operand must be numeric. |
| if (getArity() == 1) |
| *CmpCommon::diags() |
| << DgSqlCode(-4045) << DgString0(getTextUpper()); |
| else |
| *CmpCommon::diags() |
| << DgSqlCode(i ? -4052 : -4059) << DgString0(getTextUpper()); |
| |
| return NULL; |
| } |
| |
| if (typ.supportsSQLnullLogical()) |
| nullable = TRUE; |
| } |
| |
| const NAType *result = NULL; |
| |
| switch (getOperatorType()) |
| { |
| case ITM_ABS: |
| case ITM_ACOS: |
| case ITM_ASIN: |
| case ITM_ATAN: |
| case ITM_ATAN2: |
| case ITM_COS: |
| case ITM_COSH: |
| case ITM_DEGREES: |
| case ITM_EXP: |
| case ITM_EXPONENT: |
| case ITM_LOG: |
| case ITM_LOG10: |
| case ITM_LOG2: |
| case ITM_PI: |
| case ITM_POWER: |
| case ITM_RADIANS: |
| case ITM_ROUND: |
| case ITM_SCALE_TRUNC: |
| case ITM_SIN: |
| case ITM_SINH: |
| case ITM_SQRT: |
| case ITM_TAN: |
| case ITM_TANH: |
| { |
| result = new HEAP SQLDoublePrecision(HEAP, nullable); |
| } |
| break; |
| |
| case ITM_FLOOR: |
| case ITM_CEIL: |
| { |
| result = findReturnTypeForFloorCeil(nullable); |
| } |
| break; |
| |
| default: |
| { |
| // 4000 Internal Error. This function not supported. |
| *CmpCommon::diags() << DgSqlCode(-4000); |
| result = NULL; |
| } |
| break; |
| } |
| |
| return result; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class Modulus |
| // ----------------------------------------------------------------------- |
| |
| const NAType *Modulus::synthesizeType() |
| { |
| // The expression is <dividend> mod <divisor>. |
| // Both dividend and divisor must be exact numeric with scale 0. |
| // The result has values from 0 to <divisor> - 1 and therefore |
| // can always fit into the data type of the divisor. |
| |
| // |
| // Type cast any params. |
| // |
| ValueId vid1 = child(0)->getValueId(); |
| ValueId vid2 = child(1)->getValueId(); |
| SQLInt si(NULL, TRUE); |
| vid1.coerceType(si, NA_NUMERIC_TYPE); |
| vid2.coerceType(si, NA_NUMERIC_TYPE); |
| |
| const NAType &typ1 = child(0)->getValueId().getType(); |
| const NAType &typ2 = child(1)->getValueId().getType(); |
| |
| if (typ1.getTypeQualifier() != NA_NUMERIC_TYPE OR |
| typ2.getTypeQualifier() != NA_NUMERIC_TYPE) |
| { |
| // 4046 Modulus function is only defined for exact numeric types. |
| *CmpCommon::diags() << DgSqlCode(-4046) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| |
| // now it's safe to cast the types to numeric type |
| const NumericType &ntyp1 = (NumericType &) typ1; |
| const NumericType &ntyp2 = (NumericType &) typ2; |
| |
| if (NOT ntyp1.isExact() OR NOT ntyp2.isExact()) |
| { |
| // 4046 Modulus function is only defined for exact numeric types. |
| *CmpCommon::diags() << DgSqlCode(-4046) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| |
| if (ntyp1.getScale() != 0 OR |
| ntyp2.getScale() != 0) |
| { |
| // 4047 Arguments of modulus function must both have a scale of 0. |
| *CmpCommon::diags() << DgSqlCode(-4047) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| |
| if (ntyp1.decimalPrecision() && ntyp1.getPrecision() > MAX_NUMERIC_PRECISION) |
| { |
| // 3037: precision of dividend cannot exceed 18. |
| *CmpCommon::diags() << DgSqlCode(-3037) << DgString0(child(0)->getTextUpper()); |
| return NULL; |
| } |
| |
| if (ntyp2.decimalPrecision() && ntyp2.getPrecision() > MAX_NUMERIC_PRECISION) |
| { |
| // 3037: precision of divisor cannot exceed 18. |
| *CmpCommon::diags() << DgSqlCode(-3037) << DgString0(child(1)->getTextUpper()); |
| return NULL; |
| } |
| |
| NumericType * result = (NumericType*)ntyp2.newCopy(HEAP); |
| result->setNullable(typ1.supportsSQLnullLogical() || |
| typ2.supportsSQLnullLogical()); |
| if (ntyp1.isUnsigned()) |
| result->makeUnsigned(); |
| else |
| result->makeSigned(); |
| |
| return result; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class Repeat |
| // ----------------------------------------------------------------------- |
| |
| const NAType *Repeat::synthesizeType() |
| { |
| // The expression is REPEAT(<value1>, <value2>) |
| // The result is string <value1> repeated <value2> times. |
| |
| // |
| // Type cast any params. |
| // |
| ValueId vid1 = child(0)->getValueId(); |
| ValueId vid2 = child(1)->getValueId(); |
| vid1.coerceType(NA_CHARACTER_TYPE); |
| |
| const SQLInt t(FALSE); |
| vid2.coerceType(t, NA_NUMERIC_TYPE); |
| |
| const NAType &typ1 = child(0)->getValueId().getType(); |
| if (typ1.getTypeQualifier() != NA_CHARACTER_TYPE) |
| { |
| // 4051 Operand 1 must be character. |
| *CmpCommon::diags() << DgSqlCode(-4051) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| |
| const NAType &typ2 = child(1)->getValueId().getType(); |
| if (typ2.getTypeQualifier() != NA_NUMERIC_TYPE) |
| { |
| // 4052 Operand 2 must be numeric. |
| *CmpCommon::diags() << DgSqlCode(-4052) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| |
| const CharType &ctyp1 = (CharType &) typ1; |
| |
| // now it's safe to cast the type to numeric type |
| const NumericType &ntyp2 = (NumericType &) typ2; |
| |
| if (ntyp2.getScale() != 0) |
| { |
| // 4047 Operand must be not have scale. |
| *CmpCommon::diags() << DgSqlCode(-4047) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| |
| if (! ntyp2.isExact()) |
| { |
| // 4046 Operand 2 must be exact. |
| *CmpCommon::diags() << DgSqlCode(-4046) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| |
| Int64 size_in_bytes; |
| Int64 size_in_chars; |
| |
| Int32 maxCharColLen = CmpCommon::getDefaultNumeric(TRAF_MAX_CHARACTER_COL_LENGTH); |
| |
| // figure out the max length of result. |
| NABoolean negate; |
| if (maxLengthWasExplicitlySet_) |
| { |
| // cap max len at traf_max_character_col_length |
| size_in_bytes = |
| MINOF(maxCharColLen, getMaxLength()); |
| size_in_chars = |
| size_in_bytes / CharInfo::minBytesPerChar(ctyp1.getCharSet()); |
| } |
| else if ((child(1)->getOperatorType() == ITM_CONSTANT) && |
| (child(1)->castToConstValue(negate))) |
| { |
| ConstValue * cv = child(1)->castToConstValue(negate); |
| Int64 repeatCount; |
| |
| if (! cv->isNull()) |
| { |
| if (cv->canGetExactNumericValue()) { |
| repeatCount = cv->getExactNumericValue(); |
| if (repeatCount <= 0) |
| repeatCount = 1; |
| } else { |
| // 4116 The 2nd operand of REPEAT(o1, o2) is invalid |
| *CmpCommon::diags() << DgSqlCode(-4116) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| } |
| else |
| { |
| repeatCount = 1; |
| } |
| |
| size_in_bytes = typ1.getNominalSize() * repeatCount; |
| size_in_chars = ctyp1.getStrCharLimit() * repeatCount; |
| // check size limit only for fixed character type |
| if ( ! typ1.isVaryingLen() ) { |
| if ( size_in_bytes > maxCharColLen ) { |
| *CmpCommon::diags() << DgSqlCode(-4129) |
| << DgString0(getTextUpper()); |
| return NULL; |
| } |
| } else // varchar. The nominal size of the result is |
| // the min of (size, maxCharColLen). |
| { |
| size_in_bytes = MINOF(maxCharColLen, size_in_bytes); |
| size_in_chars = size_in_bytes / CharInfo::minBytesPerChar(ctyp1.getCharSet()); |
| } |
| } |
| else if (getMaxLength() > -1) |
| { |
| size_in_bytes = MINOF(maxCharColLen, |
| getMaxLength() * typ1.getNominalSize()); |
| size_in_chars = size_in_bytes / CharInfo::minBytesPerChar(ctyp1.getCharSet()); |
| } |
| else |
| { |
| // Assign some arbitrary max result size since we can't |
| // figure out the actual max size. |
| size_in_bytes = maxCharColLen; |
| size_in_chars = size_in_bytes / CharInfo::minBytesPerChar(ctyp1.getCharSet()); |
| } |
| |
| NAType *result = |
| new (HEAP) SQLVarChar(HEAP, CharLenInfo((Lng32)size_in_chars, (Lng32)size_in_bytes), |
| (typ1.supportsSQLnullLogical() || |
| typ2.supportsSQLnullLogical()), |
| ctyp1.isUpshifted(), |
| ctyp1.isCaseinsensitive(), |
| ctyp1.getCharSet(), |
| ctyp1.getCollation(), |
| ctyp1.getCoercibility()); |
| |
| return result; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class Replace |
| // ----------------------------------------------------------------------- |
| |
| const NAType *Replace::synthesizeType() |
| { |
| // |
| // Type cast any params. |
| // |
| ValueId vid1 = child(0)->getValueId(); |
| ValueId vid2 = child(1)->getValueId(); |
| ValueId vid3 = child(2)->getValueId(); |
| |
| CharInfo::CharSet new_cs = getFirstKnownCharSet(vid1, vid2, vid3); |
| |
| vid1.coerceType(NA_CHARACTER_TYPE, new_cs); |
| vid2.coerceType(NA_CHARACTER_TYPE, new_cs); |
| vid3.coerceType(NA_CHARACTER_TYPE, new_cs); |
| |
| const NAType *typ1 = &(child(0)->getValueId().getType()); |
| const NAType *typ2 = &(child(1)->getValueId().getType()); |
| const NAType *typ3 = &(child(2)->getValueId().getType()); |
| |
| |
| /* Soln-10-050426-7137 begin */ |
| |
| NAString defVal; |
| NABoolean charsetInference = |
| (CmpCommon::getDefault(INFER_CHARSET, defVal) == DF_ON); |
| |
| if(charsetInference) |
| { |
| const CharType *replaceSource = (CharType *)typ1; |
| const CharType *replaceChar= (CharType *)typ2; |
| const CharType *replacingChar = (CharType *)typ3; |
| const CharType* desiredType = |
| CharType::findPushDownCharType(getDefaultCharSet, |
| replaceSource, replaceChar, replacingChar, 0); |
| |
| if ( desiredType ) |
| { |
| // push down charset and re-synthesize |
| vid1.coerceType((NAType&)*desiredType, NA_CHARACTER_TYPE); |
| vid2.coerceType((NAType&)*desiredType, NA_CHARACTER_TYPE); |
| vid3.coerceType((NAType&)*desiredType, NA_CHARACTER_TYPE); |
| |
| // get the newly pushed-down types |
| typ1 = (CharType*)&vid1.getType(); |
| typ2 = (CharType*)&vid2.getType(); |
| typ3 = (CharType*)&vid3.getType(); |
| } |
| |
| } |
| |
| |
| /* Soln-10-050426-7137 end */ |
| |
| // typ3 does not need to be comparable, only compatible! |
| // |
| if (typ1->getTypeQualifier() != NA_CHARACTER_TYPE OR |
| NOT typ1->isCompatible(*typ3)) { |
| // 4064 The operands of a $0 function must be compatible character types. |
| // ##Should say "The FIRST and THIRD operands must be compatible..." |
| *CmpCommon::diags() << DgSqlCode(-4064) << DgString0(getTextForError());; |
| return NULL; |
| } |
| if (NOT typ1->isComparable(*typ2, this)) { |
| // 4063 The operands of a $0 function must be comparable character types. |
| // ##Should say "The FIRST and SECOND operands must be comparable..." |
| *CmpCommon::diags() << DgSqlCode(-4063) << DgString0(getTextForError()); |
| return NULL; |
| } |
| |
| const CharType *ctyp1 = (CharType *)typ1; |
| Lng32 minLength_in_bytes = ctyp1->getDataStorageSize(); |
| Lng32 minLength_in_chars = ctyp1->getStrCharLimit(); |
| |
| Lng32 ctype2Length_in_bytes = ((CharType *)typ2)->getDataStorageSize(); |
| Lng32 ctype3Length_in_bytes = ((CharType *)typ3)->getDataStorageSize(); |
| Lng32 ctype2Length_in_chars = ((CharType *)typ2)->getStrCharLimit(); |
| Lng32 ctype3Length_in_chars = ((CharType *)typ3)->getStrCharLimit(); |
| |
| if ( ctype2Length_in_bytes == 0 ) { |
| *CmpCommon::diags() << DgSqlCode(-8428) << DgString0(getTextForError()); |
| return NULL; |
| } |
| |
| // Fix for CR 10-000724-1369 |
| // figure out result size. |
| |
| Lng32 size_in_bytes = minLength_in_bytes;; |
| Lng32 size_in_chars = minLength_in_chars; |
| |
| // NOTE: We are trying to find the MAX result size! |
| |
| if ( ((CharType *)typ2)->isVaryingLen() ) |
| { |
| ctype2Length_in_chars = 1; // Use *minimum* possible length |
| ctype2Length_in_bytes = 1; // Use *minimum* possible length |
| } |
| |
| if ( ctyp1->getCharSet() == CharInfo::UNICODE ) |
| { |
| if (ctype2Length_in_chars < ctype3Length_in_chars) |
| { |
| Int32 maxOccurrences = size_in_chars / ctype2Length_in_chars; |
| Int32 remainder = size_in_chars - (maxOccurrences * ctype2Length_in_chars); |
| size_in_chars = maxOccurrences * ctype3Length_in_chars + remainder; |
| size_in_bytes = size_in_chars * ctyp1->getBytesPerChar(); |
| } |
| } |
| else |
| { |
| if (ctype2Length_in_chars < ctype3Length_in_chars) |
| { |
| Int32 maxOccurrences = size_in_chars / ctype2Length_in_chars; |
| Int32 remainder = size_in_chars - (maxOccurrences * ctype2Length_in_chars); |
| size_in_chars = maxOccurrences * ctype3Length_in_chars + remainder; |
| } |
| if (ctype2Length_in_bytes < ctype3Length_in_bytes) |
| { |
| Int32 maxOccurrences = size_in_bytes / ctype2Length_in_bytes; |
| Int32 remainder = size_in_bytes - (maxOccurrences * ctype2Length_in_bytes); |
| size_in_bytes = maxOccurrences * ctype3Length_in_bytes + remainder; |
| } |
| } |
| |
| Int32 maxLenInBytes = CmpCommon::getDefaultNumeric(TRAF_MAX_CHARACTER_COL_LENGTH); |
| if (size_in_bytes > maxLenInBytes) |
| { |
| size_in_bytes = maxLenInBytes; |
| size_in_chars = size_in_bytes / CharInfo::minBytesPerChar(ctyp1->getCharSet()); |
| } |
| |
| CharLenInfo CLInfo( size_in_chars, size_in_bytes ); |
| |
| NAType *result = |
| new (HEAP) SQLVarChar(HEAP, CLInfo, |
| (typ1->supportsSQLnullLogical() || |
| typ2->supportsSQLnullLogical() || |
| typ3->supportsSQLnullLogical()), |
| ctyp1->isUpshifted(), |
| ctyp1->isCaseinsensitive(), |
| ctyp1->getCharSet(), |
| ctyp1->getCollation(), |
| ctyp1->getCoercibility()); |
| |
| return result; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class HashDistrib |
| // ----------------------------------------------------------------------- |
| |
| const NAType *HashDistrib::synthesizeType() |
| { |
| // Both operands (hash of the partitioning keys and number of partitions) |
| // must be exact numeric with scale 0. The result has values from 0 to |
| // <number of partitions> - 1 and therefore can always fit into the data |
| // type of the number of partitions. |
| |
| const NAType &typ1 = child(0)->getValueId().getType(); |
| const NAType &typ2 = child(1)->getValueId().getType(); |
| |
| if (typ1.getTypeQualifier() != NA_NUMERIC_TYPE OR |
| typ2.getTypeQualifier() != NA_NUMERIC_TYPE) |
| { |
| // 4045 Progressive Distribution function is only defined |
| // for numeric types. |
| *CmpCommon::diags() << DgSqlCode(-4045); |
| return NULL; |
| } |
| |
| // now it's safe to cast the types to numeric type |
| const NumericType &ntyp1 = (NumericType &) typ1; |
| const NumericType &ntyp2 = (NumericType &) typ2; |
| |
| if (NOT ntyp1.isExact() OR NOT ntyp2.isExact()) |
| { |
| // 4046 Progessive Distribution function is only defined for |
| // exact numeric types. |
| *CmpCommon::diags() << DgSqlCode(-4046); |
| return NULL; |
| } |
| |
| if (ntyp1.getScale() != 0 OR |
| ntyp2.getScale() != 0) |
| { |
| // 4047 Arguments of Progessive Distribution function must both |
| // have a scale of 0. |
| *CmpCommon::diags() << DgSqlCode(-4047); |
| return NULL; |
| } |
| |
| NAType *result = typ2.newCopy(HEAP); |
| |
| // the only thing the LHS contributes is that the result may become nullable |
| if (typ1.supportsSQLnullLogical()) result->setNullable(TRUE); |
| |
| return result; |
| } |
| |
| const NAType *ProgDistribKey::synthesizeType() |
| { |
| // return: Large Int. |
| return new HEAP SQLLargeInt(HEAP, TRUE, FALSE); |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class PAGroup |
| // ----------------------------------------------------------------------- |
| |
| const NAType *PAGroup::synthesizeType() |
| { |
| // Both operands (pre-grouped number of partitions and number of groups) |
| // must be exact numeric with scale 0. The result has values from 0 to |
| // <number of groups> - 1 and therefore can always fit into the data |
| // type of the number of groups. |
| |
| const NAType &typ1 = child(0)->getValueId().getType(); |
| const NAType &typ2 = child(1)->getValueId().getType(); |
| const NAType &typ3 = child(2)->getValueId().getType(); |
| |
| if (typ1.getTypeQualifier() != NA_NUMERIC_TYPE OR |
| typ2.getTypeQualifier() != NA_NUMERIC_TYPE OR |
| typ3.getTypeQualifier() != NA_NUMERIC_TYPE) |
| { |
| // 4045 PA Group function is only defined |
| // for numeric types. |
| *CmpCommon::diags() << DgSqlCode(-4045); |
| return NULL; |
| } |
| |
| // now it's safe to cast the types to numeric type |
| const NumericType &ntyp1 = (NumericType &) typ1; |
| const NumericType &ntyp2 = (NumericType &) typ2; |
| const NumericType &ntyp3 = (NumericType &) typ3; |
| |
| if (NOT ntyp1.isExact() OR NOT ntyp2.isExact() OR NOT ntyp3.isExact()) |
| { |
| // 4046 PA Group function is only defined for |
| // exact numeric types. |
| *CmpCommon::diags() << DgSqlCode(-4046); |
| return NULL; |
| } |
| |
| if (ntyp1.getScale() != 0 OR ntyp2.getScale() != 0 OR ntyp3.getScale() != 0) |
| { |
| // 4047 Arguments of the PA Group function must both |
| // have a scale of 0. |
| *CmpCommon::diags() << DgSqlCode(-4047); |
| return NULL; |
| } |
| |
| NAType *result = typ1.newCopy(HEAP); |
| |
| return result; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class Encode |
| // ----------------------------------------------------------------------- |
| |
| const NAType *CompEncode::synthesizeType() |
| { |
| ValueId vid = child(0)->getValueId(); |
| const NAType &src = vid.getType(); |
| |
| // result of encode function is a non-nullable fixed char. |
| // Result is not nullable |
| // because null values are encoded too. |
| // Length of encode function is equal to length_ field, if it |
| // is set to a positive number. Otherwise, it is equal to the |
| // total size of operand. |
| |
| Lng32 keyLength = 0; |
| NABoolean supportsSQLnull = FALSE; |
| if (regularNullability_) |
| { |
| // should not be common for CompEncode, preserve nullability of child |
| keyLength = src.getNominalSize(); |
| supportsSQLnull = src.supportsSQLnull(); |
| } |
| else |
| { |
| if (length_ < 0) |
| // common case for encode, include prefix fields but leave |
| // out the var len header (if any), which is not order-preserving |
| keyLength = src.getTotalSize() - src.getVarLenHdrSize(); |
| else |
| keyLength = length_; |
| } |
| |
| if (src.getTypeQualifier() != NA_CHARACTER_TYPE) |
| { |
| return new HEAP SQLChar(HEAP, keyLength, supportsSQLnull); |
| } |
| else |
| { |
| const CharType &cSrc = (CharType&)src; |
| CharInfo::Collation collation = cSrc.getCollation(); |
| |
| // set casesensitivity of encoding based on child's type. |
| // This may get overwritten by the caller (for example, to |
| // build a key for a predicate of the form: where keycol = 'val', |
| // both sides of the predicate must be caseinsensitive. |
| if (cSrc.isCaseinsensitive()) |
| { |
| setCaseinsensitiveEncode(TRUE); |
| } |
| |
| setEncodedCollation(cSrc.getCollation()); |
| |
| if (CollationInfo::isSystemCollation(collation)) |
| { |
| keyLength = |
| CompEncode::getEncodedLength( collation, |
| collationType_, |
| child(0)->getValueId().getType().getNominalSize(), |
| cSrc.supportsSQLnull()); |
| |
| switch (collationType_) |
| { |
| case CollationInfo::Sort: |
| { |
| // in this case the encode is non nullable if not regularNullability |
| return new HEAP SQLChar(HEAP, keyLength, |
| supportsSQLnull); |
| } |
| case CollationInfo::Compare: |
| { |
| return new HEAP SQLChar(HEAP, keyLength, |
| cSrc.supportsSQLnull()); |
| } |
| case CollationInfo::Search: |
| { |
| return new HEAP SQLVarChar(HEAP, keyLength, |
| cSrc.supportsSQLnull()); |
| } |
| default: |
| { |
| CMPASSERT(0); |
| return NULL; |
| } |
| } |
| } |
| else |
| { |
| return new HEAP SQLChar(HEAP, keyLength, supportsSQLnull); |
| } |
| } |
| } |
| |
| const NAType *CompDecode::synthesizeType() |
| { |
| if (unencodedType_) |
| return unencodedType_; |
| else |
| return CompEncode::synthesizeType(); |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class Extract |
| // ----------------------------------------------------------------------- |
| |
| const NAType *Extract::synthesizeType() |
| { |
| // Assert that we are bound, or created by Generator, so we have type info. |
| ValueId vid = child(0)->getValueId(); |
| CMPASSERT(vid != NULL_VALUE_ID); |
| const DatetimeIntervalCommonType &dti = |
| (DatetimeIntervalCommonType &)vid.getType(); |
| |
| NABuiltInTypeEnum type = dti.getTypeQualifier(); |
| if (type != NA_DATETIME_TYPE && type != NA_INTERVAL_TYPE) { |
| // 4036 The source field must be of DateTime or Interval type. |
| *CmpCommon::diags() << DgSqlCode(-4036); |
| |
| if (getFieldFunction()) |
| { |
| // 4062 The preceding error actually occurred in function $0~String0. |
| *CmpCommon::diags() << DgSqlCode(-4062) << DgString0(dti.getFieldName(getExtractField())); |
| } |
| |
| return NULL; |
| } |
| // ANSI 6.6 SR 3a. |
| enum rec_datetime_field extractStartField = getExtractField(); |
| enum rec_datetime_field extractEndField = extractStartField; |
| |
| if (extractStartField > REC_DATE_MAX_SINGLE_FIELD) |
| { |
| // YEARQUARTER, YEARMONTH, or YEARWEEK |
| extractStartField = REC_DATE_YEAR; |
| |
| if (extractEndField > REC_DATE_YEARMONTH_EXTRACT) |
| extractEndField = REC_DATE_DAY; // extracting week requires the day |
| else |
| extractEndField = REC_DATE_MONTH; // months/quarters need only the month |
| |
| if (type == NA_INTERVAL_TYPE) // YEARQUARTER etc. are not supported on intervals |
| { |
| *CmpCommon::diags() << DgSqlCode(-4037) |
| << DgString0(dti.getFieldName(getExtractField())) |
| << DgString1(dti.getTypeSQLname(TRUE /*terse*/)); |
| return NULL; |
| } |
| } |
| |
| if (dti.getStartField() > extractStartField || |
| dti.getEndField() < extractEndField || |
| !dti.isSupportedType()) { |
| // 4037 cannot extract field from type |
| *CmpCommon::diags() << DgSqlCode(-4037) |
| << DgString0(dti.getFieldName(getExtractField())) |
| << DgString1(dti.getTypeSQLname(TRUE /*terse*/)); |
| |
| return NULL; |
| } |
| // ANSI 6.6 SR 4. Precision is implementation-defined: |
| // EXTRACT(YEAR from datetime): result precision is 4 + scale |
| // EXTRACT(other from datetime): result precision is 2 + scale |
| // EXTRACT(startfield from interval): result precision is leading prec + scal |
| // EXTRACT(other from interval): result precision is 2 + scale |
| // where scale is 0 if extract field is not SECOND, else at least fract prec. |
| // |
| Lng32 prec, scale = 0; |
| if (type == NA_INTERVAL_TYPE && getExtractField() == dti.getStartField()) |
| prec = dti.getLeadingPrecision(); |
| else if (getExtractField() == REC_DATE_YEAR) |
| prec = 4; // YEAR field can be 9999 max |
| else if (getExtractField() == REC_DATE_YEARQUARTER_EXTRACT || |
| getExtractField() == REC_DATE_YEARQUARTER_D_EXTRACT) |
| prec = 5; // YEARQUARTER is yyyyq |
| else if (getExtractField() == REC_DATE_YEARMONTH_EXTRACT || |
| getExtractField() == REC_DATE_YEARMONTH_D_EXTRACT) |
| prec = 6; // YEARMONTH is yyyymm |
| else if (getExtractField() == REC_DATE_YEARWEEK_EXTRACT || |
| getExtractField() == REC_DATE_YEARWEEK_D_EXTRACT) |
| prec = 6; // YEARMWEEK is yyyyww |
| else |
| prec = 2; // else max of 12, 31, 24, 59 |
| if (getExtractField() == REC_DATE_SECOND) { |
| prec += dti.getFractionPrecision(); |
| scale += dti.getFractionPrecision(); |
| } |
| const Int16 disAmbiguate = 0; // added for 64bit project |
| return new HEAP |
| SQLNumeric(HEAP, type == NA_INTERVAL_TYPE, /*allowNegValues*/ |
| prec, |
| scale, |
| disAmbiguate, |
| dti.supportsSQLnull()); |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class Increment |
| // ----------------------------------------------------------------------- |
| |
| const NAType *Increment::synthesizeType() |
| { |
| // It should get the type of its child |
| return &child(0)->getValueId().getType(); |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class Decrement |
| // ----------------------------------------------------------------------- |
| |
| const NAType *Decrement::synthesizeType() |
| { |
| return &child(0)->getValueId().getType(); |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class TriRelational |
| // ----------------------------------------------------------------------- |
| |
| const NAType *TriRelational::synthesizeType() |
| { |
| ItemExprList exprList1(child(0).getPtr(), HEAP); |
| ItemExprList exprList2(child(1).getPtr(), HEAP); |
| if (exprList1.entries() != exprList2.entries()) { |
| // 4042 The operands of a comparison predicate must be of equal degree. |
| *CmpCommon::diags() << DgSqlCode(-4042); |
| return NULL; |
| } |
| NABoolean allowsUnknown = FALSE; |
| for (CollIndex i = 0; i < exprList1.entries(); i++) { |
| // |
| // Type cast any params. |
| // |
| ValueId vid1 = exprList1[i]->getValueId(); |
| ValueId vid2 = exprList2[i]->getValueId(); |
| vid1.coerceType(vid2.getType(), NA_NUMERIC_TYPE); |
| vid2.coerceType(vid1.getType()); |
| // |
| // Check that the operands are compatible. |
| // |
| const NAType& operand1 = vid1.getType(); |
| const NAType& operand2 = vid2.getType(); |
| if (NOT operand1.isCompatible(operand2)) { |
| // 4041 comparison between these two types is not allowed |
| emitDyadicTypeSQLnameMsg(-4041, operand1, operand2); |
| return NULL; |
| } |
| |
| allowsUnknown = allowsUnknown OR |
| operand1.supportsSQLnullLogical() OR |
| operand2.supportsSQLnullLogical(); |
| } |
| // |
| // is the third operand a boolean? |
| // |
| if (child(2)->getValueId().getType().getTypeQualifier() != NA_BOOLEAN_TYPE) { |
| // 4048 third arg of ternary comparison must be boolean |
| *CmpCommon::diags() << DgSqlCode(-4048) << |
| DgString0(child(2)->getValueId().getType().getTypeSQLname(TRUE /*terse*/)); |
| return NULL; |
| } |
| // |
| // Return the result. |
| // |
| return new HEAP SQLBooleanRelat(HEAP, allowsUnknown); |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class RangeLookup |
| // ----------------------------------------------------------------------- |
| |
| const NAType *RangeLookup::synthesizeType() |
| { |
| // the result is a signed 32 bit number |
| return new HEAP SQLInt(HEAP, TRUE,FALSE); |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class HostVar |
| // ----------------------------------------------------------------------- |
| |
| const NAType *HostVar::synthesizeType() |
| { |
| return getType(); |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class InverseOrder |
| // ----------------------------------------------------------------------- |
| |
| const NAType *InverseOrder::synthesizeType() |
| { |
| return &child(0)->getValueId().getType(); |
| } |
| |
| // member functions for class Like |
| // ----------------------------------------------------------------------- |
| |
| const NAType *PatternMatchingFunction::synthesizeType() |
| { |
| // |
| // Type cast any params. |
| // |
| ValueId vid1 = child(0)->getValueId(); |
| ValueId vid2 = child(1)->getValueId(); |
| ValueId vid3; |
| CharType * cType1 = 0; |
| CharType * cType2 = 0; |
| |
| // if either side of LIKE was an untyped param, then assign it |
| // the same casesensitive attr, collation, and character set as the other side. |
| if ( vid1.getType().getTypeQualifier() != NA_UNKNOWN_TYPE && |
| vid2.getType().getTypeQualifier() == NA_UNKNOWN_TYPE) |
| { |
| vid1.coerceType(NA_CHARACTER_TYPE); |
| cType1 = (CharType*)&vid1.getType(); |
| vid2.coerceType(NA_CHARACTER_TYPE, cType1->getCharSet()); |
| cType2 = (CharType*)&vid2.getType(); |
| cType2->setCollation(cType1->getCollation()); |
| cType2->setCaseinsensitive(cType1->isCaseinsensitive()); |
| } |
| else if ( vid2.getType().getTypeQualifier() != NA_UNKNOWN_TYPE && |
| vid1.getType().getTypeQualifier() == NA_UNKNOWN_TYPE) |
| { |
| vid2.coerceType(NA_CHARACTER_TYPE); |
| cType2 = (CharType*)&vid2.getType(); |
| vid1.coerceType(NA_CHARACTER_TYPE, cType2->getCharSet()); |
| cType1 = (CharType*)&vid1.getType(); |
| cType1->setCollation(cType2->getCollation()); |
| cType1->setCaseinsensitive(cType2->isCaseinsensitive()); |
| } |
| else |
| { |
| vid1.coerceType(NA_CHARACTER_TYPE); |
| vid2.coerceType(NA_CHARACTER_TYPE); |
| } |
| |
| const NAType *typ1 = &vid1.getType(); |
| const NAType *typ2 = &vid2.getType(); |
| const NAType *typ3 = NULL; |
| |
| if (getArity() > 2) { // Escape clause was specified |
| vid3 = child(2)->getValueId(); |
| const SQLChar charType(NULL, 1); |
| vid3.coerceType(charType); |
| typ3 = &vid3.getType(); |
| } |
| |
| // 2/13/98: make sure like pattern and source types are comparable. |
| const NAType& operand1 = vid1.getType(); |
| const NAType& operand2 = vid2.getType(); |
| const CharType *likeSource = (CharType*)&operand1; |
| const CharType *likePat = (CharType*)&operand2; |
| const CharType *escapeChar = ( getArity() > 2 ) ? |
| (CharType*)&(vid3.getType()) : 0; |
| |
| NAString defVal; |
| NABoolean charsetInference = |
| (CmpCommon::getDefault(INFER_CHARSET, defVal) == DF_ON); |
| |
| if(charsetInference) |
| { |
| const CharType* desiredType = |
| CharType::findPushDownCharType(getDefaultCharSet, |
| likeSource, likePat, escapeChar, 0); |
| |
| if ( desiredType ) |
| { |
| // push down charset and re-synthesize |
| vid1.coerceType((NAType&)*desiredType, NA_CHARACTER_TYPE); |
| vid2.coerceType((NAType&)*desiredType, NA_CHARACTER_TYPE); |
| if ( getArity() > 2 ) |
| vid3.coerceType((NAType&)*desiredType, NA_CHARACTER_TYPE); |
| |
| // get the newly pushed-down types |
| typ1 = &vid1.getType(); |
| typ2 = &vid2.getType(); |
| typ3 = ( getArity() > 2 ) ? &vid3.getType() : 0; |
| } |
| |
| } |
| |
| // Check that the operands are comparable. |
| // |
| if (NOT typ1->isComparable(*typ2, this, NAType::EmitErrIfAnyChar) OR |
| (typ3 AND |
| NOT typ1->isComparable(*typ3, this, NAType::EmitErrIfAnyChar)) OR |
| typ1->getTypeQualifier() != NA_CHARACTER_TYPE) { |
| // 4050 The operands of a LIKE predicate must be comparable character types. |
| *CmpCommon::diags() << DgSqlCode(-4050) << DgString0("LIKE"); |
| return NULL; |
| } |
| |
| // If any of the arguments can be nullable then LIKE can evaluate to Unknown |
| NABoolean allowsUnknown = typ1->supportsSQLnull() OR |
| typ2->supportsSQLnull() OR |
| (typ3 AND |
| typ3->supportsSQLnull()); |
| |
| return new HEAP SQLBooleanRelat(HEAP, allowsUnknown); |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for classes Lower and Upper` |
| // ----------------------------------------------------------------------- |
| |
| const NAType *Lower::synthesizeType() |
| { |
| // |
| // Type cast any params. |
| // |
| ValueId vid = child(0)->getValueId(); |
| vid.coerceType(NA_CHARACTER_TYPE); |
| // |
| // Check that the operands are compatible. |
| // |
| const NAType& operand = vid.getType(); |
| if (operand.getTypeQualifier() != NA_CHARACTER_TYPE) { |
| // 4043 The operand of a LOWER function must be character. |
| *CmpCommon::diags() << DgSqlCode(-4043) << DgString0(getTextForError()); |
| return NULL; |
| } |
| |
| CharType *ct = (CharType *)&operand; |
| |
| if ( CharInfo::is_NCHAR_MP(ct->getCharSet()) ) { |
| // 3217: Character set KANJI/KSC5601 is not allowed in the LOWER function. |
| *CmpCommon::diags() << DgSqlCode(-3217) |
| << DgString0(CharInfo::getCharSetName(ct->getCharSet())) |
| << DgString1("LOWER"); |
| } |
| |
| if ((ct->isUpshifted()) || |
| (ct->isCaseinsensitive())) { |
| ct = (CharType *)ct->newCopy(HEAP); |
| if (ct->isUpshifted()) |
| ct->setUpshifted(FALSE); |
| if (ct->isCaseinsensitive()) |
| ct->setCaseinsensitive(TRUE); |
| } |
| // |
| // For UTF8 strings, we must make the TYPE be a VARCHAR because there are certain |
| // UCS2 characters (e.g. 0x0130) where the value of LOWER is actually fewer bytes in |
| // length than the original character! |
| // |
| if (ct->getCharSet() == CharInfo::UTF8) { |
| // NOTE: See comment near end of Upper::synthesizeType() for reason we don't multiply by 3 here. |
| ct = new (HEAP) SQLVarChar(HEAP, CharLenInfo(ct->getStrCharLimit(), (ct->getDataStorageSize())) |
| ,ct->supportsSQLnull() |
| ,ct->isUpshifted() |
| ,ct->isCaseinsensitive() |
| ,ct->getCharSet() |
| ,ct->getCollation() |
| ,ct->getCoercibility() |
| ); |
| } |
| |
| return ct; |
| } |
| |
| const NAType *Upper::synthesizeType() |
| { |
| // |
| // Type cast any params. |
| // |
| ValueId vid = child(0)->getValueId(); |
| vid.coerceType(NA_CHARACTER_TYPE); |
| // |
| // Check that the operands are compatible. |
| // |
| const NAType& operand = vid.getType(); |
| |
| if (operand.getTypeQualifier() != NA_CHARACTER_TYPE) { |
| // 4043 The operand of an UPPER function must be character. |
| *CmpCommon::diags() << DgSqlCode(-4043) << DgString0(getTextForError()); |
| return NULL; |
| } |
| |
| CharType *ct = (CharType *)&operand; |
| |
| if ( CharInfo::is_NCHAR_MP(ct->getCharSet()) ) { |
| *CmpCommon::diags() << DgSqlCode(-3217) |
| << DgString0(CharInfo::getCharSetName(ct->getCharSet())) |
| << DgString1("UPPER"); |
| } |
| |
| if (NOT ct->isUpshifted()) { |
| ct = (CharType *)ct->newCopy(HEAP); |
| ct->setUpshifted(TRUE); |
| } |
| |
| if (ct->getCharSet() == CharInfo::UNICODE) { |
| ct = new (HEAP) SQLVarChar(HEAP, 3*(ct->getStrCharLimit()) |
| ,ct->supportsSQLnull() |
| ,ct->isUpshifted() |
| ,ct->isCaseinsensitive() |
| ,ct->getCharSet() |
| ,ct->getCollation() |
| ,ct->getCoercibility() |
| ); |
| } |
| |
| if (ct->getCharSet() == CharInfo::UTF8) { |
| // |
| // NOTE: For some UCS2 characters, the UPPER function can produce *three* UCS2 characters |
| // and for that reason, the UPPER function provides for 3 times as much output as the |
| // input string is long. HOWEVER, such is never the case for the LOWER function. |
| // |
| ct = new (HEAP) SQLVarChar(HEAP, CharLenInfo(3*ct->getStrCharLimit(), 3*(ct->getDataStorageSize())) |
| ,ct->supportsSQLnull() |
| ,ct->isUpshifted() |
| ,ct->isCaseinsensitive() |
| ,ct->getCharSet() |
| ,ct->getCollation() |
| ,ct->getCoercibility() |
| ); |
| } |
| |
| return ct; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class NATypeToItem |
| // ----------------------------------------------------------------------- |
| const NAType * NATypeToItem::synthesizeType() |
| { |
| return natype_pointer; |
| }; |
| |
| const NAType* |
| NATypeToItem::pushDownType(NAType& newType, |
| enum NABuiltInTypeEnum defaultQualifier) |
| { |
| return &newType; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class OctetLength |
| // ----------------------------------------------------------------------- |
| |
| const NAType *OctetLength::synthesizeType() |
| { |
| // |
| // Type cast any params. |
| // |
| ValueId vid = child(0)->getValueId(); |
| vid.coerceType(NA_CHARACTER_TYPE); |
| // |
| // Check that the operands are compatible. |
| // |
| const NAType& operand = vid.getType(); |
| if (operand.getTypeQualifier() != NA_CHARACTER_TYPE) { |
| // 4043 The operand of an OCTET_LENGTH function must be character. |
| *CmpCommon::diags() << DgSqlCode(-4043) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| |
| const CharType* charOperand = (CharType*)&(vid.getType()); |
| |
| NAString defVal; |
| NABoolean charsetInference = |
| (CmpCommon::getDefault(INFER_CHARSET, defVal) == DF_ON); |
| |
| if(charsetInference) |
| { |
| const CharType* desiredType = |
| CharType::findPushDownCharType(getDefaultCharSet, charOperand, 0); |
| |
| if ( desiredType ) |
| { |
| // push down charset and re-synthesize |
| vid.coerceType((NAType&)*desiredType, NA_CHARACTER_TYPE); |
| |
| // get the newly pushed-down types |
| charOperand = (CharType*)&(vid.getType()); |
| } |
| } |
| |
| if ( charOperand -> getCharSet() == CharInfo::UnknownCharSet ) { |
| *CmpCommon::diags() << DgSqlCode(-4127); |
| return NULL; |
| } |
| |
| // |
| // Return the result. |
| // |
| return new HEAP SQLInt(HEAP, FALSE // unsigned |
| ,operand.supportsSQLnullLogical() |
| ); |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class PositionFunc |
| // ----------------------------------------------------------------------- |
| |
| const NAType *PositionFunc::synthesizeType() |
| { |
| |
| // |
| // Type cast any params. |
| // |
| ValueId vid1 = child(0)->getValueId(); |
| ValueId vid2 = child(1)->getValueId(); |
| |
| CharInfo::CharSet new_cs = getFirstKnownCharSet(vid1, vid2, vid2); |
| vid1.coerceType(NA_CHARACTER_TYPE, new_cs); |
| vid2.coerceType(NA_CHARACTER_TYPE, new_cs); |
| |
| const NAType *operand1 = &vid1.getType(); |
| const NAType *operand2 = &vid2.getType(); |
| const NAType *operand3 = NULL; |
| const NAType *operand4 = NULL; |
| |
| |
| // |
| // Check that the operands are comparable. |
| // ##Hmm, Ansi 6.6 does NOT say they need to be comparable, |
| // ##just compatible (same char repertoire, i.e. same charset), |
| // ##but that must be a mistake (we need string =ity testing |
| // ##to do POSITION, so the collations will need to be the same)... |
| // |
| /* |
| if (NOT operand1.isComparable(operand2, this, NAType::EmitErrIfAnyChar) || |
| operand1.getTypeQualifier() != NA_CHARACTER_TYPE || |
| operand2.getTypeQualifier() != NA_CHARACTER_TYPE) { |
| // 4063 The operands of a $0 function must be comparable character types. |
| *CmpCommon::diags() << DgSqlCode(-4063) << DgString0(getTextForError()); |
| return NULL; |
| } |
| */ |
| |
| |
| if (operand1->getTypeQualifier() != NA_CHARACTER_TYPE OR |
| operand2->getTypeQualifier() != NA_CHARACTER_TYPE) { |
| // 4063 The operands of a POSITION function must be character. |
| *CmpCommon::diags() << DgSqlCode(-4063) << DgString0(getTextForError()); |
| return NULL; |
| } |
| |
| |
| // 1/5/98: make sure position pattern and source types are comparable. |
| const CharType *posPat = (CharType*)operand1; |
| const CharType *posSource = (CharType*)operand2; |
| |
| |
| NAString defVal; |
| NABoolean charsetInference = |
| (CmpCommon::getDefault(INFER_CHARSET, defVal) == DF_ON); |
| |
| if(charsetInference) |
| { |
| // 9/24/98: charset inference |
| const CharType* desiredType = |
| CharType::findPushDownCharType(getDefaultCharSet, |
| posPat, posSource, 0); |
| |
| if ( desiredType ) |
| { |
| // push down charset and re-synthesize |
| vid1.coerceType((NAType&)*desiredType, NA_CHARACTER_TYPE); |
| vid2.coerceType((NAType&)*desiredType, NA_CHARACTER_TYPE); |
| |
| // get the newly pushed-down types |
| /* |
| posPat = (CharType*)&(vid1.getType()); |
| posSource = (CharType*)&(vid2.getType()); |
| */ |
| operand1 = &vid1.getType(); |
| operand2 = &vid2.getType(); |
| |
| } |
| } |
| |
| /* |
| if ( ! (posPat->isComparable(*posSource, TRUE)) ) { // } |
| */ |
| if ( ! (operand1->isComparable(*operand2, this, NAType::EmitErrIfAnyChar)) ) { |
| // 4063 The operands of a POSITION function must be character. |
| *CmpCommon::diags() << DgSqlCode(-4063) << DgString0(getTextForError()); |
| return NULL; |
| } |
| |
| |
| // |
| // Return the result. |
| // |
| return new HEAP |
| SQLInt(HEAP, FALSE, // unsigned |
| operand1->supportsSQLnullLogical() || |
| operand2->supportsSQLnullLogical() |
| ); |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class Substring |
| // ----------------------------------------------------------------------- |
| |
| const NAType *Substring::synthesizeType() |
| { |
| // |
| // Type cast any params. |
| // |
| ValueId vid1 = child(0)->getValueId(); |
| ValueId vid2 = child(1)->getValueId(); |
| vid1.coerceType(NA_CHARACTER_TYPE); |
| |
| SQLInt si(NULL); |
| vid2.coerceType(si, NA_NUMERIC_TYPE); |
| if (getArity() == 3) |
| { |
| ValueId vid3 = child(2)->getValueId(); |
| vid3.coerceType(si, NA_NUMERIC_TYPE); |
| } |
| |
| const NAType *operand1 = &child(0)->getValueId().getType(); |
| const NAType *operand2 = &child(1)->getValueId().getType(); |
| const NAType *operand3 = NULL; |
| |
| if (getArity() == 3) |
| { |
| operand3 = &child(2)->getValueId().getType(); |
| } |
| |
| if ((operand1->getTypeQualifier() != NA_CHARACTER_TYPE) && |
| (operand1->getFSDatatype() != REC_CLOB)) { |
| // 4051 The first operand of a SUBSTRING function must be character. |
| *CmpCommon::diags() << DgSqlCode(-4051) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| if (operand2->getTypeQualifier() != NA_NUMERIC_TYPE) { |
| // 4052 The second operand of a SUBSTRING function must be numeric. |
| *CmpCommon::diags() << DgSqlCode(-4052) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| if (((NumericType*)operand2)->getScale() != 0) { |
| // 4047 The second operand of a SUBSTRING function must have a scale of 0. |
| *CmpCommon::diags() << DgSqlCode(-4047) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| if (operand3) { |
| if (operand3->getTypeQualifier() != NA_NUMERIC_TYPE) { |
| // 4053 The third operand of a SUBSTRING function must be numeric. |
| *CmpCommon::diags() << DgSqlCode(-4053) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| |
| if (((NumericType*)operand3)->getScale() != 0) { |
| // 4047 The third operand of a SUBSTR function must have a scale of 0. |
| *CmpCommon::diags() << DgSqlCode(-4047) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| } |
| |
| CharInfo::CharSet op1_cs = operand1->getCharSet(); |
| /* |
| ((operand1->getFSDatatype() == REC_CLOB) ? |
| ((SQLClob*)operand1)->getCharSet() : |
| ((CharType *)operand1)->getCharSet()); |
| */ |
| |
| const CharType *charOperand = (CharType *) operand1; |
| Lng32 maxLength_bytes = charOperand->getDataStorageSize(); |
| Lng32 maxLength_chars = charOperand->getPrecisionOrMaxNumChars(); |
| if ( maxLength_chars <= 0 ) // If unlimited |
| maxLength_chars = maxLength_bytes / CharInfo::minBytesPerChar(op1_cs); |
| |
| NABoolean negate; |
| Lng32 pos = 0; |
| { |
| // The position arg is allowed to be negative (see Ansi 6.7 GR 1). |
| ConstValue *cv = child(1)->castToConstValue(negate); |
| |
| // adjust the max length for the result only if it is a positive start position. |
| // solu 10-030603-6815. |
| if (cv && negate == FALSE) { |
| if (cv->canGetExactNumericValue()) { |
| Int64 pos64 = cv->getExactNumericValue(); |
| if (pos64 <= MINOF(maxLength_chars,INT_MAX)) { |
| pos = int64ToInt32(pos64); |
| if ((pos-1) > 0) { |
| maxLength_chars -= (pos-1); // shorten max |
| maxLength_bytes -= (pos-1) * CharInfo::minBytesPerChar(op1_cs); |
| if ( maxLength_bytes > charOperand->getDataStorageSize() ) |
| maxLength_bytes = charOperand->getDataStorageSize() ; |
| } |
| } // value is in bounds |
| } // can get exact numeric |
| } // constant pos op |
| } // position operand |
| |
| NABoolean resultIsFixedChar = FALSE; |
| Lng32 length = 0; |
| Int64 length64 = 0; |
| if (operand3) { |
| ConstValue *cv = child(2)->castToConstValue(negate); |
| if (cv) { |
| |
| if (negate) { |
| // 8403 The length arg of a SUBSTRING function cannot be less than zero. |
| *CmpCommon::diags() << DgSqlCode(-8403); |
| return NULL; |
| } |
| |
| if (cv->canGetExactNumericValue()) { |
| length64 = cv->getExactNumericValue(); |
| |
| if (length64 <= INT_MAX) { |
| length = int64ToInt32(length64); |
| |
| if (maxLength_chars > length) { |
| maxLength_chars = length; |
| maxLength_bytes = MINOF(maxLength_bytes, length * CharInfo::maxBytesPerChar(op1_cs)); |
| } |
| } // value is in bounds |
| } // can get exact numeric |
| } // constant length op |
| } // length operand specified |
| |
| /* |
| length64 = length; |
| |
| if ((NOT DFS2REC::isAnyVarChar(operand1->getFSDatatype())) && |
| (pos > 0) && |
| (length64 > 0) && |
| ((pos + length64 - 1) <= maxLength)) |
| resultIsFixedChar = TRUE; |
| */ |
| |
| // 12/22/97: the substring inherits the charset, collation and |
| // coercibility from the source string. |
| |
| /* if (resultIsFixedChar) |
| return new HEAP |
| SQLChar(maxLength, |
| operand1->supportsSQLnull() OR |
| operand2->supportsSQLnull() OR |
| ((operand3 != NULL) AND operand3->supportsSQLnull()) |
| ,charOperand->isUpshifted() |
| ,charOperand->isCaseinsensitive() |
| ,FALSE |
| ,charOperand->getCharSet() |
| ,charOperand->getCollation() |
| ,charOperand->getCoercibility() |
| ); |
| else |
| */ |
| |
| |
| if (operand1->getFSDatatype() == REC_CLOB) |
| { |
| return new HEAP |
| SQLClob(HEAP, maxLength_bytes, Lob_Invalid_Storage, |
| operand1->supportsSQLnull() OR |
| operand2->supportsSQLnull() OR |
| ((operand3 != NULL) AND operand3->supportsSQLnull())); |
| } |
| else |
| { |
| |
| |
| return new HEAP |
| SQLVarChar(HEAP, CharLenInfo(maxLength_chars, maxLength_bytes), // OLD: maxLength |
| operand1->supportsSQLnull() OR |
| operand2->supportsSQLnull() OR |
| ((operand3 != NULL) AND operand3->supportsSQLnull()) |
| ,charOperand->isUpshifted() |
| ,charOperand->isCaseinsensitive() |
| ,operand1->getCharSet() |
| ,charOperand->getCollation() |
| ,charOperand->getCoercibility() |
| ); |
| |
| } |
| |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class Trim |
| // ----------------------------------------------------------------------- |
| |
| const NAType *Trim::synthesizeType() |
| { |
| // |
| // Type cast any params. |
| // |
| ValueId vid1 = child(0)->getValueId(); |
| ValueId vid2 = child(1)->getValueId(); |
| |
| CharInfo::CharSet new_cs = getFirstKnownCharSet(vid1, vid2, vid2); |
| vid1.coerceType(NA_CHARACTER_TYPE, new_cs); |
| vid2.coerceType(NA_CHARACTER_TYPE, new_cs); |
| |
| if (vid1.getType().getTypeQualifier() != NA_CHARACTER_TYPE || |
| vid2.getType().getTypeQualifier() != NA_CHARACTER_TYPE) |
| { |
| //4133: Both trim character and source have to be CHARACTER typed. |
| *CmpCommon::diags() << DgSqlCode(-4133); |
| return NULL; |
| } |
| |
| // |
| // Check that the operands are compatible. |
| // |
| const CharType *trimChar = (const CharType *)&vid1.getType(); |
| const CharType *trimSource = (const CharType *)&vid2.getType(); |
| |
| // charset inference |
| if ( trimChar->getCharSet() == CharInfo::UnknownCharSet && |
| trimSource->getCharSet() != CharInfo::UnknownCharSet |
| ) |
| { |
| // Special case for MP NCHAR when the default trim character is |
| // a single single-byte character like ' '. Here we prepend a space |
| // character to the local string copy (locale_string) inside |
| // the constant value holding the sinlge byte trim character. During |
| // invocation of vid1.coerceType(), the newly fabricated double-byte |
| // trim character will be instantiated in the constant value object. |
| if (CharInfo::is_NCHAR_MP(trimSource->getCharSet()) && |
| vid1.getItemExpr()-> getOperator() == ITM_CONSTANT) |
| { |
| ConstValue* trimCharValue = (ConstValue*)vid1.getItemExpr(); |
| if ( trimCharValue -> getStorageSize() == 1) { |
| trimCharValue->getLocaleString()->prepend(' '); |
| } |
| } |
| |
| |
| vid1.coerceType(*trimSource, NA_CHARACTER_TYPE); |
| trimChar = (CharType*)&vid1.getType(); |
| |
| } else |
| if ( trimChar->getCharSet() != CharInfo::UnknownCharSet && |
| trimSource->getCharSet() == CharInfo::UnknownCharSet |
| ) |
| { |
| vid2.coerceType(*trimChar, NA_CHARACTER_TYPE); |
| trimSource = (CharType*)&vid2.getType(); |
| } else |
| if ( trimChar->getCharSet() == CharInfo::UnknownCharSet && |
| trimSource->getCharSet() == CharInfo::UnknownCharSet |
| ) |
| { |
| const CharType* desiredType = |
| CharType::findPushDownCharType(getDefaultCharSet, 0); |
| |
| vid1.coerceType(*desiredType, NA_CHARACTER_TYPE); |
| trimChar = (CharType*)&vid1.getType(); |
| vid2.coerceType(*trimChar, NA_CHARACTER_TYPE); |
| trimSource = (CharType*)&vid2.getType(); |
| } |
| |
| if (NOT trimChar->isComparable(*trimSource, this, NAType::EmitErrIfAnyChar)) { |
| // Per Ansi 6.7 SR 6(f), trim source and trim char must be comparable. |
| // |
| // 4063 The operands of a $0 function must be comparable character types. |
| *CmpCommon::diags() << DgSqlCode(-4063) << DgString0(getTextForError()); |
| return NULL; |
| } |
| |
| // Per Ansi 6.7 SR 6(g,h), the result |
| // takes the collation and coercibility of the trim source. |
| // |
| Int32 size = trimSource->getDataStorageSize(); |
| |
| return new HEAP |
| SQLVarChar(HEAP, CharLenInfo(trimSource->getStrCharLimit(), size ) |
| ,trimChar->supportsSQLnull() OR trimSource->supportsSQLnull() |
| ,trimSource->isUpshifted() |
| ,trimSource->isCaseinsensitive() |
| ,trimSource->getCharSet() |
| ,trimSource->getCollation() |
| ,trimSource->getCoercibility() |
| ); |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class UnLogic |
| // ----------------------------------------------------------------------- |
| |
| const NAType *UnLogic::synthesizeType() |
| { |
| NABoolean allowsUnknown = FALSE; |
| |
| // All Unary Ops evaluate to TRUE/FALSE except NOT which can also |
| // evaluate to UNKNOWN |
| switch(getOperatorType()) |
| { |
| case ITM_NOT: |
| { |
| CMPASSERT(child(0).getValueId().getType().getTypeQualifier() == NA_BOOLEAN_TYPE); |
| const SQLBooleanRelat& operand0 = (SQLBooleanRelat &) child(0).getValueId().getType(); |
| allowsUnknown = operand0.canBeSQLUnknown(); |
| break; |
| } |
| |
| case ITM_IS_UNKNOWN: |
| case ITM_IS_NOT_UNKNOWN: |
| case ITM_IS_FALSE: |
| case ITM_IS_TRUE: |
| CMPASSERT(child(0).getValueId().getType().getTypeQualifier() == NA_BOOLEAN_TYPE); |
| // Falling throuuuuuuuuuu |
| case ITM_IS_NULL: |
| case ITM_IS_NOT_NULL: |
| allowsUnknown = FALSE; |
| break; |
| |
| default: |
| CMPASSERT(0); // Case not handled |
| break; |
| } |
| |
| return new HEAP SQLBooleanRelat(HEAP, allowsUnknown); |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class Translate |
| // ----------------------------------------------------------------------- |
| const NAType *Translate::synthesizeType() |
| { |
| // |
| // Type cast any params. |
| // |
| ValueId vid = child(0)->getValueId(); |
| vid.coerceType(NA_CHARACTER_TYPE); |
| // |
| // Check that the operands are compatible. |
| // |
| |
| const NAType& operand = vid.getType(); |
| if (operand.getTypeQualifier() != NA_CHARACTER_TYPE) { |
| // 4043 The operand of an UPPER function must be character. |
| *CmpCommon::diags() << DgSqlCode(-4043) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| |
| const CharType *translateSource = (CharType*)&operand; |
| |
| // pushdown ISO88591 if the charset is unknown at this time. |
| switch ( getTranslateMapTableId() ) { |
| case ISO88591_TO_UNICODE: |
| case SJIS_TO_UCS2: |
| case UTF8_TO_UCS2: case UTF8_TO_ISO88591: case UTF8_TO_SJIS: |
| case SJIS_TO_UNICODE: |
| case ISO88591_TO_UTF8: |
| case SJIS_TO_UTF8: |
| |
| if ( translateSource->getCharSet() == CharInfo::UnknownCharSet) |
| { |
| CharInfo::CharSet assumedSrcCS = CharInfo::ISO88591; |
| switch (getTranslateMapTableId()) |
| { |
| case SJIS_TO_UCS2: case SJIS_TO_UNICODE: case SJIS_TO_UTF8: |
| assumedSrcCS = CharInfo::SJIS; break; |
| case UTF8_TO_UCS2: case UTF8_TO_ISO88591: case UTF8_TO_SJIS: |
| assumedSrcCS = CharInfo::UTF8; break; |
| default: |
| break; |
| } |
| vid.coerceType(*CharType::desiredCharType(assumedSrcCS), |
| NA_CHARACTER_TYPE |
| ); |
| |
| translateSource = (CharType*)&vid.getType(); |
| } |
| break; |
| case UCS2_TO_SJIS: |
| case UCS2_TO_UTF8: |
| case UNICODE_TO_SJIS: |
| case UNICODE_TO_ISO88591: |
| case KANJI_MP_TO_ISO88591: |
| case KSC5601_MP_TO_ISO88591: |
| if ( translateSource->getCharSet() == CharInfo::UnknownCharSet ) |
| { |
| vid.coerceType(*CharType::desiredCharType(CharInfo::UNICODE), |
| NA_CHARACTER_TYPE |
| ); |
| translateSource = (CharType*)&vid.getType(); |
| } |
| break; |
| } |
| |
| CharInfo::CharSet charsetTarget = CharInfo::UnknownCharSet; |
| NAString err4106arg(CmpCommon::statementHeap()); |
| |
| switch (getTranslateMapTableId()) { |
| case ISO88591_TO_UNICODE: |
| case SJIS_TO_UCS2: |
| case UTF8_TO_UCS2: |
| case SJIS_TO_UNICODE: |
| if (translateSource->getCharSet() == CharInfo::ISO88591 |
| || translateSource->getCharSet() == CharInfo::UTF8 |
| || translateSource->getCharSet() == CharInfo::SJIS |
| ) |
| { |
| charsetTarget = CharInfo::UNICODE; |
| } |
| else |
| switch (getTranslateMapTableId()) { |
| case UTF8_TO_UCS2: |
| err4106arg = SQLCHARSETSTRING_UTF8; break; |
| case SJIS_TO_UCS2: |
| case SJIS_TO_UNICODE: |
| err4106arg = SQLCHARSETSTRING_SJIS; break; |
| case ISO88591_TO_UNICODE: |
| default: |
| err4106arg = SQLCHARSETSTRING_ISO88591; break; |
| } |
| break; |
| |
| case UNICODE_TO_SJIS: |
| if (translateSource->getCharSet() == CharInfo::UNICODE) |
| charsetTarget = CharInfo::SJIS; |
| else |
| err4106arg = SQLCHARSETSTRING_UNICODE; |
| break; |
| |
| case UNICODE_TO_ISO88591: |
| if (translateSource->getCharSet() == CharInfo::UNICODE) |
| charsetTarget = CharInfo::ISO88591; |
| else |
| err4106arg = SQLCHARSETSTRING_UNICODE; |
| break; |
| |
| case UCS2_TO_SJIS: |
| if (translateSource->getCharSet() == CharInfo::UNICODE) |
| charsetTarget = CharInfo::SJIS; |
| else |
| err4106arg = SQLCHARSETSTRING_UNICODE; |
| break; |
| |
| case UCS2_TO_UTF8: |
| if (translateSource->getCharSet() == CharInfo::UNICODE) |
| charsetTarget = CharInfo::UTF8; |
| else |
| err4106arg = SQLCHARSETSTRING_UNICODE; |
| break; |
| |
| case KANJI_MP_TO_ISO88591: |
| if (translateSource->getCharSet() == CharInfo::KANJI_MP) |
| charsetTarget = CharInfo::ISO88591; |
| else |
| err4106arg = SQLCHARSETSTRING_KANJI; |
| break; |
| |
| case KSC5601_MP_TO_ISO88591: |
| if (translateSource->getCharSet() == CharInfo::KSC5601_MP) |
| charsetTarget = CharInfo::ISO88591; |
| else |
| err4106arg = SQLCHARSETSTRING_KSC5601; |
| break; |
| |
| case UTF8_TO_SJIS: |
| if ( (translateSource->getCharSet() == CharInfo::UTF8) || |
| (translateSource->getCharSet() == CharInfo::ISO88591) ) |
| charsetTarget = CharInfo::SJIS; |
| else |
| err4106arg = SQLCHARSETSTRING_UTF8; |
| break; |
| |
| case GBK_TO_UTF8: |
| if (translateSource->getCharSet() == CharInfo::GBK ) |
| charsetTarget = CharInfo::UTF8; |
| else |
| err4106arg = SQLCHARSETSTRING_GBK; |
| break; |
| |
| case ISO88591_TO_UTF8: |
| if (translateSource->getCharSet() == CharInfo::ISO88591) |
| { |
| charsetTarget = CharInfo::UTF8; |
| } |
| else |
| err4106arg = SQLCHARSETSTRING_ISO88591; |
| break; |
| |
| case UTF8_TO_ISO88591: |
| if ( (translateSource->getCharSet() == CharInfo::UTF8) || |
| (translateSource->getCharSet() == CharInfo::ISO88591) ) |
| charsetTarget = CharInfo::ISO88591; |
| else |
| err4106arg = SQLCHARSETSTRING_UTF8; |
| break; |
| |
| default: |
| // 4105 Unknown translation |
| *CmpCommon::diags() << DgSqlCode(-4105); |
| return NULL; |
| } |
| |
| |
| if (charsetTarget != CharInfo::UnknownCharSet) |
| { |
| Lng32 resultLen = |
| CharInfo::getMaxConvertedLenInBytes(translateSource->getCharSet(), |
| translateSource->getNominalSize(), |
| charsetTarget); |
| |
| return new HEAP SQLVarChar(HEAP, CharLenInfo(translateSource->getStrCharLimit(), resultLen), |
| TRUE, FALSE, |
| translateSource->isCaseinsensitive(), |
| charsetTarget, |
| CharInfo::DefaultCollation, |
| CharInfo::IMPLICIT); // ANSI 6.7 SR 5b |
| } |
| |
| *CmpCommon::diags() << DgSqlCode(-4106) |
| << DgString0(getTextUpper()) << DgString1(err4106arg); |
| |
| return NULL; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class ValueIdUnion |
| // ----------------------------------------------------------------------- |
| |
| const NAType *ValueIdUnion::synthesizeType() |
| { |
| const NAType *result = NULL; |
| CollIndex i = 0; |
| |
| // if this is the case of insert values list tuples, then |
| // isTrueUnion() will not be set and isCastTo() will be set. |
| // Last entry of sources_ valueidlist will be set to target valueId, |
| // it is set in method TupleList::bindNode. |
| // Validate that each source entry is compatible with target type. |
| if ((NOT isTrueUnion()) && |
| (isCastTo())) |
| { |
| result = &getSource(entries()-1).getType(); |
| const NAType& opR = *result; |
| for (i = 0; i < entries()-1; i++) |
| { |
| getSource(i).coerceType(*result); |
| ValueId vidI = getSource(i); |
| |
| const NAType& opI = vidI.getType(); |
| |
| if ((NOT opR.isCompatible(opI)) && |
| (CmpCommon::getDefault(ALLOW_INCOMPATIBLE_OPERATIONS) == DF_OFF)) |
| { |
| // 4055 The select lists or tuples must have compatible data types. |
| emitDyadicTypeSQLnameMsg(-4055, opR, opI); |
| return NULL; |
| } |
| } // for |
| return result; |
| } |
| |
| for (i = 0; i < entries(); i++) { |
| result = &getSource(i).getType(); |
| if (result->getTypeQualifier() != NA_UNKNOWN_TYPE) |
| break; |
| } |
| |
| CMPASSERT(result); |
| |
| if (result->getTypeQualifier() == NA_UNKNOWN_TYPE) |
| return result->newCopy(HEAP); |
| |
| CollIndex r = i; // the r'th source was the first non-unknown |
| |
| for (i = 0; i < entries(); i++) { |
| if (i != r) { // r'th source started it all, we did it already |
| getSource(i).coerceType(*result); |
| ValueId vidi = getSource(i); |
| |
| const NAType& opR = *result; // save operand BEFORE synth |
| const NAType& opI = vidi.getType(); |
| UInt32 flags = |
| ((CmpCommon::getDefault(LIMIT_MAX_NUMERIC_PRECISION) == DF_ON) |
| ? NAType::LIMIT_MAX_NUMERIC_PRECISION : 0); |
| if (CmpCommon::getDefault(TYPE_UNIONED_CHAR_AS_VARCHAR) == DF_ON) |
| flags |= NAType::MAKE_RESULT_VARCHAR; |
| |
| if (CmpCommon::getDefault(MODE_SPECIAL_4) == DF_ON) |
| { |
| flags |= NAType::MODE_SPECIAL_4; |
| } |
| |
| result = result->synthesizeType(SYNTH_RULE_UNION, opR, opI, HEAP, &flags); |
| |
| |
| if (!result) { |
| // 4055 The select lists or tuples must have compatible data types. |
| emitDyadicTypeSQLnameMsg(-4055, opR, opI); |
| //## Here, also emit errmsg 4034 w/ unparse? |
| return NULL; |
| } |
| else if (getUnionFlags() == Union::UNION_DISTINCT) { |
| if (NOT opR.isComparable(opI, this)) { |
| // 4134 The operation (x UNION y) is not allowed. Try UNION ALL. |
| *CmpCommon::diags() << DgSqlCode(-4134) |
| << DgString0(getText(USER_FORMAT_DELUXE)); |
| return NULL; |
| } |
| } |
| } |
| } |
| |
| return result; |
| } |
| |
| // ValueIdUnion::pushDownType() ----------------------------------- |
| // Propagate type information down the ItemExpr tree. This method |
| // is called by coerceType(). It will attempt to coerce (a recursive call) |
| // the type of each member of the ValueIdUnion to the desired type. |
| // This only has an effect when none of the members of the ValueIdUnion |
| // could be typed bottom up. An example query that illustrates this |
| // is: |
| // Select NULL from t1 |
| // Union all |
| // Select NULL from t2 |
| // Union all |
| // Select c from t3; |
| // |
| // This results in a tree of ValueIdUnion nodes: |
| // ValueIdUnion(c (ValueIdUnion(Null, Null))); |
| // |
| // The inner ValueIdUnion node can not be typed bottom up, but |
| // the outer ValueIdUnion node will attempt to coerce the type of |
| // the inner node. This will in turn (through pushDownType) coerce |
| // the types of the members (NULLs) of the inner ValueIdUnion node |
| // and re-synthesize the Type of the inner ValueIdUnion node. |
| // |
| // |
| const NAType * |
| ValueIdUnion::pushDownType(NAType& desiredType, |
| enum NABuiltInTypeEnum defaultQualifier) |
| { |
| for(CollIndex i = 0; i < entries(); i++) { |
| getSource(i).coerceType(desiredType, defaultQualifier); |
| } |
| |
| return (NAType *)synthesizeType(); |
| |
| } |
| |
| |
| const NAType * |
| RowsetArrayScan::pushDownType(NAType& desiredType, |
| enum NABuiltInTypeEnum defaultQualifier) |
| { |
| elemType_ = &desiredType; |
| const NAType *newType = child(0)->pushDownType(desiredType, desiredType.getTypeQualifier()); |
| child(0)->getValueId().changeType(newType); |
| //BEGIN 10-050523-8022 |
| //When datatype has constraint NOT_NULL_DROPPABLE , getting the |
| //null indicator from |
| //supportsSQLnullogical was leading to truncation of host data in |
| //ExRowsetArrayScan::eval function of file exp_function.cpp. |
| elemNullInd_ = desiredType.supportsSQLnull(); |
| //End 10-050523-8022 |
| return &desiredType; |
| } |
| |
| const NAType * |
| HostVar::pushDownType(NAType& desiredType, |
| enum NABuiltInTypeEnum defaultQualifier) |
| { |
| // If this is a rowset host var, we need to propagate the desired type into it |
| if (varType_->getTypeQualifier() == NA_ROWSET_TYPE) { |
| SQLRowset *rw1 = (SQLRowset *) varType_; |
| SQLRowset *rw2 = new HEAP SQLRowset(HEAP, &desiredType, rw1->getMaxNumElements(), |
| rw1->getNumElements()); |
| NAType *tempType = &desiredType; |
| rw2->setNullable(*tempType); |
| varType_ = rw2; |
| return rw2; |
| } |
| |
| return &desiredType; |
| } |
| |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class ValueIdProxy |
| // ----------------------------------------------------------------------- |
| const NAType *ValueIdProxy::synthesizeType() |
| { |
| const NAType *proxyType = &getOutputId().getType(); |
| return proxyType->newCopy(HEAP); |
| } |
| |
| // Propagate type information down the node we are Proxy for. |
| // Called by coerceType(). |
| // |
| const NAType * |
| ValueIdProxy::pushDownType(NAType& desiredType, |
| enum NABuiltInTypeEnum defaultQualifier) |
| { |
| outputValueId_.coerceType(desiredType, defaultQualifier); |
| return (NAType *)synthesizeType(); |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class VEGPredicate |
| // ----------------------------------------------------------------------- |
| |
| const NAType *VEGPredicate::synthesizeType() |
| { |
| return new HEAP SQLBooleanRelat(HEAP); |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class VEGReference |
| // $$$ WORK REMAINING TO BE DONE: |
| // $$$ compute the intersection of the datatypes |
| // $$$ of the members of the VEG and assign it as the type |
| // $$$ for the VEGReference. |
| // ----------------------------------------------------------------------- |
| |
| const NAType *VEGReference::synthesizeType() |
| { |
| if (getVEG()->seenBefore()) |
| return NULL; |
| |
| getVEG()->markAsSeenBefore(); |
| |
| NAType *type = NULL; |
| if (NOT getVEG()->getAllValues().isEmpty()) |
| { |
| // return the type of any one expression from the VEG. |
| ValueId exprId; |
| |
| const ValueIdSet & vegValues = getVEG()->getAllValues(); |
| |
| for (exprId = vegValues.init(); |
| vegValues.next(exprId); |
| vegValues.advance(exprId)) |
| { |
| if (exprId.getItemExpr()->getOperatorType() == ITM_VEG_REFERENCE) |
| { |
| // Saw a VEGReference inside the VEG which is not type synthesized |
| // yet. Drive its synthesis if it's not a VEGReference to a VEG |
| // which we are in the process of type synthesizing. In that case, |
| // we could ignore the VEGRef, since (1) we want to avoid infinite |
| // recursion; (2) it will get its type after the completion of an |
| // earlier call to synthesizeType(). |
| // |
| if (exprId.getValueDesc()->getDomainDesc() == NULL) |
| { |
| VEGReference *vegref = (VEGReference *)(exprId.getItemExpr()); |
| if (NOT vegref->getVEG()->seenBefore()) |
| { |
| vegref->synthTypeAndValueId(FALSE); |
| |
| // Remember the first non-null type seen. |
| if (type == NULL) type = (NAType *) &(exprId.getType()); |
| } |
| } |
| else |
| { |
| // Remember the first non-null type seen. |
| if (type == NULL) type = (NAType *) &(exprId.getType()); |
| } |
| } |
| else |
| { |
| // Remember the first non-null type seen. |
| if (type == NULL) type = (NAType *) &(exprId.getType()); |
| } |
| } |
| } |
| else |
| { |
| type = new HEAP SQLUnknown(HEAP); |
| } |
| |
| getVEG()->markAsNotSeenBefore(); |
| CMPASSERT(type); |
| return type; |
| } |
| |
| const NAType *ScalarVariance::synthesizeType() |
| { |
| return new HEAP SQLDoublePrecision(HEAP, TRUE); // Variance is always Nullable |
| } |
| |
| // UnPackCol::synthesizeType() -------------------------------- |
| // The type of the UnPackCol is the type of the original unpacked |
| // column. This type is store within the UnPackCol node. |
| // |
| const NAType *UnPackCol::synthesizeType() |
| { |
| // The type of the original unpacked column. |
| // |
| return getType(); |
| } |
| |
| // RowsetArrayScan::synthesizeType() -------------------------------- |
| // The type of the RowsetArrayScan is the type of the original unpacked |
| // element. This type is store within the RowsetArrayScan node. |
| // |
| const NAType *RowsetArrayScan::synthesizeType() |
| { |
| // The element type |
| return getType(); |
| } |
| |
| const NAType *RowsetArrayInto::synthesizeType() |
| { |
| // The element type |
| return getType(); |
| } |
| |
| const NAType *RandomNum::synthesizeType() |
| { |
| NAType *result = NULL; |
| |
| if (getArity() == 1) |
| { |
| // |
| // Type cast any params. |
| // |
| SQLInt nType(NULL, FALSE); |
| ValueId vid = child(0)->getValueId(); |
| vid.coerceType(nType, NA_NUMERIC_TYPE); |
| |
| const NAType& operand = vid.getType(); |
| if (operand.getTypeQualifier() != NA_NUMERIC_TYPE) { |
| // 4045 The operand of a Random function must be numeric. |
| *CmpCommon::diags() << DgSqlCode(-4045) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| |
| // now it's safe to cast the types to numeric type |
| const NumericType &ntyp1 = (NumericType &) operand; |
| |
| if (NOT ntyp1.isExact()) |
| { |
| // 4070 Random function is only defined for exact numeric types. |
| *CmpCommon::diags() << DgSqlCode(-4070) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| |
| if (ntyp1.getScale() != 0) |
| { |
| // 4047 Arguments of random function must have a scale of 0. |
| *CmpCommon::diags() << DgSqlCode(-4047) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| |
| // return: int unsigned |
| result = (NAType *) new HEAP SQLInt(HEAP, FALSE, ntyp1.supportsSQLnullLogical()); |
| } |
| else |
| { |
| // return: int unsigned not null |
| result = (NAType *) new HEAP SQLInt(HEAP, FALSE,FALSE); |
| } |
| |
| return result; |
| } |
| |
| const NAType *Mask::synthesizeType() |
| { |
| // The expression is <op1> Mask <op2>. |
| // Both operands must be exact numeric with scale 0. |
| // The result can always fit into the data type of the first child. |
| |
| const NAType &typ1 = child(0)->getValueId().getType(); |
| const NAType &typ2 = child(1)->getValueId().getType(); |
| |
| // Mask is an internal operator and errors are fatal |
| CMPASSERT(typ1.getTypeQualifier() == NA_NUMERIC_TYPE AND |
| typ2.getTypeQualifier() == NA_NUMERIC_TYPE); |
| |
| // now it's safe to cast the types to numeric type |
| const NumericType &ntyp1 = (NumericType &) typ1; |
| const NumericType &ntyp2 = (NumericType &) typ2; |
| |
| // for now make sure both operands basically have the same binary |
| // type, but this may be changed in the future |
| CMPASSERT(ntyp1.isExact() AND ntyp2.isExact() AND |
| ntyp1.getScale() == 0 AND ntyp2.getScale() == 0 AND |
| ntyp1.getPrecision() == ntyp2.getPrecision() AND |
| ntyp1.binaryPrecision() AND |
| ntyp2.binaryPrecision()); |
| |
| const NAType *result = typ1.newCopy(HEAP); |
| |
| return result; |
| } |
| |
| const NAType *Shift::synthesizeType() |
| { |
| // The expression is <op1> Shift <op2>. |
| // Both operands must be exact numeric with scale 0. |
| // The result can always fit into the data type of the first child. |
| |
| const NAType &typ1 = child(0)->getValueId().getType(); |
| const NAType &typ2 = child(1)->getValueId().getType(); |
| |
| // Mask is an internal operator and errors are fatal |
| CMPASSERT(typ1.getTypeQualifier() == NA_NUMERIC_TYPE AND |
| typ2.getTypeQualifier() == NA_NUMERIC_TYPE); |
| |
| // now it's safe to cast the types to numeric type |
| const NumericType &ntyp1 = (NumericType &) typ1; |
| const NumericType &ntyp2 = (NumericType &) typ2; |
| |
| // for now make sure both operands basically have the same binary |
| // type, but this may be changed in the future |
| CMPASSERT(ntyp1.isExact() AND ntyp2.isExact() AND |
| ntyp1.getScale() == 0 AND ntyp2.getScale() == 0 AND |
| ntyp1.binaryPrecision() AND |
| ntyp2.binaryPrecision()); |
| |
| const NAType *result = typ1.newCopy(HEAP); |
| |
| return result; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class PackFunc |
| // ----------------------------------------------------------------------- |
| |
| const NAType* PackFunc::synthesizeType() |
| { |
| // --------------------------------------------------------------------- |
| // If format information is valid, type is already available. Otherwise, |
| // compute type information for the result of the PackFunc from the type |
| // information of its operand. |
| // --------------------------------------------------------------------- |
| if(NOT isFormatInfoValid_) |
| { |
| // Type of column to be packed. |
| const NAType* columnType = &child(0)->getValueId().getType(); |
| deriveFormatInfoFromUnpackType(columnType); |
| } |
| return type_; |
| } |
| |
| const NAType * ZZZBinderFunction::synthesizeType() |
| { |
| // the synthesizeType method is needed only when we process an item |
| // expression at DDL time. For DML the function gets transformed into |
| // another function in the binder before we reach type synthesis |
| switch (getOperatorType()) |
| { |
| case ITM_DATEDIFF_YEAR: |
| case ITM_DATEDIFF_MONTH: |
| case ITM_DATEDIFF_QUARTER: |
| case ITM_DATEDIFF_WEEK: |
| return new HEAP SQLInt(HEAP, TRUE, |
| child(0)->getValueId().getType().supportsSQLnull() || |
| child(1)->getValueId().getType().supportsSQLnull()); |
| |
| case ITM_LEFT: |
| { |
| // make a temporary transformation for synthesizing the right type |
| Substring *temp = new HEAP Substring(child(0).getPtr(), |
| new HEAP ConstValue((Lng32) 1, (NAMemory *) HEAP), |
| child(1)); |
| temp->synthTypeAndValueId(); |
| return temp->getValueId().getType().newCopy(HEAP); |
| } |
| |
| case ITM_YEARWEEK: |
| case ITM_YEARWEEKD: |
| return new HEAP SQLNumeric(HEAP, 4, |
| 6, |
| 0, |
| TRUE, |
| child(0)->getValueId().getType().supportsSQLnull()); |
| |
| default: |
| // use the parent class implementation by default |
| return BuiltinFunction::synthesizeType(); |
| } |
| } |
| |
| const NAType *Subquery::synthesizeType() |
| { |
| return new HEAP SQLBooleanRelat(HEAP); |
| } |
| |
| const NAType *RowSubquery::synthesizeType() |
| { |
| const NAType *rowType = &getSubquery()->selectList()->getValueId().getType(); |
| return rowType->newCopy(HEAP); |
| } |
| |
| // Propagate type information down the compExpr of the RowSubquery |
| // Called by coerceType(). We only change type if the selectList of |
| // the RowSubquery is of degree 1 and the returned value in the select |
| // list is of unknown or character type. |
| // |
| const NAType * |
| RowSubquery::pushDownType(NAType& desiredType, |
| enum NABuiltInTypeEnum defaultQualifier) |
| { |
| |
| // In the case where the select list of the rowSubquery contains |
| // a dynamic parameter, we need to change its type.. |
| if ( getDegree() == 1 ) |
| { |
| RelRoot *sq_root = (RelRoot *) getSubquery(); |
| ValueId outVid = sq_root->compExpr()[0]; |
| if ( outVid.getType().getTypeQualifier() == NA_UNKNOWN_TYPE || |
| outVid.getType().getTypeQualifier() == NA_CHARACTER_TYPE |
| ) |
| outVid.coerceType(desiredType, defaultQualifier); |
| } |
| |
| return (NAType *)synthesizeType(); |
| } |
| |
| const NAType *Exists::synthesizeType() |
| { |
| // EXISTS predicate can never evaluate to Unknown |
| return new HEAP SQLBooleanRelat(FALSE); |
| } |
| |
| const NAType *QuantifiedComp::synthesizeType() |
| { |
| // Genesis 10-980305-3294 |
| ItemExprList exprList1(child(0).getPtr(), HEAP); |
| ItemExprList exprList2(getSubquery()->selectList(), HEAP); |
| NABoolean allowsUnknown; |
| NABoolean allowIncompatibleComparison = FALSE; |
| if (CmpCommon::getDefault(ALLOW_INCOMPATIBLE_OPERATIONS) == DF_ON) |
| allowIncompatibleComparison = TRUE; |
| if (!synthItemExprLists(exprList1, exprList2, allowIncompatibleComparison, |
| allowsUnknown, this)) |
| return NULL; |
| return new HEAP SQLBooleanRelat(HEAP, allowsUnknown); |
| } |
| |
| // MV, |
| const NAType *GenericUpdateOutputFunction::synthesizeType() |
| { |
| const NAType *type = NULL; |
| |
| if( getOperator().match(ITM_JULIANTIMESTAMP) ) |
| { |
| // |
| // Type cast any params. |
| // |
| ValueId vid = child(0)->getValueId(); |
| SQLTimestamp timestampType(NULL); |
| vid.coerceType(timestampType); |
| // |
| // Check that the operands are compatible. |
| // |
| const NAType& operand = vid.getType(); |
| if (operand.getTypeQualifier() != NA_DATETIME_TYPE) { |
| // 4071 The operand of a JULIANTIMESTAMP function must be a datetime. |
| *CmpCommon::diags() << DgSqlCode(-4071) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| |
| type = new HEAP SQLLargeInt(HEAP, TRUE, operand.supportsSQLnullLogical()); |
| } |
| else |
| { |
| type = new HEAP SQLInt(HEAP, TRUE, FALSE); |
| } |
| |
| return type; |
| } |
| |
| //++Triggers, |
| |
| const NAType *UniqueExecuteId::synthesizeType() |
| { |
| return new HEAP SQLChar(HEAP, SIZEOF_UNIQUE_EXECUTE_ID, FALSE); |
| } |
| |
| |
| const NAType *GetTriggersStatus::synthesizeType() |
| { |
| return new HEAP SQLChar(HEAP, TRIGGERS_STATUS_VECTOR_SIZE, FALSE); |
| } |
| |
| const NAType *GetBitValueAt::synthesizeType() |
| { |
| const NAType *operand1 = &child(0)->getValueId().getType(); |
| const NAType *operand2 = &child(1)->getValueId().getType(); |
| |
| if (operand1->getTypeQualifier() != NA_CHARACTER_TYPE) { |
| // 4051 Operand 1 must be character. |
| *CmpCommon::diags() << DgSqlCode(-4051) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| if (operand2->getTypeQualifier() != NA_NUMERIC_TYPE) { |
| // 4052 Operand 2 must be numeric. |
| *CmpCommon::diags() << DgSqlCode(-4052) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| return new HEAP SQLInt(HEAP, FALSE, FALSE); |
| } |
| |
| //--Triggers, |
| |
| const NAType *IsBitwiseAndTrue::synthesizeType() |
| { |
| const NAType *operand1 = &child(0)->getValueId().getType(); |
| const NAType *operand2 = &child(1)->getValueId().getType(); |
| |
| if (operand1->getTypeQualifier() != NA_CHARACTER_TYPE) { |
| // 4051 Operand 1 must be character. |
| *CmpCommon::diags() << DgSqlCode(-4051) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| |
| if (operand1->getTypeQualifier() != NA_CHARACTER_TYPE) { |
| // 4051 Operand 1 must be character. |
| *CmpCommon::diags() << DgSqlCode(-4051) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| |
| return new HEAP SQLBooleanRelat(FALSE); |
| } |
| |
| //--MV |
| |
| |
| const NAType *ItemList::synthesizeType() |
| { |
| const NAType * elementType = &child(0)->getValueId().getType(); |
| |
| SQLRecord *restOfRecord; |
| |
| if (child(1)->getOperatorType() == ITM_ITEM_LIST) |
| { |
| restOfRecord = (SQLRecord *)&child(1)->getValueId().getType(); |
| CMPASSERT(restOfRecord->getTypeQualifier() == NA_RECORD_TYPE); |
| } |
| else |
| { |
| restOfRecord = new HEAP SQLRecord(HEAP, &child(1)->getValueId().getType(),NULL); |
| } |
| |
| return new HEAP SQLRecord(HEAP, elementType,restOfRecord); |
| } |
| // ----------------------------------------------------------------------- |
| // member functions for class ItmSeqOffset |
| // ----------------------------------------------------------------------- |
| |
| const NAType *ItmSeqOffset::synthesizeType() |
| { |
| // Verify that child 1 is numeric. |
| // Return the type of child 0. |
| |
| const NAType &operand1 = child(0)->getValueId().getType(); |
| |
| if (getArity() > 1) { |
| const NAType &operand2 = child(1)->getValueId().getType(); |
| |
| if (operand2.getTypeQualifier() != NA_NUMERIC_TYPE) { |
| // The second operand of an OFFSET function must be numeric. |
| *CmpCommon::diags() << DgSqlCode(-4052) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| } |
| if (getArity() > 2) { |
| const NAType &operand3 = child(2)->getValueId().getType(); |
| |
| if (operand3.getTypeQualifier() != NA_NUMERIC_TYPE) { |
| // The third operand of an OFFSET function must be numeric. |
| *CmpCommon::diags() << DgSqlCode(-4053) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| } |
| |
| NAType *result = operand1.newCopy(HEAP); |
| |
| if(nullRowIsZero()) { |
| result->setNullable(FALSE); |
| CMPASSERT(result->getTypeQualifier() == NA_NUMERIC_TYPE || |
| result->getTypeQualifier() == NA_INTERVAL_TYPE); |
| } else { |
| result->setNullable(TRUE); |
| } |
| |
| if (isOLAP()) |
| { |
| result->setNullable(TRUE); |
| } |
| return result; |
| } |
| |
| |
| const NAType *ItmLagOlapFunction::synthesizeType() |
| { |
| // Return the type of child 0. |
| const NAType &operand1 = child(0)->getValueId().getType(); |
| |
| if (getArity() > 1) { |
| const NAType &operand2 = child(1)->getValueId().getType(); |
| |
| if (operand2.getTypeQualifier() != NA_NUMERIC_TYPE) { |
| // The second operand of an OFFSET function must be numeric. |
| *CmpCommon::diags() << DgSqlCode(-4052) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| |
| // check the default expression which should have the same type as the |
| // child(0). |
| if (getArity() > 2) { |
| const NAType& typeForOp0 = child(0)->castToItemExpr()->getValueId().getType(); |
| const NAType& typeForDefault = child(2)->castToItemExpr()->getValueId().getType(); |
| |
| UInt32 flags = |
| ((CmpCommon::getDefault(LIMIT_MAX_NUMERIC_PRECISION) == DF_ON) |
| ? NAType::LIMIT_MAX_NUMERIC_PRECISION : 0); |
| |
| if ( !(typeForOp0 == typeForDefault) ) { |
| |
| NATypeSynthRuleEnum rule = SYNTH_RULE_ADD; |
| |
| if ( typeForOp0.getTypeQualifier() == NA_CHARACTER_TYPE ) |
| rule = SYNTH_RULE_CONCAT; |
| |
| const NAType* resultType = typeForOp0.synthesizeType(rule, |
| typeForOp0, |
| typeForDefault, |
| HEAP, &flags); |
| |
| if ( !resultType ) { |
| *CmpCommon::diags() << DgSqlCode(-4141) << DgString0("LAG"); |
| return NULL; |
| } |
| |
| // Set the null attribute of the default value to TRUE. |
| NAType* newType = typeForDefault.newCopy(HEAP); |
| newType->setNullable(TRUE); |
| ValueId vid2 = child(2)->castToItemExpr()->getValueId(); |
| vid2.changeType(newType); |
| } |
| } |
| } |
| |
| NAType *result = operand1.newCopy(HEAP); |
| |
| result->setNullable(TRUE); |
| |
| return result; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class ItmSeqDiff1 |
| // ----------------------------------------------------------------------- |
| |
| const NAType *ItmSeqDiff1::synthesizeType() |
| { |
| // Verify that children are numeric. |
| // Return the result type of child(0) - child(0). |
| |
| for (Int32 i = 0; i < getArity(); i++) { |
| const NAType &operand = child(i)->getValueId().getType(); |
| NABuiltInTypeEnum opType = operand.getTypeQualifier() ; |
| if ((opType != NA_NUMERIC_TYPE && |
| opType != NA_DATETIME_TYPE && |
| opType != NA_INTERVAL_TYPE) || |
| !operand.isSupportedType()) { |
| if (i == 0) { |
| // The first operand of a DIFF1 function must be numeric. |
| *CmpCommon::diags() << DgSqlCode(-4059) << DgString0(getTextUpper()); |
| } |
| else { |
| // The second operand of a DIFF1 function must be numeric. |
| *CmpCommon::diags() << DgSqlCode(-4052) << DgString0(getTextUpper()); |
| } |
| return NULL; |
| } // if not numeric |
| } |
| |
| const NAType &operand1 = child(0)->getValueId().getType(); |
| const NAType *result1 = operand1.synthesizeType(SYNTH_RULE_SUB, operand1, operand1, HEAP); |
| NAType *result; |
| if (getArity()==2) // will be transformed into: DIFF1(child(0)) / DIFF1(child(1)) |
| { |
| const NAType &operand2 = child(1)->getValueId().getType(); |
| const NAType *result2 = operand2.synthesizeType(SYNTH_RULE_SUB, operand2, operand2, HEAP); |
| if (result2->getTypeQualifier() == NA_INTERVAL_TYPE) |
| { |
| result2 = new HEAP SQLLargeInt(HEAP, TRUE, FALSE ); |
| } |
| result = (NAType *)result2->synthesizeType(SYNTH_RULE_DIV, *result1, *result2, HEAP); |
| } |
| else |
| { |
| result = (NAType *)result1; |
| } |
| |
| result->setNullable(TRUE); |
| return result; |
| } |
| // ----------------------------------------------------------------------- |
| // member functions for class ItmSeqDiff2 |
| // ----------------------------------------------------------------------- |
| |
| const NAType *ItmSeqDiff2::synthesizeType() |
| { |
| // Verify that children are numeric. |
| // Return the result type of child(0) - child(0). |
| |
| for (Int32 i = 0; i < getArity(); i++) { |
| const NAType &operand = child(i)->getValueId().getType(); |
| NABuiltInTypeEnum opType = operand.getTypeQualifier() ; |
| if ((opType != NA_NUMERIC_TYPE && |
| opType != NA_DATETIME_TYPE && |
| opType != NA_INTERVAL_TYPE ) || |
| !operand.isSupportedType()) { |
| if (i == 0) { |
| // The first operand of a DIFF1 function must be numeric. |
| *CmpCommon::diags() << DgSqlCode(-4059) << DgString0(getTextUpper()); |
| } |
| else { |
| // The second operand of a DIFF1 function must be numeric. |
| *CmpCommon::diags() << DgSqlCode(-4052) << DgString0(getTextUpper()); |
| } |
| return NULL; |
| } // if not numeric |
| } |
| const NAType &operand1 = child(0)->getValueId().getType(); |
| const NAType *result1 = operand1.synthesizeType(SYNTH_RULE_SUB, operand1, operand1, HEAP); |
| NAType *result = (NAType *)result1->synthesizeType(SYNTH_RULE_SUB, *result1, *result1, HEAP); |
| |
| if (getArity()==2) // will be transformed into: DIFF2(child(0)) / DIFF1(child(1)) |
| { |
| const NAType &operand2 = child(1)->getValueId().getType(); |
| const NAType *result2 = operand2.synthesizeType(SYNTH_RULE_SUB, operand2, operand2, HEAP); |
| if (result2->getTypeQualifier() == NA_INTERVAL_TYPE) |
| { |
| result2 = new HEAP SQLLargeInt(HEAP, TRUE, FALSE ); |
| } |
| result = (NAType *)result2->synthesizeType(SYNTH_RULE_DIV, *result, *result2, HEAP); |
| } |
| |
| result->setNullable(TRUE); |
| return result; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class ItmSeqRunningFunction |
| // ----------------------------------------------------------------------- |
| |
| |
| const NAType *ItmSeqRunningFunction::synthesizeType() |
| { |
| const NAType *result = NULL; |
| |
| if ((getOperatorType() == ITM_RUNNING_COUNT) || |
| (getOperatorType() == ITM_RUNNING_RANK) || |
| (getOperatorType() == ITM_RUNNING_DRANK) || |
| (getOperatorType() == ITM_RUNNING_CHANGE)) { |
| result = new HEAP |
| SQLLargeInt(HEAP, TRUE /* 'long long' on NSK can't be unsigned */, |
| FALSE /*not null*/); |
| } |
| else { |
| const NAType& operand = child(0)->castToItemExpr()->getValueId().getType(); |
| |
| switch (getOperatorType()) { |
| case ITM_RUNNING_AVG: { // needs to mimic what will happen after transformation |
| const NAType *operand1 = synthAvgSum(operand, FALSE); |
| const NAType *newInt = new HEAP SQLLargeInt(HEAP, TRUE,FALSE); |
| if (operand1){ |
| result = operand1->synthesizeType(SYNTH_RULE_DIV, *operand1, *newInt, HEAP); |
| } |
| } |
| break; |
| |
| case ITM_RUNNING_SUM: |
| result = synthAvgSum(operand, FALSE); |
| break; |
| |
| case ITM_LAST_NOT_NULL: |
| case ITM_RUNNING_MAX: |
| case ITM_RUNNING_MIN: |
| result = operand.newCopy(HEAP); |
| break; |
| |
| case ITM_RUNNING_SDEV: |
| case ITM_RUNNING_VARIANCE: |
| result = new HEAP SQLDoublePrecision(HEAP, TRUE); // See ScalarVariance::synthesizeType() |
| break; |
| |
| default: |
| CMPASSERT("Unknown running function in synthesizeType()."); |
| break; |
| } // end switch getOperatorType() |
| if (result){ |
| ((NAType *)result)->setNullable(TRUE); |
| } |
| } // end else not RUNNINGCOUNT |
| |
| return result; |
| } |
| |
| |
| |
| const NAType *ItmSeqOlapFunction::synthesizeType() |
| { |
| const NAType *result = NULL; |
| |
| if (getOperatorType() == ITM_OLAP_COUNT) |
| { |
| result = new HEAP |
| SQLLargeInt(HEAP, TRUE /* 'long long' on NSK can't be unsigned */, |
| TRUE /* null*/); |
| } |
| else |
| if (/*(getOperatorType() == ITM_OLAP_COUNT) || */ //-- causes runtime error: ERROR[8421] NULL cannot be assigned to a NOT NULL column. |
| (getOperatorType() == ITM_OLAP_RANK) || |
| (getOperatorType() == ITM_OLAP_DRANK)) |
| { |
| result = new HEAP |
| SQLLargeInt(HEAP, TRUE /* 'long long' on NSK can't be unsigned */, |
| FALSE /*not null*/); |
| } |
| else |
| { |
| const NAType& operand = child(0)->castToItemExpr()->getValueId().getType(); |
| |
| switch (getOperatorType()) |
| { |
| case ITM_OLAP_AVG: |
| { // needs to mimic what will happen after transformation |
| const NAType *operand1 = synthAvgSum(operand, FALSE); |
| const NAType *newInt = new HEAP SQLLargeInt(HEAP, TRUE, TRUE /*FALSE*/); |
| if (operand1) |
| { |
| result = operand1->synthesizeType(SYNTH_RULE_DIV, *operand1, *newInt, HEAP); |
| } |
| } |
| break; |
| |
| case ITM_OLAP_SUM: |
| result = synthAvgSum(operand, FALSE); |
| break; |
| |
| case ITM_OLAP_MAX: |
| case ITM_OLAP_MIN: |
| result = operand.newCopy(HEAP); |
| break; |
| |
| case ITM_OLAP_SDEV: |
| case ITM_OLAP_VARIANCE: |
| result = new HEAP SQLDoublePrecision(HEAP, TRUE); |
| break; |
| |
| default: |
| CMPASSERT("Unknown running function in synthesizeType()."); |
| break; |
| } // end switch getOperatorType() |
| if (result) |
| { |
| ((NAType *)result)->setNullable(TRUE); |
| } |
| } // end else not RUNNINGCOUNT |
| |
| return result; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class ItmSeqRowsSince |
| // ----------------------------------------------------------------------- |
| const NAType *ItmSeqRowsSince::synthesizeType() |
| { |
| if (getArity() == 2) |
| { |
| const NAType &operand2 = child(1)->getValueId().getType(); |
| if (operand2.getTypeQualifier() != NA_NUMERIC_TYPE) { |
| // The second operand of a ROWS SINCE function must be numeric. |
| *CmpCommon::diags() << DgSqlCode(-4052) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| } |
| return new HEAP |
| SQLInt(HEAP, TRUE /* 'long long' on NSK can't be unsigned */, |
| TRUE /* nullable */); |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class ItmSeqMovingFunction |
| // ----------------------------------------------------------------------- |
| |
| const NAType *ItmSeqMovingFunction::synthesizeType() |
| { |
| const NAType *result=NULL; |
| |
| // |
| // Verify that moving window sizes are numeric values |
| // |
| NABoolean child2isOK = TRUE; |
| const NAType &operand1 = child(1)->getValueId().getType(); |
| |
| if (operand1.getTypeQualifier() != NA_NUMERIC_TYPE) { |
| // The second operand of a MOVING sequence function must be numeric. |
| *CmpCommon::diags() << DgSqlCode(-4052) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| |
| if (getArity() > 2 ) // check child(2) type |
| { |
| const NAType &operand2 = child(2)->getValueId().getType(); |
| |
| if (operand2.getTypeQualifier() != NA_NUMERIC_TYPE) { |
| // The third operand of a MOVING sequence function must be numeric. |
| *CmpCommon::diags() << DgSqlCode(-4053) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| } |
| |
| if ((getOperatorType() == ITM_MOVING_COUNT) || |
| (getOperatorType() == ITM_MOVING_RANK) || |
| (getOperatorType() == ITM_MOVING_DRANK)) { |
| result = new HEAP |
| SQLLargeInt(HEAP, TRUE /* 'long long' on NSK can't be unsigned */, |
| FALSE /*not null*/); |
| } |
| else { |
| const NAType& operand = child(0)->castToItemExpr()->getValueId().getType(); |
| |
| switch (getOperatorType()) { |
| case ITM_MOVING_AVG: { // needs to mimic what will happen after transformation |
| const NAType *operand1 = synthAvgSum(operand, FALSE); |
| const NAType *newInt = new HEAP SQLLargeInt(HEAP, TRUE,FALSE); |
| if (operand1) { |
| result = operand1->synthesizeType(SYNTH_RULE_DIV, *operand1, *newInt, HEAP); |
| } |
| } |
| break; |
| |
| case ITM_MOVING_SUM: |
| result = synthAvgSum(operand, FALSE); |
| break; |
| |
| case ITM_MOVING_MAX: |
| case ITM_MOVING_MIN: |
| result = operand.newCopy(HEAP); |
| break; |
| |
| case ITM_MOVING_SDEV: |
| case ITM_MOVING_VARIANCE: |
| result = new HEAP SQLDoublePrecision(HEAP, TRUE); // See ScalarVariance::synthesizeType() |
| break; |
| |
| default: |
| CMPASSERT("Unknown moving function in synthesizeType()."); |
| break; |
| } // end switch getOperatorType() |
| if (result) { |
| ((NAType *)result)->setNullable(TRUE); |
| } |
| } // end else not MOVING_COUNT |
| return result; |
| |
| } |
| // ----------------------------------------------------------------------- |
| // member functions for class ItmSeqThisFunction |
| // ----------------------------------------------------------------------- |
| |
| const NAType *ItmSeqThisFunction::synthesizeType() |
| { |
| // Return the type of child |
| |
| const NAType &operand = child(0)->getValueId().getType(); |
| NAType *result = operand.newCopy(HEAP); |
| result->setNullable(TRUE); |
| return result; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class ItmScalarMinMax |
| // ----------------------------------------------------------------------- |
| |
| const NAType *ItmScalarMinMax::synthesizeType() |
| { |
| // The expression is SCALAR_MIN(<val1>, <val2>) or SCALAR_MAX(<val1>, <val2>) |
| // The result is the min or max value of the operands. |
| |
| ValueId valId1 = child(0)->getValueId(); |
| ValueId valId2 = child(1)->getValueId(); |
| // |
| // Type cast any params. |
| // |
| valId1.coerceType(valId2.getType(), NA_NUMERIC_TYPE); |
| valId2.coerceType(valId1.getType()); |
| // |
| // Synthesize the result. |
| // |
| const NAType& op1 = valId1.getType(); |
| const NAType& op2 = valId2.getType(); |
| UInt32 flags = |
| ((CmpCommon::getDefault(LIMIT_MAX_NUMERIC_PRECISION) == DF_ON) |
| ? NAType::LIMIT_MAX_NUMERIC_PRECISION : 0); |
| |
| const NAType *result = op1.synthesizeType(SYNTH_RULE_UNION, |
| op1, |
| op2, |
| HEAP, |
| &flags); |
| if (result == NULL) { |
| // 4041 Type $1 cannot be compared with type $2. |
| emitDyadicTypeSQLnameMsg(-4041, op1, op2); |
| return NULL; |
| } |
| |
| if (result->getTypeQualifier() == NA_CHARACTER_TYPE) { |
| CharType *ct = (CharType *)result; |
| propagateCoAndCoToChildren(this, ct->getCollation(), ct->getCoercibility()); |
| } |
| |
| return result; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class ItmSeqNotTHISFunction |
| // ----------------------------------------------------------------------- |
| |
| const NAType *ItmSeqNotTHISFunction::synthesizeType() |
| { |
| // Return the type of child |
| |
| const NAType &operand = child(0)->getValueId().getType(); |
| NAType *result = operand.newCopy(HEAP); |
| result->setNullable(TRUE); |
| return result; |
| } |
| |
| const NAType * HbaseColumnLookup::synthesizeType() |
| { |
| NAType * type = NULL; |
| |
| if (naType_) |
| type = (NAType*)naType_; |
| else |
| type = new HEAP SQLVarChar(HEAP, 100000); |
| |
| return type; |
| } |
| |
| const NAType * HbaseColumnsDisplay::synthesizeType() |
| { |
| NAType * type = new HEAP SQLVarChar(HEAP, displayWidth_); |
| |
| return type; |
| } |
| |
| const NAType * HbaseColumnCreate::synthesizeType() |
| { |
| NAType * type = NULL; |
| |
| if (resultType_) |
| type = (NAType*)resultType_; |
| else |
| type = new HEAP SQLVarChar(HEAP, 100000); |
| |
| return type; |
| } |
| |
| const NAType * SequenceValue::synthesizeType() |
| { |
| NAType * type = NULL; |
| |
| type = new HEAP SQLLargeInt(HEAP, TRUE, FALSE); |
| |
| return type; |
| } |
| |
| const NAType * HbaseTimestamp::synthesizeType() |
| { |
| NAType * type = NULL; |
| |
| type = new HEAP SQLLargeInt(HEAP, TRUE, |
| col_->getValueId().getType().supportsSQLnull()); |
| |
| return type; |
| } |
| |
| const NAType * HbaseVersion::synthesizeType() |
| { |
| NAType * type = NULL; |
| |
| type = new HEAP SQLLargeInt(HEAP, TRUE, FALSE); |
| |
| return type; |
| } |
| |
| const NAType * RowNumFunc::synthesizeType() |
| { |
| NAType * type = NULL; |
| |
| type = new HEAP SQLLargeInt(HEAP, TRUE, FALSE); |
| |
| return type; |
| } |
| |
| const NAType *LOBoper::synthesizeType() |
| { |
| // Return blob or clob type |
| |
| NAType *result = new HEAP SQLBlob(HEAP, ((Int64) CmpCommon::getDefaultNumeric(LOB_MAX_SIZE)*1024*1024)); |
| |
| if (child(0)) |
| { |
| ValueId vid1 = child(0)->getValueId(); |
| const NAType &typ1 = (NAType&)vid1.getType(); |
| |
| if (typ1.getFSDatatype() == REC_BLOB) |
| { |
| result = new HEAP SQLBlob(HEAP, ((Int64) CmpCommon::getDefaultNumeric(LOB_MAX_SIZE)*1024*1024), Lob_Invalid_Storage, |
| typ1.supportsSQLnull()); |
| } |
| else if (typ1.getFSDatatype() == REC_CLOB) |
| { |
| result = new HEAP SQLClob(HEAP, ((Int64) CmpCommon::getDefaultNumeric(LOB_MAX_SIZE)*1024*1024), Lob_Invalid_Storage, |
| typ1.supportsSQLnull()); |
| } |
| } |
| return result; |
| } |
| |
| const NAType *LOBinsert::synthesizeType() |
| { |
| // Return blob type |
| ValueId vid1; |
| const NAType *typ1 = NULL; |
| if (child(0)) |
| { |
| vid1 = child(0)->getValueId(); |
| typ1 = &vid1.getType(); |
| } |
| |
| if ((obj_ == STRING_) || |
| (obj_ == FILE_) || |
| (obj_ == EXTERNAL_) || |
| (obj_ == LOAD_)) |
| { |
| |
| if (typ1 && typ1->getTypeQualifier() != NA_CHARACTER_TYPE) |
| { |
| if (!lobAsVarchar()) |
| { |
| // 4221 The operand of a $0~String0 function must be character. |
| *CmpCommon::diags() << DgSqlCode(-4221) << DgString0("LOBINSERT") |
| << DgString1("CHARACTER"); |
| return NULL; |
| } |
| } |
| } |
| else if (obj_ == LOB_) |
| { |
| if (typ1 && typ1->getTypeQualifier() != NA_LOB_TYPE) |
| { |
| // 4043 The operand of a $0~String0 function must be blob |
| *CmpCommon::diags() << DgSqlCode(-4221) << DgString0("LOBINSERT") |
| << DgString1("LOB"); |
| return NULL; |
| } |
| } |
| else if (obj_ == BUFFER_) |
| { |
| if (typ1 && typ1->getTypeQualifier() != NA_NUMERIC_TYPE) |
| { |
| // 4043 The operand of a $0~String0 function must be blob |
| *CmpCommon::diags() << DgSqlCode(-4221) << DgString0("LOBINSERT") |
| << DgString1("LARGEINT"); |
| return NULL; |
| } |
| } |
| else if(obj_ == EMPTY_LOB_) |
| { |
| } |
| else |
| { |
| // 4221 The operand of a $0~String0 function must be character. |
| *CmpCommon::diags() << DgSqlCode(-4221) << DgString0("LOBINSERT") |
| << DgString1("BLOB"); |
| return NULL; |
| } |
| |
| |
| |
| NAType * result = NULL; |
| if (lobFsType() == REC_BLOB) |
| { |
| result = new HEAP SQLBlob(HEAP, lobSize(), Lob_Invalid_Storage, |
| (obj_ ==EMPTY_LOB_) ? FALSE:typ1->supportsSQLnull()); |
| } |
| else if (lobFsType() == REC_CLOB) |
| { |
| result = new HEAP SQLClob(HEAP, lobSize(), Lob_Invalid_Storage, |
| (obj_ == EMPTY_LOB_)? FALSE:typ1->supportsSQLnull()); |
| } |
| |
| return result; |
| } |
| |
| const NAType *LOBupdate::synthesizeType() |
| { |
| // Return blob type |
| |
| ValueId vid1,vid2 ; |
| const NAType *typ1 = NULL; |
| const NAType *typ2 = NULL; |
| |
| if(child(0)) |
| { |
| vid1= child(0)->getValueId(); |
| typ1 = &vid1.getType(); |
| } |
| |
| if(child(1)) |
| { |
| vid2 = child(1)->getValueId(); |
| typ2 = &vid2.getType(); |
| } |
| |
| |
| |
| if ((obj_ == STRING_) || |
| (obj_ == FILE_) || |
| (obj_ == EXTERNAL_)) |
| { |
| |
| if (typ1->getTypeQualifier() != NA_CHARACTER_TYPE) |
| { |
| if(!lobAsVarchar()) |
| { |
| // 4221 The operand of a $0~String0 function must be character. |
| *CmpCommon::diags() << DgSqlCode(-4221) << DgString0("LOBUPDATE") |
| << DgString1("CHARACTER"); |
| return NULL; |
| } |
| } |
| } |
| else if (obj_ == LOB_) |
| { |
| if (typ1->getTypeQualifier() != NA_LOB_TYPE) |
| { |
| |
| // 4043 The operand of a $0~String0 function must be blob |
| *CmpCommon::diags() << DgSqlCode(-4221) << DgString0("LOBUPDATE") |
| << DgString1("LOB"); |
| return NULL; |
| |
| } |
| } |
| else if (obj_ == BUFFER_) |
| { |
| if (typ1->getTypeQualifier() != NA_NUMERIC_TYPE) |
| { |
| // 4043 The operand of a $0~String0 function must be blob |
| *CmpCommon::diags() << DgSqlCode(-4221) << DgString0("LOBUPDATE") |
| << DgString1("LARGEINT"); |
| return NULL; |
| } |
| ValueId vid3 = child(2)->getValueId(); |
| const NAType &typ3 = (NAType&)vid3.getType(); |
| if (typ3.getTypeQualifier() != NA_NUMERIC_TYPE) |
| { |
| // 4043 The operand of a $0~String0 function must be blob |
| *CmpCommon::diags() << DgSqlCode(-4221) << DgString0("LOBUPDATE") |
| << DgString1("LARGEINT"); |
| return NULL; |
| } |
| } |
| else if (obj_ == EMPTY_LOB_) |
| { |
| } |
| else |
| { |
| // 4221 The operand of a $0~String0 function must be character. |
| *CmpCommon::diags() << DgSqlCode(-4221) << DgString0("LOBUPDATE") |
| << DgString1("BLOB"); |
| return NULL; |
| } |
| |
| NAType * result = NULL; |
| if (typ2->getFSDatatype() == REC_BLOB) |
| { |
| SQLBlob &blob = (SQLBlob&)*typ2; |
| result = new HEAP SQLBlob(HEAP, blob.getLobLength(), Lob_Invalid_Storage, |
| (obj_ ==EMPTY_LOB_) ? FALSE:typ2->supportsSQLnull()); |
| } |
| else if (typ2->getFSDatatype() == REC_CLOB) |
| { |
| SQLClob &clob = (SQLClob&)*typ2; |
| |
| result = new HEAP SQLClob(HEAP, clob.getLobLength(), Lob_Invalid_Storage, |
| (obj_ ==EMPTY_LOB_) ? FALSE:typ2->supportsSQLnull()); |
| } |
| |
| return result; |
| } |
| |
| const NAType *LOBconvertHandle::synthesizeType() |
| { |
| // Return blob type |
| |
| ValueId vid1 = child(0)->getValueId(); |
| const NAType &typ1 = (NAType&)vid1.getType(); |
| |
| NAType *result = NULL; |
| |
| if (obj_ == STRING_) |
| { |
| if (typ1.getTypeQualifier() != NA_LOB_TYPE) |
| { |
| // 4043 The operand of a $0~String0 function must be character. |
| *CmpCommon::diags() << DgSqlCode(-4221) << DgString0("LOBCONVERTHANDLE") |
| << DgString1("LOB"); |
| return NULL; |
| } |
| |
| if (typ1.getFSDatatype() == REC_BLOB) |
| { |
| SQLBlob& op1 = (SQLBlob&)vid1.getType(); |
| |
| result = new HEAP SQLBlob(HEAP, op1.getLobLength(), Lob_Invalid_Storage, |
| typ1.supportsSQLnull(), FALSE, |
| TRUE); |
| } |
| else if (typ1.getFSDatatype() == REC_CLOB) |
| { |
| SQLClob& op1 = (SQLClob&)vid1.getType(); |
| |
| result = new HEAP SQLClob(HEAP, op1.getLobLength(), Lob_Invalid_Storage, |
| typ1.supportsSQLnull(), FALSE, |
| TRUE); |
| } |
| |
| return result; |
| } |
| else if (obj_ == LOB_) |
| { |
| if (typ1.getTypeQualifier() != NA_CHARACTER_TYPE) |
| { |
| // 4221 The operand of a $0~String0 function must be character. |
| *CmpCommon::diags() << DgSqlCode(-4221) << DgString0("LOBCONVERTHANDLE") |
| << DgString1("CHARACTER"); |
| return NULL; |
| } |
| |
| result = new HEAP SQLBlob(HEAP, ((Int64) CmpCommon::getDefaultNumeric(LOB_MAX_SIZE)*1024*1024), Lob_Invalid_Storage, typ1.supportsSQLnull(), FALSE, |
| FALSE); |
| return result; |
| } |
| else |
| { |
| *CmpCommon::diags() << DgSqlCode(-4221) << DgString0("LOBCONVERTHANDLE") |
| << DgString1("CHARACTER"); |
| return NULL; |
| } |
| } |
| |
| const NAType *LOBconvert::synthesizeType() |
| { |
| // Return blob type |
| |
| ValueId vid1 = child(0)->getValueId(); |
| const NAType &typ1 = (NAType&)vid1.getType(); |
| |
| if (obj_ == STRING_ || obj_ == FILE_) |
| { |
| if (typ1.getTypeQualifier() != NA_LOB_TYPE) |
| { |
| // 4043 The operand of a $0~String0 function must be blob |
| *CmpCommon::diags() << DgSqlCode(-4221) << DgString0("LOBCONVERT") |
| << DgString1("LOB"); |
| return NULL; |
| } |
| |
| SQLlob& op1 = (SQLlob&)vid1.getType(); |
| |
| Lng32 tgtSize = MINOF((Lng32)op1.getLobLength(), tgtSize_); |
| |
| NAType *result = new HEAP SQLVarChar(HEAP, tgtSize, Lob_Invalid_Storage, |
| typ1.supportsSQLnull()); |
| return result; |
| } |
| else |
| { |
| // 4221 The operand of a $0~String0 function must be character. |
| *CmpCommon::diags() << DgSqlCode(-4221) << DgString0("LOBCONVERT") |
| << DgString1("LOB"); |
| return NULL; |
| } |
| } |
| |
| const NAType *LOBextract::synthesizeType() |
| { |
| // Return blob type |
| |
| ValueId vid1 = child(0)->getValueId(); |
| const NAType &typ1 = (NAType&)vid1.getType(); |
| |
| if (typ1.getTypeQualifier() != NA_LOB_TYPE) |
| { |
| // 4043 The operand of a $0~String0 function must be blob |
| *CmpCommon::diags() << DgSqlCode(-4221) << DgString0("LOBEXTRACT") |
| << DgString1("LOB"); |
| return NULL; |
| } |
| |
| SQLlob& op1 = (SQLlob&)vid1.getType(); |
| |
| Lng32 tgtSize = MINOF((Lng32)op1.getLobLength(), tgtSize_); |
| |
| NAType *result = new HEAP SQLVarChar(HEAP, tgtSize, Lob_Invalid_Storage, |
| typ1.supportsSQLnull()); |
| return result; |
| } |
| |
| const NAType * ItmLeadOlapFunction::synthesizeType() |
| { |
| // check the type of the offset operand, if present |
| if (getArity() > 1) { |
| const NAType &operand2 = child(1)->getValueId().getType(); |
| |
| NABoolean isInteger = FALSE; |
| if (operand2.getTypeQualifier() == NA_NUMERIC_TYPE ) { |
| |
| const NumericType& nt = (NumericType&)operand2; |
| if ( nt.isInteger() ) |
| isInteger = TRUE; |
| } |
| |
| // The second operand of a LEAD function must be of integer type. |
| if ( !isInteger ) { |
| *CmpCommon::diags() << DgSqlCode(-4140) << DgString0(getTextUpper()); |
| return NULL; |
| } |
| |
| // check the value of the offset expression constant |
| NABoolean offsetOK = FALSE; |
| Int64 value = 0; |
| |
| if ( getArity() > 1 ) |
| { |
| ValueId vid1 = child(1)->getValueId(); |
| ItemExpr *offsetExpr = vid1.getItemExpr(); |
| |
| if (offsetExpr) { |
| if ( offsetExpr->getOperatorType() == ITM_CONSTANT ) |
| { |
| ConstValue* cv = (ConstValue*)offsetExpr; |
| if ( cv->canGetExactNumericValue() ) |
| { |
| value = cv->getExactNumericValue(); |
| if ( value >= 0 ) { |
| offsetOK = TRUE; |
| offset_ = (Int32)value; |
| } |
| } |
| } else |
| offsetOK = TRUE; // delay making the decision. It coud be a row subquery |
| } |
| |
| } else { |
| |
| if ( offset_ >= 0 ) |
| offsetOK = TRUE; |
| } |
| |
| if ( !offsetOK ) { |
| |
| *CmpCommon::diags() << DgSqlCode(-4249) << DgString0("LEAD"); |
| return NULL; |
| } |
| } |
| |
| // check the default expression which should have the same type as the |
| // child(0). |
| if (getArity() > 2) { |
| const NAType& typeForOp0 = child(0)->castToItemExpr()->getValueId().getType(); |
| const NAType& typeForDefault = child(2)->castToItemExpr()->getValueId().getType(); |
| |
| UInt32 flags = |
| ((CmpCommon::getDefault(LIMIT_MAX_NUMERIC_PRECISION) == DF_ON) |
| ? NAType::LIMIT_MAX_NUMERIC_PRECISION : 0); |
| |
| if ( !(typeForOp0 == typeForDefault) ) { |
| |
| NATypeSynthRuleEnum rule = SYNTH_RULE_ADD; |
| |
| if ( typeForOp0.getTypeQualifier() == NA_CHARACTER_TYPE ) |
| rule = SYNTH_RULE_CONCAT; |
| |
| const NAType* resultType = typeForOp0.synthesizeType(rule, |
| typeForOp0, |
| typeForDefault, |
| HEAP, &flags); |
| |
| if ( !resultType ) { |
| *CmpCommon::diags() << DgSqlCode(-4141) << DgString0("LEAD"); |
| return NULL; |
| } |
| |
| // Set the null attribute of the default value to TRUE. |
| NAType* newType = typeForDefault.newCopy(HEAP); |
| newType->setNullable(TRUE); |
| ValueId vid2 = child(2)->castToItemExpr()->getValueId(); |
| vid2.changeType(newType); |
| } |
| } |
| |
| // the type of the LEAD() is the type of the 1st argument. |
| const NAType& operand = child(0)->castToItemExpr()->getValueId().getType(); |
| NAType* result = operand.newCopy(HEAP); |
| |
| // LEAD can return NULL. |
| result->setNullable(TRUE); |
| |
| return result; |
| } |
| |