1 /* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License, version 2.0,
5 as published by the Free Software Foundation.
6
7 This program is also distributed with certain software (including
8 but not limited to OpenSSL) that is licensed under separate terms,
9 as designated in a particular file or component or in included license
10 documentation. The authors of MySQL hereby grant you an additional
11 permission to link the program and your derivative works with the
12 separately licensed software that they have included with MySQL.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License, version 2.0, for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
22
23 #include "sql_priv.h"
24 #include "my_global.h" // HAVE_*
25
26 #ifdef HAVE_QUERY_CACHE
27 #include <mysql.h>
28 #include "emb_qcache.h"
29 #include "embedded_priv.h"
30 #include "sql_class.h" // THD
31
store_uchar(uchar c)32 void Querycache_stream::store_uchar(uchar c)
33 {
34 if (data_end == cur_data)
35 use_next_block(TRUE);
36 *(cur_data++)= c;
37 #ifndef DBUG_OFF
38 stored_size++;
39 #endif
40 }
41
store_short(ushort s)42 void Querycache_stream::store_short(ushort s)
43 {
44 #ifndef DBUG_OFF
45 stored_size+= 2;
46 #endif
47 if (data_end - cur_data > 1)
48 {
49 int2store(cur_data, s);
50 cur_data+= 2;
51 return;
52 }
53 if (data_end == cur_data)
54 {
55 use_next_block(TRUE);
56 int2store(cur_data, s);
57 cur_data+= 2;
58 return;
59 }
60 *cur_data= ((uchar *)(&s))[0];
61 use_next_block(TRUE);
62 *(cur_data++)= ((uchar *)(&s))[1];
63 }
64
store_int(uint i)65 void Querycache_stream::store_int(uint i)
66 {
67 #ifndef DBUG_OFF
68 stored_size+= 4;
69 #endif
70 size_t rest_len= data_end - cur_data;
71 if (rest_len > 3)
72 {
73 int4store(cur_data, i);
74 cur_data+= 4;
75 return;
76 }
77 if (!rest_len)
78 {
79 use_next_block(TRUE);
80 int4store(cur_data, i);
81 cur_data+= 4;
82 return;
83 }
84 char buf[4];
85 int4store(buf, i);
86 memcpy(cur_data, buf, rest_len);
87 use_next_block(TRUE);
88 memcpy(cur_data, buf+rest_len, 4-rest_len);
89 cur_data+= 4-rest_len;
90 }
91
store_ll(ulonglong ll)92 void Querycache_stream::store_ll(ulonglong ll)
93 {
94 #ifndef DBUG_OFF
95 stored_size+= 8;
96 #endif
97 size_t rest_len= data_end - cur_data;
98 if (rest_len > 7)
99 {
100 int8store(cur_data, ll);
101 cur_data+= 8;
102 return;
103 }
104 if (!rest_len)
105 {
106 use_next_block(TRUE);
107 int8store(cur_data, ll);
108 cur_data+= 8;
109 return;
110 }
111 memcpy(cur_data, &ll, rest_len);
112 use_next_block(TRUE);
113 memcpy(cur_data, ((uchar*)&ll)+rest_len, 8-rest_len);
114 cur_data+= 8-rest_len;
115 }
116
store_str_only(const char * str,uint str_len)117 void Querycache_stream::store_str_only(const char *str, uint str_len)
118 {
119 #ifndef DBUG_OFF
120 stored_size+= str_len;
121 #endif
122 do
123 {
124 size_t rest_len= data_end - cur_data;
125 if (rest_len >= str_len)
126 {
127 memcpy(cur_data, str, str_len);
128 cur_data+= str_len;
129 return;
130 }
131 memcpy(cur_data, str, rest_len);
132 use_next_block(TRUE);
133 str_len-= rest_len;
134 str+= rest_len;
135 } while(str_len);
136 }
137
store_str(const char * str,uint str_len)138 void Querycache_stream::store_str(const char *str, uint str_len)
139 {
140 store_int(str_len);
141 store_str_only(str, str_len);
142 }
143
store_safe_str(const char * str,uint str_len)144 void Querycache_stream::store_safe_str(const char *str, uint str_len)
145 {
146 if (str)
147 {
148 store_int(str_len+1);
149 store_str_only(str, str_len);
150 }
151 else
152 store_int(0);
153 }
154
load_uchar()155 uchar Querycache_stream::load_uchar()
156 {
157 if (cur_data == data_end)
158 use_next_block(FALSE);
159 return *(cur_data++);
160 }
161
load_short()162 ushort Querycache_stream::load_short()
163 {
164 ushort result;
165 if (data_end-cur_data > 1)
166 {
167 result= uint2korr(cur_data);
168 cur_data+= 2;
169 return result;
170 }
171 if (data_end == cur_data)
172 {
173 use_next_block(FALSE);
174 result= uint2korr(cur_data);
175 cur_data+= 2;
176 return result;
177 }
178 ((uchar*)&result)[0]= *cur_data;
179 use_next_block(FALSE);
180 ((uchar*)&result)[1]= *(cur_data++);
181 return result;
182 }
183
load_int()184 uint Querycache_stream::load_int()
185 {
186 int result;
187 size_t rest_len= data_end - cur_data;
188 if (rest_len > 3)
189 {
190 result= uint4korr(cur_data);
191 cur_data+= 4;
192 return result;
193 }
194 if (!rest_len)
195 {
196 use_next_block(FALSE);
197 result= uint4korr(cur_data);
198 cur_data+= 4;
199 return result;
200 }
201 char buf[4], *buf_p= buf;
202 memcpy(buf, cur_data, rest_len);
203 use_next_block(FALSE);
204 memcpy(buf+rest_len, cur_data, 4-rest_len);
205 cur_data+= 4-rest_len;
206 result= uint4korr(buf_p);
207 return result;
208 }
209
load_ll()210 ulonglong Querycache_stream::load_ll()
211 {
212 ulonglong result;
213 size_t rest_len= data_end - cur_data;
214 if (rest_len > 7)
215 {
216 result= uint8korr(cur_data);
217 cur_data+= 8;
218 return result;
219 }
220 if (!rest_len)
221 {
222 use_next_block(FALSE);
223 result= uint8korr(cur_data);
224 cur_data+= 8;
225 return result;
226 }
227 memcpy(&result, cur_data, rest_len);
228 use_next_block(FALSE);
229 memcpy(((uchar*)&result)+rest_len, cur_data, 8-rest_len);
230 cur_data+= 8-rest_len;
231 return result;
232 }
233
load_str_only(char * buffer,uint str_len)234 void Querycache_stream::load_str_only(char *buffer, uint str_len)
235 {
236 do
237 {
238 size_t rest_len= data_end - cur_data;
239 if (rest_len >= str_len)
240 {
241 memcpy(buffer, cur_data, str_len);
242 cur_data+= str_len;
243 buffer+= str_len;
244 break;
245 }
246 memcpy(buffer, cur_data, rest_len);
247 use_next_block(FALSE);
248 str_len-= rest_len;
249 buffer+= rest_len;
250 } while(str_len);
251 *buffer= 0;
252 }
253
load_str(MEM_ROOT * alloc,uint * str_len)254 char *Querycache_stream::load_str(MEM_ROOT *alloc, uint *str_len)
255 {
256 char *result;
257 *str_len= load_int();
258 if (!(result= (char*) alloc_root(alloc, *str_len + 1)))
259 return 0;
260 load_str_only(result, *str_len);
261 return result;
262 }
263
load_safe_str(MEM_ROOT * alloc,char ** str,uint * str_len)264 int Querycache_stream::load_safe_str(MEM_ROOT *alloc, char **str, uint *str_len)
265 {
266 if (!(*str_len= load_int()))
267 {
268 *str= NULL;
269 return 0;
270 }
271 (*str_len)--;
272 if (!(*str= (char*) alloc_root(alloc, *str_len + 1)))
273 return 1;
274 load_str_only(*str, *str_len);
275 return 0;
276 }
277
load_column(MEM_ROOT * alloc,char ** column)278 int Querycache_stream::load_column(MEM_ROOT *alloc, char** column)
279 {
280 int len;
281 if (!(len = load_int()))
282 {
283 *column= NULL;
284 return 0;
285 }
286 len--;
287 if (!(*column= (char *)alloc_root(alloc, len + sizeof(uint) + 1)))
288 return 1;
289 *((uint*)*column)= len;
290 (*column)+= sizeof(uint);
291 load_str_only(*column, len);
292 return 1;
293 }
294
emb_count_querycache_size(THD * thd)295 uint emb_count_querycache_size(THD *thd)
296 {
297 uint result= 0;
298 MYSQL_FIELD *field;
299 MYSQL_FIELD *field_end;
300 MYSQL_ROWS *cur_row;
301 my_ulonglong n_rows;
302 MYSQL_DATA *data= thd->first_data;
303
304 while (data->embedded_info->next)
305 data= data->embedded_info->next;
306 field= data->embedded_info->fields_list;
307 field_end= field + data->fields;
308
309 if (!field)
310 return result;
311 *data->embedded_info->prev_ptr= NULL; // this marks the last record
312 cur_row= data->data;
313 n_rows= data->rows;
314 /* n_fields + n_rows + field_info * n_fields */
315 result+= (uint) (4+8 + 42*data->fields);
316
317 for(; field < field_end; field++)
318 {
319 result+= field->name_length + field->table_length +
320 field->org_name_length + field->org_table_length + field->db_length +
321 field->catalog_length;
322 if (field->def)
323 result+= field->def_length;
324 }
325
326 if (thd->protocol == &thd->protocol_binary)
327 {
328 result+= (uint) (4*n_rows);
329 for (; cur_row; cur_row=cur_row->next)
330 result+= cur_row->length;
331 }
332 else
333 {
334 result+= (uint) (4*n_rows*data->fields);
335 for (; cur_row; cur_row=cur_row->next)
336 {
337 MYSQL_ROW col= cur_row->data;
338 MYSQL_ROW col_end= col + data->fields;
339 for (; col < col_end; col++)
340 if (*col)
341 result+= *(uint *)((*col) - sizeof(uint));
342 }
343 }
344 return result;
345 }
346
emb_store_querycache_result(Querycache_stream * dst,THD * thd)347 void emb_store_querycache_result(Querycache_stream *dst, THD *thd)
348 {
349 MYSQL_FIELD *field;
350 MYSQL_FIELD *field_end;
351 MYSQL_ROWS *cur_row;
352 my_ulonglong n_rows;
353 MYSQL_DATA *data= thd->first_data;
354
355 DBUG_ENTER("emb_store_querycache_result");
356
357 while (data->embedded_info->next)
358 data= data->embedded_info->next;
359 field= data->embedded_info->fields_list;
360 field_end= field + data->fields;
361
362 if (!field)
363 DBUG_VOID_RETURN;
364
365 *data->embedded_info->prev_ptr= NULL; // this marks the last record
366 cur_row= data->data;
367 n_rows= data->rows;
368
369 dst->store_int((uint)data->fields);
370 dst->store_ll((ulonglong)n_rows);
371
372 for(; field < field_end; field++)
373 {
374 dst->store_int((uint)field->length);
375 dst->store_int((uint)field->max_length);
376 dst->store_uchar((uchar)field->type);
377 dst->store_short((ushort)field->flags);
378 dst->store_short((ushort)field->charsetnr);
379 dst->store_uchar((uchar)field->decimals);
380 dst->store_str(field->name, field->name_length);
381 dst->store_str(field->table, field->table_length);
382 dst->store_str(field->org_name, field->org_name_length);
383 dst->store_str(field->org_table, field->org_table_length);
384 dst->store_str(field->db, field->db_length);
385 dst->store_str(field->catalog, field->catalog_length);
386 dst->store_safe_str(field->def, field->def_length);
387 }
388
389 if (thd->protocol == &thd->protocol_binary)
390 {
391 for (; cur_row; cur_row=cur_row->next)
392 dst->store_str((char *) cur_row->data, cur_row->length);
393 }
394 else
395 {
396 for (; cur_row; cur_row=cur_row->next)
397 {
398 MYSQL_ROW col= cur_row->data;
399 MYSQL_ROW col_end= col + data->fields;
400 for (; col < col_end; col++)
401 {
402 uint len= *col ? *(uint *)((*col) - sizeof(uint)) : 0;
403 dst->store_safe_str(*col, len);
404 }
405 }
406 }
407 DBUG_ASSERT(emb_count_querycache_size(thd) == dst->stored_size);
408 DBUG_VOID_RETURN;
409 }
410
emb_load_querycache_result(THD * thd,Querycache_stream * src)411 int emb_load_querycache_result(THD *thd, Querycache_stream *src)
412 {
413 MYSQL_DATA *data= thd->alloc_new_dataset();
414 MYSQL_FIELD *field;
415 MYSQL_FIELD *field_end;
416 MEM_ROOT *f_alloc;
417 MYSQL_ROWS *row, *end_row;
418 MYSQL_ROWS **prev_row;
419 ulonglong rows;
420 MYSQL_ROW columns;
421 DBUG_ENTER("emb_load_querycache_result");
422
423 if (!data)
424 goto err;
425 init_alloc_root(&data->alloc, 8192,0);
426 f_alloc= &data->alloc;
427
428 data->fields= src->load_int();
429 rows= src->load_ll();
430
431 if (!(field= (MYSQL_FIELD *)
432 alloc_root(f_alloc,data->fields*sizeof(MYSQL_FIELD))))
433 goto err;
434 data->embedded_info->fields_list= field;
435 for(field_end= field+data->fields; field < field_end; field++)
436 {
437 field->length= src->load_int();
438 field->max_length= (unsigned int)src->load_int();
439 field->type= (enum enum_field_types)src->load_uchar();
440 field->flags= (unsigned int)src->load_short();
441 field->charsetnr= (unsigned int)src->load_short();
442 field->decimals= src->load_uchar();
443
444 if (!(field->name= src->load_str(f_alloc, &field->name_length)) ||
445 !(field->table= src->load_str(f_alloc,&field->table_length)) ||
446 !(field->org_name= src->load_str(f_alloc, &field->org_name_length)) ||
447 !(field->org_table= src->load_str(f_alloc, &field->org_table_length))||
448 !(field->db= src->load_str(f_alloc, &field->db_length)) ||
449 !(field->catalog= src->load_str(f_alloc, &field->catalog_length)) ||
450 src->load_safe_str(f_alloc, &field->def, &field->def_length))
451 goto err;
452 }
453
454 data->rows= rows;
455 if (!rows)
456 goto return_ok;
457 if (thd->protocol == &thd->protocol_binary)
458 {
459 uint length;
460 row= (MYSQL_ROWS *)alloc_root(&data->alloc,
461 (size_t) (rows * sizeof(MYSQL_ROWS)));
462 end_row= row + rows;
463 data->data= row;
464
465 for (prev_row= &row->next; row < end_row; prev_row= &row->next, row++)
466 {
467 *prev_row= row;
468 row->data= (MYSQL_ROW) src->load_str(&data->alloc, &length);
469 row->length= length;
470 }
471 }
472 else
473 {
474 row= (MYSQL_ROWS *)alloc_root(&data->alloc,
475 (uint) (rows * sizeof(MYSQL_ROWS) +
476 rows*(data->fields+1)*sizeof(char*)));
477 end_row= row + rows;
478 columns= (MYSQL_ROW)end_row;
479
480 data->data= row;
481
482 for (prev_row= &row->next; row < end_row; prev_row= &row->next, row++)
483 {
484 *prev_row= row;
485 row->data= columns;
486 MYSQL_ROW col_end= columns + data->fields;
487 for (; columns < col_end; columns++)
488 src->load_column(&data->alloc, columns);
489
490 *(columns++)= NULL;
491 }
492 }
493 *prev_row= NULL;
494 data->embedded_info->prev_ptr= prev_row;
495 return_ok:
496 net_send_eof(thd, thd->server_status,
497 thd->get_stmt_da()->current_statement_warn_count());
498 DBUG_RETURN(0);
499 err:
500 DBUG_RETURN(1);
501 }
502
503 #endif /*HAVE_QUERY_CACHE*/
504
505