1/* Copyright (c) 2010, 2015, 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
14 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
15
16/**
17 @file storage/perfschema/pfs_account.cc
18 Performance schema user@host (implementation).
19*/
20
21#include "my_global.h"
22#include "my_sys.h"
23#include "pfs.h"
24#include "pfs_stat.h"
25#include "pfs_instr.h"
26#include "pfs_setup_actor.h"
27#include "pfs_host.h"
28#include "pfs_host.h"
29#include "pfs_user.h"
30#include "pfs_account.h"
31#include "pfs_global.h"
32#include "pfs_instr_class.h"
33
34/**
35 @addtogroup Performance_schema_buffers
36 @{
37*/
38
39ulong account_max;
40ulong account_lost;
41
42PFS_account *account_array= NULL;
43
44static PFS_single_stat *account_instr_class_waits_array= NULL;
45static PFS_stage_stat *account_instr_class_stages_array= NULL;
46static PFS_statement_stat *account_instr_class_statements_array= NULL;
47
48LF_HASH account_hash;
49static bool account_hash_inited= false;
50
51/**
52 Initialize the user buffers.
53 @param param sizing parameters
54 @return 0 on success
55*/
56int init_account(const PFS_global_param *param)
57{
58 uint index;
59
60 account_max= param->m_account_sizing;
61
62 account_array= NULL;
63 account_instr_class_waits_array= NULL;
64 account_instr_class_stages_array= NULL;
65 account_instr_class_statements_array= NULL;
66 uint waits_sizing= account_max * wait_class_max;
67 uint stages_sizing= account_max * stage_class_max;
68 uint statements_sizing= account_max * statement_class_max;
69
70 if (account_max > 0)
71 {
72 account_array= PFS_MALLOC_ARRAY(account_max, sizeof(PFS_account), PFS_account,
73 MYF(MY_ZEROFILL));
74 if (unlikely(account_array == NULL))
75 return 1;
76 }
77
78 if (waits_sizing > 0)
79 {
80 account_instr_class_waits_array=
81 PFS_connection_slice::alloc_waits_slice(waits_sizing);
82 if (unlikely(account_instr_class_waits_array == NULL))
83 return 1;
84 }
85
86 if (stages_sizing > 0)
87 {
88 account_instr_class_stages_array=
89 PFS_connection_slice::alloc_stages_slice(stages_sizing);
90 if (unlikely(account_instr_class_stages_array == NULL))
91 return 1;
92 }
93
94 if (statements_sizing > 0)
95 {
96 account_instr_class_statements_array=
97 PFS_connection_slice::alloc_statements_slice(statements_sizing);
98 if (unlikely(account_instr_class_statements_array == NULL))
99 return 1;
100 }
101
102 for (index= 0; index < account_max; index++)
103 {
104 account_array[index].m_instr_class_waits_stats=
105 &account_instr_class_waits_array[index * wait_class_max];
106 account_array[index].m_instr_class_stages_stats=
107 &account_instr_class_stages_array[index * stage_class_max];
108 account_array[index].m_instr_class_statements_stats=
109 &account_instr_class_statements_array[index * statement_class_max];
110 }
111
112 return 0;
113}
114
115/** Cleanup all the user buffers. */
116void cleanup_account(void)
117{
118 pfs_free(account_array);
119 account_array= NULL;
120 pfs_free(account_instr_class_waits_array);
121 account_instr_class_waits_array= NULL;
122 pfs_free(account_instr_class_stages_array);
123 account_instr_class_stages_array= 0;
124 pfs_free(account_instr_class_statements_array);
125 account_instr_class_statements_array=0;
126 account_max= 0;
127}
128
129C_MODE_START
130static uchar *account_hash_get_key(const uchar *entry, size_t *length,
131 my_bool)
132{
133 const PFS_account * const *typed_entry;
134 const PFS_account *account;
135 const void *result;
136 typed_entry= reinterpret_cast<const PFS_account* const *> (entry);
137 DBUG_ASSERT(typed_entry != NULL);
138 account= *typed_entry;
139 DBUG_ASSERT(account != NULL);
140 *length= account->m_key.m_key_length;
141 result= account->m_key.m_hash_key;
142 return const_cast<uchar*> (reinterpret_cast<const uchar*> (result));
143}
144C_MODE_END
145
146/**
147 Initialize the user hash.
148 @return 0 on success
149*/
150int init_account_hash(void)
151{
152 if ((! account_hash_inited) && (account_max > 0))
153 {
154 lf_hash_init(&account_hash, sizeof(PFS_account*), LF_HASH_UNIQUE,
155 0, 0, account_hash_get_key, &my_charset_bin);
156 /* account_hash.size= account_max; */
157 account_hash_inited= true;
158 }
159 return 0;
160}
161
162/** Cleanup the user hash. */
163void cleanup_account_hash(void)
164{
165 if (account_hash_inited)
166 {
167 lf_hash_destroy(&account_hash);
168 account_hash_inited= false;
169 }
170}
171
172static LF_PINS* get_account_hash_pins(PFS_thread *thread)
173{
174 if (unlikely(thread->m_account_hash_pins == NULL))
175 {
176 if (! account_hash_inited)
177 return NULL;
178 thread->m_account_hash_pins= lf_hash_get_pins(&account_hash);
179 }
180 return thread->m_account_hash_pins;
181}
182
183static void set_account_key(PFS_account_key *key,
184 const char *user, uint user_length,
185 const char *host, uint host_length)
186{
187 DBUG_ASSERT(user_length <= USERNAME_LENGTH);
188 DBUG_ASSERT(host_length <= HOSTNAME_LENGTH);
189
190 char *ptr= &key->m_hash_key[0];
191 if (user_length > 0)
192 {
193 memcpy(ptr, user, user_length);
194 ptr+= user_length;
195 }
196 ptr[0]= 0;
197 ptr++;
198 if (host_length > 0)
199 {
200 memcpy(ptr, host, host_length);
201 ptr+= host_length;
202 }
203 ptr[0]= 0;
204 ptr++;
205 key->m_key_length= (uint)(ptr - &key->m_hash_key[0]);
206}
207
208PFS_account *
209find_or_create_account(PFS_thread *thread,
210 const char *username, uint username_length,
211 const char *hostname, uint hostname_length)
212{
213 if (account_max == 0)
214 {
215 account_lost++;
216 return NULL;
217 }
218
219 LF_PINS *pins= get_account_hash_pins(thread);
220 if (unlikely(pins == NULL))
221 {
222 account_lost++;
223 return NULL;
224 }
225
226 PFS_account_key key;
227 set_account_key(&key, username, username_length,
228 hostname, hostname_length);
229
230 PFS_account **entry;
231 uint retry_count= 0;
232 const uint retry_max= 3;
233
234search:
235 entry= reinterpret_cast<PFS_account**>
236 (lf_hash_search(&account_hash, pins,
237 key.m_hash_key, key.m_key_length));
238 if (entry && (entry != MY_ERRPTR))
239 {
240 PFS_account *pfs;
241 pfs= *entry;
242 pfs->inc_refcount();
243 lf_hash_search_unpin(pins);
244 return pfs;
245 }
246
247 lf_hash_search_unpin(pins);
248
249 PFS_scan scan;
250 uint random= randomized_index(username, account_max);
251
252 for (scan.init(random, account_max);
253 scan.has_pass();
254 scan.next_pass())
255 {
256 PFS_account *pfs= account_array + scan.first();
257 PFS_account *pfs_last= account_array + scan.last();
258 for ( ; pfs < pfs_last; pfs++)
259 {
260 if (pfs->m_lock.is_free())
261 {
262 if (pfs->m_lock.free_to_dirty())
263 {
264 pfs->m_key= key;
265 if (username_length > 0)
266 pfs->m_username= &pfs->m_key.m_hash_key[0];
267 else
268 pfs->m_username= NULL;
269 pfs->m_username_length= username_length;
270
271 if (hostname_length > 0)
272 pfs->m_hostname= &pfs->m_key.m_hash_key[username_length + 1];
273 else
274 pfs->m_hostname= NULL;
275 pfs->m_hostname_length= hostname_length;
276
277 pfs->m_user= find_or_create_user(thread, username, username_length);
278 pfs->m_host= find_or_create_host(thread, hostname, hostname_length);
279
280 pfs->init_refcount();
281 pfs->reset_stats();
282 pfs->m_disconnected_count= 0;
283
284 int res;
285 res= lf_hash_insert(&account_hash, pins, &pfs);
286 if (likely(res == 0))
287 {
288 pfs->m_lock.dirty_to_allocated();
289 return pfs;
290 }
291
292 if (pfs->m_user)
293 {
294 pfs->m_user->release();
295 pfs->m_user= NULL;
296 }
297 if (pfs->m_host)
298 {
299 pfs->m_host->release();
300 pfs->m_host= NULL;
301 }
302
303 pfs->m_lock.dirty_to_free();
304
305 if (res > 0)
306 {
307 if (++retry_count > retry_max)
308 {
309 account_lost++;
310 return NULL;
311 }
312 goto search;
313 }
314
315 account_lost++;
316 return NULL;
317 }
318 }
319 }
320 }
321
322 account_lost++;
323 return NULL;
324}
325
326void PFS_account::aggregate(PFS_user *safe_user, PFS_host *safe_host)
327{
328 aggregate_waits(safe_user, safe_host);
329 aggregate_stages(safe_user, safe_host);
330 aggregate_statements(safe_user, safe_host);
331 aggregate_stats(safe_user, safe_host);
332}
333
334void PFS_account::aggregate_waits(PFS_user *safe_user, PFS_host *safe_host)
335{
336 if (likely(safe_user != NULL && safe_host != NULL))
337 {
338 /*
339 Aggregate EVENTS_WAITS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME to:
340 - EVENTS_WAITS_SUMMARY_BY_USER_BY_EVENT_NAME
341 - EVENTS_WAITS_SUMMARY_BY_HOST_BY_EVENT_NAME
342 in parallel.
343 */
344 aggregate_all_event_names(m_instr_class_waits_stats,
345 safe_user->m_instr_class_waits_stats,
346 safe_host->m_instr_class_waits_stats);
347 return;
348 }
349
350 if (safe_user != NULL)
351 {
352 /*
353 Aggregate EVENTS_WAITS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME to:
354 - EVENTS_WAITS_SUMMARY_BY_USER_BY_EVENT_NAME
355 */
356 aggregate_all_event_names(m_instr_class_waits_stats,
357 safe_user->m_instr_class_waits_stats);
358 return;
359 }
360
361 if (safe_host != NULL)
362 {
363 /*
364 Aggregate EVENTS_WAITS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME to:
365 - EVENTS_WAITS_SUMMARY_BY_HOST_BY_EVENT_NAME
366 */
367 aggregate_all_event_names(m_instr_class_waits_stats,
368 safe_host->m_instr_class_waits_stats);
369 return;
370 }
371
372 /* Orphan account, no parent to aggregate to. */
373 reset_waits_stats();
374 return;
375}
376
377void PFS_account::aggregate_stages(PFS_user *safe_user, PFS_host *safe_host)
378{
379 if (likely(safe_user != NULL && safe_host != NULL))
380 {
381 /*
382 Aggregate EVENTS_STAGES_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME to:
383 - EVENTS_STAGES_SUMMARY_BY_USER_BY_EVENT_NAME
384 - EVENTS_STAGES_SUMMARY_BY_HOST_BY_EVENT_NAME
385 in parallel.
386 */
387 aggregate_all_stages(m_instr_class_stages_stats,
388 safe_user->m_instr_class_stages_stats,
389 safe_host->m_instr_class_stages_stats);
390 return;
391 }
392
393 if (safe_user != NULL)
394 {
395 /*
396 Aggregate EVENTS_STAGES_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME to:
397 - EVENTS_STAGES_SUMMARY_BY_USER_BY_EVENT_NAME
398 - EVENTS_STAGES_SUMMARY_GLOBAL_BY_EVENT_NAME
399 in parallel.
400 */
401 aggregate_all_stages(m_instr_class_stages_stats,
402 safe_user->m_instr_class_stages_stats,
403 global_instr_class_stages_array);
404 return;
405 }
406
407 if (safe_host != NULL)
408 {
409 /*
410 Aggregate EVENTS_STAGES_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME to:
411 - EVENTS_STAGES_SUMMARY_BY_HOST_BY_EVENT_NAME
412 */
413 aggregate_all_stages(m_instr_class_stages_stats,
414 safe_host->m_instr_class_stages_stats);
415 return;
416 }
417
418 /*
419 Aggregate EVENTS_STAGES_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME to:
420 - EVENTS_STAGES_SUMMARY_GLOBAL_BY_EVENT_NAME
421 */
422 aggregate_all_stages(m_instr_class_stages_stats,
423 global_instr_class_stages_array);
424 return;
425}
426
427void PFS_account::aggregate_statements(PFS_user *safe_user, PFS_host *safe_host)
428{
429 if (likely(safe_user != NULL && safe_host != NULL))
430 {
431 /*
432 Aggregate EVENTS_STATEMENTS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME to:
433 - EVENTS_STATEMENTS_SUMMARY_BY_USER_BY_EVENT_NAME
434 - EVENTS_STATEMENTS_SUMMARY_BY_HOST_BY_EVENT_NAME
435 in parallel.
436 */
437 aggregate_all_statements(m_instr_class_statements_stats,
438 safe_user->m_instr_class_statements_stats,
439 safe_host->m_instr_class_statements_stats);
440 return;
441 }
442
443 if (safe_user != NULL)
444 {
445 /*
446 Aggregate EVENTS_STATEMENTS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME to:
447 - EVENTS_STATEMENTS_SUMMARY_BY_USER_BY_EVENT_NAME
448 - EVENTS_STATEMENTS_SUMMARY_GLOBAL_BY_EVENT_NAME
449 in parallel.
450 */
451 aggregate_all_statements(m_instr_class_statements_stats,
452 safe_user->m_instr_class_statements_stats,
453 global_instr_class_statements_array);
454 return;
455 }
456
457 if (safe_host != NULL)
458 {
459 /*
460 Aggregate EVENTS_STATEMENTS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME to:
461 - EVENTS_STATEMENTS_SUMMARY_BY_HOST_BY_EVENT_NAME
462 */
463 aggregate_all_statements(m_instr_class_statements_stats,
464 safe_host->m_instr_class_statements_stats);
465 return;
466 }
467
468 /*
469 Aggregate EVENTS_STATEMENTS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME to:
470 - EVENTS_STATEMENTS_SUMMARY_GLOBAL_BY_EVENT_NAME
471 */
472 aggregate_all_statements(m_instr_class_statements_stats,
473 global_instr_class_statements_array);
474 return;
475}
476
477void PFS_account::aggregate_stats(PFS_user *safe_user, PFS_host *safe_host)
478{
479 if (likely(safe_user != NULL && safe_host != NULL))
480 {
481 safe_user->m_disconnected_count+= m_disconnected_count;
482 safe_host->m_disconnected_count+= m_disconnected_count;
483 m_disconnected_count= 0;
484 return;
485 }
486
487 if (safe_user != NULL)
488 {
489 safe_user->m_disconnected_count+= m_disconnected_count;
490 m_disconnected_count= 0;
491 return;
492 }
493
494 if (safe_host != NULL)
495 {
496 safe_host->m_disconnected_count+= m_disconnected_count;
497 m_disconnected_count= 0;
498 return;
499 }
500
501 m_disconnected_count= 0;
502 return;
503}
504
505void PFS_account::release()
506{
507 dec_refcount();
508}
509
510PFS_account *sanitize_account(PFS_account *unsafe)
511{
512 if ((&account_array[0] <= unsafe) &&
513 (unsafe < &account_array[account_max]))
514 return unsafe;
515 return NULL;
516}
517
518void purge_account(PFS_thread *thread, PFS_account *account,
519 PFS_user *safe_user, PFS_host *safe_host)
520{
521 account->aggregate(safe_user, safe_host);
522
523 LF_PINS *pins= get_account_hash_pins(thread);
524 if (unlikely(pins == NULL))
525 return;
526
527 PFS_account **entry;
528 entry= reinterpret_cast<PFS_account**>
529 (lf_hash_search(&account_hash, pins,
530 account->m_key.m_hash_key,
531 account->m_key.m_key_length));
532 if (entry && (entry != MY_ERRPTR))
533 {
534 DBUG_ASSERT(*entry == account);
535 if (account->get_refcount() == 0)
536 {
537 lf_hash_delete(&account_hash, pins,
538 account->m_key.m_hash_key,
539 account->m_key.m_key_length);
540 if (account->m_user != NULL)
541 {
542 account->m_user->release();
543 account->m_user= NULL;
544 }
545 if (account->m_host != NULL)
546 {
547 account->m_host->release();
548 account->m_host= NULL;
549 }
550 account->m_lock.allocated_to_free();
551 }
552 }
553
554 lf_hash_search_unpin(pins);
555}
556
557/** Purge non connected user@host, reset stats of connected user@host. */
558void purge_all_account(void)
559{
560 PFS_thread *thread= PFS_thread::get_current_thread();
561 if (unlikely(thread == NULL))
562 return;
563
564 PFS_account *pfs= account_array;
565 PFS_account *pfs_last= account_array + account_max;
566 PFS_user *user;
567 PFS_host *host;
568
569 for ( ; pfs < pfs_last; pfs++)
570 {
571 if (pfs->m_lock.is_populated())
572 {
573 user= sanitize_user(pfs->m_user);
574 host= sanitize_host(pfs->m_host);
575 pfs->aggregate_stats(user, host);
576
577 if (pfs->get_refcount() == 0)
578 purge_account(thread, pfs, user, host);
579 }
580 }
581}
582
583/** @} */
584