1
0
mirror of https://github.com/mariadb-corporation/mariadb-columnstore-engine.git synced 2025-07-29 08:21:15 +03:00

Make date handling more in-line with MySQL

Date limit of year 1400 was used due to Boost's limits.

This patch strips out the use of Boost for date handling and sets the
lower limit to year 1000.
This commit is contained in:
Andrew Hutchings
2016-08-30 11:25:16 +01:00
parent 9a76bfc0f3
commit 0bea3e4ab1
16 changed files with 129 additions and 168 deletions

View File

@ -31,9 +31,6 @@ using namespace std;
#include <boost/algorithm/string/case_conv.hpp> #include <boost/algorithm/string/case_conv.hpp>
using namespace boost::algorithm; using namespace boost::algorithm;
#include <boost/date_time/gregorian/gregorian.hpp>
using namespace boost::gregorian;
using namespace execplan; using namespace execplan;
using namespace ddlpackage; using namespace ddlpackage;
using namespace logging; using namespace logging;

View File

@ -45,9 +45,6 @@ using namespace WriteEngine;
#include <boost/algorithm/string/case_conv.hpp> #include <boost/algorithm/string/case_conv.hpp>
using namespace boost::algorithm; using namespace boost::algorithm;
#include <boost/date_time/gregorian/gregorian.hpp>
using namespace boost::gregorian;
#include "cacheutils.h" #include "cacheutils.h"
using namespace cacheutils; using namespace cacheutils;

View File

@ -28,8 +28,6 @@ using namespace std;
#include <boost/algorithm/string/case_conv.hpp> #include <boost/algorithm/string/case_conv.hpp>
using namespace boost::algorithm; using namespace boost::algorithm;
#include <boost/tokenizer.hpp> #include <boost/tokenizer.hpp>
#include <boost/date_time/gregorian/gregorian.hpp>
using namespace boost::gregorian;
#include <boost/shared_ptr.hpp> #include <boost/shared_ptr.hpp>
#include "we_messages.h" #include "we_messages.h"

View File

@ -33,7 +33,6 @@ using namespace std;
#include <boost/filesystem/operations.hpp> #include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.hpp> #include <boost/filesystem/path.hpp>
#include <boost/date_time/gregorian/gregorian.hpp>
#include <boost/algorithm/string/case_conv.hpp> #include <boost/algorithm/string/case_conv.hpp>
namespace fs = boost::filesystem; namespace fs = boost::filesystem;

View File

@ -31,8 +31,6 @@ using namespace std;
#include <boost/algorithm/string/case_conv.hpp> #include <boost/algorithm/string/case_conv.hpp>
using namespace boost::algorithm; using namespace boost::algorithm;
#include <boost/tokenizer.hpp> #include <boost/tokenizer.hpp>
#include <boost/date_time/gregorian/gregorian.hpp>
using namespace boost::gregorian;
#include "calpontsystemcatalog.h" #include "calpontsystemcatalog.h"
#include "calpontselectexecutionplan.h" #include "calpontselectexecutionplan.h"
#include "columnresult.h" #include "columnresult.h"
@ -644,24 +642,14 @@ bool mysql_str_to_datetime( const string& input, DateTime& output, bool& isDate
return false; return false;
} }
try if (!isDateValid(day, mon, year))
{
boost::gregorian::date d(year, mon, day);
// one more check - boost allows year 10000 but we want to limit at 9999
if( year > 9999 )
{
output.reset();
return false;
}
output.year = d.year();
output.month = d.month();
output.day = d.day();
}
catch (...)
{ {
output.reset(); output.reset();
return false; return false;
} }
output.year = year;
output.month = mon;
output.day = day;
/** /**
* Now we need to deal with the time portion. * Now we need to deal with the time portion.

View File

@ -228,11 +228,17 @@ inline
bool isDateValid ( int day, int month, int year) bool isDateValid ( int day, int month, int year)
{ {
bool valid = true; bool valid = true;
if ( year == 0 && month == 0 && year == 0 )
{
return true;
}
int daycheck = getDaysInMonth( month ); int daycheck = getDaysInMonth( month );
if( month == 2 && isLeapYear( year ) ) if( month == 2 && isLeapYear( year ) )
// 29 days in February in a leap year // 29 days in February in a leap year
daycheck = 29; daycheck = 29;
if ( ( year < 1400 ) || ( year > 9999 ) ) if ( ( year < 1000 ) || ( year > 9999 ) )
valid = false; valid = false;
else if ( month < 1 || month > 12 ) else if ( month < 1 || month > 12 )
valid = false; valid = false;

View File

@ -34,7 +34,6 @@
#include <boost/algorithm/string/case_conv.hpp> #include <boost/algorithm/string/case_conv.hpp>
#include <boost/regex.hpp> #include <boost/regex.hpp>
#include <boost/tokenizer.hpp> #include <boost/tokenizer.hpp>
#include <boost/date_time/gregorian/gregorian.hpp>
#include "dataconvert.h" #include "dataconvert.h"
#include "operator.h" #include "operator.h"
@ -172,39 +171,81 @@ static std::string dayOfMonth[32] =
"31st" "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 // 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 ) 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 ) ) if( !dataconvert::isDateValid( day, month, year ) )
return 0; return 0;
boost::gregorian::date d( year, month, day ); delsum= (long) (365 * y + 31 *((int) month - 1) + (int) day);
// this is the number of days between the beginning of the if (month <= 2)
// Gregorian calendar (November 24, 4714 B.C.) and the starting y--;
// date offset in MySQL which is year 0. else
const uint32_t JULIAN_DAY_OFFSET = 1721060; delsum-= (long) ((int) month * 4 + 23) / 10;
return d.julian_day() - JULIAN_DAY_OFFSET; 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 // 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) inline void get_date_from_mysql_daynr(long daynr,dataconvert::DateTime & dateTime)
{ {
// the MySQL day numbers for min/max boost supported dates uint32_t year, temp, leap_day, day_of_year, days_in_year;
const int BOOST_START_OFFSET = 511340; // 1400-01-01 uint8_t *month_pos;
const int BOOST_END_OFFSET = 3652424; // 9999-12-31 uint32_t ret_year, ret_month, ret_day;
const int MAX_DAY_NUMBER= 3652424;
if( daynr < BOOST_START_OFFSET || daynr > BOOST_END_OFFSET )
{ if (daynr < 366 || daynr > MAX_DAY_NUMBER)
dateTime.year= dateTime.month = dateTime.day =0; {
} dateTime.year= dateTime.month = dateTime.day =0;
else return;
{ }
boost::gregorian::date d(1400,1,1);
d += boost::gregorian::date_duration( daynr - BOOST_START_OFFSET ); year= (uint32_t) (daynr*100 / 36525L);
dateTime.year = d.year(); temp=(((year-1)/100+1)*3)/4;
dateTime.month = d.month(); day_of_year=(uint) (daynr - (long) year * 365L) - (year-1)/4 +temp;
dateTime.day = d.day(); 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: // Returns the weekday index for a given date:
@ -212,14 +253,15 @@ inline void get_date_from_mysql_daynr(long daynr,dataconvert::DateTime & dateTim
// 0 = Sunday, 1 = Monday, ..., 6 = Saturday // 0 = Sunday, 1 = Monday, ..., 6 = Saturday
// else: // else:
// 0 = Monday, 1 = Tuesday, ..., 6 = Sunday // 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 ) inline uint32_t calc_mysql_weekday( uint32_t year, uint32_t month, uint32_t day, bool sundayFirst )
{ {
if( !dataconvert::isDateValid( day, month, year ) ) if( !dataconvert::isDateValid( day, month, year ) )
return 0; return 0;
boost::gregorian::date d( year, month, day ); uint32_t daynr = calc_mysql_daynr(year, month, day);
uint32_t ret = d.day_of_week(); return ((int) ((daynr + 5L + (sundayFirst ? 1L : 0L)) % 7));
return sundayFirst ? ret : (ret + 6) % 7;
} }
// Flags for calc_mysql_week // Flags for calc_mysql_week
@ -250,98 +292,56 @@ inline int16_t convert_mysql_mode_to_modeflags( int16_t mode )
// that need to know the year that the week actually corresponds to - // that need to know the year that the week actually corresponds to -
// see MySQL documentation for how the year returned can be different // see MySQL documentation for how the year returned can be different
// than the year of the input date // 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, inline uint32_t calc_mysql_week( uint32_t year, uint32_t month, uint32_t day,
int16_t modeflags, int16_t modeflags,
uint32_t* weekyear = 0 ) uint32_t* weekyear = 0 )
{ {
// need to make sure that the date is valid for boost::gregorian::date // need to make sure that the date is valid
if( !dataconvert::isDateValid( day, month, year ) ) if( !dataconvert::isDateValid( day, month, year ) )
return 0; return 0;
boost::gregorian::date d( year, month, day );
// default this to the year of the input date - will update in the uint32_t days;
// scenarios where it is either 1 behind or 1 ahead uint32_t daynr=calc_mysql_daynr(year,month,day);
if( weekyear ) uint32_t first_daynr=calc_mysql_daynr(year,1,1);
*weekyear = d.year(); bool monday_first= modeflags & WEEK_MONDAY_FIRST;
bool week_year= modeflags & WEEK_NO_ZERO;
bool first_weekday= modeflags & WEEK_GT_THREE_DAYS;
// get a date object for the first day of they year in question uint32_t weekday=calc_mysql_weekday(year, 1, 1, !monday_first);
boost::gregorian::date yearfirst = boost::gregorian::date(d.year(),1,1); *weekyear=year;
// figure out which day of week Jan-01 is if (month == 1 && day <= 7-weekday)
uint32_t firstweekday = calc_mysql_weekday( d.year(), 1, 1, !( modeflags & WEEK_MONDAY_FIRST ) ); {
if (!week_year &&
((first_weekday && weekday != 0) ||
(!first_weekday && weekday >= 4)))
return 0;
week_year= 1;
(*weekyear)--;
first_daynr-= (days=calc_mysql_days_in_year(*weekyear));
weekday= (weekday + 53*7- days) % 7;
}
// calculate the offset to the first week starting day if ((first_weekday && weekday != 0) ||
uint32_t firstoffset = firstweekday ? ( 7 - firstweekday ) : 0; (!first_weekday && weekday >= 4))
days= daynr - (first_daynr+ (7-weekday));
else
days= daynr - (first_daynr - weekday);
// julian day number for the first day of the first week if (week_year && days >= 52*7)
uint32_t baseday = yearfirst.julian_day() + firstoffset; {
weekday= (weekday + calc_mysql_days_in_year(*weekyear)) % 7;
// if using a modeflags where the first week must have >3 days in this year if ((!first_weekday && weekday < 4) ||
// we need to check where firstweekday fell. There are 3 cases for Jan-01 (first_weekday && weekday == 0))
// 1) Jan-01 fell on week start - there are obviously at least {
// three days this year and baseday already correct (*weekyear)++;
// 2) Jan-01 fell on weekday 1 - 3 - this means that our baseday is return 1;
// pointing to next week but there must be at least 3 days this year }
// so we need to adjust baseday back by one week }
// 3) Jan-01 fell on weekday 4 - 6 - this means there cannot be >3 return days/7+1;
// days in the week with Jan 01 so our baseday is already correct
if( modeflags & WEEK_GT_THREE_DAYS )
{
if( firstweekday > 0 && firstweekday < 4 )
{
baseday = baseday - 7;
}
}
uint32_t weeknum = 0;
if( d.julian_day() < baseday && ( modeflags & WEEK_NO_ZERO ) )
{
// this is somewhat of a pain - since we aren't using a 0 week, the date
// is actually going to be in the last week of last year and we need to
// figure out which number that is. This code is identical to above
// except we are compuing for last year instead of current year.
boost::gregorian::date lastyearfirst = boost::gregorian::date(d.year()-1,1,1);
firstweekday = calc_mysql_weekday( d.year()-1, 1, 1, !( modeflags & WEEK_MONDAY_FIRST ) );
firstoffset = firstweekday ? ( 7 - firstweekday ) : 0;
baseday = lastyearfirst.julian_day() + firstoffset;
// same logic as above
if( modeflags & WEEK_GT_THREE_DAYS )
{
if( firstweekday > 0 && firstweekday < 4 )
{
baseday = baseday - 7;
}
}
weeknum = (d.julian_day() - baseday)/ 7 + 1;
if( weekyear )
*weekyear = d.year() - 1;
}
else
{
weeknum = (d.julian_day() >= baseday) ? (d.julian_day() - baseday)/ 7 + 1 : 0;
if( ( modeflags & WEEK_GT_THREE_DAYS ) &&
( modeflags & WEEK_NO_ZERO ) && weeknum > 52 )
{
// if this happens we aren't sure whether the week will be 53 or 1.
// to get 53, we know this has to be a December date and can subtract
// our date from 32 to figure out how many days of this week
uint32_t daysthisyear = 32 - d.day();
uint32_t firstweekday = calc_mysql_weekday( d.year(), d.month(), d.day(), !( modeflags & 0x1 ) );
// to be 1st week of next year there must be > 3 days of next year
if( ( firstweekday + daysthisyear ) < 4 )
{
weeknum = 1;
if( weekyear )
*weekyear = d.year() + 1;
}
}
}
return weeknum;
} }
inline bool calc_time_diff(int64_t time1, int64_t time2, int l_sign, long long *seconds_out, long long *microseconds_out) inline bool calc_time_diff(int64_t time1, int64_t time2, int l_sign, long long *seconds_out, long long *microseconds_out)

View File

@ -67,8 +67,7 @@ public:
if( dayOfWeek < 0 || !dataconvert::isDateValid( 1, 1, dateTime.year ) ) if( dayOfWeek < 0 || !dataconvert::isDateValid( 1, 1, dateTime.year ) )
return returnError( dateTime ); return returnError( dateTime );
boost::gregorian::date yearfirst( dateTime.year, 1, 1 ); uint32_t yearfirst = helpers::calc_mysql_daynr(dateTime.year, 1, 1);
// figure out which day of week Jan-01 is // figure out which day of week Jan-01 is
uint32_t firstweekday = helpers::calc_mysql_weekday( dateTime.year, 1, 1, sundayFirst ); uint32_t firstweekday = helpers::calc_mysql_weekday( dateTime.year, 1, 1, sundayFirst );
@ -76,10 +75,8 @@ public:
uint32_t firstoffset = firstweekday ? ( 7 - firstweekday ) : 0; uint32_t firstoffset = firstweekday ? ( 7 - firstweekday ) : 0;
firstoffset += ( ( weekOfYear - 1) * 7 ) + dayOfWeek - ( sundayFirst ? 0 : 1 ); firstoffset += ( ( weekOfYear - 1) * 7 ) + dayOfWeek - ( sundayFirst ? 0 : 1 );
yearfirst += boost::gregorian::date_duration( firstoffset ); yearfirst += firstoffset;
dateTime.year = yearfirst.year(); helpers::get_date_from_mysql_daynr(yearfirst, dateTime);
dateTime.month = yearfirst.month();
dateTime.day = yearfirst.day();
} }
if( !dataconvert::isDateTimeValid( dateTime.hour, dateTime.minute, dateTime.second, dateTime.msecond ) ) if( !dataconvert::isDateTimeValid( dateTime.hour, dateTime.minute, dateTime.second, dateTime.msecond ) )

View File

@ -25,8 +25,6 @@ using namespace messageqcpp;
#include "resourcemanager.h" #include "resourcemanager.h"
#include "ddlpkg.h" #include "ddlpkg.h"
#include "ddlpackageprocessor.h" #include "ddlpackageprocessor.h"
#include <boost/date_time/gregorian/gregorian.hpp>
using namespace boost::gregorian;
#include "dataconvert.h" #include "dataconvert.h"
using namespace dataconvert; using namespace dataconvert;
using namespace ddlpackage; using namespace ddlpackage;

View File

@ -34,7 +34,6 @@
#define EXPORT #define EXPORT
#endif #endif
#include <boost/date_time/gregorian/gregorian.hpp>
#include "dataconvert.h" #include "dataconvert.h"
namespace WriteEngine namespace WriteEngine

View File

@ -25,8 +25,6 @@ using namespace messageqcpp;
#include "resourcemanager.h" #include "resourcemanager.h"
#include "../../dbcon/dmlpackage/dmlpkg.h" #include "../../dbcon/dmlpackage/dmlpkg.h"
#include "ddlpackageprocessor.h" #include "ddlpackageprocessor.h"
#include <boost/date_time/gregorian/gregorian.hpp>
using namespace boost::gregorian;
#include "dataconvert.h" #include "dataconvert.h"
using namespace dataconvert; using namespace dataconvert;
using namespace dmlpackage; using namespace dmlpackage;

View File

@ -34,7 +34,6 @@
#define EXPORT #define EXPORT
#endif #endif
#include <boost/date_time/gregorian/gregorian.hpp>
#include "dataconvert.h" #include "dataconvert.h"
namespace WriteEngine namespace WriteEngine

View File

@ -34,8 +34,7 @@ using namespace messageqcpp;
#include "we_ddlcommandproc.h" #include "we_ddlcommandproc.h"
#include "ddlpkg.h" #include "ddlpkg.h"
using namespace ddlpackage; using namespace ddlpackage;
#include <boost/date_time/gregorian/gregorian.hpp> #include <ctime>
using namespace boost::gregorian;
#include "dataconvert.h" #include "dataconvert.h"
using namespace dataconvert; using namespace dataconvert;
//#include "we_brm.h" //#include "we_brm.h"
@ -188,25 +187,15 @@ uint8_t WE_DDLCommandProc::writeSystable(ByteStream& bs, std::string &err)
} }
else if (CREATEDATE_COL == column.tableColName.column) else if (CREATEDATE_COL == column.tableColName.column)
{ {
date d(day_clock::universal_day()); time_t t;
std::string date = to_iso_string(d); struct tm tmp;
Date aDay; Date aDay;
int intvalue;
std::string s = date.substr(0, 4); t = time(NULL);
if (from_string<int>(intvalue, s, std::dec)) gmtime_r(&t, &tmp);
{ aDay.year = tmp.tm_year;
aDay.year = intvalue; aDay.month = tmp.tm_mon+1;
} aDay.day = tmp.tm_mday;
s = date.substr(4, 2);
if (from_string<int>(intvalue, s, std::dec))
{
aDay.month = intvalue;
}
s = date.substr(6, 2);
if (from_string<int>(intvalue, s, std::dec))
{
aDay.day = intvalue;
}
colTuple.data = *(reinterpret_cast<int *> (&aDay)); colTuple.data = *(reinterpret_cast<int *> (&aDay));

View File

@ -28,7 +28,6 @@
#include "dbrm.h" #include "dbrm.h"
#include "we_message_handlers.h" #include "we_message_handlers.h"
#include "liboamcpp.h" #include "liboamcpp.h"
#include <boost/date_time/gregorian/gregorian.hpp>
#include "dataconvert.h" #include "dataconvert.h"
#include "writeengine.h" #include "writeengine.h"

View File

@ -30,8 +30,6 @@ using namespace messageqcpp;
using namespace dmlpackage; using namespace dmlpackage;
#include "dmlpackageprocessor.h" #include "dmlpackageprocessor.h"
using namespace dmlpackageprocessor; using namespace dmlpackageprocessor;
#include <boost/date_time/gregorian/gregorian.hpp>
using namespace boost::gregorian;
#include "dataconvert.h" #include "dataconvert.h"
using namespace dataconvert; using namespace dataconvert;
#include "calpontsystemcatalog.h" #include "calpontsystemcatalog.h"

View File

@ -33,7 +33,6 @@
#include "calpontsystemcatalog.h" #include "calpontsystemcatalog.h"
#include "insertdmlpackage.h" #include "insertdmlpackage.h"
#include "liboamcpp.h" #include "liboamcpp.h"
#include <boost/date_time/gregorian/gregorian.hpp>
#include "dataconvert.h" #include "dataconvert.h"
#include "writeengine.h" #include "writeengine.h"
#include "we_convertor.h" #include "we_convertor.h"