1
0
mirror of https://github.com/mariadb-corporation/mariadb-columnstore-engine.git synced 2025-12-12 11:01:17 +03:00
Files
mariadb-columnstore-engine/utils/funcexp/func_sec_to_time.cpp
Alexander Barkov a86f432f35 Fixing DOUBLE-to-[U]INT conversion (MCOL-4649, MCOL-4631, MCOL-4647)
Bugs fixed:
- MCOL-4649 CAST(double AS UNSIGNED) returns 0
- MCOL-4631 CAST(double AS SIGNED) returns 0 or NULL
- MCOL-4647 SEC_TO_TIME(double_or_float) returns a wrong result

Problems:
- The code in Func_cast_unsigned::getUintVal() and
  Func_cast_signed::getIntVal() did not properly check
  the double value to fit inside a uint64_t/int64_t range.
  So the corner cases:
  - numeric_limits<uint64_t>::max()-2 for uint64_t
  - numeric_limits<int64_t>::max() for int64_t
  produced unexpected results.

  The problem was in tests like this:
    if (value > (double) numeric_limits<int64_t>::max())
  A correct test would be:
    if (value >= (double) numeric_limits<int64_t>::max())

- The code in Func_sec_to_time::getStrVal() searched for the decimal
  dot character, assuming that the next character after the dot
  was the leftmost fractional digit.
  This assumption was wrong because huge double numbers use
  scientific notation. So for example in "2.5e-40" the
  digit "5" following the dot is NOT the leftmost fractional digit.
  Also, the code in Func_sec_to_time::getStrVal() was slow
  because of using non necessary to-string and from-string
  data conversion.
  Also, the code in Func_sec_to_time::getStrVal() evaluated
  the argument two times: using getStrVal() then using getIntVal().

Solution:
- Adding new classes TDouble and TLongDouble.
- Adding a few function templates to reuse the code easier.
- Moving the conversion code inside TDouble and TLongDouble
  methods toMCSSInt64Round() and toMCSUInt64Round().
- Reusing new classes and their methods in func_cast.cc and
  func_sec_to_time.cc.
2021-04-05 11:30:52 +04:00

245 lines
6.6 KiB
C++

/* Copyright (C) 2014 InfiniDB, Inc.
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. */
/****************************************************************************
* $Id: func_sec_to_time.cpp 2477 2011-05-12 16:07:35Z chao $
*
*
****************************************************************************/
#include <cstdlib>
#include <string>
#include <sstream>
using namespace std;
#include "functor_str.h"
#include "functioncolumn.h"
#include "rowgroup.h"
#include "funchelpers.h"
#include "predicateoperator.h"
using namespace execplan;
#include "dataconvert.h"
#include "errorcodes.h"
#include "idberrorinfo.h"
#include "errorids.h"
using namespace logging;
namespace funcexp
{
CalpontSystemCatalog::ColType Func_sec_to_time::operationType(FunctionParm& fp, CalpontSystemCatalog::ColType& resultType)
{
return resultType;
}
string Func_sec_to_time::getStrVal(rowgroup::Row& row,
FunctionParm& parm,
bool& isNull,
CalpontSystemCatalog::ColType& ct)
{
int64_t val = 0;
CalpontSystemCatalog::ColType curCt = parm[0]->data()->resultType();
switch (parm[0]->data()->resultType().colDataType)
{
case execplan::CalpontSystemCatalog::BIGINT:
case execplan::CalpontSystemCatalog::INT:
case execplan::CalpontSystemCatalog::MEDINT:
case execplan::CalpontSystemCatalog::TINYINT:
case execplan::CalpontSystemCatalog::SMALLINT:
case execplan::CalpontSystemCatalog::UBIGINT:
case execplan::CalpontSystemCatalog::UINT:
case execplan::CalpontSystemCatalog::UMEDINT:
case execplan::CalpontSystemCatalog::UTINYINT:
case execplan::CalpontSystemCatalog::USMALLINT:
{
val = parm[0]->data()->getIntVal(row, isNull);
}
break;
case execplan::CalpontSystemCatalog::DOUBLE:
{
datatypes::TDouble d(parm[0]->data()->getDoubleVal(row, isNull));
val = d.toMCSSInt64Round();
break;
}
case execplan::CalpontSystemCatalog::FLOAT:
{
datatypes::TDouble d(parm[0]->data()->getFloatVal(row, isNull));
val = d.toMCSSInt64Round();
}
break;
case execplan::CalpontSystemCatalog::DECIMAL:
case execplan::CalpontSystemCatalog::UDECIMAL:
val = parm[0]->data()->getDecimalVal(row, isNull).toSInt64Round();
break;
case execplan::CalpontSystemCatalog::CHAR:
case execplan::CalpontSystemCatalog::VARCHAR:
case execplan::CalpontSystemCatalog::TEXT:
{
val = parm[0]->data()->getIntVal(row, isNull);
break;
}
default:
{
std::ostringstream oss;
oss << "sec_to_time: datatype of " << execplan::colDataTypeToString(parm[0]->data()->resultType().colDataType);
throw logging::IDBExcept(oss.str(), ERR_DATATYPE_NOT_SUPPORT);
}
}
int64_t posVal = llabs(val);
if (val > 3020399)
return ("838:59:59");
if (val < -3020399)
return ("-838:59:59");
//Format the time
uint32_t hour = 0;
uint32_t minute = 0;
uint32_t second = 0;
hour = posVal / 3600;
minute = (posVal - (hour * 3600)) / 60;
second = posVal - (hour * 3600) - (minute * 60);
const char* minus = "-";
const char* nominus = "";
const char* signstr = (val < 0) ? minus : nominus;
char buf[32]; // actual string either 9 or 10 characters
snprintf(buf, 32, "%s%02d:%02d:%02d", signstr, hour, minute, second);
return buf;
}
int64_t Func_sec_to_time::getIntVal(rowgroup::Row& row,
FunctionParm& parm,
bool& isNull,
CalpontSystemCatalog::ColType& op_ct)
{
int64_t val = parm[0]->data()->getIntVal(row, isNull);
if (val > 3020399)
val = 8385959;
else if (val < -3020399)
val = 4286581337LL;
else
{
string time = getStrVal(row, parm, isNull, op_ct);
size_t x = time.find(":");
while (x < string::npos)
{
time.erase(x, 1);
x = time.find(":");
}
char* ep = NULL;
const char* str = time.c_str();
errno = 0;
val = strtoll(str, &ep, 10);
}
return val;
}
double Func_sec_to_time::getDoubleVal(rowgroup::Row& row,
FunctionParm& parm,
bool& isNull,
execplan::CalpontSystemCatalog::ColType& op_ct)
{
double val = parm[0]->data()->getDoubleVal(row, isNull);
if (val > 3020399)
val = 8385959;
else if (val < -3020399)
val = 4286581337LL;
else
{
string time = getStrVal(row, parm, isNull, op_ct);
size_t x = time.find(":");
while (x < string::npos)
{
time.erase(x, 1);
x = time.find(":");
}
char* ep = NULL;
const char* str = time.c_str();
errno = 0;
val = (double)strtoll(str, &ep, 10);
}
return val;
}
execplan::IDB_Decimal Func_sec_to_time::getDecimalVal(rowgroup::Row& row,
FunctionParm& parm,
bool& isNull,
execplan::CalpontSystemCatalog::ColType& op_ct)
{
IDB_Decimal d;
int64_t val = parm[0]->data()->getIntVal(row, isNull);
int64_t tmpVal;
if (val > 3020399)
tmpVal = 8385959;
else if (val < -3020399)
tmpVal = 4286581337LL;
else
{
string time = getStrVal(row, parm, isNull, op_ct);
size_t x = time.find(":");
while (x < string::npos)
{
time.erase(x, 1);
x = time.find(":");
}
char* ep = NULL;
const char* str = time.c_str();
errno = 0;
tmpVal = strtoll(str, &ep, 10);
}
if (parm[0]->data()->resultType().isWideDecimalType())
d.s128Value = tmpVal;
else
d.value = tmpVal;
d.scale = 0;
return d;
}
} // namespace funcexp
// vim:ts=4 sw=4: