MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
pfs_digest.cc
1 /* Copyright (c) 2008, 2013, 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 as published by
5  the Free Software Foundation; version 2 of the License.
6 
7  This program is distributed in the hope that it will be useful,
8  but WITHOUT ANY WARRANTY; without even the implied warranty of
9  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  GNU General Public License for more details.
11 
12  You should have received a copy of the GNU General Public License
13  along with this program; if not, write to the Free Software Foundation,
14  51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
15 
21 /*
22  This code needs extra visibility in the lexer structures
23 */
24 
25 #include "my_global.h"
26 #include "my_sys.h"
27 #include "pfs_instr.h"
28 #include "pfs_digest.h"
29 #include "pfs_global.h"
30 #include "table_helper.h"
31 #include "my_md5.h"
32 #include "sql_lex.h"
33 #include "sql_get_diagnostics.h"
34 #include "sql_string.h"
35 #include <string.h>
36 
37 /* Generated code */
38 #include "../sql/sql_yacc.h"
39 #include "../storage/perfschema/pfs_lex_token.h"
40 
41 /* Name pollution from sql/sql_lex.h */
42 #ifdef LEX_YYSTYPE
43 #undef LEX_YYSTYPE
44 #endif
45 
46 #define LEX_YYSTYPE YYSTYPE
47 
60 ulong digest_max= 0;
61 ulong digest_lost= 0;
62 
64 PFS_statements_digest_stat *statements_digest_stat_array= NULL;
66 bool flag_statements_digest= true;
71 volatile uint32 digest_index= 1;
72 
73 LF_HASH digest_hash;
74 static bool digest_hash_inited= false;
75 
80 int init_digest(const PFS_global_param *param)
81 {
82  unsigned int index;
83 
84  /*
85  Allocate memory for statements_digest_stat_array based on
86  performance_schema_digests_size values
87  */
88  digest_max= param->m_digest_sizing;
89  digest_lost= 0;
90 
91  if (digest_max == 0)
92  return 0;
93 
94  statements_digest_stat_array=
96  MYF(MY_ZEROFILL));
97  if (unlikely(statements_digest_stat_array == NULL))
98  return 1;
99 
100  for (index= 0; index < digest_max; index++)
101  {
102  statements_digest_stat_array[index].reset_data();
103  }
104 
105  return 0;
106 }
107 
109 void cleanup_digest(void)
110 {
111  /* Free memory allocated to statements_digest_stat_array. */
112  pfs_free(statements_digest_stat_array);
113  statements_digest_stat_array= NULL;
114 }
115 
116 C_MODE_START
117 static uchar *digest_hash_get_key(const uchar *entry, size_t *length,
118  my_bool)
119 {
120  const PFS_statements_digest_stat * const *typed_entry;
121  const PFS_statements_digest_stat *digest;
122  const void *result;
123  typed_entry= reinterpret_cast<const PFS_statements_digest_stat*const*>(entry);
124  DBUG_ASSERT(typed_entry != NULL);
125  digest= *typed_entry;
126  DBUG_ASSERT(digest != NULL);
127  *length= sizeof (PFS_digest_key);
128  result= & digest->m_digest_key;
129  return const_cast<uchar*> (reinterpret_cast<const uchar*> (result));
130 }
131 C_MODE_END
132 
133 
138 int init_digest_hash(void)
139 {
140  if ((! digest_hash_inited) && (digest_max > 0))
141  {
142  lf_hash_init(&digest_hash, sizeof(PFS_statements_digest_stat*),
143  LF_HASH_UNIQUE, 0, 0, digest_hash_get_key,
144  &my_charset_bin);
145  digest_hash.size= digest_max;
146  digest_hash_inited= true;
147  }
148  return 0;
149 }
150 
151 void cleanup_digest_hash(void)
152 {
153  if (digest_hash_inited)
154  {
155  lf_hash_destroy(&digest_hash);
156  digest_hash_inited= false;
157  }
158 }
159 
160 static LF_PINS* get_digest_hash_pins(PFS_thread *thread)
161 {
162  if (unlikely(thread->m_digest_hash_pins == NULL))
163  {
164  if (!digest_hash_inited)
165  return NULL;
166  thread->m_digest_hash_pins= lf_hash_get_pins(&digest_hash);
167  }
168  return thread->m_digest_hash_pins;
169 }
170 
172 find_or_create_digest(PFS_thread *thread,
173  PSI_digest_storage *digest_storage,
174  const char *schema_name,
175  uint schema_name_length)
176 {
177  if (statements_digest_stat_array == NULL)
178  return NULL;
179 
180  if (digest_storage->m_byte_count <= 0)
181  return NULL;
182 
183  LF_PINS *pins= get_digest_hash_pins(thread);
184  if (unlikely(pins == NULL))
185  return NULL;
186 
187  /*
188  Note: the LF_HASH key is a block of memory,
189  make sure to clean unused bytes,
190  so that memcmp() can compare keys.
191  */
192  PFS_digest_key hash_key;
193  memset(& hash_key, 0, sizeof(hash_key));
194  /* Compute MD5 Hash of the tokens received. */
195  compute_md5_hash((char *) hash_key.m_md5,
196  (char *) digest_storage->m_token_array,
197  digest_storage->m_byte_count);
198  /* Add the current schema to the key */
199  hash_key.m_schema_name_length= schema_name_length;
200  if (schema_name_length > 0)
201  memcpy(hash_key.m_schema_name, schema_name, schema_name_length);
202 
203  int res;
204  ulong safe_index;
205  uint retry_count= 0;
206  const uint retry_max= 3;
208  PFS_statements_digest_stat *pfs= NULL;
209 
210  ulonglong now= my_micro_time();
211 
212 search:
213 
214  /* Lookup LF_HASH using this new key. */
215  entry= reinterpret_cast<PFS_statements_digest_stat**>
216  (lf_hash_search(&digest_hash, pins,
217  &hash_key, sizeof(PFS_digest_key)));
218 
219  if (entry && (entry != MY_ERRPTR))
220  {
221  /* If digest already exists, update stats and return. */
222  pfs= *entry;
223  pfs->m_last_seen= now;
224  lf_hash_search_unpin(pins);
225  return & pfs->m_stat;
226  }
227 
228  lf_hash_search_unpin(pins);
229 
230  /* Dirty read of digest_index */
231  if (digest_index == 0)
232  {
233  /* digest_stat array is full. Add stat at index 0 and return. */
234  pfs= &statements_digest_stat_array[0];
235 
236  if (pfs->m_first_seen == 0)
237  pfs->m_first_seen= now;
238  pfs->m_last_seen= now;
239  return & pfs->m_stat;
240  }
241 
242  safe_index= PFS_atomic::add_u32(& digest_index, 1);
243  if (safe_index >= digest_max)
244  {
245  /* The digest array is now full. */
246  digest_index= 0;
247  pfs= &statements_digest_stat_array[0];
248 
249  if (pfs->m_first_seen == 0)
250  pfs->m_first_seen= now;
251  pfs->m_last_seen= now;
252  return & pfs->m_stat;
253  }
254 
255  /* Add a new record in digest stat array. */
256  pfs= &statements_digest_stat_array[safe_index];
257 
258  /* Copy digest hash/LF Hash search key. */
259  memcpy(& pfs->m_digest_key, &hash_key, sizeof(PFS_digest_key));
260 
261  /*
262  Copy digest storage to statement_digest_stat_array so that it could be
263  used later to generate digest text.
264  */
265  digest_copy(& pfs->m_digest_storage, digest_storage);
266 
267  pfs->m_first_seen= now;
268  pfs->m_last_seen= now;
269 
270  res= lf_hash_insert(&digest_hash, pins, &pfs);
271  if (likely(res == 0))
272  {
273  return & pfs->m_stat;
274  }
275 
276  if (res > 0)
277  {
278  /* Duplicate insert by another thread */
279  if (++retry_count > retry_max)
280  {
281  /* Avoid infinite loops */
282  digest_lost++;
283  return NULL;
284  }
285  goto search;
286  }
287 
288  /* OOM in lf_hash_insert */
289  digest_lost++;
290  return NULL;
291 }
292 
293 void purge_digest(PFS_thread* thread, PFS_digest_key *hash_key)
294 {
295  LF_PINS *pins= get_digest_hash_pins(thread);
296  if (unlikely(pins == NULL))
297  return;
298 
300 
301  /* Lookup LF_HASH using this new key. */
302  entry= reinterpret_cast<PFS_statements_digest_stat**>
303  (lf_hash_search(&digest_hash, pins,
304  hash_key, sizeof(PFS_digest_key)));
305 
306  if (entry && (entry != MY_ERRPTR))
307  {
308  lf_hash_delete(&digest_hash, pins,
309  hash_key, sizeof(PFS_digest_key));
310  }
311  lf_hash_search_unpin(pins);
312  return;
313 }
314 
316 {
317  digest_reset(& m_digest_storage);
318  m_stat.reset();
319  m_first_seen= 0;
320  m_last_seen= 0;
321 }
322 
324 {
325  /* Only remove entries that exists in the HASH index. */
326  if (m_digest_storage.m_byte_count > 0)
327  {
328  purge_digest(thread, & m_digest_key);
329  }
330 }
331 
332 void reset_esms_by_digest()
333 {
334  uint index;
335 
336  if (statements_digest_stat_array == NULL)
337  return;
338 
339  PFS_thread *thread= PFS_thread::get_current_thread();
340  if (unlikely(thread == NULL))
341  return;
342 
343  /* Reset statements_digest_stat_array. */
344  for (index= 0; index < digest_max; index++)
345  {
346  statements_digest_stat_array[index].reset_index(thread);
347  statements_digest_stat_array[index].reset_data();
348  }
349 
350  /*
351  Reset index which indicates where the next calculated digest information
352  to be inserted in statements_digest_stat_array.
353  */
354  digest_index= 1;
355 }
356 
357 /*
358  Iterate token array and updates digest_text.
359 */
360 void get_digest_text(char* digest_text, PSI_digest_storage* digest_storage)
361 {
362  DBUG_ASSERT(digest_storage != NULL);
363  bool truncated= false;
364  int byte_count= digest_storage->m_byte_count;
365  char *digest_output= digest_text;
366  int bytes_needed= 0;
367  uint tok= 0;
368  int current_byte= 0;
369  lex_token_string *tok_data;
370  /* -4 is to make sure extra space for '...' and a '\0' at the end. */
371  int bytes_available= COL_DIGEST_TEXT_SIZE - 4;
372 
373  if (byte_count <= 0 || byte_count > PSI_MAX_DIGEST_STORAGE_SIZE)
374  {
375  *digest_text= '\0';
376  return;
377  }
378 
379  /* Convert text to utf8 */
380  const CHARSET_INFO *from_cs= get_charset(digest_storage->m_charset_number, MYF(0));
381  const CHARSET_INFO *to_cs= &my_charset_utf8_bin;
382 
383  if (from_cs == NULL)
384  {
385  /*
386  Can happen, as we do dirty reads on digest_storage,
387  which can be written to in another thread.
388  */
389  *digest_text= '\0';
390  return;
391  }
392 
393  /*
394  Max converted size is number of characters * max multibyte length of the
395  target charset, which is 4 for UTF8.
396  */
397  const uint max_converted_size= PSI_MAX_DIGEST_STORAGE_SIZE * 4;
398  char id_buffer[max_converted_size];
399  char *id_string;
400  int id_length;
401  bool convert_text= !my_charset_same(from_cs, to_cs);
402 
403  DBUG_ASSERT(byte_count <= PSI_MAX_DIGEST_STORAGE_SIZE);
404 
405  while ((current_byte < byte_count) &&
406  (bytes_available > 0) &&
407  !truncated)
408  {
409  current_byte= read_token(digest_storage, current_byte, &tok);
410 
411  if (tok <= 0 || tok >= array_elements(lex_token_array))
412  {
413  *digest_text='\0';
414  return;
415  }
416 
417  tok_data= &lex_token_array[tok];
418 
419  switch (tok)
420  {
421  /* All identifiers are printed with their name. */
422  case IDENT:
423  case IDENT_QUOTED:
424  {
425  char *id_ptr;
426  int id_len;
427  uint err_cs= 0;
428 
429  /* Get the next identifier from the storage buffer. */
430  current_byte= read_identifier(digest_storage, current_byte,
431  &id_ptr, &id_len);
432  if (convert_text)
433  {
434  /* Verify that the converted text will fit. */
435  if (to_cs->mbmaxlen*id_len > max_converted_size)
436  {
437  truncated= true;
438  break;
439  }
440  /* Convert identifier string into the storage character set. */
441  id_length= my_convert(id_buffer, max_converted_size, to_cs,
442  id_ptr, id_len, from_cs, &err_cs);
443  id_string= id_buffer;
444  }
445  else
446  {
447  id_string= id_ptr;
448  id_length= id_len;
449  }
450 
451  if (id_length == 0 || err_cs != 0)
452  {
453  truncated= true;
454  break;
455  }
456  /* Copy the converted identifier into the digest string. */
457  bytes_needed= id_length + (tok == IDENT ? 1 : 3);
458  if (bytes_needed <= bytes_available)
459  {
460  if (tok == IDENT_QUOTED)
461  *digest_output++= '`';
462  if (id_length > 0)
463  {
464  memcpy(digest_output, id_string, id_length);
465  digest_output+= id_length;
466  }
467  if (tok == IDENT_QUOTED)
468  *digest_output++= '`';
469  *digest_output++= ' ';
470  bytes_available-= bytes_needed;
471  }
472  else
473  {
474  truncated= true;
475  }
476  }
477  break;
478 
479  /* Everything else is printed as is. */
480  default:
481  /*
482  Make sure not to overflow digest_text buffer.
483  +1 is to make sure extra space for ' '.
484  */
485  int tok_length= tok_data->m_token_length;
486  bytes_needed= tok_length + 1;
487 
488  if (bytes_needed <= bytes_available)
489  {
490  strncpy(digest_output, tok_data->m_token_string, tok_length);
491  digest_output+= tok_length;
492  *digest_output++= ' ';
493  bytes_available-= bytes_needed;
494  }
495  else
496  {
497  truncated= true;
498  }
499  break;
500  }
501  }
502 
503  /* Truncate digest text in case of long queries. */
504  if (digest_storage->m_full || truncated)
505  {
506  strcpy(digest_output, "...");
507  digest_output+= 3;
508  }
509 
510  *digest_output= '\0';
511 }
512 
513 static inline uint peek_token(const PSI_digest_storage *digest, int index)
514 {
515  uint token;
516  DBUG_ASSERT(index >= 0);
517  DBUG_ASSERT(index + PFS_SIZE_OF_A_TOKEN <= digest->m_byte_count);
518  DBUG_ASSERT(digest->m_byte_count <= PSI_MAX_DIGEST_STORAGE_SIZE);
519 
520  token= ((digest->m_token_array[index + 1])<<8) | digest->m_token_array[index];
521  return token;
522 }
523 
528 static inline void peek_last_two_tokens(const PSI_digest_storage* digest_storage,
529  int last_id_index, uint *t1, uint *t2)
530 {
531  int byte_count= digest_storage->m_byte_count;
532  int peek_index= byte_count - PFS_SIZE_OF_A_TOKEN;
533 
534  if (last_id_index <= peek_index)
535  {
536  /* Take last token. */
537  *t1= peek_token(digest_storage, peek_index);
538 
539  peek_index-= PFS_SIZE_OF_A_TOKEN;
540  if (last_id_index <= peek_index)
541  {
542  /* Take 2nd token from last. */
543  *t2= peek_token(digest_storage, peek_index);
544  }
545  else
546  {
547  *t2= TOK_PFS_UNUSED;
548  }
549  }
550  else
551  {
552  *t1= TOK_PFS_UNUSED;
553  *t2= TOK_PFS_UNUSED;
554  }
555 }
556 
557 struct PSI_digest_locker* pfs_digest_start_v1(PSI_statement_locker *locker)
558 {
559  PSI_statement_locker_state *statement_state;
560  statement_state= reinterpret_cast<PSI_statement_locker_state*> (locker);
561  DBUG_ASSERT(statement_state != NULL);
562 
563  if (statement_state->m_discarded)
564  return NULL;
565 
566  if (statement_state->m_flags & STATE_FLAG_DIGEST)
567  {
568  PSI_digest_locker_state *digest_state;
569  digest_state= &statement_state->m_digest_state;
570  return reinterpret_cast<PSI_digest_locker*> (digest_state);
571  }
572 
573  return NULL;
574 }
575 
576 PSI_digest_locker* pfs_digest_add_token_v1(PSI_digest_locker *locker,
577  uint token,
578  OPAQUE_LEX_YYSTYPE *yylval)
579 {
580  PSI_digest_locker_state *state= NULL;
581  PSI_digest_storage *digest_storage= NULL;
582 
583  state= reinterpret_cast<PSI_digest_locker_state*> (locker);
584  DBUG_ASSERT(state != NULL);
585 
586  digest_storage= &state->m_digest_storage;
587 
588  /*
589  Stop collecting further tokens if digest storage is full or
590  if END token is received.
591  */
592  if (digest_storage->m_full || token == END_OF_INPUT)
593  return NULL;
594 
595  /*
596  Take last_token 2 tokens collected till now. These tokens will be used
597  in reduce for normalisation. Make sure not to consider ID tokens in reduce.
598  */
599  uint last_token;
600  uint last_token2;
601 
602  switch (token)
603  {
604  case BIN_NUM:
605  case DECIMAL_NUM:
606  case FLOAT_NUM:
607  case HEX_NUM:
608  case LEX_HOSTNAME:
609  case LONG_NUM:
610  case NUM:
611  case TEXT_STRING:
612  case NCHAR_STRING:
613  case ULONGLONG_NUM:
614  {
615  /*
616  REDUCE:
617  TOK_PFS_GENERIC_VALUE := BIN_NUM | DECIMAL_NUM | ... | ULONGLONG_NUM
618  */
619  token= TOK_PFS_GENERIC_VALUE;
620  }
621  /* fall through */
622  case NULL_SYM:
623  {
624  peek_last_two_tokens(digest_storage, state->m_last_id_index,
625  &last_token, &last_token2);
626 
627  if ((last_token2 == TOK_PFS_GENERIC_VALUE ||
628  last_token2 == TOK_PFS_GENERIC_VALUE_LIST ||
629  last_token2 == NULL_SYM) &&
630  (last_token == ','))
631  {
632  /*
633  REDUCE:
634  TOK_PFS_GENERIC_VALUE_LIST :=
635  (TOK_PFS_GENERIC_VALUE|NULL_SYM) ',' (TOK_PFS_GENERIC_VALUE|NULL_SYM)
636 
637  REDUCE:
638  TOK_PFS_GENERIC_VALUE_LIST :=
639  TOK_PFS_GENERIC_VALUE_LIST ',' (TOK_PFS_GENERIC_VALUE|NULL_SYM)
640  */
641  digest_storage->m_byte_count-= 2*PFS_SIZE_OF_A_TOKEN;
642  token= TOK_PFS_GENERIC_VALUE_LIST;
643  }
644  /*
645  Add this token or the resulting reduce to digest storage.
646  */
647  store_token(digest_storage, token);
648  break;
649  }
650  case ')':
651  {
652  peek_last_two_tokens(digest_storage, state->m_last_id_index,
653  &last_token, &last_token2);
654 
655  if (last_token == TOK_PFS_GENERIC_VALUE &&
656  last_token2 == '(')
657  {
658  /*
659  REDUCE:
660  TOK_PFS_ROW_SINGLE_VALUE :=
661  '(' TOK_PFS_GENERIC_VALUE ')'
662  */
663  digest_storage->m_byte_count-= 2*PFS_SIZE_OF_A_TOKEN;
664  token= TOK_PFS_ROW_SINGLE_VALUE;
665 
666  /* Read last two tokens again */
667  peek_last_two_tokens(digest_storage, state->m_last_id_index,
668  &last_token, &last_token2);
669 
670  if ((last_token2 == TOK_PFS_ROW_SINGLE_VALUE ||
671  last_token2 == TOK_PFS_ROW_SINGLE_VALUE_LIST) &&
672  (last_token == ','))
673  {
674  /*
675  REDUCE:
676  TOK_PFS_ROW_SINGLE_VALUE_LIST :=
677  TOK_PFS_ROW_SINGLE_VALUE ',' TOK_PFS_ROW_SINGLE_VALUE
678 
679  REDUCE:
680  TOK_PFS_ROW_SINGLE_VALUE_LIST :=
681  TOK_PFS_ROW_SINGLE_VALUE_LIST ',' TOK_PFS_ROW_SINGLE_VALUE
682  */
683  digest_storage->m_byte_count-= 2*PFS_SIZE_OF_A_TOKEN;
684  token= TOK_PFS_ROW_SINGLE_VALUE_LIST;
685  }
686  }
687  else if (last_token == TOK_PFS_GENERIC_VALUE_LIST &&
688  last_token2 == '(')
689  {
690  /*
691  REDUCE:
692  TOK_PFS_ROW_MULTIPLE_VALUE :=
693  '(' TOK_PFS_GENERIC_VALUE_LIST ')'
694  */
695  digest_storage->m_byte_count-= 2*PFS_SIZE_OF_A_TOKEN;
696  token= TOK_PFS_ROW_MULTIPLE_VALUE;
697 
698  /* Read last two tokens again */
699  peek_last_two_tokens(digest_storage, state->m_last_id_index,
700  &last_token, &last_token2);
701 
702  if ((last_token2 == TOK_PFS_ROW_MULTIPLE_VALUE ||
703  last_token2 == TOK_PFS_ROW_MULTIPLE_VALUE_LIST) &&
704  (last_token == ','))
705  {
706  /*
707  REDUCE:
708  TOK_PFS_ROW_MULTIPLE_VALUE_LIST :=
709  TOK_PFS_ROW_MULTIPLE_VALUE ',' TOK_PFS_ROW_MULTIPLE_VALUE
710 
711  REDUCE:
712  TOK_PFS_ROW_MULTIPLE_VALUE_LIST :=
713  TOK_PFS_ROW_MULTIPLE_VALUE_LIST ',' TOK_PFS_ROW_MULTIPLE_VALUE
714  */
715  digest_storage->m_byte_count-= 2*PFS_SIZE_OF_A_TOKEN;
716  token= TOK_PFS_ROW_MULTIPLE_VALUE_LIST;
717  }
718  }
719  /*
720  Add this token or the resulting reduce to digest storage.
721  */
722  store_token(digest_storage, token);
723  break;
724  }
725  case IDENT:
726  case IDENT_QUOTED:
727  {
728  LEX_YYSTYPE *lex_token= (LEX_YYSTYPE*) yylval;
729  char *yytext= lex_token->lex_str.str;
730  int yylen= lex_token->lex_str.length;
731 
732  /* Add this token and identifier string to digest storage. */
733  store_token_identifier(digest_storage, token, yylen, yytext);
734 
735  /* Update the index of last identifier found. */
736  state->m_last_id_index= digest_storage->m_byte_count;
737  break;
738  }
739  default:
740  {
741  /* Add this token to digest storage. */
742  store_token(digest_storage, token);
743  break;
744  }
745  }
746 
747  return locker;
748 }