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_variables_by_thread.cc
25 Table VARIABLES_BY_THREAD (implementation).
26 */
27
28 #include "my_global.h"
29 #include "table_variables_by_thread.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
36 THR_LOCK table_variables_by_thread::m_table_lock;
37
38 static const TABLE_FIELD_TYPE field_types[]=
39 {
40 {
41 { C_STRING_WITH_LEN("THREAD_ID") },
42 { C_STRING_WITH_LEN("bigint(20)") },
43 { NULL, 0}
44 },
45 {
46 { C_STRING_WITH_LEN("VARIABLE_NAME") },
47 { C_STRING_WITH_LEN("varchar(64)") },
48 { NULL, 0}
49 },
50 {
51 { C_STRING_WITH_LEN("VARIABLE_VALUE") },
52 { C_STRING_WITH_LEN("varchar(1024)") },
53 { NULL, 0}
54 }
55 };
56
57 TABLE_FIELD_DEF
58 table_variables_by_thread::m_field_def=
59 { 3, field_types };
60
61 PFS_engine_table_share
62 table_variables_by_thread::m_share=
63 {
64 { C_STRING_WITH_LEN("variables_by_thread") },
65 &pfs_readonly_acl,
66 table_variables_by_thread::create,
67 NULL, /* write_row */
68 NULL, /* delete_all_rows */
69 table_variables_by_thread::get_row_count,
70 sizeof(pos_t),
71 &m_table_lock,
72 &m_field_def,
73 false, /* checked */
74 true /* perpetual */
75 };
76
77 PFS_engine_table*
create(void)78 table_variables_by_thread::create(void)
79 {
80 return new table_variables_by_thread();
81 }
82
get_row_count(void)83 ha_rows table_variables_by_thread::get_row_count(void)
84 {
85 mysql_mutex_lock(&LOCK_plugin_delete);
86 mysql_rwlock_rdlock(&LOCK_system_variables_hash);
87 ulong system_var_count= get_system_variable_hash_records();
88 mysql_rwlock_unlock(&LOCK_system_variables_hash);
89 mysql_mutex_unlock(&LOCK_plugin_delete);
90 return (global_thread_container.get_row_count() * system_var_count);
91 }
92
table_variables_by_thread()93 table_variables_by_thread::table_variables_by_thread()
94 : PFS_engine_table(&m_share, &m_pos),
95 m_sysvar_cache(true), m_row_exists(false), m_pos(), m_next_pos(), m_context(NULL)
96 {}
97
reset_position(void)98 void table_variables_by_thread::reset_position(void)
99 {
100 m_pos.reset();
101 m_next_pos.reset();
102 }
103
rnd_init(bool scan)104 int table_variables_by_thread::rnd_init(bool scan)
105 {
106 /*
107 Build array of SHOW_VARs from system variable hash prior to materializing
108 threads in rnd_next() or rnd_pos().
109 */
110 m_sysvar_cache.initialize_session();
111
112 /* Record the version of the system variable hash. */
113 ulonglong hash_version= m_sysvar_cache.get_sysvar_hash_version();
114
115 /*
116 The table context holds the current version of the system variable hash and
117 a record of which threads were materialized.
118 If scan == true, then allocate a new context from mem_root and store in TLS.
119 If scan == false, then restore from TLS.
120 */
121 m_context= (table_variables_by_thread_context *)current_thd->alloc(sizeof(table_variables_by_thread_context));
122 new(m_context) table_variables_by_thread_context(hash_version, !scan);
123 return 0;
124 }
125
rnd_next(void)126 int table_variables_by_thread::rnd_next(void)
127 {
128 /* If system variable hash changes, exit with warning. */ // TODO: Issue warning
129 if (!m_context->versions_match())
130 return HA_ERR_END_OF_FILE;
131
132 bool has_more_thread= true;
133
134 for (m_pos.set_at(&m_next_pos);
135 has_more_thread;
136 m_pos.next_thread())
137 {
138 PFS_thread *pfs_thread= global_thread_container.get(m_pos.m_index_1, &has_more_thread);
139
140 /* Materialize all variables for the current thread. Assign a dedicated mem_root. */
141 if (m_sysvar_cache.materialize_session(pfs_thread, true) == 0)
142 {
143 /* Mark this thread as materialized. */
144 m_context->set_item(m_pos.m_index_1);
145
146 const System_variable *system_var= m_sysvar_cache.get(m_pos.m_index_2);
147 if (system_var != NULL)
148 {
149 make_row(pfs_thread, system_var);
150 m_next_pos.set_after(&m_pos);
151 return 0;
152 }
153 }
154 }
155 return HA_ERR_END_OF_FILE;
156 }
157
158 int
rnd_pos(const void * pos)159 table_variables_by_thread::rnd_pos(const void *pos)
160 {
161 /* If system variable hash changes, do nothing. */
162 if (!m_context->versions_match())
163 return HA_ERR_RECORD_DELETED;
164
165 set_position(pos);
166 assert(m_pos.m_index_1 < global_thread_container.get_row_count());
167
168 PFS_thread *pfs_thread= global_thread_container.get(m_pos.m_index_1);
169 /*
170 Only materialize threads that were previously materialized by rnd_next().
171 If a thread cannot be rematerialized, then do nothing.
172 Only materialize the requested system variable to avoid repeated
173 materialization of each thread, such as with ORDER BY variable_name.
174 */
175 if (m_context->is_item_set(m_pos.m_index_1) &&
176 /* Materialize only the requested variable. */
177 m_sysvar_cache.materialize_session(pfs_thread, m_pos.m_index_2) == 0)
178 {
179 /* Get the first (and only) element from the cache. */
180 const System_variable *system_var= m_sysvar_cache.get();
181 if (system_var != NULL)
182 {
183 make_row(pfs_thread, system_var);
184 m_next_pos.set_after(&m_pos);
185 return 0;
186 }
187 }
188 return HA_ERR_RECORD_DELETED;
189 }
190
191 void table_variables_by_thread
make_row(PFS_thread * thread,const System_variable * system_var)192 ::make_row(PFS_thread *thread, const System_variable *system_var)
193 {
194 pfs_optimistic_state lock;
195 m_row_exists= false;
196 if (system_var->is_null() || system_var->is_ignored())
197 return;
198
199 /* Protect this reader against a thread termination */
200 thread->m_lock.begin_optimistic_lock(&lock);
201
202 m_row.m_thread_internal_id= thread->m_thread_internal_id;
203 m_row.m_variable_name.make_row(system_var->m_name, system_var->m_name_length);
204 m_row.m_variable_value.make_row(system_var);
205
206 if (!thread->m_lock.end_optimistic_lock(&lock))
207 return;
208
209 m_row_exists= true;
210 }
211
212 int table_variables_by_thread
read_row_values(TABLE * table,unsigned char * buf,Field ** fields,bool read_all)213 ::read_row_values(TABLE *table,
214 unsigned char *buf,
215 Field **fields,
216 bool read_all)
217 {
218 Field *f;
219
220 if (unlikely(! m_row_exists))
221 return HA_ERR_RECORD_DELETED;
222
223 /* Set the null bits */
224 assert(table->s->null_bytes == 1);
225 buf[0]= 0;
226
227 for (; (f= *fields) ; fields++)
228 {
229 if (read_all || bitmap_is_set(table->read_set, f->field_index))
230 {
231 switch(f->field_index)
232 {
233 case 0: /* THREAD_ID */
234 set_field_ulonglong(f, m_row.m_thread_internal_id);
235 break;
236 case 1: /* VARIABLE_NAME */
237 set_field_varchar_utf8(f, m_row.m_variable_name.m_str, m_row.m_variable_name.m_length);
238 break;
239 case 2: /* VARIABLE_VALUE */
240 m_row.m_variable_value.set_field(f);
241 break;
242 default:
243 assert(false);
244 }
245 }
246 }
247
248 return 0;
249 }
250
251