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