Drizzled Public API Documentation

concat.cc
1 /* -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
3  *
4  * Copyright (C) 2008 Sun Microsystems, Inc.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; version 2 of the License.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18  */
19 
20 #include <config.h>
21 
22 #include <drizzled/function/str/concat.h>
23 #include <drizzled/error.h>
24 #include <drizzled/session.h>
25 #include <drizzled/system_variables.h>
26 
27 #include <algorithm>
28 
29 using namespace std;
30 
31 namespace drizzled {
32 
33 String *Item_func_concat::val_str(String *str)
34 {
35  assert(fixed == 1);
36  String *res,*res2,*use_as_buff;
37  uint32_t i;
38  bool is_const= 0;
39 
40  null_value=0;
41  if (!(res=args[0]->val_str(str)))
42  goto null;
43  use_as_buff= &tmp_value;
44  /* Item_subselect in --ps-protocol mode will state it as a non-const */
45  is_const= args[0]->const_item() || !args[0]->used_tables();
46  for (i=1 ; i < arg_count ; i++)
47  {
48  if (res->length() == 0)
49  {
50  if (!(res=args[i]->val_str(str)))
51  goto null;
52  }
53  else
54  {
55  if (!(res2=args[i]->val_str(use_as_buff)))
56  goto null;
57  if (res2->length() == 0)
58  continue;
59  if (res->length()+res2->length() >
60  session.variables.max_allowed_packet)
61  {
62  push_warning_printf(&session, DRIZZLE_ERROR::WARN_LEVEL_WARN,
63  ER_WARN_ALLOWED_PACKET_OVERFLOWED,
64  ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED), func_name(),
65  session.variables.max_allowed_packet);
66  goto null;
67  }
68  if (!is_const && res->alloced_length() >= res->length()+res2->length())
69  { // Use old buffer
70  res->append(*res2);
71  }
72  else if (str->alloced_length() >= res->length()+res2->length())
73  {
74  if (str == res2)
75  str->replace(0,0,*res);
76  else
77  {
78  str->copy(*res);
79  str->append(*res2);
80  }
81  res= str;
82  use_as_buff= &tmp_value;
83  }
84  else if (res == &tmp_value)
85  {
86  res->append(*res2);
87  }
88  else if (res2 == &tmp_value)
89  { // This can happend only 1 time
90  tmp_value.replace(0,0,*res);
91  res= &tmp_value;
92  use_as_buff=str; // Put next arg here
93  }
94  else if (tmp_value.is_alloced() && res2->ptr() >= tmp_value.ptr() &&
95  res2->ptr() <= tmp_value.ptr() + tmp_value.alloced_length())
96  {
97  /*
98  This happens really seldom:
99  In this case res2 is sub string of tmp_value. We will
100  now work in place in tmp_value to set it to res | res2
101  */
102  /* Chop the last characters in tmp_value that isn't in res2 */
103  tmp_value.length((uint32_t) (res2->ptr() - tmp_value.ptr()) +
104  res2->length());
105  /* Place res2 at start of tmp_value, remove chars before res2 */
106  tmp_value.replace(0,(uint32_t) (res2->ptr() - tmp_value.ptr()), *res);
107  res= &tmp_value;
108  use_as_buff=str; // Put next arg here
109  }
110  else
111  { // Two big const strings
112  /*
113  @note We should be prudent in the initial allocation unit -- the
114  size of the arguments is a function of data distribution, which
115  can be any. Instead of overcommitting at the first row, we grow
116  the allocated amount by the factor of 2. This ensures that no
117  more than 25% of memory will be overcommitted on average.
118  */
119 
120  size_t concat_len= res->length() + res2->length();
121 
122  if (tmp_value.alloced_length() < concat_len)
123  {
124  if (tmp_value.alloced_length() == 0)
125  {
126  tmp_value.alloc(concat_len);
127  }
128  else
129  {
130  uint32_t new_len= max(tmp_value.alloced_length() * 2, concat_len);
131 
132  tmp_value.realloc(new_len);
133  }
134  }
135 
136  tmp_value.copy(*res);
137  tmp_value.append(*res2);
138 
139  res= &tmp_value;
140  use_as_buff=str;
141  }
142  is_const= 0;
143  }
144  }
145  res->set_charset(collation.collation);
146  return res;
147 
148 null:
149  null_value=1;
150  return 0;
151 }
152 
153 
154 void Item_func_concat::fix_length_and_dec()
155 {
156  uint64_t max_result_length= 0;
157 
158  if (agg_arg_charsets(collation, args, arg_count, MY_COLL_ALLOW_CONV, 1))
159  return;
160 
161  for (uint32_t i=0 ; i < arg_count ; i++)
162  {
163  if (args[i]->collation.collation->mbmaxlen != collation.collation->mbmaxlen)
164  max_result_length+= (args[i]->max_length /
165  args[i]->collation.collation->mbmaxlen) *
166  collation.collation->mbmaxlen;
167  else
168  max_result_length+= args[i]->max_length;
169  }
170 
171  if (max_result_length >= MAX_BLOB_WIDTH)
172  {
173  max_result_length= MAX_BLOB_WIDTH;
174  maybe_null= 1;
175  }
176  max_length= (ulong) max_result_length;
177 }
178 
179 
185 String *Item_func_concat_ws::val_str(String *str)
186 {
187  assert(fixed == 1);
188  char tmp_str_buff[10];
189  String tmp_sep_str(tmp_str_buff, sizeof(tmp_str_buff),default_charset_info),
190  *sep_str, *res, *res2,*use_as_buff;
191  uint32_t i;
192 
193  null_value=0;
194  if (!(sep_str= args[0]->val_str(&tmp_sep_str)))
195  goto null;
196 
197  use_as_buff= &tmp_value;
198  str->length(0); // QQ; Should be removed
199  res=str;
200 
201  // Skip until non-null argument is found.
202  // If not, return the empty string
203  for (i=1; i < arg_count; i++)
204  if ((res= args[i]->val_str(str)))
205  break;
206  if (i == arg_count)
207  return &my_empty_string;
208 
209  for (i++; i < arg_count ; i++)
210  {
211  if (!(res2= args[i]->val_str(use_as_buff)))
212  continue; // Skip NULL
213 
214  if (res->length() + sep_str->length() + res2->length() >
215  session.variables.max_allowed_packet)
216  {
217  push_warning_printf(&session, DRIZZLE_ERROR::WARN_LEVEL_WARN,
218  ER_WARN_ALLOWED_PACKET_OVERFLOWED,
219  ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED), func_name(),
220  session.variables.max_allowed_packet);
221  goto null;
222  }
223  if (res->alloced_length() >=
224  res->length() + sep_str->length() + res2->length())
225  { // Use old buffer
226  res->append(*sep_str); // res->length() > 0 always
227  res->append(*res2);
228  }
229  else if (str->alloced_length() >=
230  res->length() + sep_str->length() + res2->length())
231  {
232  /* We have room in str; We can't get any errors here */
233  if (str == res2)
234  { // This is quote uncommon!
235  str->replace(0,0,*sep_str);
236  str->replace(0,0,*res);
237  }
238  else
239  {
240  str->copy(*res);
241  str->append(*sep_str);
242  str->append(*res2);
243  }
244  res=str;
245  use_as_buff= &tmp_value;
246  }
247  else if (res == &tmp_value)
248  {
249  res->append(*sep_str);
250  res->append(*res2);
251  }
252  else if (res2 == &tmp_value)
253  { // This can happend only 1 time
254  tmp_value.replace(0,0,*sep_str);
255  tmp_value.replace(0,0,*res);
256  res= &tmp_value;
257  use_as_buff=str; // Put next arg here
258  }
259  else if (tmp_value.is_alloced() && res2->ptr() >= tmp_value.ptr() &&
260  res2->ptr() < tmp_value.ptr() + tmp_value.alloced_length())
261  {
262  /*
263  This happens really seldom:
264  In this case res2 is sub string of tmp_value. We will
265  now work in place in tmp_value to set it to res | sep_str | res2
266  */
267  /* Chop the last characters in tmp_value that isn't in res2 */
268  tmp_value.length((uint32_t) (res2->ptr() - tmp_value.ptr()) +
269  res2->length());
270  /* Place res2 at start of tmp_value, remove chars before res2 */
271  tmp_value.replace(0,(uint32_t) (res2->ptr() - tmp_value.ptr()), *res);
272  tmp_value.replace(res->length(),0, *sep_str);
273  res= &tmp_value;
274  use_as_buff=str; // Put next arg here
275  }
276  else
277  { // Two big const strings
278  /*
279  @note We should be prudent in the initial allocation unit -- the
280  size of the arguments is a function of data distribution, which can
281  be any. Instead of overcommitting at the first row, we grow the
282  allocated amount by the factor of 2. This ensures that no more than
283  25% of memory will be overcommitted on average.
284  */
285 
286  size_t concat_len= res->length() + sep_str->length() + res2->length();
287 
288  if (tmp_value.alloced_length() < concat_len)
289  {
290  if (tmp_value.alloced_length() == 0)
291  {
292  tmp_value.alloc(concat_len);
293  }
294  else
295  {
296  uint32_t new_len= max(tmp_value.alloced_length() * 2, concat_len);
297 
298  tmp_value.realloc(new_len);
299  }
300  }
301 
302  tmp_value.copy(*res);
303  tmp_value.append(*sep_str);
304  tmp_value.append(*res2);
305  res= &tmp_value;
306  use_as_buff=str;
307  }
308  }
309  res->set_charset(collation.collation);
310  return res;
311 
312 null:
313  null_value=1;
314  return 0;
315 }
316 
317 
318 void Item_func_concat_ws::fix_length_and_dec()
319 {
320  uint64_t max_result_length;
321 
322  if (agg_arg_charsets(collation, args, arg_count, MY_COLL_ALLOW_CONV, 1))
323  return;
324 
325  /*
326  arg_count cannot be less than 2,
327  it is done on parser level in sql_yacc.yy
328  so, (arg_count - 2) is safe here.
329  */
330  max_result_length= (uint64_t) args[0]->max_length * (arg_count - 2);
331  for (uint32_t i=1 ; i < arg_count ; i++)
332  max_result_length+=args[i]->max_length;
333 
334  if (max_result_length >= MAX_BLOB_WIDTH)
335  {
336  max_result_length= MAX_BLOB_WIDTH;
337  maybe_null= 1;
338  }
339  max_length= (ulong) max_result_length;
340 }
341 
342 } /* namespace drizzled */