Drizzled Public API Documentation

concat.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/function/str/concat.h>
00023 #include <drizzled/error.h>
00024 #include <drizzled/session.h>
00025 
00026 #include <algorithm>
00027 
00028 using namespace std;
00029 
00030 namespace drizzled
00031 {
00032 
00033 String *Item_func_concat::val_str(String *str)
00034 {
00035   assert(fixed == 1);
00036   String *res,*res2,*use_as_buff;
00037   uint32_t i;
00038   bool is_const= 0;
00039 
00040   null_value=0;
00041   if (!(res=args[0]->val_str(str)))
00042     goto null;
00043   use_as_buff= &tmp_value;
00044   /* Item_subselect in --ps-protocol mode will state it as a non-const */
00045   is_const= args[0]->const_item() || !args[0]->used_tables();
00046   for (i=1 ; i < arg_count ; i++)
00047   {
00048     if (res->length() == 0)
00049     {
00050       if (!(res=args[i]->val_str(str)))
00051         goto null;
00052     }
00053     else
00054     {
00055       if (!(res2=args[i]->val_str(use_as_buff)))
00056         goto null;
00057       if (res2->length() == 0)
00058         continue;
00059       if (res->length()+res2->length() >
00060           session.variables.max_allowed_packet)
00061       {
00062         push_warning_printf(&session, DRIZZLE_ERROR::WARN_LEVEL_WARN,
00063                             ER_WARN_ALLOWED_PACKET_OVERFLOWED,
00064                             ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED), func_name(),
00065                             session.variables.max_allowed_packet);
00066         goto null;
00067       }
00068       if (!is_const && res->alloced_length() >= res->length()+res2->length())
00069       {           // Use old buffer
00070         res->append(*res2);
00071       }
00072       else if (str->alloced_length() >= res->length()+res2->length())
00073       {
00074         if (str == res2)
00075           str->replace(0,0,*res);
00076         else
00077         {
00078           str->copy(*res);
00079           str->append(*res2);
00080         }
00081         res= str;
00082         use_as_buff= &tmp_value;
00083       }
00084       else if (res == &tmp_value)
00085       {
00086         if (res->append(*res2))     // Must be a blob
00087           goto null;
00088       }
00089       else if (res2 == &tmp_value)
00090       {           // This can happend only 1 time
00091         if (tmp_value.replace(0,0,*res))
00092           goto null;
00093         res= &tmp_value;
00094         use_as_buff=str;      // Put next arg here
00095       }
00096       else if (tmp_value.is_alloced() && res2->ptr() >= tmp_value.ptr() &&
00097                res2->ptr() <= tmp_value.ptr() + tmp_value.alloced_length())
00098       {
00099         /*
00100           This happens really seldom:
00101           In this case res2 is sub string of tmp_value.  We will
00102           now work in place in tmp_value to set it to res | res2
00103         */
00104         /* Chop the last characters in tmp_value that isn't in res2 */
00105         tmp_value.length((uint32_t) (res2->ptr() - tmp_value.ptr()) +
00106                          res2->length());
00107         /* Place res2 at start of tmp_value, remove chars before res2 */
00108         if (tmp_value.replace(0,(uint32_t) (res2->ptr() - tmp_value.ptr()),
00109                               *res))
00110           goto null;
00111         res= &tmp_value;
00112         use_as_buff=str;      // Put next arg here
00113       }
00114       else
00115       {           // Two big const strings
00116         /*
00117           @note We should be prudent in the initial allocation unit -- the
00118           size of the arguments is a function of data distribution, which
00119           can be any. Instead of overcommitting at the first row, we grow
00120           the allocated amount by the factor of 2. This ensures that no
00121           more than 25% of memory will be overcommitted on average.
00122         */
00123 
00124         size_t concat_len= res->length() + res2->length();
00125 
00126         if (tmp_value.alloced_length() < concat_len)
00127         {
00128           if (tmp_value.alloced_length() == 0)
00129           {
00130             if (tmp_value.alloc(concat_len))
00131               goto null;
00132           }
00133           else
00134           {
00135             uint32_t new_len= max(tmp_value.alloced_length() * 2, concat_len);
00136 
00137             if (tmp_value.realloc(new_len))
00138               goto null;
00139           }
00140         }
00141 
00142         if (tmp_value.copy(*res) || tmp_value.append(*res2))
00143           goto null;
00144 
00145         res= &tmp_value;
00146         use_as_buff=str;
00147       }
00148       is_const= 0;
00149     }
00150   }
00151   res->set_charset(collation.collation);
00152   return res;
00153 
00154 null:
00155   null_value=1;
00156   return 0;
00157 }
00158 
00159 
00160 void Item_func_concat::fix_length_and_dec()
00161 {
00162   uint64_t max_result_length= 0;
00163 
00164   if (agg_arg_charsets(collation, args, arg_count, MY_COLL_ALLOW_CONV, 1))
00165     return;
00166 
00167   for (uint32_t i=0 ; i < arg_count ; i++)
00168   {
00169     if (args[i]->collation.collation->mbmaxlen != collation.collation->mbmaxlen)
00170       max_result_length+= (args[i]->max_length /
00171                            args[i]->collation.collation->mbmaxlen) *
00172         collation.collation->mbmaxlen;
00173     else
00174       max_result_length+= args[i]->max_length;
00175   }
00176 
00177   if (max_result_length >= MAX_BLOB_WIDTH)
00178   {
00179     max_result_length= MAX_BLOB_WIDTH;
00180     maybe_null= 1;
00181   }
00182   max_length= (ulong) max_result_length;
00183 }
00184 
00185 
00191 String *Item_func_concat_ws::val_str(String *str)
00192 {
00193   assert(fixed == 1);
00194   char tmp_str_buff[10];
00195   String tmp_sep_str(tmp_str_buff, sizeof(tmp_str_buff),default_charset_info),
00196          *sep_str, *res, *res2,*use_as_buff;
00197   uint32_t i;
00198 
00199   null_value=0;
00200   if (!(sep_str= args[0]->val_str(&tmp_sep_str)))
00201     goto null;
00202 
00203   use_as_buff= &tmp_value;
00204   str->length(0);       // QQ; Should be removed
00205   res=str;
00206 
00207   // Skip until non-null argument is found.
00208   // If not, return the empty string
00209   for (i=1; i < arg_count; i++)
00210     if ((res= args[i]->val_str(str)))
00211       break;
00212   if (i ==  arg_count)
00213     return &my_empty_string;
00214 
00215   for (i++; i < arg_count ; i++)
00216   {
00217     if (!(res2= args[i]->val_str(use_as_buff)))
00218       continue;         // Skip NULL
00219 
00220     if (res->length() + sep_str->length() + res2->length() >
00221         session.variables.max_allowed_packet)
00222     {
00223       push_warning_printf(&session, DRIZZLE_ERROR::WARN_LEVEL_WARN,
00224                           ER_WARN_ALLOWED_PACKET_OVERFLOWED,
00225                           ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED), func_name(),
00226                           session.variables.max_allowed_packet);
00227       goto null;
00228     }
00229     if (res->alloced_length() >=
00230         res->length() + sep_str->length() + res2->length())
00231     {           // Use old buffer
00232       res->append(*sep_str);      // res->length() > 0 always
00233       res->append(*res2);
00234     }
00235     else if (str->alloced_length() >=
00236              res->length() + sep_str->length() + res2->length())
00237     {
00238       /* We have room in str;  We can't get any errors here */
00239       if (str == res2)
00240       {           // This is quote uncommon!
00241         str->replace(0,0,*sep_str);
00242         str->replace(0,0,*res);
00243       }
00244       else
00245       {
00246         str->copy(*res);
00247         str->append(*sep_str);
00248         str->append(*res2);
00249       }
00250       res=str;
00251       use_as_buff= &tmp_value;
00252     }
00253     else if (res == &tmp_value)
00254     {
00255       if (res->append(*sep_str) || res->append(*res2))
00256         goto null; // Must be a blob
00257     }
00258     else if (res2 == &tmp_value)
00259     {           // This can happend only 1 time
00260       if (tmp_value.replace(0,0,*sep_str) || tmp_value.replace(0,0,*res))
00261         goto null;
00262       res= &tmp_value;
00263       use_as_buff=str;        // Put next arg here
00264     }
00265     else if (tmp_value.is_alloced() && res2->ptr() >= tmp_value.ptr() &&
00266              res2->ptr() < tmp_value.ptr() + tmp_value.alloced_length())
00267     {
00268       /*
00269         This happens really seldom:
00270         In this case res2 is sub string of tmp_value.  We will
00271         now work in place in tmp_value to set it to res | sep_str | res2
00272       */
00273       /* Chop the last characters in tmp_value that isn't in res2 */
00274       tmp_value.length((uint32_t) (res2->ptr() - tmp_value.ptr()) +
00275                        res2->length());
00276       /* Place res2 at start of tmp_value, remove chars before res2 */
00277       if (tmp_value.replace(0,(uint32_t) (res2->ptr() - tmp_value.ptr()),
00278                             *res) ||
00279           tmp_value.replace(res->length(),0, *sep_str))
00280         goto null;
00281       res= &tmp_value;
00282       use_as_buff=str;      // Put next arg here
00283     }
00284     else
00285     {           // Two big const strings
00286       /*
00287         @note We should be prudent in the initial allocation unit -- the
00288         size of the arguments is a function of data distribution, which can
00289         be any. Instead of overcommitting at the first row, we grow the
00290         allocated amount by the factor of 2. This ensures that no more than
00291         25% of memory will be overcommitted on average.
00292       */
00293 
00294       size_t concat_len= res->length() + sep_str->length() + res2->length();
00295 
00296       if (tmp_value.alloced_length() < concat_len)
00297       {
00298         if (tmp_value.alloced_length() == 0)
00299         {
00300           if (tmp_value.alloc(concat_len))
00301             goto null;
00302         }
00303         else
00304         {
00305           uint32_t new_len= max(tmp_value.alloced_length() * 2, concat_len);
00306 
00307           if (tmp_value.realloc(new_len))
00308             goto null;
00309         }
00310       }
00311 
00312       if (tmp_value.copy(*res) ||
00313           tmp_value.append(*sep_str) ||
00314           tmp_value.append(*res2))
00315         goto null;
00316       res= &tmp_value;
00317       use_as_buff=str;
00318     }
00319   }
00320   res->set_charset(collation.collation);
00321   return res;
00322 
00323 null:
00324   null_value=1;
00325   return 0;
00326 }
00327 
00328 
00329 void Item_func_concat_ws::fix_length_and_dec()
00330 {
00331   uint64_t max_result_length;
00332 
00333   if (agg_arg_charsets(collation, args, arg_count, MY_COLL_ALLOW_CONV, 1))
00334     return;
00335 
00336   /*
00337     arg_count cannot be less than 2,
00338     it is done on parser level in sql_yacc.yy
00339     so, (arg_count - 2) is safe here.
00340   */
00341   max_result_length= (uint64_t) args[0]->max_length * (arg_count - 2);
00342   for (uint32_t i=1 ; i < arg_count ; i++)
00343     max_result_length+=args[i]->max_length;
00344 
00345   if (max_result_length >= MAX_BLOB_WIDTH)
00346   {
00347     max_result_length= MAX_BLOB_WIDTH;
00348     maybe_null= 1;
00349   }
00350   max_length= (ulong) max_result_length;
00351 }
00352 
00353 } /* namespace drizzled */