1 /* Copyright (c) 2015, 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
21   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
22 
23 /**
24   @file storage/perfschema/table_status_by_account.cc
25   Table STATUS_BY_ACCOUNT (implementation).
26 */
27 
28 #include "my_global.h"
29 #include "table_status_by_account.h"
30 #include "my_thread.h"
31 #include "pfs_instr_class.h"
32 #include "pfs_column_types.h"
33 #include "pfs_column_values.h"
34 #include "pfs_global.h"
35 #include "pfs_account.h"
36 
37 THR_LOCK table_status_by_account::m_table_lock;
38 
39 static const TABLE_FIELD_TYPE field_types[]=
40 {
41   {
42     { C_STRING_WITH_LEN("USER") },
43     { C_STRING_WITH_LEN("char(" USERNAME_CHAR_LENGTH_STR ")") },
44     { NULL, 0}
45   },
46   {
47     { C_STRING_WITH_LEN("HOST") },
48     { C_STRING_WITH_LEN("char(60)") },
49     { NULL, 0}
50   },
51   {
52     { C_STRING_WITH_LEN("VARIABLE_NAME") },
53     { C_STRING_WITH_LEN("varchar(64)") },
54     { NULL, 0}
55   },
56   {
57     { C_STRING_WITH_LEN("VARIABLE_VALUE") },
58     { C_STRING_WITH_LEN("varchar(1024)") },
59     { NULL, 0}
60   }
61 };
62 
63 TABLE_FIELD_DEF
64 table_status_by_account::m_field_def=
65 { 4, field_types };
66 
67 PFS_engine_table_share
68 table_status_by_account::m_share=
69 {
70   { C_STRING_WITH_LEN("status_by_account") },
71   &pfs_truncatable_acl,
72   table_status_by_account::create,
73   NULL, /* write_row */
74   table_status_by_account::delete_all_rows,
75   table_status_by_account::get_row_count,
76   sizeof(pos_t),
77   &m_table_lock,
78   &m_field_def,
79   false, /* checked */
80   false  /* perpetual */
81 };
82 
83 PFS_engine_table*
create(void)84 table_status_by_account::create(void)
85 {
86   return new table_status_by_account();
87 }
88 
delete_all_rows(void)89 int table_status_by_account::delete_all_rows(void)
90 {
91   mysql_mutex_lock(&LOCK_status);
92   reset_status_by_thread();
93   reset_status_by_account();
94   mysql_mutex_unlock(&LOCK_status);
95   return 0;
96 }
97 
get_row_count(void)98 ha_rows table_status_by_account::get_row_count(void)
99 {
100   mysql_mutex_lock(&LOCK_status);
101   size_t status_var_count= all_status_vars.size();
102   mysql_mutex_unlock(&LOCK_status);
103   return (global_account_container.get_row_count() * status_var_count);
104 }
105 
table_status_by_account()106 table_status_by_account::table_status_by_account()
107   : PFS_engine_table(&m_share, &m_pos),
108     m_status_cache(true), m_row_exists(false), m_pos(), m_next_pos()
109 {}
110 
reset_position(void)111 void table_status_by_account::reset_position(void)
112 {
113   m_pos.reset();
114   m_next_pos.reset();
115 }
116 
rnd_init(bool scan)117 int table_status_by_account::rnd_init(bool scan)
118 {
119   if (show_compatibility_56)
120     return 0;
121 
122   /*
123     Build array of SHOW_VARs from the global status array prior to materializing
124     threads in rnd_next() or rnd_pos().
125   */
126   m_status_cache.initialize_client_session();
127 
128   /* Use the current number of status variables to detect changes. */
129   ulonglong status_version= m_status_cache.get_status_array_version();
130 
131   /*
132     The table context holds the current version of the global status array
133     and a record of which accounts were materialized. If scan == true, then
134     allocate a new context from mem_root and store in TLS. If scan == false,
135     then restore from TLS.
136   */
137   m_context= (table_status_by_account_context *)current_thd->alloc(sizeof(table_status_by_account_context));
138   new(m_context) table_status_by_account_context(status_version, !scan);
139   return 0;
140 }
141 
rnd_next(void)142 int table_status_by_account::rnd_next(void)
143 {
144   if (show_compatibility_56)
145     return HA_ERR_END_OF_FILE;
146 
147   /* If status array changes, exit with warning. */ // TODO: Issue warning
148   if (!m_context->versions_match())
149     return HA_ERR_END_OF_FILE;
150 
151   /*
152     For each account, build a cache of status variables using totals from all
153     threads associated with the account.
154   */
155   bool has_more_account= true;
156 
157   for (m_pos.set_at(&m_next_pos);
158        has_more_account;
159        m_pos.next_account())
160   {
161     PFS_account *pfs_account= global_account_container.get(m_pos.m_index_1, &has_more_account);
162 
163     if (m_status_cache.materialize_account(pfs_account) == 0)
164     {
165       /* Mark this account as materialized. */
166       m_context->set_item(m_pos.m_index_1);
167 
168       /* Get the next status variable. */
169       const Status_variable *stat_var= m_status_cache.get(m_pos.m_index_2);
170       if (stat_var != NULL)
171       {
172         make_row(pfs_account, stat_var);
173         m_next_pos.set_after(&m_pos);
174         return 0;
175       }
176     }
177   }
178   return HA_ERR_END_OF_FILE;
179 }
180 
181 int
rnd_pos(const void * pos)182 table_status_by_account::rnd_pos(const void *pos)
183 {
184   if (show_compatibility_56)
185     return HA_ERR_RECORD_DELETED;
186 
187   /* If status array changes, exit with warning. */ // TODO: Issue warning
188   if (!m_context->versions_match())
189     return HA_ERR_END_OF_FILE;
190 
191   set_position(pos);
192   assert(m_pos.m_index_1 < global_account_container.get_row_count());
193 
194   PFS_account *pfs_account= global_account_container.get(m_pos.m_index_1);
195 
196   /*
197     Only materialize threads that were previously materialized by rnd_next().
198     If a account cannot be rematerialized, then do nothing.
199   */
200   if (m_context->is_item_set(m_pos.m_index_1) &&
201       m_status_cache.materialize_account(pfs_account) == 0)
202   {
203     const Status_variable *stat_var= m_status_cache.get(m_pos.m_index_2);
204     if (stat_var != NULL)
205     {
206       make_row(pfs_account, stat_var);
207       return 0;
208     }
209   }
210   return HA_ERR_RECORD_DELETED;
211 }
212 
213 void table_status_by_account
make_row(PFS_account * pfs_account,const Status_variable * status_var)214 ::make_row(PFS_account *pfs_account, const Status_variable *status_var)
215 {
216   pfs_optimistic_state lock;
217   m_row_exists= false;
218   pfs_account->m_lock.begin_optimistic_lock(&lock);
219 
220   if (m_row.m_account.make_row(pfs_account))
221     return;
222 
223   m_row.m_variable_name.make_row(status_var->m_name, status_var->m_name_length);
224   m_row.m_variable_value.make_row(status_var);
225 
226   if (!pfs_account->m_lock.end_optimistic_lock(&lock))
227     return;
228 
229   m_row_exists= true;
230 }
231 
232 int table_status_by_account
read_row_values(TABLE * table,unsigned char * buf,Field ** fields,bool read_all)233 ::read_row_values(TABLE *table,
234                   unsigned char *buf,
235                   Field **fields,
236                   bool read_all)
237 {
238   Field *f;
239 
240   if (unlikely(! m_row_exists))
241     return HA_ERR_RECORD_DELETED;
242 
243   /* Set the null bits */
244   assert(table->s->null_bytes == 1);
245   buf[0]= 0;
246 
247   for (; (f= *fields) ; fields++)
248   {
249     if (read_all || bitmap_is_set(table->read_set, f->field_index))
250     {
251       switch(f->field_index)
252       {
253       case 0: /* USER */
254       case 1: /* HOST */
255         m_row.m_account.set_field(f->field_index, f);
256         break;
257       case 2: /* VARIABLE_NAME */
258         set_field_varchar_utf8(f, m_row.m_variable_name.m_str, m_row.m_variable_name.m_length);
259         break;
260       case 3: /* VARIABLE_VALUE */
261         m_row.m_variable_value.set_field(f);
262         break;
263       default:
264         assert(false);
265       }
266     }
267   }
268 
269   return 0;
270 }
271 
272