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