Drizzled Public API Documentation

temporal_interval.cc

00001 /* -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
00002  *  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
00003  *
00004  *  Copyright (C) 2008 Sun Microsystems, Inc.
00005  *
00006  *  This program is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License as published by
00008  *  the Free Software Foundation; version 2 of the License.
00009  *
00010  *  This program is distributed in the hope that it will be useful,
00011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  *  GNU General Public License for more details.
00014  *
00015  *  You should have received a copy of the GNU General Public License
00016  *  along with this program; if not, write to the Free Software
00017  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00018  */
00019 
00020 #include <config.h>
00021 
00022 #include <drizzled/internal/m_string.h>
00023 #include <drizzled/error.h>
00024 #include <drizzled/session.h>
00025 #include <drizzled/current_session.h>
00026 #include <drizzled/function/time/date.h>
00027 #include <drizzled/temporal_interval.h>
00028 #include <drizzled/time_functions.h>
00029 
00030 namespace drizzled
00031 {
00032 
00033 bool TemporalInterval::initFromItem(Item *args,
00034                                     interval_type int_type,
00035                                     String *str_value)
00036 {
00037   uint64_t array[MAX_STRING_ELEMENTS];
00038   int64_t value= 0;
00039   const char *str= NULL;
00040   size_t length= 0;
00041   const CHARSET_INFO * const cs= str_value->charset();
00042 
00043 
00044   // Types <= microsecond can be converted as an integer
00045   if (static_cast<int>(int_type) <= INTERVAL_MICROSECOND)
00046   {
00047     value= args->val_int();
00048     if (args->null_value)
00049       return true;
00050     if (value < 0)
00051     {
00052       neg= true;
00053       value= -value;
00054     }
00055   }
00056   else
00057   {
00058     // Otherwise we must convert to a string and extract the multiple parts
00059     String *res;
00060     if (!(res= args->val_str(str_value)))
00061       return true;
00062 
00063     // record negative intervalls in interval->neg 
00064     str= res->ptr();
00065     const char *end= str+res->length();
00066     // Skip the whitespace
00067     while (str != end && my_isspace(cs,*str))
00068       str++;
00069     if (str != end && *str == '-')
00070     {
00071       neg= true;
00072       // skip the -
00073       str++;
00074     }
00075     length= static_cast<size_t>(end-str);   // Set up pointers to new str
00076   }
00077 
00078   switch (int_type)
00079   {
00080   case INTERVAL_YEAR:
00081     year= static_cast<uint32_t>(value);
00082     break;
00083   case INTERVAL_QUARTER:
00084     month= static_cast<uint32_t>(value*3);
00085     break;
00086   case INTERVAL_MONTH:
00087     month= static_cast<uint32_t>(value);
00088     break;
00089   case INTERVAL_WEEK:
00090     day= static_cast<uint32_t>(value*7);
00091     break;
00092   case INTERVAL_DAY:
00093     day= static_cast<uint32_t>(value);
00094     break;
00095   case INTERVAL_HOUR:
00096     hour= static_cast<uint32_t>(value);
00097     break;
00098   case INTERVAL_MICROSECOND:
00099     second_part= value;
00100     break;
00101   case INTERVAL_MINUTE:
00102     minute= value;
00103     break;
00104   case INTERVAL_SECOND:
00105     second= value;
00106     break;
00107   case INTERVAL_YEAR_MONTH:     // Allow YEAR-MONTH YYYYYMM
00108     if (getIntervalFromString(str,length,cs,NUM_YEAR_MONTH_STRING_ELEMENTS,array,false))
00109       return true;
00110     year=  static_cast<uint32_t>(array[0]);
00111     month= static_cast<uint32_t>(array[1]);
00112     break;
00113   case INTERVAL_DAY_HOUR:
00114     if (getIntervalFromString(str,length,cs,NUM_DAY_HOUR_STRING_ELEMENTS,array,false))
00115       return true;
00116     day=  static_cast<uint32_t>(array[0]);
00117     hour= static_cast<uint32_t>(array[1]);
00118     break;
00119   case INTERVAL_DAY_MICROSECOND:
00120     if (getIntervalFromString(str,length,cs,NUM_DAY_MICROSECOND_STRING_ELEMENTS,array,true))
00121       return true;
00122     day=    static_cast<uint32_t>(array[0]);
00123     hour=   static_cast<uint32_t>(array[1]);
00124     minute= array[2];
00125     second= array[3];
00126     second_part= array[4];
00127     break;
00128   case INTERVAL_DAY_MINUTE:
00129     if (getIntervalFromString(str,length,cs,NUM_DAY_MINUTE_STRING_ELEMENTS,array,false))
00130       return true;
00131     day=    static_cast<uint32_t>(array[0]);
00132     hour=   static_cast<uint32_t>(array[1]);
00133     minute= array[2];
00134     break;
00135   case INTERVAL_DAY_SECOND:
00136     if (getIntervalFromString(str,length,cs,NUM_DAY_SECOND_STRING_ELEMENTS,array,false))
00137       return true;
00138     day=    static_cast<uint32_t>(array[0]);
00139     hour=   static_cast<uint32_t>(array[1]);
00140     minute= array[2];
00141     second= array[3];
00142     break;
00143   case INTERVAL_HOUR_MICROSECOND:
00144     if (getIntervalFromString(str,length,cs,NUM_HOUR_MICROSECOND_STRING_ELEMENTS,array,true))
00145       return true;
00146     hour=   static_cast<uint32_t>(array[0]);
00147     minute= array[1];
00148     second= array[2];
00149     second_part= array[3];
00150     break;
00151   case INTERVAL_HOUR_MINUTE:
00152     if (getIntervalFromString(str,length,cs,NUM_HOUR_MINUTE_STRING_ELEMENTS,array,false))
00153       return true;
00154     hour=   static_cast<uint32_t>(array[0]);
00155     minute= array[1];
00156     break;
00157   case INTERVAL_HOUR_SECOND:
00158     if (getIntervalFromString(str,length,cs,NUM_HOUR_SECOND_STRING_ELEMENTS,array,false))
00159       return true;
00160     hour=   static_cast<uint32_t>(array[0]);
00161     minute= array[1];
00162     second= array[2];
00163     break;
00164   case INTERVAL_MINUTE_MICROSECOND:
00165     if (getIntervalFromString(str,length,cs,NUM_MINUTE_MICROSECOND_STRING_ELEMENTS,array,true))
00166       return true;
00167     minute= array[0];
00168     second= array[1];
00169     second_part= array[2];
00170     break;
00171   case INTERVAL_MINUTE_SECOND:
00172     if (getIntervalFromString(str,length,cs,NUM_MINUTE_SECOND_STRING_ELEMENTS,array,false))
00173       return true;
00174     minute= array[0];
00175     second= array[1];
00176     break;
00177   case INTERVAL_SECOND_MICROSECOND:
00178     if (getIntervalFromString(str,length,cs,NUM_SECOND_MICROSECOND_STRING_ELEMENTS,array,true))
00179       return true;
00180     second= array[0];
00181     second_part= array[1];
00182     break;
00183   case INTERVAL_LAST:
00184     assert(0);
00185     break;
00186   }
00187   return false;
00188 }
00189 
00190 bool TemporalInterval::addDate(type::Time *ltime, interval_type int_type)
00191 {
00192   long period, sign;
00193 
00194   ltime->neg= 0;
00195 
00196   sign= (neg ? -1 : 1);
00197 
00198   switch (int_type)
00199   {
00200   case INTERVAL_SECOND:
00201   case INTERVAL_SECOND_MICROSECOND:
00202   case INTERVAL_MICROSECOND:
00203   case INTERVAL_MINUTE:
00204   case INTERVAL_HOUR:
00205   case INTERVAL_MINUTE_MICROSECOND:
00206   case INTERVAL_MINUTE_SECOND:
00207   case INTERVAL_HOUR_MICROSECOND:
00208   case INTERVAL_HOUR_SECOND:
00209   case INTERVAL_HOUR_MINUTE:
00210   case INTERVAL_DAY_MICROSECOND:
00211   case INTERVAL_DAY_SECOND:
00212   case INTERVAL_DAY_MINUTE:
00213   case INTERVAL_DAY_HOUR:
00214     int64_t sec, days, daynr, microseconds, extra_sec;
00215     ltime->time_type= type::DRIZZLE_TIMESTAMP_DATETIME; // Return full date
00216     microseconds= ltime->second_part + sign*second_part;
00217     extra_sec= microseconds/1000000L;
00218     microseconds= microseconds%1000000L;
00219 
00220     sec= ((ltime->day-1)*3600*24L+ltime->hour*3600+ltime->minute*60+
00221         ltime->second +
00222         sign* (int64_t) (day*3600*24L +
00223           hour*3600L+minute*60L+
00224           second))+ extra_sec;
00225     if (microseconds < 0)
00226     {
00227       microseconds+= 1000000L;
00228       sec--;
00229     }
00230     days= sec/(3600*24L);
00231     sec-= days*3600*24L;
00232     if (sec < 0)
00233     {
00234       days--;
00235       sec+= 3600*24L;
00236     }
00237     ltime->second_part= (uint32_t) microseconds;
00238     ltime->second= (uint32_t) (sec % 60);
00239     ltime->minute= (uint32_t) (sec/60 % 60);
00240     ltime->hour=   (uint32_t) (sec/3600);
00241     daynr= calc_daynr(ltime->year,ltime->month,1) + days;
00242     /* Day number from year 0 to 9999-12-31 */
00243     if ((uint64_t) daynr > MAX_DAY_NUMBER)
00244       goto invalid_date;
00245     get_date_from_daynr((long) daynr, &ltime->year, &ltime->month, &ltime->day);
00246     break;
00247   case INTERVAL_DAY:
00248   case INTERVAL_WEEK:
00249     period= (calc_daynr(ltime->year,ltime->month,ltime->day) +
00250         sign * (long) day);
00251     /* Daynumber from year 0 to 9999-12-31 */
00252     if (period > MAX_DAY_NUMBER)
00253       goto invalid_date;
00254     get_date_from_daynr((long) period,&ltime->year,&ltime->month,&ltime->day);
00255     break;
00256   case INTERVAL_YEAR:
00257     ltime->year+= sign * (long) year;
00258     if (ltime->year >= 10000L)
00259       goto invalid_date;
00260     if (ltime->month == 2 && ltime->day == 29 &&
00261         calc_days_in_year(ltime->year) != 366)
00262       ltime->day= 28;       // Was leap-year
00263     break;
00264   case INTERVAL_YEAR_MONTH:
00265   case INTERVAL_QUARTER:
00266   case INTERVAL_MONTH:
00267     period= (ltime->year*12 + sign * (long) year*12 +
00268         ltime->month-1 + sign * (long) month);
00269     if (period >= 120000L)
00270       goto invalid_date;
00271     ltime->year= (uint32_t) (period / 12);
00272     ltime->month= (uint32_t) (period % 12L)+1;
00273     /* Adjust day if the new month doesn't have enough days */
00274     if (ltime->day > days_in_month[ltime->month-1])
00275     {
00276       ltime->day= days_in_month[ltime->month-1];
00277       if (ltime->month == 2 && calc_days_in_year(ltime->year) == 366)
00278         ltime->day++;       // Leap-year
00279     }
00280     break;
00281   default:
00282     goto null_date;
00283   }
00284 
00285   return 0;         // Ok
00286 
00287 invalid_date:
00288   push_warning_printf(current_session, DRIZZLE_ERROR::WARN_LEVEL_WARN,
00289                       ER_DATETIME_FUNCTION_OVERFLOW,
00290                       ER(ER_DATETIME_FUNCTION_OVERFLOW),
00291                       "datetime");
00292 null_date:
00293   return 1;
00294 }
00295 
00296 bool TemporalInterval::getIntervalFromString(const char *str,
00297                                              uint32_t length,
00298                                              const CHARSET_INFO * const cs,
00299                                              uint32_t count, uint64_t *values,
00300                                              bool transform_msec)
00301 {
00302   const char *end= str+length;
00303   uint32_t x;
00304 
00305   while (str != end && !my_isdigit(cs,*str))
00306     str++;
00307 
00308   for (x= 0 ; x < count ; x++)
00309   {
00310     int64_t value;
00311     const char *start= str;
00312     for (value= 0 ; str != end && my_isdigit(cs,*str) ; str++)
00313       value= value * 10L + (int64_t) (*str - '0');
00314     if (transform_msec && (x == count - 1 || str == end)) // microseconds always last
00315     {
00316       long msec_length= 6 - (str - start);
00317       if (msec_length > 0)
00318         value*= (long) log_10_int[msec_length];
00319     }
00320     values[x]= value;
00321     while (str != end && !my_isdigit(cs,*str))
00322       str++;
00323     if (str == end && x != count-1)
00324     {
00325       x++;
00326       /* Change values[0...x-1] -> values[count-x...count-1] */
00327       internal::bmove_upp((unsigned char*) (values+count),
00328                           (unsigned char*) (values+x),
00329                           sizeof(*values)*x);
00330       memset(values, 0, sizeof(*values)*(count-x));
00331       break;
00332     }
00333   }
00334   return (str != end);
00335 }
00336 
00337 } /* namespace drizzled */