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