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