Drizzled Public API Documentation

typecast.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 <cstdio>
00023 
00024 #include <drizzled/current_session.h>
00025 #include <drizzled/error.h>
00026 #include <drizzled/function/time/typecast.h>
00027 #include <drizzled/time_functions.h>
00028 #include <drizzled/charset.h>
00029 
00030 namespace drizzled {
00031 
00032 bool Item_char_typecast::eq(const Item *item, bool binary_cmp) const
00033 {
00034   if (this == item)
00035     return 1;
00036   if (item->type() != FUNC_ITEM ||
00037       functype() != ((Item_func*)item)->functype())
00038     return 0;
00039 
00040   Item_char_typecast *cast= (Item_char_typecast*)item;
00041   if (cast_length != cast->cast_length ||
00042       cast_cs     != cast->cast_cs)
00043     return 0;
00044 
00045   if (!args[0]->eq(cast->args[0], binary_cmp))
00046       return 0;
00047   return 1;
00048 }
00049 
00050 void Item_typecast::print(String *str)
00051 {
00052   str->append(STRING_WITH_LEN("cast("));
00053   args[0]->print(str);
00054   str->append(STRING_WITH_LEN(" as "));
00055   str->append(cast_type());
00056   str->append(')');
00057 }
00058 
00059 
00060 void Item_char_typecast::print(String *str)
00061 {
00062   str->append(STRING_WITH_LEN("cast("));
00063   args[0]->print(str);
00064   str->append(STRING_WITH_LEN(" as char"));
00065   if (cast_length >= 0)
00066   {
00067     str->append('(');
00068     char buffer[20];
00069     // my_charset_bin is good enough for numbers
00070     String st(buffer, sizeof(buffer), &my_charset_bin);
00071     st.set((uint64_t)cast_length, &my_charset_bin);
00072     str->append(st);
00073     str->append(')');
00074   }
00075   if (cast_cs)
00076   {
00077     str->append(STRING_WITH_LEN(" charset "));
00078     str->append(cast_cs->csname);
00079   }
00080   str->append(')');
00081 }
00082 
00083 String *Item_char_typecast::val_str(String *str)
00084 {
00085   assert(fixed == 1);
00086   String *res;
00087   uint32_t length;
00088 
00089   if (!charset_conversion)
00090   {
00091     if (!(res= args[0]->val_str(str)))
00092     {
00093       null_value= 1;
00094       return 0;
00095     }
00096   }
00097   else
00098   {
00099     // Convert character set if differ
00100     if (!(res= args[0]->val_str(&tmp_value)))
00101     {
00102       null_value= 1;
00103       return 0;
00104     }
00105     str->copy(res->ptr(), res->length(), cast_cs);
00106     res= str;
00107   }
00108 
00109   res->set_charset(cast_cs);
00110 
00111   /*
00112     Cut the tail if cast with length
00113     and the result is longer than cast length, e.g.
00114     CAST('string' AS CHAR(1))
00115   */
00116   if (cast_length >= 0)
00117   {
00118     if (res->length() > (length= (uint32_t) res->charpos(cast_length)))
00119     {                                           // Safe even if const arg
00120       char char_type[40];
00121       snprintf(char_type, sizeof(char_type), "%s(%lu)",
00122                cast_cs == &my_charset_bin ? "BINARY" : "CHAR",
00123                (ulong) cast_length);
00124 
00125       if (!res->alloced_length())
00126       {                                         // Don't change const str
00127         str_value= *res;                        // Not malloced string
00128         res= &str_value;
00129       }
00130       push_warning_printf(current_session, DRIZZLE_ERROR::WARN_LEVEL_WARN,
00131                           ER_TRUNCATED_WRONG_VALUE,
00132                           ER(ER_TRUNCATED_WRONG_VALUE), char_type,
00133                           res->c_str());
00134       res->length((uint) length);
00135     }
00136     else if (cast_cs == &my_charset_bin && res->length() < (uint) cast_length)
00137     {
00138       if (res->alloced_length() < (uint) cast_length)
00139       {
00140         str->alloc(cast_length);
00141         str->copy(*res);
00142         res= str;
00143       }
00144       memset(res->ptr() + res->length(), 0,
00145              (uint) cast_length - res->length());
00146       res->length(cast_length);
00147     }
00148   }
00149   null_value= 0;
00150   return res;
00151 }
00152 
00153 
00154 void Item_char_typecast::fix_length_and_dec()
00155 {
00156   uint32_t char_length;
00157   /*
00158      We always force character set conversion if cast_cs
00159      is a multi-byte character set. It garantees that the
00160      result of CAST is a well-formed string.
00161      For single-byte character sets we allow just to copy
00162      from the argument. A single-byte character sets string
00163      is always well-formed.
00164 
00165      There is a special trick to convert form a number to ucs2.
00166      As numbers have my_charset_bin as their character set,
00167      it wouldn't do conversion to ucs2 without an additional action.
00168      To force conversion, we should pretend to be non-binary.
00169      Let's choose from_cs this way:
00170      - If the argument in a number and cast_cs is ucs2 (i.e. mbminlen > 1),
00171        then from_cs is set to latin1, to perform latin1 -> ucs2 conversion.
00172      - If the argument is a number and cast_cs is ASCII-compatible
00173        (i.e. mbminlen == 1), then from_cs is set to cast_cs,
00174        which allows just to take over the args[0]->val_str() result
00175        and thus avoid unnecessary character set conversion.
00176      - If the argument is not a number, then from_cs is set to
00177        the argument's charset.
00178   */
00179   from_cs= (args[0]->result_type() == INT_RESULT ||
00180             args[0]->result_type() == DECIMAL_RESULT ||
00181             args[0]->result_type() == REAL_RESULT) ?
00182            (cast_cs->mbminlen == 1 ? cast_cs : &my_charset_utf8_general_ci) :
00183            args[0]->collation.collation;
00184   charset_conversion= (cast_cs->mbmaxlen > 1) ||
00185                       (!my_charset_same(from_cs, cast_cs) && from_cs != &my_charset_bin && cast_cs != &my_charset_bin);
00186   collation.set(cast_cs, DERIVATION_IMPLICIT);
00187   char_length= (cast_length >= 0) ? (uint32_t)cast_length :
00188          (uint32_t)args[0]->max_length/from_cs->mbmaxlen;
00189   max_length= char_length * cast_cs->mbmaxlen;
00190 }
00191 
00192 
00193 String *Item_datetime_typecast::val_str(String *str)
00194 {
00195   assert(fixed == 1);
00196   type::Time ltime;
00197 
00198   if (not get_arg0_date(ltime, TIME_FUZZY_DATE))
00199   {
00200     if (ltime.second_part)
00201     {
00202       ltime.convert(*str);
00203     }
00204     else
00205     {
00206       ltime.convert(*str);
00207     }
00208 
00209     return str;
00210   }
00211 
00212   null_value=1;
00213   return 0;
00214 }
00215 
00216 
00217 int64_t Item_datetime_typecast::val_int()
00218 {
00219   assert(fixed == 1);
00220   type::Time ltime;
00221   if (get_arg0_date(ltime, 1))
00222   {
00223     null_value= 1;
00224     return 0;
00225   }
00226 
00227   int64_t tmp;
00228   ltime.convert(tmp);
00229 
00230   return tmp;
00231 }
00232 
00233 
00234 bool Item_date_typecast::get_date(type::Time &ltime, uint32_t )
00235 {
00236   bool res= get_arg0_date(ltime, TIME_FUZZY_DATE);
00237 
00238   ltime.hour= ltime.minute= ltime.second= ltime.second_part= 0;
00239   ltime.time_type= type::DRIZZLE_TIMESTAMP_DATE;
00240 
00241   return res;
00242 }
00243 
00244 
00245 bool Item_date_typecast::get_time(type::Time &ltime)
00246 {
00247   ltime.reset();
00248 
00249   return args[0]->null_value;
00250 }
00251 
00252 
00253 String *Item_date_typecast::val_str(String *str)
00254 {
00255   assert(fixed == 1);
00256   type::Time ltime;
00257 
00258   if (!get_arg0_date(ltime, TIME_FUZZY_DATE))
00259   {
00260     str->alloc(type::Time::MAX_STRING_LENGTH);
00261     ltime.convert(*str, type::DRIZZLE_TIMESTAMP_DATE);
00262 
00263     return str;
00264   }
00265 
00266   null_value=1;
00267   return 0;
00268 }
00269 
00270 int64_t Item_date_typecast::val_int()
00271 {
00272   assert(fixed == 1);
00273   type::Time ltime;
00274 
00275   if ((null_value= args[0]->get_date(ltime, TIME_FUZZY_DATE)))
00276     return 0;
00277 
00278   return (int64_t) (ltime.year * 10000L + ltime.month * 100 + ltime.day);
00279 }
00280 
00281 } /* namespace drizzled */