Drizzled Public API Documentation

extract.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/temporal.h>
00023 #include <drizzled/error.h>
00024 #include <drizzled/session.h>
00025 #include <drizzled/calendar.h>
00026 #include <drizzled/function/time/extract.h>
00027 
00028 namespace drizzled
00029 {
00030 
00031 /*
00032    'interval_names' reflects the order of the enumeration interval_type.
00033    See item/time.h
00034  */
00035 
00036 extern const char *interval_names[];
00037 
00038 void Item_extract::print(String *str)
00039 {
00040   str->append(STRING_WITH_LEN("extract("));
00041   str->append(interval_names[int_type]);
00042   str->append(STRING_WITH_LEN(" from "));
00043   args[0]->print(str);
00044   str->append(')');
00045 }
00046 
00047 void Item_extract::fix_length_and_dec()
00048 {
00049   value.alloc(DRIZZLE_MAX_LENGTH_DATETIME_AS_STRING);       
00050 
00051   maybe_null= 1;          /* If NULL supplied only */
00052   switch (int_type) {
00053   case INTERVAL_YEAR:   max_length=4; date_value=1; break;
00054   case INTERVAL_YEAR_MONTH: max_length=6; date_value=1; break;
00055   case INTERVAL_QUARTER:        max_length=2; date_value=1; break;
00056   case INTERVAL_MONTH:    max_length=2; date_value=1; break;
00057   case INTERVAL_WEEK:   max_length=2; date_value=1; break;
00058   case INTERVAL_DAY:    max_length=2; date_value=1; break;
00059   case INTERVAL_DAY_HOUR: max_length=9; date_value=0; break;
00060   case INTERVAL_DAY_MINUTE: max_length=11; date_value=0; break;
00061   case INTERVAL_DAY_SECOND: max_length=13; date_value=0; break;
00062   case INTERVAL_HOUR:   max_length=2; date_value=0; break;
00063   case INTERVAL_HOUR_MINUTE:  max_length=4; date_value=0; break;
00064   case INTERVAL_HOUR_SECOND:  max_length=6; date_value=0; break;
00065   case INTERVAL_MINUTE:   max_length=2; date_value=0; break;
00066   case INTERVAL_MINUTE_SECOND:  max_length=4; date_value=0; break;
00067   case INTERVAL_SECOND:   max_length=2; date_value=0; break;
00068   case INTERVAL_MICROSECOND:  max_length=2; date_value=0; break;
00069   case INTERVAL_DAY_MICROSECOND: max_length=20; date_value=0; break;
00070   case INTERVAL_HOUR_MICROSECOND: max_length=13; date_value=0; break;
00071   case INTERVAL_MINUTE_MICROSECOND: max_length=11; date_value=0; break;
00072   case INTERVAL_SECOND_MICROSECOND: max_length=9; date_value=0; break;
00073   case INTERVAL_LAST: assert(0); break;
00074   }
00075 }
00076 
00077 int64_t Item_extract::val_int()
00078 {
00079   assert(fixed);
00080 
00081   if (args[0]->is_null())
00082   {
00083     /* For NULL argument, we return a NULL result */
00084     null_value= true;
00085     return 0;
00086   }
00087 
00088   /* We could have either a datetime or a time.. */
00089   DateTime datetime_temporal;
00090   Time time_temporal;
00091 
00092   /* Abstract pointer type we'll use in the final switch */
00093   Temporal *temporal;
00094 
00095   if (date_value)
00096   {
00097     /* Grab the first argument as a DateTime object */
00098     Item_result arg0_result_type= args[0]->result_type();
00099     
00100     switch (arg0_result_type)
00101     {
00102       case DECIMAL_RESULT: 
00103         /* 
00104         * For doubles supplied, interpret the arg as a string, 
00105         * so intentionally fall-through here...
00106         * This allows us to accept double parameters like 
00107         * 19971231235959.01 and interpret it the way MySQL does:
00108         * as a TIMESTAMP-like thing with a microsecond component.
00109         * Ugh, but need to keep backwards-compat.
00110         */
00111       case STRING_RESULT:
00112         {
00113           char buff[DRIZZLE_MAX_LENGTH_DATETIME_AS_STRING];
00114           String tmp(buff,sizeof(buff), &my_charset_utf8_bin);
00115           String *res= args[0]->val_str(&tmp);
00116 
00117           if (res && (res != &tmp))
00118           {
00119             tmp.copy(*res);
00120           }
00121 
00122           if (! datetime_temporal.from_string(tmp.c_ptr(), tmp.length()))
00123           {
00124             /* 
00125             * Could not interpret the function argument as a temporal value, 
00126             * so throw an error and return 0
00127             */
00128             my_error(ER_INVALID_DATETIME_VALUE, MYF(0), tmp.c_ptr());
00129             return 0;
00130           }
00131         }
00132         break;
00133       case INT_RESULT:
00134         if (datetime_temporal.from_int64_t(args[0]->val_int()))
00135           break;
00136         /* Intentionally fall-through on invalid conversion from integer */
00137       default:
00138         {
00139           /* 
00140           * Could not interpret the function argument as a temporal value, 
00141           * so throw an error and return 0
00142           */
00143           null_value= true;
00144           char buff[DRIZZLE_MAX_LENGTH_DATETIME_AS_STRING];
00145           String tmp(buff,sizeof(buff), &my_charset_utf8_bin);
00146           String *res;
00147 
00148           res= args[0]->val_str(&tmp);
00149 
00150           if (res && (res != &tmp))
00151           {
00152             tmp.copy(*res);
00153           }
00154 
00155           my_error(ER_INVALID_DATETIME_VALUE, MYF(0), tmp.c_ptr());
00156           return 0;
00157         }
00158     }
00159     /* 
00160      * If we got here, we have a successfully converted DateTime temporal. 
00161      * Point our working temporal to this.
00162      */
00163     temporal= &datetime_temporal;
00164   }
00165   else
00166   {
00167     /* 
00168      * Because of the ridiculous way in which MySQL handles
00169      * TIME values (it does implicit integer -> string conversions
00170      * but only for DATETIME, not TIME values) we must first 
00171      * try a conversion into a TIME from a string.  If this
00172      * fails, we fall back on a DATETIME conversion.  This is
00173      * necessary because of the fact that DateTime::from_string()
00174      * looks first for DATETIME, then DATE regex matches.  6 consecutive
00175      * numbers, say 231130, will match the DATE regex YYMMDD
00176      * with no TIME part, but MySQL actually implicitly treats
00177      * parameters to SECOND(), HOUR(), and MINUTE() as TIME-only
00178      * values and matches 231130 as HHmmSS!
00179      *
00180      * Oh, and Brian Aker MADE me do this. :) --JRP
00181      */
00182     
00183     char time_buff[DRIZZLE_MAX_LENGTH_DATETIME_AS_STRING];
00184     String tmp_time(time_buff,sizeof(time_buff), &my_charset_utf8_bin);
00185     String *time_res= args[0]->val_str(&tmp_time);
00186 
00187     if (time_res && (time_res != &tmp_time))
00188     {
00189       tmp_time.copy(*time_res);
00190     }
00191 
00192     if (! time_temporal.from_string(tmp_time.c_ptr(), tmp_time.length()))
00193     {
00194       /* 
00195        * OK, we failed to match the first argument as a string
00196        * representing a time value, so we grab the first argument 
00197        * as a DateTime object and try that for a match...
00198        */
00199       Item_result arg0_result_type= args[0]->result_type();
00200       
00201       switch (arg0_result_type)
00202       {
00203         case DECIMAL_RESULT: 
00204           /* 
00205            * For doubles supplied, interpret the arg as a string, 
00206            * so intentionally fall-through here...
00207            * This allows us to accept double parameters like 
00208            * 19971231235959.01 and interpret it the way MySQL does:
00209            * as a TIMESTAMP-like thing with a microsecond component.
00210            * Ugh, but need to keep backwards-compat.
00211            */
00212         case STRING_RESULT:
00213           {
00214             char buff[DRIZZLE_MAX_LENGTH_DATETIME_AS_STRING];
00215             String tmp(buff,sizeof(buff), &my_charset_utf8_bin);
00216             String *res= args[0]->val_str(&tmp);
00217 
00218             if (res && (res != &tmp))
00219             {
00220               tmp.copy(*res);
00221             }
00222 
00223             if (! datetime_temporal.from_string(tmp.c_ptr(), tmp.length()))
00224             {
00225               /* 
00226                * Could not interpret the function argument as a temporal value, 
00227                * so throw an error and return 0
00228                */
00229               my_error(ER_INVALID_DATETIME_VALUE, MYF(0), tmp.c_ptr());
00230               return 0;
00231             }
00232           }
00233           break;
00234         case INT_RESULT:
00235           if (datetime_temporal.from_int64_t(args[0]->val_int()))
00236             break;
00237           /* Intentionally fall-through on invalid conversion from integer */
00238         default:
00239           {
00240             /* 
00241              * Could not interpret the function argument as a temporal value, 
00242              * so throw an error and return 0
00243              */
00244             null_value= true;
00245             char buff[DRIZZLE_MAX_LENGTH_DATETIME_AS_STRING];
00246             String tmp(buff,sizeof(buff), &my_charset_utf8_bin);
00247             String *res;
00248 
00249             res= args[0]->val_str(&tmp);
00250 
00251             my_error(ER_INVALID_DATETIME_VALUE, MYF(0), res->c_ptr());
00252             return 0;
00253           }
00254       }
00255       /* If we're here, our time failed, but our datetime succeeded... */
00256       temporal= &datetime_temporal;
00257     }
00258     else
00259     {
00260       /* If we're here, our time succeeded... */
00261       temporal= &time_temporal;
00262     }
00263   }
00264 
00265   /* Return the requested datetime component */
00266   switch (int_type) {
00267     case INTERVAL_YEAR:   
00268       return (int64_t) temporal->years();
00269     case INTERVAL_YEAR_MONTH: 
00270       return (int64_t) ((temporal->years() * 100L) + temporal->months());
00271     case INTERVAL_QUARTER:
00272       return (int64_t) (temporal->months() + 2) / 3;
00273     case INTERVAL_MONTH:
00274       return (int64_t) temporal->months();
00275     case INTERVAL_WEEK:
00276       return iso_week_number_from_gregorian_date(temporal->years()
00277                                                , temporal->months()
00278                                                , temporal->days());
00279     case INTERVAL_DAY:
00280       return (int64_t) temporal->days();
00281     case INTERVAL_DAY_HOUR: 
00282       return (int64_t) ((temporal->days() * 100L) + temporal->hours());
00283     case INTERVAL_DAY_MINUTE: 
00284       return (int64_t) ((temporal->days() * 10000L)
00285             + (temporal->hours() * 100L) 
00286             + temporal->minutes());
00287     case INTERVAL_DAY_SECOND:  
00288       return (int64_t) (
00289               (int64_t) (temporal->days() * 1000000L) 
00290             + (int64_t) (temporal->hours() * 10000L)
00291             + (temporal->minutes() * 100L) 
00292             + temporal->seconds());
00293     case INTERVAL_HOUR:   
00294       return (int64_t) temporal->hours();
00295     case INTERVAL_HOUR_MINUTE:  
00296       return (int64_t) (temporal->hours() * 100L) 
00297             + temporal->minutes();
00298     case INTERVAL_HOUR_SECOND:  
00299       return (int64_t) (temporal->hours() * 10000L) 
00300             + (temporal->minutes() * 100L) 
00301             + temporal->seconds();
00302     case INTERVAL_MINUTE:
00303       return (int64_t) temporal->minutes();
00304     case INTERVAL_MINUTE_SECOND:  
00305       return (int64_t) (temporal->minutes() * 100L) + temporal->seconds();
00306     case INTERVAL_SECOND:
00307       return (int64_t) temporal->seconds();
00308     case INTERVAL_MICROSECOND:  
00309       return (int64_t) temporal->useconds();
00310     case INTERVAL_DAY_MICROSECOND: 
00311       return (int64_t) 
00312               (
00313               (
00314               (int64_t) (temporal->days() * 1000000L) 
00315             + (int64_t) (temporal->hours() * 10000L) 
00316             + (temporal->minutes() * 100L) 
00317             + temporal->seconds()
00318               ) 
00319               * 1000000L
00320               ) 
00321             + temporal->useconds();
00322     case INTERVAL_HOUR_MICROSECOND:
00323         return (int64_t)
00324               (
00325               (
00326               (int64_t) (temporal->hours() * 10000L) 
00327             + (temporal->minutes() * 100L) 
00328             + temporal->seconds()
00329               ) 
00330               * 1000000L
00331               ) 
00332             + temporal->useconds();
00333     case INTERVAL_MINUTE_MICROSECOND:
00334         return (int64_t)
00335               (
00336               (
00337               (temporal->minutes() * 100L) 
00338             + temporal->seconds()
00339               ) 
00340               * 1000000L
00341               ) 
00342             + temporal->useconds();
00343     case INTERVAL_SECOND_MICROSECOND: 
00344         return (int64_t) (temporal->seconds() * 1000000L)
00345             + temporal->useconds();
00346     case INTERVAL_LAST: 
00347     default:
00348         assert(0); 
00349         return 0;
00350   }
00351 }
00352 
00353 bool Item_extract::eq(const Item *item, bool binary_cmp) const
00354 {
00355   if (this == item)
00356     return 1;
00357   if (item->type() != FUNC_ITEM ||
00358       functype() != ((Item_func*)item)->functype())
00359     return 0;
00360 
00361   Item_extract* ie= (Item_extract*)item;
00362   if (ie->int_type != int_type)
00363     return 0;
00364 
00365   if (!args[0]->eq(ie->args[0], binary_cmp))
00366       return 0;
00367   return 1;
00368 }
00369 
00370 } /* namespace drizzled */