diff --git a/datatypes/mcs_datatype.h b/datatypes/mcs_datatype.h index 4b67429a9..d996db981 100644 --- a/datatypes/mcs_datatype.h +++ b/datatypes/mcs_datatype.h @@ -267,6 +267,57 @@ public: colDataType == UDECIMAL) && colWidth == MAXDECIMALWIDTH; } + + bool isUnsignedInteger() const + { + switch (colDataType) + { + case datatypes::SystemCatalog::UTINYINT: + case datatypes::SystemCatalog::USMALLINT: + case datatypes::SystemCatalog::UMEDINT: + case datatypes::SystemCatalog::UINT: + case datatypes::SystemCatalog::UBIGINT: + return true; + default: + return false; + } + } + + bool isSignedInteger() const + { + switch (colDataType) + { + case datatypes::SystemCatalog::TINYINT: + case datatypes::SystemCatalog::SMALLINT: + case datatypes::SystemCatalog::MEDINT: + case datatypes::SystemCatalog::INT: + case datatypes::SystemCatalog::BIGINT: + return true; + + default: + return false; + } + } + + bool canReturnXInt64() const + { + switch (colDataType) + { + case datatypes::SystemCatalog::BIT: + case datatypes::SystemCatalog::VARBINARY: + case datatypes::SystemCatalog::CLOB: + case datatypes::SystemCatalog::BLOB: + case datatypes::SystemCatalog::NUM_OF_COL_DATA_TYPE: + case datatypes::SystemCatalog::LONGDOUBLE: + case datatypes::SystemCatalog::STRINT: + case datatypes::SystemCatalog::UNDEFINED: + return false; + default: + break; + } + return true; + } + }; }; diff --git a/datatypes/mcs_decimal.h b/datatypes/mcs_decimal.h index 2d49e667a..46cf0b88d 100644 --- a/datatypes/mcs_decimal.h +++ b/datatypes/mcs_decimal.h @@ -464,6 +464,19 @@ class Decimal: public TSInt128 return TSInt128(roundedValue); } + int64_t narrowRound() const + { + int64_t scaleDivisor; + getScaleDivisor(scaleDivisor, scale); + int64_t intg = value / scaleDivisor; + int64_t frac2 = 2 * (value % scaleDivisor); + if (frac2 >= scaleDivisor) + return intg + 1; + if (frac2 <= -scaleDivisor) + return intg - 1; + return intg; + } + // MOD operator for an integer divisor to be used // for integer rhs inline TSInt128 operator%(const TSInt128& div) const diff --git a/datatypes/mcs_int64.h b/datatypes/mcs_int64.h new file mode 100644 index 000000000..9df096b77 --- /dev/null +++ b/datatypes/mcs_int64.h @@ -0,0 +1,168 @@ +/* + Copyright (C) 2020 MariaDB Corporation + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; version 2 of + the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. +*/ +#ifndef MCS_INT64_H_INCLUDED +#define MCS_INT64_H_INCLUDED + +#include "exceptclasses.h" + +namespace datatypes +{ + + +class TNullFlag +{ +protected: + bool mIsNull; +public: + explicit TNullFlag(bool val) :mIsNull(val) { } + bool isNull() const + { + return mIsNull; + } +}; + + +class TUInt64 +{ +protected: + uint64_t mValue; +public: + TUInt64(): mValue(0) { } + + explicit TUInt64(uint64_t value): mValue(value) { } + + explicit operator uint64_t () const + { + return mValue; + } +}; + + +class TSInt64 +{ +protected: + int64_t mValue; +public: + TSInt64(): mValue(0) { } + + explicit TSInt64(int64_t value): mValue(value) { } + + explicit operator int64_t () const + { + return mValue; + } +}; + + + +class TUInt64Null: public TUInt64, public TNullFlag +{ +public: + + TUInt64Null(): TNullFlag(true) { } + + explicit TUInt64Null(uint64_t value, bool isNull = false): + TUInt64(value), TNullFlag(isNull) + { } + + explicit operator uint64_t () const + { + idbassert(!mIsNull); + return mValue; + } + uint64_t nullSafeValue(bool & isNullRef) const + { + return (isNullRef = isNull()) ? 0 : mValue; + } + + TUInt64Null operator&(const TUInt64Null &rhs) const + { + return TUInt64Null(mValue & rhs.mValue, mIsNull || rhs.mIsNull); + } + TUInt64Null operator|(const TUInt64Null &rhs) const + { + return TUInt64Null(mValue | rhs.mValue, mIsNull || rhs.mIsNull); + } + TUInt64Null operator^(const TUInt64Null &rhs) const + { + return TUInt64Null(mValue ^ rhs.mValue, mIsNull || rhs.mIsNull); + } + TUInt64Null MariaDBShiftLeft(const TUInt64Null &rhs) const + { + return TUInt64Null(rhs.mValue >= 64 ? 0 : mValue << rhs.mValue, mIsNull || rhs.mIsNull); + } + TUInt64Null MariaDBShiftRight(const TUInt64Null &rhs) const + { + return TUInt64Null(rhs.mValue >= 64 ? 0 : mValue >> rhs.mValue, mIsNull || rhs.mIsNull); + } +}; + + +class TSInt64Null: public TSInt64, public TNullFlag +{ +public: + + TSInt64Null(): TNullFlag(true) { } + + explicit TSInt64Null(int64_t value, bool isNull = false): + TSInt64(value), TNullFlag(isNull) + { } + + explicit operator int64_t () const + { + idbassert(!mIsNull); + return mValue; + } + + int64_t nullSafeValue(bool & isNullRef) const + { + return (isNullRef = isNull()) ? 0 : mValue; + } + + TSInt64Null operator&(const TSInt64Null &rhs) const + { + return TSInt64Null(mValue & rhs.mValue, mIsNull || rhs.mIsNull); + } + TSInt64Null operator|(const TSInt64Null &rhs) const + { + return TSInt64Null(mValue | rhs.mValue, mIsNull || rhs.mIsNull); + } + TSInt64Null operator^(const TSInt64Null &rhs) const + { + return TSInt64Null(mValue ^ rhs.mValue, mIsNull || rhs.mIsNull); + } + TSInt64Null MariaDBShiftLeft(const TUInt64Null &rhs) const + { + if (isNull() || rhs.isNull()) + return TSInt64Null(); + return TSInt64Null((uint64_t) rhs >= 64 ? 0 : mValue << (uint64_t) rhs, false); + } + TSInt64Null MariaDBShiftRight(const TUInt64Null &rhs) const + { + if (isNull() || rhs.isNull()) + return TSInt64Null(); + return TSInt64Null((uint64_t) rhs >= 64 ? 0 : mValue >> (uint64_t) rhs, false); + } +}; + + +} //end of namespace datatypes + +#endif // MCS_INT64_H_INCLUDED +// vim:ts=2 sw=2: diff --git a/dbcon/execplan/functioncolumn.h b/dbcon/execplan/functioncolumn.h index 760b934e8..59dacc2ef 100644 --- a/dbcon/execplan/functioncolumn.h +++ b/dbcon/execplan/functioncolumn.h @@ -311,10 +311,24 @@ public: return fFunctor->getTimeIntVal(row, fFunctionParms, isNull, fOperationType); } + bool fixIfNeeded() override + { + if (fFixed) + return false; + if (fFunctor->fix(*this)) + return true; + fFixed = true; + return false; + } + void setFunctor(funcexp::Func* functor) + { + fFunctor = functor; + } private: funcexp::FunctionParm fFunctionParms; funcexp::Func* fFunctor; /// functor to execute this function funcexp::Func* fDynamicFunctor = NULL; // for rand encode decode + bool fFixed = false; }; /** diff --git a/dbcon/execplan/returnedcolumn.h b/dbcon/execplan/returnedcolumn.h index 6c07f5694..22e1dc15b 100644 --- a/dbcon/execplan/returnedcolumn.h +++ b/dbcon/execplan/returnedcolumn.h @@ -328,6 +328,11 @@ public: return false; } + virtual bool fixIfNeeded() + { + return false; + } + protected: // return all flag set if the other column is outer join column (+) bool fReturnAll; diff --git a/dbcon/execplan/treenode.h b/dbcon/execplan/treenode.h index 7f5d0fb2c..b83b26fcd 100644 --- a/dbcon/execplan/treenode.h +++ b/dbcon/execplan/treenode.h @@ -37,6 +37,7 @@ #include "dataconvert.h" #include "columnwidth.h" #include "mcs_decimal.h" +#include "mcs_int64.h" namespace messageqcpp { @@ -264,6 +265,18 @@ public: { return fResult.uintVal; } + datatypes::TUInt64Null toTUInt64Null(rowgroup::Row& row) + { + bool isNull; + uint64_t val = getUintVal(row, isNull); + return datatypes::TUInt64Null(val, isNull); + } + datatypes::TSInt64Null toTSInt64Null(rowgroup::Row& row) + { + bool isNull; + int64_t val = getIntVal(row, isNull); + return datatypes::TSInt64Null(val, isNull); + } virtual float getFloatVal(rowgroup::Row& row, bool& isNull) { return fResult.floatVal; diff --git a/utils/funcexp/func_bitwise.cpp b/utils/funcexp/func_bitwise.cpp index 98336d0bd..ddc9c1d2c 100644 --- a/utils/funcexp/func_bitwise.cpp +++ b/utils/funcexp/func_bitwise.cpp @@ -38,6 +38,8 @@ using namespace rowgroup; #include "errorids.h" using namespace logging; +#include "mcs_int64.h" +#include "mcs_decimal.h" #include "dataconvert.h" using namespace dataconvert; @@ -45,23 +47,84 @@ namespace { using namespace funcexp; + +void bitWiseExceptionHandler(const std::string& funcName, + const CalpontSystemCatalog::ColType& colType) +{ + std::ostringstream oss; + oss << funcName << ": datatype of " << execplan::colDataTypeToString(colType.colDataType); + throw logging::IDBExcept(oss.str(), ERR_DATATYPE_NOT_SUPPORT); +} + + +bool validateBitOperandTypeOrError(execplan::FunctionColumn &col, + const Func & func, + uint argno) +{ + auto & type = col.functionParms()[argno]->data()->resultType(); + if (type.canReturnXInt64()) + return false; + bitWiseExceptionHandler(func.funcName(), type); + return true; +} + + +template +datatypes::TUInt64Null ConvertToBitOperand(const T &val) +{ + if (val > static_cast(UINT64_MAX)) + return datatypes::TUInt64Null(UINT64_MAX); + if (val >= 0) + return datatypes::TUInt64Null(static_cast(val)); + if (val < static_cast(INT64_MIN)) + return datatypes::TUInt64Null(static_cast(INT64_MAX)+1); + return datatypes::TUInt64Null((uint64_t) (int64_t) val); +} + + +static +datatypes::TUInt64Null DecimalToBitOperand(Row& row, + const execplan::SPTP& parm, + const funcexp::Func& thisFunc) +{ + bool tmpIsNull = false; + datatypes::Decimal d = parm->data()->getDecimalVal(row, tmpIsNull); + if (tmpIsNull) + return datatypes::TUInt64Null(); + + if (parm->data()->resultType().colWidth == datatypes::MAXDECIMALWIDTH) + { + int128_t val = d.getPosNegRoundedIntegralPart(0).getValue(); + return ConvertToBitOperand(val); + } + + return datatypes::TUInt64Null((uint64_t) d.narrowRound()); +} + + +// Functions TypeHolderStd::canReturnXInt64() and GenericToBitOperand() +// should be splitted eventually to virtual methods in TypeHandler. +// +// However, TypeHandler::getBitOperand() would seem to be too specific. +// It would be nice to have a more generic functionality in TypeHandler. +// +// Let's consider having something similar to MariaDB Longlong_hybrid, +// which holds a signed/unsigned 64bit value together with a sign flag. +// Having TypeHandler::getXInt64Hybrid() would be more useful: +// it can be reused for other purposes, not only for bitwise operations. + // @bug 4703 - the actual bug was only in the DATETIME case // part of this statement below, but instead of leaving 5 identical // copies of this code, extracted into a single utility function // here. This same method is potentially useful in other methods // and could be extracted into a utility class with its own header // if that is the case - this is left as future exercise -bool getUIntValFromParm( +datatypes::TUInt64Null GenericToBitOperand( Row& row, const execplan::SPTP& parm, - uint64_t& value, - bool& isNull, const funcexp::Func& thisFunc, - bool& isBigVal, - int128_t& bigval) + bool temporalRounding) { - isBigVal = false; - switch (parm->data()->resultType().colDataType) { case execplan::CalpontSystemCatalog::BIGINT: @@ -69,200 +132,258 @@ bool getUIntValFromParm( case execplan::CalpontSystemCatalog::MEDINT: case execplan::CalpontSystemCatalog::TINYINT: case execplan::CalpontSystemCatalog::SMALLINT: + { + datatypes::TSInt64Null tmp= parm->data()->toTSInt64Null(row); + return tmp.isNull() ? datatypes::TUInt64Null() : + datatypes::TUInt64Null((uint64_t) (int64_t) tmp); + } case execplan::CalpontSystemCatalog::DOUBLE: case execplan::CalpontSystemCatalog::FLOAT: case execplan::CalpontSystemCatalog::UDOUBLE: case execplan::CalpontSystemCatalog::UFLOAT: { - value = parm->data()->getIntVal(row, isNull); + bool tmpIsNull; + double val = parm->data()->getDoubleVal(row, tmpIsNull); + return tmpIsNull ? datatypes::TUInt64Null() : + ConvertToBitOperand(round(val)); } - break; case execplan::CalpontSystemCatalog::UBIGINT: case execplan::CalpontSystemCatalog::UINT: case execplan::CalpontSystemCatalog::UMEDINT: case execplan::CalpontSystemCatalog::UTINYINT: case execplan::CalpontSystemCatalog::USMALLINT: - { - value = parm->data()->getUintVal(row, isNull); - } - break; + return parm->data()->toTUInt64Null(row); case execplan::CalpontSystemCatalog::VARCHAR: case execplan::CalpontSystemCatalog::CHAR: case execplan::CalpontSystemCatalog::TEXT: { - value = parm->data()->getIntVal(row, isNull); - - if (isNull) - { - isNull = true; - } + bool tmpIsNull; + const string& str = parm->data()->getStrVal(row, tmpIsNull); + if (tmpIsNull) + return datatypes::TUInt64Null(); + static const datatypes::SystemCatalog::TypeAttributesStd + attr(datatypes::MAXDECIMALWIDTH, 6, datatypes::INT128MAXPRECISION); + int128_t val = attr.decimal128FromString(str); + datatypes::Decimal d(0, attr.scale, attr.precision, &val); + val = d.getPosNegRoundedIntegralPart(0).getValue(); + return ConvertToBitOperand(val); } - break; case execplan::CalpontSystemCatalog::DECIMAL: case execplan::CalpontSystemCatalog::UDECIMAL: - { - IDB_Decimal d = parm->data()->getDecimalVal(row, isNull); - - if (parm->data()->resultType().colWidth == datatypes::MAXDECIMALWIDTH) - { - isBigVal = true; - - if (parm->data()->resultType().colDataType == execplan::CalpontSystemCatalog::UDECIMAL && - d.value < 0) - { - bigval = 0; - break; - } - - int128_t scaleDivisor, scaleDivisor2; - - datatypes::getScaleDivisor(scaleDivisor, d.scale); - - scaleDivisor2 = (scaleDivisor <= 10) ? 1 : (scaleDivisor / 10); - - int128_t tmpval = d.s128Value / scaleDivisor; - int128_t lefto = (d.s128Value - tmpval * scaleDivisor) / scaleDivisor2; - - if (tmpval >= 0 && lefto > 4) - tmpval++; - - if (tmpval < 0 && lefto < -4) - tmpval--; - - bigval = tmpval; - } - else - { - if (parm->data()->resultType().colDataType == execplan::CalpontSystemCatalog::UDECIMAL && - d.value < 0) - { - d.value = 0; - } - double dscale = d.scale; - int64_t tmpval = d.value / pow(10.0, dscale); - int lefto = (d.value - tmpval * pow(10.0, dscale)) / pow(10.0, dscale - 1); - - if (tmpval >= 0 && lefto > 4) - tmpval++; - - if (tmpval < 0 && lefto < -4) - tmpval--; - - value = tmpval; - } - } - break; + return DecimalToBitOperand(row, parm, thisFunc); case execplan::CalpontSystemCatalog::DATE: { - int32_t time = parm->data()->getDateIntVal(row, isNull); + bool tmpIsNull; + int32_t time = parm->data()->getDateIntVal(row, tmpIsNull); + if (tmpIsNull) + return datatypes::TUInt64Null(); - Date d(time); - value = d.convertToMySQLint(); + int64_t value = Date(time).convertToMySQLint(); + return datatypes::TUInt64Null((uint64_t) value); } - break; case execplan::CalpontSystemCatalog::DATETIME: { - int64_t time = parm->data()->getDatetimeIntVal(row, isNull); + bool tmpIsNull; + int64_t time = parm->data()->getDatetimeIntVal(row, tmpIsNull); + if (tmpIsNull) + return datatypes::TUInt64Null(); // @bug 4703 - missing year when convering to int DateTime dt(time); - value = dt.convertToMySQLint(); + int64_t value = dt.convertToMySQLint(); + if (temporalRounding && dt.msecond >= 500000) + value++; + return datatypes::TUInt64Null((uint64_t) value); } - break; case execplan::CalpontSystemCatalog::TIMESTAMP: { - int64_t time = parm->data()->getTimestampIntVal(row, isNull); + bool tmpIsNull; + int64_t time = parm->data()->getTimestampIntVal(row, tmpIsNull); + if (tmpIsNull) + return datatypes::TUInt64Null(); TimeStamp dt(time); - value = dt.convertToMySQLint(thisFunc.timeZone()); + int64_t value = dt.convertToMySQLint(thisFunc.timeZone()); + if (temporalRounding && dt.msecond >= 500000) + value++; + return datatypes::TUInt64Null((uint64_t) value); } - break; case execplan::CalpontSystemCatalog::TIME: { - int64_t time = parm->data()->getTimeIntVal(row, isNull); + bool tmpIsNull; + int64_t time = parm->data()->getTimeIntVal(row, tmpIsNull); Time dt(time); - value = dt.convertToMySQLint(); + int64_t value = dt.convertToMySQLint(); + if (temporalRounding && dt.msecond >= 500000) + value < 0 ? value-- : value++; + return datatypes::TUInt64Null((uint64_t) value); } - break; default: - { - return false; - } + idbassert(0); // Not possible: checked during the preparation stage. + break; } - return true; + return datatypes::TUInt64Null(); } + } namespace funcexp { + +class BitOperandGeneric: public datatypes::TUInt64Null +{ +public: + BitOperandGeneric() { } + BitOperandGeneric(Row& row, + const execplan::SPTP& parm, + const funcexp::Func& thisFunc) + :TUInt64Null(GenericToBitOperand(row, parm, thisFunc, true)) + { } +}; + + +// The shift amount operand in MariaDB does not round temporal values +// when sql_mode=TIME_FRAC_ROUND is not set. +class BitOperandGenericShiftAmount: public datatypes::TUInt64Null +{ +public: + BitOperandGenericShiftAmount() { } + BitOperandGenericShiftAmount(Row& row, + const execplan::SPTP& parm, + const funcexp::Func& thisFunc) + :TUInt64Null(GenericToBitOperand(row, parm, thisFunc, false)) + { } +}; + + + +// A functor to return NULL as a bitwise operation result. +// Used when an unexpected argument count +// is encounteded during the preparation step. +class Func_bitwise_null: public Func_BitOp +{ +public: + Func_bitwise_null(): Func_BitOp("bitwise") { } + int64_t getIntVal(Row& row, + FunctionParm& parm, + bool& isNull, + CalpontSystemCatalog::ColType& operationColType) override + { + isNull = true; + return 0; + } +}; + + +bool Func_BitOp::validateArgCount(execplan::FunctionColumn &col, uint expected) const +{ + static Func_bitwise_null return_null; + if (col.functionParms().size() == expected) + return false; + col.setFunctor(&return_null); + return true; +} + + +void Func_BitOp::setFunctorByParm(execplan::FunctionColumn &col, + const execplan::SPTP& parm, + Func_Int & return_uint64_from_uint64, + Func_Int & return_uint64_from_sint64, + Func_Int & return_uint64_generic) const +{ + if (parm->data()->resultType().isUnsignedInteger()) + col.setFunctor(&return_uint64_from_uint64); + else if (parm->data()->resultType().isSignedInteger()) + col.setFunctor(&return_uint64_from_sint64); + else + col.setFunctor(&return_uint64_generic); +} + + +bool Func_BitOp::fixForBitShift(execplan::FunctionColumn &col, + Func_Int & return_uint64_from_uint64, + Func_Int & return_uint64_from_sint64, + Func_Int & return_uint64_generic) const +{ + if (validateArgCount(col, 2)) + return false; + // The functor detection is done using functionParms()[0] only. + // This is how MariaDB performs it. + setFunctorByParm(col, col.functionParms()[0], + return_uint64_from_uint64, + return_uint64_from_sint64, + return_uint64_generic); + return validateBitOperandTypeOrError(col, *this, 0) || + validateBitOperandTypeOrError(col, *this, 1); +} + + +bool Func_BitOp::fixForBitOp2(execplan::FunctionColumn &col, + Func_Int & return_uint64_from_uint64_uint64, + Func_Int & return_uint64_from_sint64_sint64, + Func_Int & return_uint64_generic) const +{ + if (validateArgCount(col, 2)) + return false; + + if (col.functionParms()[0]->data()->resultType().isUnsignedInteger() && + col.functionParms()[1]->data()->resultType().isUnsignedInteger()) + { + col.setFunctor(&return_uint64_from_uint64_uint64); + return false; + } + if (col.functionParms()[0]->data()->resultType().isSignedInteger() && + col.functionParms()[1]->data()->resultType().isSignedInteger()) + { + col.setFunctor(&return_uint64_from_sint64_sint64); + return false; + } + col.setFunctor(&return_uint64_generic); + return validateBitOperandTypeOrError(col, *this, 0) || + validateBitOperandTypeOrError(col, *this, 1); +} + + // // BITAND // -CalpontSystemCatalog::ColType Func_bitand::operationType( FunctionParm& fp, CalpontSystemCatalog::ColType& resultType ) +template +class Func_bitand_return_uint64: public Func_bitand { - return resultType; -} +public: + int64_t getIntVal(Row& row, + FunctionParm& parm, + bool& isNull, + CalpontSystemCatalog::ColType& operationColType) override + { + idbassert(parm.size() == 2); + Arg2Lazy args(row, parm, *this); + return (int64_t) (args.a & args.b).nullSafeValue(isNull); + } +}; -int64_t Func_bitand::getIntVal(Row& row, - FunctionParm& parm, - bool& isNull, - CalpontSystemCatalog::ColType& operationColType) + +bool Func_bitand::fix(execplan::FunctionColumn &col) const { - if ( parm.size() < 2 ) - { - isNull = true; - return 0; - } - - uint64_t val1 = 0; - uint64_t val2 = 0; - - int128_t bigval1 = 0; - int128_t bigval2 = 0; - bool isBigVal1; - bool isBigVal2; - - if (!getUIntValFromParm(row, parm[0], val1, isNull, *this, isBigVal1, bigval1) || - !getUIntValFromParm(row, parm[1], val2, isNull, *this, isBigVal2, bigval2)) - { - std::ostringstream oss; - oss << "bitand: datatype of " << execplan::colDataTypeToString(operationColType.colDataType); - throw logging::IDBExcept(oss.str(), ERR_DATATYPE_NOT_SUPPORT); - } - - if (LIKELY(!isBigVal1 && !isBigVal2)) - { - return val1 & val2; - } - - // Type promotion to int128_t - if (!isBigVal1) - bigval1 = val1; - - if (!isBigVal2) - bigval2 = val2; - - int128_t res = bigval1 & bigval2; - - if (res > static_cast(UINT64_MAX)) - res = UINT64_MAX; - else if (res < static_cast(INT64_MIN)) - res = INT64_MIN; - - return (int64_t) res; + static Func_bitand_return_uint64 return_uint64_from_uint64_uint64; + static Func_bitand_return_uint64 return_uint64_from_sint64_sint64; + static Func_bitand_return_uint64 return_uint64_generic; + return fixForBitOp2(col, return_uint64_from_uint64_uint64, + return_uint64_from_sint64_sint64, + return_uint64_generic); } @@ -271,58 +392,30 @@ int64_t Func_bitand::getIntVal(Row& row, // -CalpontSystemCatalog::ColType Func_leftshift::operationType( FunctionParm& fp, CalpontSystemCatalog::ColType& resultType ) +template +class Func_leftshift_return_uint64: public Func_leftshift { - return resultType; -} +public: + int64_t getIntVal(Row& row, + FunctionParm& parm, + bool& isNull, + CalpontSystemCatalog::ColType& operationColType) override + { + idbassert(parm.size() == 2); + Arg2Eager args(row, parm, *this); + return (int64_t) args.a.MariaDBShiftLeft(args.b).nullSafeValue(isNull); + } +}; -int64_t Func_leftshift::getIntVal(Row& row, - FunctionParm& parm, - bool& isNull, - CalpontSystemCatalog::ColType& operationColType) + +bool Func_leftshift::fix(execplan::FunctionColumn &col) const { - if ( parm.size() < 2 ) - { - isNull = true; - return 0; - } - - uint64_t val1 = 0; - uint64_t val2 = 0; - - int128_t bigval1 = 0; - int128_t bigval2 = 0; - bool isBigVal1; - bool isBigVal2; - - if (!getUIntValFromParm(row, parm[0], val1, isNull, *this, isBigVal1, bigval1) || - !getUIntValFromParm(row, parm[1], val2, isNull, *this, isBigVal2, bigval2)) - { - std::ostringstream oss; - oss << "leftshift: datatype of " << execplan::colDataTypeToString(operationColType.colDataType); - throw logging::IDBExcept(oss.str(), ERR_DATATYPE_NOT_SUPPORT); - } - - if (LIKELY(!isBigVal1 && !isBigVal2)) - { - return val1 << val2; - } - - // Type promotion to int128_t - if (!isBigVal1) - bigval1 = val1; - - if (!isBigVal2) - bigval2 = val2; - - int128_t res = bigval1 << bigval2; - - if (res > static_cast(UINT64_MAX)) - res = UINT64_MAX; - else if (res < static_cast(INT64_MIN)) - res = INT64_MIN; - - return (int64_t) res; + static Func_leftshift_return_uint64 return_uint64_from_uint64; + static Func_leftshift_return_uint64 return_uint64_from_sint64; + static Func_leftshift_return_uint64 return_uint64_generic; + return fixForBitShift(col, return_uint64_from_uint64, + return_uint64_from_sint64, + return_uint64_generic); } @@ -331,58 +424,30 @@ int64_t Func_leftshift::getIntVal(Row& row, // -CalpontSystemCatalog::ColType Func_rightshift::operationType( FunctionParm& fp, CalpontSystemCatalog::ColType& resultType ) +template +class Func_rightshift_return_uint64: public Func_rightshift { - return resultType; -} +public: + int64_t getIntVal(Row& row, + FunctionParm& parm, + bool& isNull, + CalpontSystemCatalog::ColType& operationColType) override + { + idbassert(parm.size() == 2); + Arg2Eager args(row, parm, *this); + return (int64_t) args.a.MariaDBShiftRight(args.b).nullSafeValue(isNull); + } +}; -int64_t Func_rightshift::getIntVal(Row& row, - FunctionParm& parm, - bool& isNull, - CalpontSystemCatalog::ColType& operationColType) + +bool Func_rightshift::fix(execplan::FunctionColumn &col) const { - if ( parm.size() < 2 ) - { - isNull = true; - return 0; - } - - uint64_t val1 = 0; - uint64_t val2 = 0; - - int128_t bigval1 = 0; - int128_t bigval2 = 0; - bool isBigVal1; - bool isBigVal2; - - if (!getUIntValFromParm(row, parm[0], val1, isNull, *this, isBigVal1, bigval1) || - !getUIntValFromParm(row, parm[1], val2, isNull, *this, isBigVal2, bigval2)) - { - std::ostringstream oss; - oss << "rightshift: datatype of " << execplan::colDataTypeToString(operationColType.colDataType); - throw logging::IDBExcept(oss.str(), ERR_DATATYPE_NOT_SUPPORT); - } - - if (LIKELY(!isBigVal1 && !isBigVal2)) - { - return val1 >> val2; - } - - // Type promotion to int128_t - if (!isBigVal1) - bigval1 = val1; - - if (!isBigVal2) - bigval2 = val2; - - int128_t res = bigval1 >> bigval2; - - if (res > static_cast(UINT64_MAX)) - res = UINT64_MAX; - else if (res < static_cast(INT64_MIN)) - res = INT64_MIN; - - return (int64_t) res; + static Func_rightshift_return_uint64 return_uint64_from_uint64; + static Func_rightshift_return_uint64 return_uint64_from_sint64; + static Func_rightshift_return_uint64 return_uint64_generic; + return fixForBitShift(col, return_uint64_from_uint64, + return_uint64_from_sint64, + return_uint64_generic); } @@ -391,60 +456,6 @@ int64_t Func_rightshift::getIntVal(Row& row, // -CalpontSystemCatalog::ColType Func_bitor::operationType( FunctionParm& fp, CalpontSystemCatalog::ColType& resultType ) -{ - return resultType; -} - -int64_t Func_bitor::getIntVal(Row& row, - FunctionParm& parm, - bool& isNull, - CalpontSystemCatalog::ColType& operationColType) -{ - if ( parm.size() < 2 ) - { - isNull = true; - return 0; - } - - uint64_t val1 = 0; - uint64_t val2 = 0; - - int128_t bigval1 = 0; - int128_t bigval2 = 0; - bool isBigVal1; - bool isBigVal2; - - if (!getUIntValFromParm(row, parm[0], val1, isNull, *this, isBigVal1, bigval1) || - !getUIntValFromParm(row, parm[1], val2, isNull, *this, isBigVal2, bigval2)) - { - std::ostringstream oss; - oss << "bitor: datatype of " << execplan::colDataTypeToString(operationColType.colDataType); - throw logging::IDBExcept(oss.str(), ERR_DATATYPE_NOT_SUPPORT); - } - - if (LIKELY(!isBigVal1 && !isBigVal2)) - { - return val1 | val2; - } - - // Type promotion to int128_t - if (!isBigVal1) - bigval1 = val1; - - if (!isBigVal2) - bigval2 = val2; - - int128_t res = bigval1 | bigval2; - - if (res > static_cast(UINT64_MAX)) - res = UINT64_MAX; - else if (res < static_cast(INT64_MIN)) - res = INT64_MIN; - - return (int64_t) res; -} - uint64_t Func_bitor::getUintVal(rowgroup::Row& row, FunctionParm& fp, bool& isNull, @@ -454,63 +465,62 @@ uint64_t Func_bitor::getUintVal(rowgroup::Row& row, } +template +class Func_bitor_return_uint64: public Func_bitor +{ +public: + int64_t getIntVal(Row& row, + FunctionParm& parm, + bool& isNull, + CalpontSystemCatalog::ColType& operationColType) override + { + idbassert(parm.size() == 2); + Arg2Lazy args(row, parm, *this); + return (int64_t) (args.a | args.b).nullSafeValue(isNull); + } +}; + + +bool Func_bitor::fix(execplan::FunctionColumn &col) const +{ + static Func_bitor_return_uint64 return_uint64_from_uint64_uint64; + static Func_bitor_return_uint64 return_uint64_from_sint64_sint64; + static Func_bitor_return_uint64 return_uint64_generic; + return fixForBitOp2(col, return_uint64_from_uint64_uint64, + return_uint64_from_sint64_sint64, + return_uint64_generic); +} + + // // BIT XOR // -CalpontSystemCatalog::ColType Func_bitxor::operationType( FunctionParm& fp, CalpontSystemCatalog::ColType& resultType ) +template +class Func_bitxor_return_uint64: public Func_bitxor { - return resultType; -} +public: + int64_t getIntVal(Row& row, + FunctionParm& parm, + bool& isNull, + CalpontSystemCatalog::ColType& operationColType) override + { + idbassert(parm.size() == 2); + Arg2Eager args(row, parm, *this); + return (int64_t) (args.a ^ args.b).nullSafeValue(isNull); + } +}; -int64_t Func_bitxor::getIntVal(Row& row, - FunctionParm& parm, - bool& isNull, - CalpontSystemCatalog::ColType& operationColType) + +bool Func_bitxor::fix(execplan::FunctionColumn &col) const { - if ( parm.size() < 2 ) - { - isNull = true; - return 0; - } - - uint64_t val1 = 0; - uint64_t val2 = 0; - - int128_t bigval1 = 0; - int128_t bigval2 = 0; - bool isBigVal1; - bool isBigVal2; - - if (!getUIntValFromParm(row, parm[0], val1, isNull, *this, isBigVal1, bigval1) || - !getUIntValFromParm(row, parm[1], val2, isNull, *this, isBigVal2, bigval2)) - { - std::ostringstream oss; - oss << "bitxor: datatype of " << execplan::colDataTypeToString(operationColType.colDataType); - throw logging::IDBExcept(oss.str(), ERR_DATATYPE_NOT_SUPPORT); - } - - if (LIKELY(!isBigVal1 && !isBigVal2)) - { - return val1 ^ val2; - } - - // Type promotion to int128_t - if (!isBigVal1) - bigval1 = val1; - - if (!isBigVal2) - bigval2 = val2; - - int128_t res = bigval1 ^ bigval2; - - if (res > static_cast(UINT64_MAX)) - res = UINT64_MAX; - else if (res < static_cast(INT64_MIN)) - res = INT64_MIN; - - return (int64_t) res; + static Func_bitxor_return_uint64 return_uint64_from_uint64_uint64; + static Func_bitxor_return_uint64 return_uint64_from_sint64_sint64; + static Func_bitxor_return_uint64 return_uint64_generic; + return fixForBitOp2(col, return_uint64_from_uint64_uint64, + return_uint64_from_sint64_sint64, + return_uint64_generic); } @@ -519,11 +529,6 @@ int64_t Func_bitxor::getIntVal(Row& row, // -CalpontSystemCatalog::ColType Func_bit_count::operationType( FunctionParm& fp, CalpontSystemCatalog::ColType& resultType ) -{ - return resultType; -} - inline int64_t bitCount(uint64_t val) { // Refer to Hacker's Delight Chapter 5 @@ -538,38 +543,34 @@ inline int64_t bitCount(uint64_t val) return (int64_t)(val & 0x000000000000007F); } -int64_t Func_bit_count::getIntVal(Row& row, - FunctionParm& parm, - bool& isNull, - CalpontSystemCatalog::ColType& operationColType) + +template +class Func_bit_count_return_uint64: public Func_bit_count { - if ( parm.size() != 1 ) +public: + int64_t getIntVal(Row& row, + FunctionParm& parm, + bool& isNull, + CalpontSystemCatalog::ColType& operationColType) override { - isNull = true; - return 0; + idbassert(parm.size() == 1); + return bitCount((uint64_t) TA(row, parm[0], *this).nullSafeValue(isNull)); } +}; - uint64_t val = 0; - int128_t bigval = 0; - bool isBigVal; - - if (!getUIntValFromParm(row, parm[0], val, isNull, *this, isBigVal, bigval)) - { - std::ostringstream oss; - oss << "bit_count: datatype of " << execplan::colDataTypeToString(operationColType.colDataType); - throw logging::IDBExcept(oss.str(), ERR_DATATYPE_NOT_SUPPORT); - } - - if (LIKELY(!isBigVal)) - { - return bitCount(val); - } - else - { - return (bitCount(*reinterpret_cast(&bigval)) + - bitCount(*(reinterpret_cast(&bigval) + 1))); - } +bool Func_bit_count::fix(execplan::FunctionColumn &col) const +{ + static Func_bit_count_return_uint64 return_uint64_from_uint64; + static Func_bit_count_return_uint64 return_uint64_from_sint64; + static Func_bit_count_return_uint64 return_uint64_generic; + if (validateArgCount(col, 1)) + return false; + setFunctorByParm(col, col.functionParms()[0], + return_uint64_from_uint64, + return_uint64_from_sint64, + return_uint64_generic); + return validateBitOperandTypeOrError(col, *this, 0); } diff --git a/utils/funcexp/funcexp.cpp b/utils/funcexp/funcexp.cpp index 6b33bad6e..150e3ee0c 100644 --- a/utils/funcexp/funcexp.cpp +++ b/utils/funcexp/funcexp.cpp @@ -262,6 +262,8 @@ void FuncExp::evaluate(rowgroup::Row& row, std::vector& expressi { isNull = false; + expression[i]->fixIfNeeded(); + switch (expression[i]->resultType().colDataType) { case CalpontSystemCatalog::DATE: diff --git a/utils/funcexp/functor.h b/utils/funcexp/functor.h index c9a74e08c..9ee1dd676 100644 --- a/utils/funcexp/functor.h +++ b/utils/funcexp/functor.h @@ -45,6 +45,7 @@ class Row; namespace execplan { +class FunctionColumn; extern const std::string colDataTypeToString(CalpontSystemCatalog::ColDataType cdt); } @@ -83,6 +84,11 @@ public: fTimeZone = timeZone; } + virtual bool fix(execplan::FunctionColumn &col) const + { + return false; + } + virtual execplan::CalpontSystemCatalog::ColType operationType(FunctionParm& fp, execplan::CalpontSystemCatalog::ColType& resultType) = 0; virtual int64_t getIntVal(rowgroup::Row& row, @@ -221,6 +227,58 @@ private: }; +class ParmTSInt64: public datatypes::TSInt64Null +{ +public: + ParmTSInt64() { } + ParmTSInt64(rowgroup::Row& row, + const execplan::SPTP& parm, + const funcexp::Func& thisFunc) + :TSInt64Null(parm->data()->toTSInt64Null(row)) + { } +}; + + +class ParmTUInt64: public datatypes::TUInt64Null +{ +public: + ParmTUInt64() { } + ParmTUInt64(rowgroup::Row& row, + const execplan::SPTP& parm, + const funcexp::Func& thisFunc) + :TUInt64Null(parm->data()->toTUInt64Null(row)) + { } +}; + + +template class Arg2Lazy +{ +public: + TA a; + TB b; + Arg2Lazy(rowgroup::Row& row, + FunctionParm& parm, + const Func& thisFunc) + :a(row, parm[0], thisFunc), + b(a.isNull() ? TB() : TB(row, parm[1], thisFunc)) + { } +}; + + +template class Arg2Eager +{ +public: + TA a; + TB b; + Arg2Eager(rowgroup::Row& row, + FunctionParm& parm, + const Func& thisFunc) + :a(row, parm[0], thisFunc), + b(row, parm[1], thisFunc) + { } +}; + + } #endif diff --git a/utils/funcexp/functor_int.h b/utils/funcexp/functor_int.h index 425e6b485..0ba6a31cf 100644 --- a/utils/funcexp/functor_int.h +++ b/utils/funcexp/functor_int.h @@ -71,6 +71,43 @@ public: { return intToString(getIntVal(row, fp, isNull, op_ct)); } + +}; + + +class Func_BitOp : public Func_Int +{ +public: + Func_BitOp(const std::string& funcName) : Func_Int(funcName) {} + execplan::CalpontSystemCatalog::ColType operationType(FunctionParm& fp, execplan::CalpontSystemCatalog::ColType& resultType) override + { + return resultType; + } + bool validateArgCount(execplan::FunctionColumn &col, uint expected) const; + void setFunctorByParm(execplan::FunctionColumn & col, + const execplan::SPTP& parm, + Func_Int & return_uint64_from_uint64, + Func_Int & return_uint64_from_sint64, + Func_Int & return_uint64_from_generic) const; + // Fix for << and >> + bool fixForBitShift(execplan::FunctionColumn & col, + Func_Int & return_uint64_from_uint64, + Func_Int & return_uint64_from_sint64, + Func_Int & return_uint64_from_generic) const; + // Fix for & | ^ + bool fixForBitOp2(execplan::FunctionColumn & col, + Func_Int & return_uint64_from_uint64_uint64, + Func_Int & return_uint64_from_sint64_sint64, + Func_Int & return_uint64_from_generic_generic) const; + + int64_t getIntVal(rowgroup::Row& row, + FunctionParm& fp, + bool& isNull, + execplan::CalpontSystemCatalog::ColType& op_ct) override + { + isNull = true; + return 0; + } }; @@ -310,35 +347,25 @@ public: /** @brief Func_bitand class */ -class Func_bitand : public Func_Int +class Func_bitand : public Func_BitOp { public: - Func_bitand() : Func_Int("bitand") {} + Func_bitand() : Func_BitOp("bitand") {} virtual ~Func_bitand() {} - - execplan::CalpontSystemCatalog::ColType operationType(FunctionParm& fp, execplan::CalpontSystemCatalog::ColType& resultType); - - int64_t getIntVal(rowgroup::Row& row, - FunctionParm& fp, - bool& isNull, - execplan::CalpontSystemCatalog::ColType& op_ct); + bool fix(execplan::FunctionColumn &col) const override; }; /** @brief Func_bitor class */ -class Func_bitor : public Func_Int +class Func_bitor : public Func_BitOp { public: - Func_bitor() : Func_Int("bitor") {} + Func_bitor() : Func_BitOp("bitor") {} virtual ~Func_bitor() {} - execplan::CalpontSystemCatalog::ColType operationType(FunctionParm& fp, execplan::CalpontSystemCatalog::ColType& resultType); + bool fix(execplan::FunctionColumn &col) const override; - int64_t getIntVal(rowgroup::Row& row, - FunctionParm& fp, - bool& isNull, - execplan::CalpontSystemCatalog::ColType& op_ct); uint64_t getUintVal(rowgroup::Row& row, FunctionParm& fp, bool& isNull, @@ -348,35 +375,23 @@ public: /** @brief Func_bitxor class */ -class Func_bitxor : public Func_Int +class Func_bitxor : public Func_BitOp { public: - Func_bitxor() : Func_Int("bitxor") {} + Func_bitxor() : Func_BitOp("bitxor") {} virtual ~Func_bitxor() {} - - execplan::CalpontSystemCatalog::ColType operationType(FunctionParm& fp, execplan::CalpontSystemCatalog::ColType& resultType); - - int64_t getIntVal(rowgroup::Row& row, - FunctionParm& fp, - bool& isNull, - execplan::CalpontSystemCatalog::ColType& op_ct); + bool fix(execplan::FunctionColumn &col) const override; }; /** @brief Func_bit_count class */ -class Func_bit_count : public Func_Int +class Func_bit_count : public Func_BitOp { public: - Func_bit_count() : Func_Int("bit_count") {} + Func_bit_count() : Func_BitOp("bit_count") {} virtual ~Func_bit_count() {} - - execplan::CalpontSystemCatalog::ColType operationType(FunctionParm& fp, execplan::CalpontSystemCatalog::ColType& resultType); - - int64_t getIntVal(rowgroup::Row& row, - FunctionParm& fp, - bool& isNull, - execplan::CalpontSystemCatalog::ColType& op_ct); + bool fix(execplan::FunctionColumn &col) const override; }; @@ -450,35 +465,23 @@ public: /** @brief Func_leftshift class */ -class Func_leftshift : public Func_Int +class Func_leftshift : public Func_BitOp { public: - Func_leftshift() : Func_Int("leftshift") {} + Func_leftshift() : Func_BitOp("leftshift") {} virtual ~Func_leftshift() {} - - execplan::CalpontSystemCatalog::ColType operationType(FunctionParm& fp, execplan::CalpontSystemCatalog::ColType& resultType); - - int64_t getIntVal(rowgroup::Row& row, - FunctionParm& fp, - bool& isNull, - execplan::CalpontSystemCatalog::ColType& op_ct); + bool fix(execplan::FunctionColumn &col) const override; }; /** @brief Func_rightshift class */ -class Func_rightshift : public Func_Int +class Func_rightshift : public Func_BitOp { public: - Func_rightshift() : Func_Int("rightshift") {} + Func_rightshift() : Func_BitOp("rightshift") {} virtual ~Func_rightshift() {} - - execplan::CalpontSystemCatalog::ColType operationType(FunctionParm& fp, execplan::CalpontSystemCatalog::ColType& resultType); - - int64_t getIntVal(rowgroup::Row& row, - FunctionParm& fp, - bool& isNull, - execplan::CalpontSystemCatalog::ColType& op_ct); + bool fix(execplan::FunctionColumn &col) const override; };