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