/* 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_date_add.cpp 3905 2013-06-18 12:27:32Z bwilkinson $ * * ****************************************************************************/ #include #include using namespace std; #include "boost/assign.hpp" using namespace boost::assign; #include "functor_dtm.h" #include "functioncolumn.h" #include "intervalcolumn.h" #include "constantcolumn.h" #include "rowgroup.h" using namespace execplan; #include "dataconvert.h" using namespace dataconvert; #include "funchelpers.h" namespace funcexp { namespace helpers { uint64_t dateAdd(uint64_t time, const string& expr, IntervalColumn::interval_type unit, bool dateType, OpType funcType) { int array[10]; int64_t array2[10]; int month_length[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; int32_t year = 0, month = 0, day = 0, hour = 0, monthSave = 0; int64_t min = 0, sec = 0, msec = 0; int32_t yearAdd = 0, monthAdd = 0, dayAdd = 0, hourAdd = 0; int64_t minAdd = 0, secAdd = 0, msecAdd = 0; if (dateType) { year = (uint32_t)((time >> 16) & 0xffff); month = (uint32_t)((time >> 12) & 0xf); day = (uint32_t)((time >> 6) & 0x3f); } else { year = (uint32_t)((time >> 48) & 0xffff); month = (uint32_t)((time >> 44) & 0xf); day = (uint32_t)((time >> 38) & 0x3f); hour = (uint32_t)((time >> 32) & 0x3f); min = (uint32_t)((time >> 26) & 0x3f); sec = (uint32_t)((time >> 20) & 0x3f); msec = (uint32_t)((time & 0xfffff)); } monthSave = month; int index = -1; switch (unit) { case IntervalColumn::INTERVAL_MINUTE: case IntervalColumn::INTERVAL_SECOND: case IntervalColumn::INTERVAL_MICROSECOND: index = getNumbers(expr, array2, funcType); break; default: index = getNumbers(expr, array, funcType); break; }; if (index <= 0) throw runtime_error("expression type is not supported"); switch (unit) { case IntervalColumn::INTERVAL_YEAR: { yearAdd = array[0]; break; } case IntervalColumn::INTERVAL_QUARTER: { monthAdd = array[0] * 3; break; } case IntervalColumn::INTERVAL_MONTH: { monthAdd = array[0]; break; } case IntervalColumn::INTERVAL_WEEK: { dayAdd = array[0] * 7; break; } case IntervalColumn::INTERVAL_DAY: { dayAdd = array[0]; break; } case IntervalColumn::INTERVAL_HOUR: { hourAdd = array[0]; break; } case IntervalColumn::INTERVAL_MINUTE: { minAdd = array2[0]; break; } case IntervalColumn::INTERVAL_SECOND: { secAdd = array2[0]; break; } case IntervalColumn::INTERVAL_MICROSECOND: { msecAdd = array2[0]; break; } case IntervalColumn::INTERVAL_YEAR_MONTH: { if (index > 2) { return 0; } if (index == 1) monthAdd = array[0]; else { yearAdd = array[0]; monthAdd = array[1]; } break; } case IntervalColumn::INTERVAL_DAY_HOUR: { if (index > 2) { return 0; } if (index == 1) hourAdd = array[0]; else { dayAdd = array[0]; hourAdd = array[1]; } break; } case IntervalColumn::INTERVAL_DAY_MINUTE: { if (index > 3) { return 0; } if (index == 1) minAdd = array[0]; else { if (index == 2) { hourAdd = array[0]; minAdd = array[1]; } else { dayAdd = array[0]; hourAdd = array[1]; minAdd = array[2]; } } break; } case IntervalColumn::INTERVAL_DAY_SECOND: { if (index > 4) { return 0; } if (index == 1) secAdd = array[0]; else { if (index == 2) { minAdd = array[0]; secAdd = array[1]; } else { if (index == 3) { hourAdd = array[0]; minAdd = array[1]; secAdd = array[2]; } else { dayAdd = array[0]; hourAdd = array[1]; minAdd = array[2]; secAdd = array[3]; } } } break; } case IntervalColumn::INTERVAL_HOUR_MINUTE: { if (index > 2) { return 0; } if (index == 1) minAdd = array[0]; else { hourAdd = array[0]; minAdd = array[1]; } break; } case IntervalColumn::INTERVAL_HOUR_SECOND: { if (index > 3) { return 0; } if (index == 1) secAdd = array[0]; else { if (index == 2) { minAdd = array[0]; secAdd = array[1]; } else { hourAdd = array[0]; minAdd = array[1]; secAdd = array[2]; } } break; } case IntervalColumn::INTERVAL_MINUTE_SECOND: { if (index > 2) { return 0; } if (index == 1) secAdd = array[0]; else { minAdd = array[0]; secAdd = array[1]; } break; } case IntervalColumn::INTERVAL_DAY_MICROSECOND: { if (index > 5) { return 0; } if (index == 1) msecAdd = array[0]; else { if (index == 2) { secAdd = array[0]; msecAdd = array[1]; } else { if (index == 3) { minAdd = array[0]; secAdd = array[1]; msecAdd = array[2]; } else { if (index == 4) { hourAdd = array[0]; minAdd = array[1]; secAdd = array[2]; msecAdd = array[3]; } else { dayAdd = array[0]; hourAdd = array[1]; minAdd = array[2]; secAdd = array[3]; msecAdd = array[4]; } } } } break; } case IntervalColumn::INTERVAL_HOUR_MICROSECOND: { if (index > 4) { return 0; } if (index == 1) msecAdd = array[0]; else { if (index == 2) { secAdd = array[0]; msecAdd = array[1]; } else { if (index == 3) { minAdd = array[0]; secAdd = array[1]; msecAdd = array[2]; } else { hourAdd = array[0]; minAdd = array[1]; secAdd = array[2]; msecAdd = array[3]; } } } break; } case IntervalColumn::INTERVAL_MINUTE_MICROSECOND: { if (index > 3) { return 0; } if (index == 1) msecAdd = array[0]; else { if (index == 2) { secAdd = array[0]; msecAdd = array[1]; } else { minAdd = array[0]; secAdd = array[1]; msecAdd = array[2]; } } break; } case IntervalColumn::INTERVAL_SECOND_MICROSECOND: { if (index > 2) { return 0; } if (index == 1) msecAdd = array[0]; else { secAdd = array[0]; msecAdd = array[1]; } break; } default: // should be impossible to get here since we checked the presence // in the map, but just to be safe... throw runtime_error("unit type is not supported"); }; // calulate new date year += yearAdd; month += monthAdd; day += dayAdd; hour += hourAdd; min += minAdd; sec += secAdd; msec += msecAdd; if (msec > 999999) { int64_t secs = msec / 1000000; sec += secs; msec = msec - (secs * 1000000); } if (msec < 0) { int64_t secs = 1 + (-msec / 1000000); sec -= secs; msec = msec + (secs * 1000000); } if (sec > 59) { int64_t mins = sec / 60; min += mins; sec = sec - (mins * 60); } if (sec < 0) { int64_t mins = 0; if ((sec + (-sec / 60) * 60) == 0 && sec != 0) mins = (-sec / 60); else mins = 1 + (-sec / 60); min -= mins; sec = sec + (mins * 60); if (sec >= 60) sec = 0; } if (min > 59) { int hours = min / 60; hour += hours; min = min - (hours * 60); } if (min < 0) { int hours = 0; if ((min + (-min / 60) * 60) == 0 && min != 0) hours = (-min / 60); else hours = 1 + (-min / 60); hour -= hours; min = min + (hours * 60); if (min >= 60) min = 0; } if (hour > 23) { int days = hour / 24; day += days; hour = hour - (days * 24); } if (hour < 0) { int days = 0; if ((hour + (-hour / 24) * 24) == 0 && hour != 0) days = (-hour / 24); else days = 1 + (-hour / 24); day -= days; hour = hour + (days * 24); if (hour >= 24) hour = 0; } int tmpYear = year; if (isLeapYear(tmpYear)) month_length[2] = 29; else month_length[2] = 28; if (day > 0) { while (true) { int years = (monthSave - 1) / 12; // tmpYear += years; if (isLeapYear(tmpYear)) month_length[2] = 29; else month_length[2] = 28; int tmpMonth = monthSave - (years * 12); if (day <= month_length[tmpMonth]) break; month++; day = day - month_length[tmpMonth]; monthSave++; if (monthSave > 12) { monthSave = 1; tmpYear++; } } } else { while (true) { if (day > 0) break; if (-day < month_length[monthSave]) { if (monthSave == 0) { monthSave = 12; tmpYear--; } month--; monthSave--; if (monthSave == 0) { monthSave = 12; tmpYear--; } if (monthSave == 2) { // if ( dataconvert::DataConvert::isLeapYear(year) ) if (isLeapYear(tmpYear)) month_length[2] = 29; } if (day < 1) day = month_length[monthSave] + day; // BUG 5448 - changed from '==' to '<=' if (day <= 0) { if (monthSave == 0) { monthSave = 12; tmpYear--; } month--; monthSave--; if (monthSave == 0) { monthSave = 12; tmpYear--; } if (monthSave == 2) { // if ( dataconvert::DataConvert::isLeapYear(year) ) if (isLeapYear(tmpYear)) month_length[2] = 29; } day = day + month_length[monthSave]; } break; } if (monthSave == 0) { monthSave = 12; tmpYear--; if (isLeapYear(tmpYear)) month_length[2] = 29; else month_length[2] = 28; } month--; monthSave--; if (monthSave == 0) { monthSave = 12; tmpYear--; if (isLeapYear(tmpYear)) month_length[2] = 29; else month_length[2] = 28; } day = day + month_length[monthSave]; } } if (month > 12) { int years = (month - 1) / 12; year += years; month = month - (years * 12); } if (month < 1) { int years = 1 + ((-month) / 12); year -= years; month = month + (years * 12); } if (isLeapYear(year)) month_length[2] = 29; else month_length[2] = 28; if (day > month_length[month]) day = month_length[month]; if (year < 1000 || year > 9999) { return 0; } uint64_t value; dataconvert::DateTime aDatetime; aDatetime.year = year; aDatetime.month = month; aDatetime.day = day; aDatetime.hour = hour; aDatetime.minute = min; aDatetime.second = sec; aDatetime.msecond = msec; value = *(reinterpret_cast(&aDatetime)); return value; } } // namespace helpers CalpontSystemCatalog::ColType Func_date_add::operationType(FunctionParm& fp, CalpontSystemCatalog::ColType& resultType) { resultType.colDataType = CalpontSystemCatalog::DATETIME; resultType.colWidth = 8; return resultType; } int64_t Func_date_add::getIntVal(rowgroup::Row& row, FunctionParm& parm, bool& isNull, CalpontSystemCatalog::ColType& ct) { int64_t val = 0; bool dateType = false; 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: { val = dataconvert::DataConvert::intToDatetime(parm[0]->data()->getIntVal(row, isNull)); break; } case execplan::CalpontSystemCatalog::DECIMAL: case execplan::CalpontSystemCatalog::UDECIMAL: { if (parm[0]->data()->resultType().scale == 0) val = dataconvert::DataConvert::intToDatetime(parm[0]->data()->getIntVal(row, isNull)); else isNull = true; break; } case execplan::CalpontSystemCatalog::VARCHAR: case execplan::CalpontSystemCatalog::CHAR: case execplan::CalpontSystemCatalog::TEXT: { val = dataconvert::DataConvert::stringToDatetime(parm[0]->data()->getStrVal(row, isNull).safeString("")); break; } case execplan::CalpontSystemCatalog::DATE: { val = parm[0]->data()->getDateIntVal(row, isNull); dateType = true; break; } case execplan::CalpontSystemCatalog::DATETIME: { val = parm[0]->data()->getDatetimeIntVal(row, isNull); break; } case execplan::CalpontSystemCatalog::TIMESTAMP: { TimeStamp timestamp(parm[0]->data()->getTimestampIntVal(row, isNull)); int64_t seconds = timestamp.second; MySQLTime m_time; gmtSecToMySQLTime(seconds, m_time, ct.getTimeZone()); DateTime dt; dt.year = m_time.year; dt.month = m_time.month; dt.day = m_time.day; dt.hour = m_time.hour; dt.minute = m_time.minute; dt.second = m_time.second; dt.msecond = timestamp.msecond; val = *(reinterpret_cast(&dt)); break; } default: { isNull = true; } } if (isNull || val == -1) { isNull = true; return 0; } IntervalColumn::interval_type unit = static_cast(parm[2]->data()->getIntVal(row, isNull)); OpType funcType = OP_ADD; ConstantColumn* constCol = dynamic_cast(parm[3]->data()); execplan::CalpontSystemCatalog::ColType ct3 = parm[3]->data()->resultType(); if ((ct3.colDataType == execplan::CalpontSystemCatalog::CHAR || ct3.colDataType == execplan::CalpontSystemCatalog::TEXT || ct3.colDataType == execplan::CalpontSystemCatalog::VARCHAR) && constCol != NULL && constCol->constval().safeString().compare("SUB") == 0) funcType = OP_SUB; else funcType = static_cast(parm[3]->data()->getIntVal(row, isNull)); uint64_t value = helpers::dateAdd(val, parm[1]->data()->getStrVal(row, isNull).safeString(""), unit, dateType, funcType); if (value == 0) isNull = true; // if (dateType) // value = (((value >> 32) & 0xFFFFFFC0) | 0x3E); return value; } string Func_date_add::getStrVal(rowgroup::Row& row, FunctionParm& parm, bool& isNull, CalpontSystemCatalog::ColType& ct) { return dataconvert::DataConvert::datetimeToString(getIntVal(row, parm, isNull, ct)); } } // namespace funcexp