1 /* Copyright (c) 2008, 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 Foundation,
21 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
22
23 #include "table_session_connect.h"
24 #include "field.h"
25
26 static const TABLE_FIELD_TYPE field_types[]=
27 {
28 {
29 { C_STRING_WITH_LEN("PROCESSLIST_ID") },
30 { C_STRING_WITH_LEN("int(11)") },
31 { NULL, 0}
32 },
33 {
34 { C_STRING_WITH_LEN("ATTR_NAME") },
35 { C_STRING_WITH_LEN("varchar(32)") },
36 { NULL, 0}
37 },
38 {
39 { C_STRING_WITH_LEN("ATTR_VALUE") },
40 { C_STRING_WITH_LEN("varchar(1024)") },
41 { NULL, 0}
42 },
43 {
44 { C_STRING_WITH_LEN("ORDINAL_POSITION") },
45 { C_STRING_WITH_LEN("int(11)") },
46 { NULL, 0}
47 }
48 };
49
50 TABLE_FIELD_DEF table_session_connect::m_field_def=
51 { 4, field_types };
52
table_session_connect(const PFS_engine_table_share * share)53 table_session_connect::table_session_connect(const PFS_engine_table_share *share)
54 : cursor_by_thread_connect_attr(share)
55 {
56 if (session_connect_attrs_size_per_thread > 0)
57 {
58 m_copy_session_connect_attrs= (char *) my_malloc(PSI_INSTRUMENT_ME,
59 session_connect_attrs_size_per_thread,
60 MYF(0));
61 }
62 else
63 {
64 m_copy_session_connect_attrs= NULL;
65 }
66 m_copy_session_connect_attrs_length= 0;
67 }
68
~table_session_connect()69 table_session_connect::~table_session_connect()
70 {
71 my_free(m_copy_session_connect_attrs);
72 }
73
74 /**
75 Take a length encoded string
76
77 @arg ptr inout the input string array
78 @arg dest where to store the result
79 @arg dest_size max size of @c dest
80 @arg copied_len the actual length of the data copied
81 @arg start_ptr pointer to the start of input
82 @arg input_length the length of the incoming data
83 @arg copy_data copy the data or just skip the input
84 @arg from_cs character set in which @c ptr is encoded
85 @arg nchars_max maximum number of characters to read
86 @return status
87 @retval true parsing failed
88 @retval false parsing succeeded
89 */
parse_length_encoded_string(const char ** ptr,char * dest,uint dest_size,uint * copied_len,const char * start_ptr,uint input_length,bool copy_data,const CHARSET_INFO * from_cs,uint nchars_max)90 bool parse_length_encoded_string(const char **ptr,
91 char *dest, uint dest_size,
92 uint *copied_len,
93 const char *start_ptr, uint input_length,
94 bool copy_data,
95 const CHARSET_INFO *from_cs,
96 uint nchars_max)
97 {
98 ulong copy_length, data_length;
99 const char *well_formed_error_pos= NULL, *cannot_convert_error_pos= NULL,
100 *from_end_pos= NULL;
101
102 copy_length= data_length= net_field_length((uchar **) ptr);
103
104 /* we don't tolerate NULL as a length */
105 if (data_length == NULL_LENGTH)
106 return true;
107
108 if (*ptr - start_ptr + data_length > input_length)
109 return true;
110
111 copy_length= well_formed_copy_nchars(&my_charset_utf8_bin, dest, dest_size,
112 from_cs, *ptr, data_length, nchars_max,
113 &well_formed_error_pos,
114 &cannot_convert_error_pos,
115 &from_end_pos);
116 *copied_len= copy_length;
117 (*ptr)+= data_length;
118
119 return false;
120 }
121
122 /**
123 Take the nth attribute name/value pair
124
125 Parse the attributes blob form the beginning, skipping the attributes
126 whose number is lower than the one we seek.
127 When we reach the attribute at an index we're looking for the values
128 are copied to the output parameters.
129 If parsing fails or no more attributes are found the function stops
130 and returns an error code.
131
132 @arg connect_attrs pointer to the connect attributes blob
133 @arg connect_attrs_length length of @c connect_attrs
134 @arg connect_attrs_cs character set used to encode @c connect_attrs
135 @arg ordinal index of the attribute we need
136 @arg attr_name [out] buffer to receive the attribute name
137 @arg max_attr_name max size of @c attr_name in bytes
138 @arg attr_name_length [out] number of bytes written in @attr_name
139 @arg attr_value [out] buffer to receive the attribute name
140 @arg max_attr_value max size of @c attr_value in bytes
141 @arg attr_value_length [out] number of bytes written in @attr_value
142 @return status
143 @retval true requested attribute pair is found and copied
144 @retval false error. Either because of parsing or too few attributes.
145 */
read_nth_attr(const char * connect_attrs,uint connect_attrs_length,const CHARSET_INFO * connect_attrs_cs,uint ordinal,char * attr_name,uint max_attr_name,uint * attr_name_length,char * attr_value,uint max_attr_value,uint * attr_value_length)146 bool read_nth_attr(const char *connect_attrs,
147 uint connect_attrs_length,
148 const CHARSET_INFO *connect_attrs_cs,
149 uint ordinal,
150 char *attr_name, uint max_attr_name,
151 uint *attr_name_length,
152 char *attr_value, uint max_attr_value,
153 uint *attr_value_length)
154 {
155 uint idx;
156 const char *ptr;
157
158 for (ptr= connect_attrs, idx= 0;
159 (uint)(ptr - connect_attrs) < connect_attrs_length && idx <= ordinal;
160 idx++)
161 {
162 uint copy_length;
163 /* do the copying only if we absolutely have to */
164 bool fill_in_attr_name= idx == ordinal;
165 bool fill_in_attr_value= idx == ordinal;
166
167 /* read the key */
168 if (parse_length_encoded_string(&ptr,
169 attr_name, max_attr_name, ©_length,
170 connect_attrs,
171 connect_attrs_length,
172 fill_in_attr_name,
173 connect_attrs_cs, 32) ||
174 !copy_length
175 )
176 return false;
177
178 if (idx == ordinal)
179 *attr_name_length= copy_length;
180
181 /* read the value */
182 if (parse_length_encoded_string(&ptr,
183 attr_value, max_attr_value, ©_length,
184 connect_attrs,
185 connect_attrs_length,
186 fill_in_attr_value,
187 connect_attrs_cs, 1024))
188 return false;
189
190 if (idx == ordinal)
191 *attr_value_length= copy_length;
192
193 if (idx == ordinal)
194 return true;
195 }
196
197 return false;
198 }
199
make_row(PFS_thread * pfs,uint ordinal)200 void table_session_connect::make_row(PFS_thread *pfs, uint ordinal)
201 {
202 pfs_optimistic_state lock;
203 pfs_optimistic_state session_lock;
204 PFS_thread_class *safe_class;
205 const CHARSET_INFO *cs;
206
207 m_row_exists= false;
208
209 /* Protect this reader against thread termination */
210 pfs->m_lock.begin_optimistic_lock(&lock);
211 /* Protect this reader against writing on session attributes */
212 pfs->m_session_lock.begin_optimistic_lock(&session_lock);
213
214 safe_class= sanitize_thread_class(pfs->m_class);
215 if (unlikely(safe_class == NULL))
216 return;
217
218 /* Filtering threads must be done under the protection of the optimistic lock. */
219 if (! thread_fits(pfs))
220 return;
221
222 /* Make a safe copy of the session attributes */
223
224 if (m_copy_session_connect_attrs == NULL)
225 return;
226
227 m_copy_session_connect_attrs_length= pfs->m_session_connect_attrs_length;
228
229 if (m_copy_session_connect_attrs_length > session_connect_attrs_size_per_thread)
230 return;
231
232 memcpy(m_copy_session_connect_attrs,
233 pfs->m_session_connect_attrs,
234 m_copy_session_connect_attrs_length);
235
236 cs= get_charset(pfs->m_session_connect_attrs_cs_number, MYF(0));
237 if (cs == NULL)
238 return;
239
240 if (! pfs->m_session_lock.end_optimistic_lock(& session_lock))
241 return;
242
243 if (! pfs->m_lock.end_optimistic_lock(& lock))
244 return;
245
246 /*
247 Now we have a safe copy of the data,
248 that will not change while parsing it
249 */
250
251 /* populate the row */
252 if (read_nth_attr(m_copy_session_connect_attrs,
253 m_copy_session_connect_attrs_length,
254 cs,
255 ordinal,
256 m_row.m_attr_name, (uint) sizeof(m_row.m_attr_name),
257 &m_row.m_attr_name_length,
258 m_row.m_attr_value, (uint) sizeof(m_row.m_attr_value),
259 &m_row.m_attr_value_length))
260 {
261 /* we don't expect internal threads to have connection attributes */
262 if (pfs->m_processlist_id == 0)
263 return;
264
265 m_row.m_ordinal_position= ordinal;
266 m_row.m_process_id= pfs->m_processlist_id;
267
268 m_row_exists= true;
269 }
270 }
271
read_row_values(TABLE * table,unsigned char * buf,Field ** fields,bool read_all)272 int table_session_connect::read_row_values(TABLE *table,
273 unsigned char *buf,
274 Field **fields,
275 bool read_all)
276 {
277 Field *f;
278
279 if (unlikely(!m_row_exists))
280 return HA_ERR_RECORD_DELETED;
281
282 /* Set the null bits */
283 assert(table->s->null_bytes == 1);
284 buf[0]= 0;
285
286 for (; (f= *fields) ; fields++)
287 {
288 if (read_all || bitmap_is_set(table->read_set, f->field_index))
289 {
290 switch(f->field_index)
291 {
292 case FO_PROCESS_ID:
293 if (m_row.m_process_id != 0)
294 set_field_ulong(f, m_row.m_process_id);
295 else
296 f->set_null();
297 break;
298 case FO_ATTR_NAME:
299 set_field_varchar_utf8(f, m_row.m_attr_name,
300 m_row.m_attr_name_length);
301 break;
302 case FO_ATTR_VALUE:
303 if (m_row.m_attr_value_length)
304 set_field_varchar_utf8(f, m_row.m_attr_value,
305 m_row.m_attr_value_length);
306 else
307 f->set_null();
308 break;
309 case FO_ORDINAL_POSITION:
310 set_field_ulong(f, m_row.m_ordinal_position);
311 break;
312 default:
313 assert(false);
314 }
315 }
316 }
317 return 0;
318 }
319
320 bool
thread_fits(PFS_thread * thread)321 table_session_connect::thread_fits(PFS_thread *thread)
322 {
323 return true;
324 }
325
326