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