You've already forked mariadb-columnstore-engine
mirror of
https://github.com/mariadb-corporation/mariadb-columnstore-engine.git
synced 2025-08-08 14:22:09 +03:00
The most important fix here is the fix of possible buffer overrun in DATEFORMAT() function. A "%W" format, repeated enough times, would overflow the 256-bytes buffer for result. Now we use ostringstream to construct result and we are safe. Changes in date/time projection functions made me fix difference between us and server behavior. The new, better behavior is reflected in changes in tests' results. Also, there was incorrect logic in TRUNCATE() and ROUND() functions in computing the decimal "shift."
696 lines
18 KiB
C++
696 lines
18 KiB
C++
/* Copyright (C) 2014 InfiniDB, Inc.
|
|
Copyright (C) 2019 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. */
|
|
|
|
// $Id: funchelpers.h 3921 2013-06-19 18:59:56Z bwilkinson $
|
|
|
|
/** @file */
|
|
|
|
#pragma once
|
|
|
|
#include <string>
|
|
|
|
#ifndef __STDC_FORMAT_MACROS
|
|
#define __STDC_FORMAT_MACROS
|
|
#endif
|
|
|
|
#include <inttypes.h>
|
|
#include <boost/algorithm/string/case_conv.hpp>
|
|
#include <boost/tokenizer.hpp>
|
|
|
|
#include "dataconvert.h"
|
|
#include "operator.h"
|
|
#include "intervalcolumn.h"
|
|
#include "treenode.h"
|
|
|
|
#ifndef ULONGLONG_MAX
|
|
#define ULONGLONG_MAX ulonglong_max
|
|
#endif
|
|
namespace funcexp
|
|
{
|
|
namespace helpers
|
|
{
|
|
// 10 ** i
|
|
const int64_t powerOf10_c[] = {1ll,
|
|
10ll,
|
|
100ll,
|
|
1000ll,
|
|
10000ll,
|
|
100000ll,
|
|
1000000ll,
|
|
10000000ll,
|
|
100000000ll,
|
|
1000000000ll,
|
|
10000000000ll,
|
|
100000000000ll,
|
|
1000000000000ll,
|
|
10000000000000ll,
|
|
100000000000000ll,
|
|
1000000000000000ll,
|
|
10000000000000000ll,
|
|
100000000000000000ll,
|
|
1000000000000000000ll};
|
|
|
|
// max integer number of i digits
|
|
const int64_t maxNumber_c[] = {0ll,
|
|
9ll,
|
|
99ll,
|
|
999ll,
|
|
9999ll,
|
|
99999ll,
|
|
999999ll,
|
|
9999999ll,
|
|
99999999ll,
|
|
999999999ll,
|
|
9999999999ll,
|
|
99999999999ll,
|
|
999999999999ll,
|
|
9999999999999ll,
|
|
99999999999999ll,
|
|
999999999999999ll,
|
|
9999999999999999ll,
|
|
99999999999999999ll,
|
|
999999999999999999ll};
|
|
|
|
const uint32_t TIMESTAMP_MAX_YEAR = 2038;
|
|
const uint32_t TIMESTAMP_MIN_YEAR = (1970 - 1);
|
|
const int TIMESTAMP_MIN_VALUE = 1;
|
|
const int64_t TIMESTAMP_MAX_VALUE = 0x7FFFFFFFL;
|
|
const unsigned long long MAX_NEGATIVE_NUMBER = 0x8000000000000000ULL;
|
|
const long long LONGLONG_MIN = 0x8000000000000000LL;
|
|
const int INIT_CNT = 9;
|
|
const unsigned long LFACTOR = 1000000000;
|
|
const unsigned long long LFACTOR1 = 10000000000ULL;
|
|
const unsigned long long LFACTOR2 = 100000000000ULL;
|
|
const unsigned long long ulonglong_max = ~(unsigned long long)0;
|
|
|
|
static std::string monthFullNames[13] = {"NON_VALID", "January", "February", "March", "April",
|
|
"May", "June", "July", "August", "September",
|
|
"October", "November", "December"};
|
|
|
|
static std::string monthAbNames[13] = {"NON_VALID", "Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
|
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
|
|
|
|
static std::string weekdayFullNames[8] = {"Monday", "Tuesday", "Wednesday", "Thursday",
|
|
"Friday", "Saturday", "Sunday"};
|
|
|
|
static std::string weekdayAbNames[8] = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
|
|
|
|
static std::string dayOfMonth[32] = {"0th", "1st", "2nd", "3rd", "4th", "5th", "6th", "7th",
|
|
"8th", "9th", "10th", "11th", "12th", "13th", "14th", "15th",
|
|
"16th", "17th", "18th", "19th", "20th", "21st", "22nd", "23rd",
|
|
"24th", "25th", "26th", "27th", "28th", "29th", "30th", "31st"};
|
|
|
|
static uint8_t days_in_month[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0};
|
|
|
|
// Given a date, calculate the number of days since year 0
|
|
// This is a mirror of calc_daynr, at a later date we should use my_time.h
|
|
inline uint32_t calc_mysql_daynr(uint32_t year, uint32_t month, uint32_t day)
|
|
{
|
|
int temp;
|
|
int y = year;
|
|
long delsum;
|
|
|
|
if (!dataconvert::isDateValid(day, month, year) || (day == 0 && month == 0 && year == 0))
|
|
return 0;
|
|
|
|
delsum = (long)(365 * y + 31 * ((int)month - 1) + (int)day);
|
|
|
|
if (month <= 2)
|
|
y--;
|
|
else
|
|
delsum -= (long)((int)month * 4 + 23) / 10;
|
|
|
|
temp = (int)((y / 100 + 1) * 3) / 4;
|
|
|
|
return delsum + (int)y / 4 - temp;
|
|
}
|
|
|
|
// used by get_date_from_mysql_daynr() and calc_mysql_week()
|
|
inline uint32_t calc_mysql_days_in_year(uint32_t year)
|
|
{
|
|
return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)) ? 366 : 365);
|
|
}
|
|
|
|
// convert from a MySQL day number (offset from year 0) to a date
|
|
// This is a mirror of get_date_from_daynr, at a later date we should use sql_time.h
|
|
inline void get_date_from_mysql_daynr(long daynr, dataconvert::DateTime& dateTime)
|
|
{
|
|
uint32_t year, temp, leap_day, day_of_year, days_in_year;
|
|
uint8_t* month_pos;
|
|
uint32_t ret_year, ret_month, ret_day;
|
|
const int MAX_DAY_NUMBER = 3652424;
|
|
|
|
if (daynr < 366 || daynr > MAX_DAY_NUMBER)
|
|
{
|
|
dateTime.year = dateTime.month = dateTime.day = 0;
|
|
return;
|
|
}
|
|
|
|
year = (uint32_t)(daynr * 100 / 36525L);
|
|
temp = (((year - 1) / 100 + 1) * 3) / 4;
|
|
day_of_year = (uint)(daynr - (long)year * 365L) - (year - 1) / 4 + temp;
|
|
|
|
while (day_of_year > (days_in_year = calc_mysql_days_in_year(year)))
|
|
{
|
|
day_of_year -= days_in_year;
|
|
(year)++;
|
|
}
|
|
|
|
leap_day = 0;
|
|
|
|
if (days_in_year == 366)
|
|
{
|
|
if (day_of_year > 31 + 28)
|
|
{
|
|
day_of_year--;
|
|
|
|
if (day_of_year == 31 + 28)
|
|
leap_day = 1; /* Handle leapyears leapday */
|
|
}
|
|
}
|
|
|
|
ret_month = 1;
|
|
|
|
for (month_pos = days_in_month; day_of_year > (uint32_t)*month_pos;
|
|
day_of_year -= *(month_pos++), (ret_month)++)
|
|
;
|
|
|
|
ret_year = year;
|
|
ret_day = day_of_year + leap_day;
|
|
|
|
dateTime.year = ret_year;
|
|
dateTime.month = ret_month;
|
|
dateTime.day = ret_day;
|
|
}
|
|
|
|
// Returns the weekday index for a given date:
|
|
// if sundayFirst:
|
|
// 0 = Sunday, 1 = Monday, ..., 6 = Saturday
|
|
// else:
|
|
// 0 = Monday, 1 = Tuesday, ..., 6 = Sunday
|
|
// This is a mirror of calc_weekday, at a later date we should use sql_time.h
|
|
inline uint32_t calc_mysql_weekday(uint32_t year, uint32_t month, uint32_t day, bool sundayFirst, bool& isNull)
|
|
{
|
|
if (!dataconvert::isDateValid(day, month, year) || (day == 0 && month == 0 && year == 0))
|
|
{
|
|
isNull = true;
|
|
return 0;
|
|
}
|
|
|
|
uint32_t daynr = calc_mysql_daynr(year, month, day);
|
|
return ((int)((daynr + 5L + (sundayFirst ? 1L : 0L)) % 7));
|
|
}
|
|
|
|
// Flags for calc_mysql_week
|
|
const uint32_t WEEK_MONDAY_FIRST = 1;
|
|
const uint32_t WEEK_NO_ZERO = 2;
|
|
const uint32_t WEEK_GT_THREE_DAYS = 4;
|
|
|
|
// Takes a MySQL WEEK() function mode setting and converts to a bitmask
|
|
// used by calc_mysql_week() mirror of MariaDB's week_mode()
|
|
inline int16_t convert_mysql_mode_to_modeflags(int16_t mode)
|
|
{
|
|
if (!(mode & WEEK_MONDAY_FIRST))
|
|
mode ^= WEEK_GT_THREE_DAYS;
|
|
|
|
return mode;
|
|
}
|
|
|
|
// Returns a week index conforming to the MySQL WEEK() function. Note
|
|
// that modeflags is not the MySQL mode - it is a bitmask of the abvoe
|
|
// 3 flags. The utility function convert_mysql_mode_to_modeflags should
|
|
// be applied to the MySQL mode before calling this function (or the
|
|
// flags may be used directly). The optional argument is for callers
|
|
// that need to know the year that the week actually corresponds to -
|
|
// see MySQL documentation for how the year returned can be different
|
|
// than the year of the input date
|
|
//
|
|
// This is a mirror of calc_week, at a later date we should use sql_time.h
|
|
inline uint32_t calc_mysql_week(uint32_t year, uint32_t month, uint32_t day, int16_t modeflags,
|
|
uint32_t* weekyear = 0)
|
|
{
|
|
// need to make sure that the date is valid
|
|
if (!dataconvert::isDateValid(day, month, year))
|
|
return 0;
|
|
|
|
uint32_t days;
|
|
uint32_t daynr = calc_mysql_daynr(year, month, day);
|
|
uint32_t first_daynr = calc_mysql_daynr(year, 1, 1);
|
|
bool monday_first = modeflags & WEEK_MONDAY_FIRST;
|
|
bool week_year = modeflags & WEEK_NO_ZERO;
|
|
bool first_weekday = modeflags & WEEK_GT_THREE_DAYS;
|
|
|
|
bool isNullDummy = false;
|
|
uint32_t weekday = calc_mysql_weekday(year, 1, 1, !monday_first, isNullDummy);
|
|
|
|
if (weekyear)
|
|
{
|
|
*weekyear = year;
|
|
}
|
|
|
|
if (month == 1 && day <= 7 - weekday)
|
|
{
|
|
if (!week_year && ((first_weekday && weekday != 0) || (!first_weekday && weekday >= 4)))
|
|
return 0;
|
|
|
|
week_year = 1;
|
|
|
|
if (weekyear)
|
|
{
|
|
(*weekyear)--;
|
|
}
|
|
|
|
year--;
|
|
first_daynr -= (days = calc_mysql_days_in_year(year));
|
|
weekday = (weekday + 53 * 7 - days) % 7;
|
|
}
|
|
|
|
if ((first_weekday && weekday != 0) || (!first_weekday && weekday >= 4))
|
|
days = daynr - (first_daynr + (7 - weekday));
|
|
else
|
|
days = daynr - (first_daynr - weekday);
|
|
|
|
if (week_year && days >= 52 * 7)
|
|
{
|
|
weekday = (weekday + calc_mysql_days_in_year(year)) % 7;
|
|
|
|
if ((!first_weekday && weekday < 4) || (first_weekday && weekday == 0))
|
|
{
|
|
if (weekyear)
|
|
{
|
|
(*weekyear)++;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return days / 7 + 1;
|
|
}
|
|
|
|
inline bool calc_time_diff(int64_t time1, int64_t time2, int l_sign, long long* seconds_out,
|
|
long long* microseconds_out, bool isDateTime = true)
|
|
{
|
|
int64_t days;
|
|
bool neg;
|
|
int128_t microseconds;
|
|
|
|
uint64_t year1 = 0, month1 = 0, day1 = 0, hour1 = 0, min1 = 0, sec1 = 0, msec1 = 0;
|
|
|
|
uint64_t year2 = 0, month2 = 0, day2 = 0, hour2 = 0, min2 = 0, sec2 = 0, msec2 = 0;
|
|
|
|
if (isDateTime)
|
|
{
|
|
year1 = (uint32_t)((time1 >> 48) & 0xffff);
|
|
month1 = (uint32_t)((time1 >> 44) & 0xf);
|
|
day1 = (uint32_t)((time1 >> 38) & 0x3f);
|
|
hour1 = (uint32_t)((time1 >> 32) & 0x3f);
|
|
min1 = (uint32_t)((time1 >> 26) & 0x3f);
|
|
sec1 = (uint32_t)((time1 >> 20) & 0x3f);
|
|
msec1 = (uint32_t)((time1 & 0xfffff));
|
|
|
|
year2 = (uint32_t)((time2 >> 48) & 0xffff);
|
|
month2 = (uint32_t)((time2 >> 44) & 0xf);
|
|
day2 = (uint32_t)((time2 >> 38) & 0x3f);
|
|
hour2 = (uint32_t)((time2 >> 32) & 0x3f);
|
|
min2 = (uint32_t)((time2 >> 26) & 0x3f);
|
|
sec2 = (uint32_t)((time2 >> 20) & 0x3f);
|
|
msec2 = (uint32_t)(time2 & 0xfffff);
|
|
}
|
|
else
|
|
{
|
|
year1 = 0;
|
|
month1 = 0;
|
|
day1 = (time1 >> 52) & 0x7ff;
|
|
hour1 = (time1 >> 40) & 0xfff;
|
|
min1 = (time1 >> 32) & 0xff;
|
|
sec1 = (time1 >> 24) & 0xff;
|
|
msec1 = time1 & 0xffffff;
|
|
|
|
year2 = 0;
|
|
month2 = 0;
|
|
day2 = (time2 >> 52) & 0x7ff;
|
|
hour2 = (time2 >> 40) & 0xfff;
|
|
min2 = (time2 >> 32) & 0xff;
|
|
sec2 = (time2 >> 24) & 0xff;
|
|
msec2 = time2 & 0xffffff;
|
|
}
|
|
|
|
days = calc_mysql_daynr(year1, month1, day1);
|
|
|
|
days -= l_sign * calc_mysql_daynr(year2, month2, day2);
|
|
|
|
microseconds = (int128_t(days) * (86400) + (long long)(hour1 * 3600L + min1 * 60L + sec1) -
|
|
l_sign * (long long)(hour2 * 3600L + min2 * 60L + sec2)) *
|
|
(long long)(1000000) +
|
|
(long long)msec1 - l_sign * (long long)msec2;
|
|
|
|
neg = 0;
|
|
|
|
if (microseconds < 0)
|
|
{
|
|
microseconds = -microseconds;
|
|
neg = 1;
|
|
}
|
|
|
|
*seconds_out = microseconds / 1000000L;
|
|
*microseconds_out = (long long)(microseconds % 1000000L);
|
|
return neg;
|
|
}
|
|
|
|
inline int power(int16_t a)
|
|
{
|
|
int b = 1;
|
|
|
|
for (int i = 0; i < a; i++)
|
|
{
|
|
b = b * 10;
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
template <typename T>
|
|
inline void decimalPlaceDec(int64_t& d, T& p, int8_t& s)
|
|
{
|
|
// find new scale if D < s
|
|
if (d < s)
|
|
{
|
|
int64_t t = s;
|
|
s = d; // the new scale
|
|
d -= t;
|
|
int64_t i = (d >= 0) ? d : (-d);
|
|
|
|
while (i--)
|
|
p *= 10;
|
|
}
|
|
else
|
|
{
|
|
d = s;
|
|
}
|
|
}
|
|
|
|
inline uint32_t convertMonth(std::string month)
|
|
{
|
|
uint32_t value = 0;
|
|
|
|
boost::algorithm::to_lower(month);
|
|
|
|
if (month == "jan" || month == "january")
|
|
{
|
|
value = 1;
|
|
}
|
|
else if (month == "feb" || month == "february")
|
|
{
|
|
value = 2;
|
|
}
|
|
else if (month == "mar" || month == "march")
|
|
{
|
|
value = 3;
|
|
}
|
|
else if (month == "apr" || month == "april")
|
|
{
|
|
value = 4;
|
|
}
|
|
else if (month == "may")
|
|
{
|
|
value = 5;
|
|
}
|
|
else if (month == "jun" || month == "june")
|
|
{
|
|
value = 6;
|
|
}
|
|
else if (month == "jul" || month == "july")
|
|
{
|
|
value = 7;
|
|
}
|
|
else if (month == "aug" || month == "august")
|
|
{
|
|
value = 8;
|
|
}
|
|
else if (month == "sep" || month == "september")
|
|
{
|
|
value = 9;
|
|
}
|
|
else if (month == "oct" || month == "october")
|
|
{
|
|
value = 10;
|
|
}
|
|
else if (month == "nov" || month == "november")
|
|
{
|
|
value = 11;
|
|
}
|
|
else if (month == "dec" || month == "december")
|
|
{
|
|
value = 12;
|
|
}
|
|
else
|
|
{
|
|
value = 0;
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
class to_lower
|
|
{
|
|
public:
|
|
char operator()(char c) const // notice the return type
|
|
{
|
|
return tolower(c);
|
|
}
|
|
};
|
|
|
|
inline int getNumbers(const std::string& expr, int64_t* array, execplan::OpType funcType)
|
|
{
|
|
int index = 0;
|
|
|
|
int funcNeg = 1;
|
|
|
|
if (funcType == execplan::OP_SUB)
|
|
funcNeg = -1;
|
|
|
|
if (expr.size() == 0)
|
|
return 0;
|
|
|
|
// @bug 4703 reworked this code to avoid use of incrementally
|
|
// built string to hold temporary values while
|
|
// scanning expr for numbers. This function is now
|
|
// covered by a unit test in tdriver.cpp
|
|
bool foundNumber = false;
|
|
int64_t number = 0;
|
|
int neg = 1;
|
|
|
|
for (unsigned int i = 0; i < expr.size(); i++)
|
|
{
|
|
char value = expr[i];
|
|
|
|
if ((value >= '0' && value <= '9'))
|
|
{
|
|
foundNumber = true;
|
|
number = (number * 10) + (value - '0');
|
|
}
|
|
else if (value == '-' && !foundNumber)
|
|
{
|
|
neg = -1;
|
|
}
|
|
else if (value == '-')
|
|
{
|
|
// this is actually an error condition - it means that
|
|
// input came in with something like NN-NN (i.e. a dash
|
|
// between two numbers. To match prior code we will
|
|
// return the number up to the dash and just return
|
|
array[index] = number * funcNeg * neg;
|
|
index++;
|
|
|
|
return index;
|
|
}
|
|
else
|
|
{
|
|
if (foundNumber)
|
|
{
|
|
array[index] = number * funcNeg * neg;
|
|
number = 0;
|
|
neg = 1;
|
|
index++;
|
|
|
|
if (index > 9)
|
|
return index;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (foundNumber)
|
|
{
|
|
array[index] = number * funcNeg * neg;
|
|
index++;
|
|
}
|
|
|
|
return index;
|
|
}
|
|
|
|
inline int getNumbers(const std::string& expr, int* array, execplan::OpType funcType)
|
|
{
|
|
int index = 0;
|
|
|
|
int funcNeg = 1;
|
|
|
|
if (funcType == execplan::OP_SUB)
|
|
funcNeg = -1;
|
|
|
|
if (expr.size() == 0)
|
|
return 0;
|
|
|
|
// @bug 4703 reworked this code to avoid use of incrementally
|
|
// built string to hold temporary values while
|
|
// scanning expr for numbers. This function is now
|
|
// covered by a unit test in tdriver.cpp
|
|
bool foundNumber = false;
|
|
int number = 0;
|
|
int neg = 1;
|
|
|
|
for (unsigned int i = 0; i < expr.size(); i++)
|
|
{
|
|
char value = expr[i];
|
|
|
|
if ((value >= '0' && value <= '9'))
|
|
{
|
|
foundNumber = true;
|
|
number = (number * 10) + (value - '0');
|
|
}
|
|
else if (value == '-' && !foundNumber)
|
|
{
|
|
neg = -1;
|
|
}
|
|
else if (value == '-')
|
|
{
|
|
// this is actually an error condition - it means that
|
|
// input came in with something like NN-NN (i.e. a dash
|
|
// between two numbers. To match prior code we will
|
|
// return the number up to the dash and just return
|
|
array[index] = number * funcNeg * neg;
|
|
index++;
|
|
|
|
return index;
|
|
}
|
|
else
|
|
{
|
|
if (foundNumber)
|
|
{
|
|
array[index] = number * funcNeg * neg;
|
|
number = 0;
|
|
neg = 1;
|
|
index++;
|
|
|
|
if (index > 9)
|
|
return index;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (foundNumber)
|
|
{
|
|
array[index] = number * funcNeg * neg;
|
|
index++;
|
|
}
|
|
|
|
return index;
|
|
}
|
|
|
|
inline int dayOfWeek(std::string day) // Sunday = 0
|
|
{
|
|
int value = -1;
|
|
boost::to_lower(day);
|
|
|
|
if (day == "sunday" || day == "sun")
|
|
{
|
|
value = 0;
|
|
}
|
|
else if (day == "monday" || day == "mon")
|
|
{
|
|
value = 1;
|
|
}
|
|
else if (day == "tuesday" || day == "tue")
|
|
{
|
|
value = 2;
|
|
}
|
|
else if (day == "wednesday" || day == "wed")
|
|
{
|
|
value = 3;
|
|
}
|
|
else if (day == "thursday" || day == "thu")
|
|
{
|
|
value = 4;
|
|
}
|
|
else if (day == "friday" || day == "fri")
|
|
{
|
|
value = 5;
|
|
}
|
|
else if (day == "saturday" || day == "sat")
|
|
{
|
|
value = 6;
|
|
}
|
|
else
|
|
{
|
|
value = -1;
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
inline string intToString(int64_t i)
|
|
{
|
|
char buf[32];
|
|
snprintf(buf, 32, "%" PRId64 "", i);
|
|
return buf;
|
|
}
|
|
|
|
inline string uintToString(uint64_t i)
|
|
{
|
|
char buf[32];
|
|
snprintf(buf, 32, "%" PRIu64 "", i);
|
|
return buf;
|
|
}
|
|
|
|
inline string doubleToString(double d)
|
|
{
|
|
// double's can be *really* long to print out. Max mysql
|
|
// is e308 so allow for 308 + 36 decimal places minimum.
|
|
char buf[384];
|
|
snprintf(buf, 384, "%f", d);
|
|
return buf;
|
|
}
|
|
|
|
inline string longDoubleToString(long double ld)
|
|
{
|
|
// long double's can be *really* long to print out. Max mysql
|
|
// is e308 so allow for 308 + 36 decimal places minimum.
|
|
char buf[384];
|
|
snprintf(buf, 384, "%Lf", ld);
|
|
return buf;
|
|
}
|
|
|
|
uint64_t dateAdd(uint64_t time, const std::string& expr, execplan::IntervalColumn::interval_type unit,
|
|
bool dateType, execplan::OpType funcType);
|
|
const std::string IDB_date_format(const dataconvert::DateTime&, const std::string&, bool& isNull);
|
|
const std::string timediff(int64_t, int64_t, bool isDateTime = true);
|
|
const char* convNumToStr(int64_t, char*, int);
|
|
|
|
} // namespace helpers
|
|
} // namespace funcexp
|