1 /* Copyright (c) 2013, 2021, Oracle and/or its affiliates.
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 Foundation,
21   51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
22 
23 /**
24   @file storage/perfschema/pfs_program.cc
25   Statement Digest data structures (implementation).
26 */
27 
28 /*
29   This code needs extra visibility in the lexer structures
30 */
31 
32 #include "my_global.h"
33 #include "my_sys.h"
34 #include "pfs_instr.h"
35 #include "pfs_program.h"
36 #include "pfs_global.h"
37 #include "sql_string.h"
38 #include "pfs_setup_object.h"
39 #include "pfs_buffer_container.h"
40 #include "mysqld.h"                //system_charset_info
41 #include <string.h>
42 
43 LF_HASH program_hash;
44 static bool program_hash_inited= false;
45 
46 /**
47   Initialize table EVENTS_STATEMENTS_SUMMARY_BY_PROGRAM.
48   @param param performance schema sizing
49 */
init_program(const PFS_global_param * param)50 int init_program(const PFS_global_param *param)
51 {
52   if (global_program_container.init(param->m_program_sizing))
53     return 1;
54 
55   reset_esms_by_program();
56   return 0;
57 }
58 
59 /** Cleanup table EVENTS_STATEMENTS_SUMMARY_BY_PROGRAM. */
cleanup_program(void)60 void cleanup_program(void)
61 {
62   global_program_container.cleanup();
63 }
64 
65 C_MODE_START
program_hash_get_key(const uchar * entry,size_t * length,my_bool)66 static uchar *program_hash_get_key(const uchar *entry, size_t *length,
67                                    my_bool)
68 {
69   const PFS_program * const *typed_entry;
70   const PFS_program *program;
71   const void *result;
72   typed_entry= reinterpret_cast<const PFS_program* const *> (entry);
73   assert(typed_entry != NULL);
74   program= *typed_entry;
75   assert(program != NULL);
76   *length= program->m_key.m_key_length;
77   result= program->m_key.m_hash_key;
78   return const_cast<uchar*> (reinterpret_cast<const uchar*> (result));
79 }
80 C_MODE_END
81 
82 /**
83   Initialize the program hash.
84   @return 0 on success
85 */
init_program_hash(const PFS_global_param * param)86 int init_program_hash(const PFS_global_param *param)
87 {
88   if ((! program_hash_inited) && (param->m_program_sizing != 0))
89   {
90     lf_hash_init(&program_hash, sizeof(PFS_program*), LF_HASH_UNIQUE,
91                  0, 0, program_hash_get_key, &my_charset_bin);
92     program_hash_inited= true;
93   }
94   return 0;
95 }
96 
97 /** Cleanup the program hash. */
cleanup_program_hash(void)98 void cleanup_program_hash(void)
99 {
100   if (program_hash_inited)
101   {
102     lf_hash_destroy(&program_hash);
103     program_hash_inited= false;
104   }
105 }
106 
set_program_key(PFS_program_key * key,enum_object_type object_type,const char * object_name,uint object_name_length,const char * schema_name,uint schema_name_length)107 static void set_program_key(PFS_program_key *key,
108                             enum_object_type object_type,
109                             const char *object_name, uint object_name_length,
110                             const char *schema_name, uint schema_name_length)
111 {
112   assert(object_name_length <= COL_OBJECT_NAME_SIZE);
113   assert(schema_name_length <= COL_OBJECT_SCHEMA_SIZE);
114 
115   /*
116     To make sure generated key is case insensitive,
117     convert object_name/schema_name to lowercase.
118    */
119 
120   char *ptr= &key->m_hash_key[0];
121 
122   ptr[0]= object_type;
123   ptr++;
124 
125   if (object_name_length > 0)
126   {
127     char tmp_object_name[COL_OBJECT_NAME_SIZE + 1];
128     memcpy(tmp_object_name, object_name, object_name_length);
129     tmp_object_name[object_name_length]= '\0';
130     my_casedn_str(system_charset_info, tmp_object_name);
131     memcpy(ptr, tmp_object_name, object_name_length);
132     ptr+= object_name_length;
133   }
134   ptr[0]= 0;
135   ptr++;
136 
137   if (schema_name_length > 0)
138   {
139     char tmp_schema_name[COL_OBJECT_SCHEMA_SIZE + 1];
140     memcpy(tmp_schema_name, schema_name, schema_name_length);
141     tmp_schema_name[schema_name_length]='\0';
142     my_casedn_str(system_charset_info, tmp_schema_name);
143     memcpy(ptr, tmp_schema_name, schema_name_length);
144     ptr+= schema_name_length;
145   }
146   ptr[0]= 0;
147   ptr++;
148 
149   key->m_key_length= ptr - &key->m_hash_key[0];
150 }
151 
152 
153 
reset_data()154 void PFS_program::reset_data()
155 {
156   m_sp_stat.reset();
157   m_stmt_stat.reset();
158 }
159 
fct_reset_esms_by_program(PFS_program * pfs)160 static void fct_reset_esms_by_program(PFS_program *pfs)
161 {
162   pfs->reset_data();
163 }
164 
reset_esms_by_program()165 void reset_esms_by_program()
166 {
167   global_program_container.apply_all(fct_reset_esms_by_program);
168 }
169 
get_program_hash_pins(PFS_thread * thread)170 static LF_PINS* get_program_hash_pins(PFS_thread *thread)
171 {
172   if (unlikely(thread->m_program_hash_pins == NULL))
173   {
174     if (! program_hash_inited)
175       return NULL;
176     thread->m_program_hash_pins= lf_hash_get_pins(&program_hash);
177   }
178   return thread->m_program_hash_pins;
179 }
180 
181 PFS_program*
find_or_create_program(PFS_thread * thread,enum_object_type object_type,const char * object_name,uint object_name_length,const char * schema_name,uint schema_name_length)182 find_or_create_program(PFS_thread *thread,
183                       enum_object_type object_type,
184                       const char *object_name,
185                       uint object_name_length,
186                       const char *schema_name,
187                       uint schema_name_length)
188 {
189   bool is_enabled, is_timed;
190 
191   LF_PINS *pins= get_program_hash_pins(thread);
192   if (unlikely(pins == NULL))
193   {
194     global_program_container.m_lost++;
195     return NULL;
196   }
197 
198   /* Prepare program key */
199   PFS_program_key key;
200   set_program_key(&key, object_type,
201                   object_name, object_name_length,
202                   schema_name, schema_name_length);
203 
204   PFS_program **entry;
205   PFS_program *pfs= NULL;
206   uint retry_count= 0;
207   const uint retry_max= 3;
208   pfs_dirty_state dirty_state;
209 
210 search:
211   entry= reinterpret_cast<PFS_program**>
212     (lf_hash_search(&program_hash, pins,
213                     key.m_hash_key, key.m_key_length));
214 
215   if (entry && (entry != MY_ERRPTR))
216   {
217     /* If record already exists then return its pointer. */
218     pfs= *entry;
219     lf_hash_search_unpin(pins);
220     return pfs;
221   }
222 
223   lf_hash_search_unpin(pins);
224 
225   /*
226     First time while inserting this record to program array we need to
227     find out if it is enabled and timed.
228   */
229   lookup_setup_object(thread, object_type,
230                       schema_name, schema_name_length,
231                       object_name, object_name_length,
232                       &is_enabled, &is_timed);
233 
234   /* Else create a new record in program stat array. */
235   pfs= global_program_container.allocate(& dirty_state);
236   if (pfs != NULL)
237   {
238     /* Do the assignments. */
239     memcpy(pfs->m_key.m_hash_key, key.m_hash_key, key.m_key_length);
240     pfs->m_key.m_key_length= key.m_key_length;
241     pfs->m_type= object_type;
242 
243     pfs->m_object_name= pfs->m_key.m_hash_key + 1;
244     pfs->m_object_name_length= object_name_length;
245     pfs->m_schema_name= pfs->m_object_name + object_name_length + 1;
246     pfs->m_schema_name_length= schema_name_length;
247     pfs->m_enabled= is_enabled;
248     pfs->m_timed= is_timed;
249 
250     /* Insert this record. */
251     pfs->m_lock.dirty_to_allocated(& dirty_state);
252     int res= lf_hash_insert(&program_hash, pins, &pfs);
253 
254     if (likely(res == 0))
255     {
256       return pfs;
257     }
258 
259     global_program_container.deallocate(pfs);
260 
261     if (res > 0)
262     {
263       /* Duplicate insert by another thread */
264       if (++retry_count > retry_max)
265       {
266         /* Avoid infinite loops */
267         global_program_container.m_lost++;
268         return NULL;
269       }
270        goto search;
271     }
272     /* OOM in lf_hash_insert */
273     global_program_container.m_lost++;
274     return NULL;
275   }
276 
277   return NULL;
278 }
279 
drop_program(PFS_thread * thread,enum_object_type object_type,const char * object_name,uint object_name_length,const char * schema_name,uint schema_name_length)280 void drop_program(PFS_thread *thread,
281                  enum_object_type object_type,
282                  const char *object_name,
283                  uint object_name_length,
284                  const char *schema_name,
285                  uint schema_name_length)
286 {
287   LF_PINS *pins= get_program_hash_pins(thread);
288   if (unlikely(pins == NULL))
289     return;
290 
291   /* Prepare program key */
292   PFS_program_key key;
293   set_program_key(&key, object_type,
294                   object_name, object_name_length,
295                   schema_name, schema_name_length);
296 
297   PFS_program **entry;
298   entry= reinterpret_cast<PFS_program**>
299     (lf_hash_search(&program_hash, pins,
300                     key.m_hash_key, key.m_key_length));
301 
302   if (entry && (entry != MY_ERRPTR))
303   {
304     PFS_program *pfs= NULL;
305     pfs= *entry;
306 
307     lf_hash_delete(&program_hash, pins,
308                    key.m_hash_key, key.m_key_length);
309     global_program_container.deallocate(pfs);
310   }
311 
312   lf_hash_search_unpin(pins);
313   return;
314 }
315 
refresh_setup_object_flags(PFS_thread * thread)316 void PFS_program::refresh_setup_object_flags(PFS_thread *thread)
317 {
318   lookup_setup_object(thread, m_type,
319                       m_schema_name, m_schema_name_length,
320                       m_object_name, m_object_name_length,
321                       &m_enabled, &m_timed);
322 }
323