1 /* Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved.
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 "sql_priv.h"
24 #include "unireg.h"
25 #include "sp_cache.h"
26 #include "sp_head.h"
27
28 static mysql_mutex_t Cversion_lock;
29 static ulong volatile Cversion= 0;
30
31
32 /*
33 Cache of stored routines.
34 */
35
36 class sp_cache
37 {
38 public:
39 sp_cache();
40 ~sp_cache();
41
42 /**
43 Inserts a sp_head object into a hash table.
44
45 @returns Success status
46 @return TRUE Failure
47 @return FALSE Success
48 */
insert(sp_head * sp)49 inline bool insert(sp_head *sp)
50 {
51 return my_hash_insert(&m_hashtable, (const uchar *)sp);
52 }
53
lookup(char * name,uint namelen)54 inline sp_head *lookup(char *name, uint namelen)
55 {
56 return (sp_head *) my_hash_search(&m_hashtable, (const uchar *)name,
57 namelen);
58 }
59
remove(sp_head * sp)60 inline void remove(sp_head *sp)
61 {
62 my_hash_delete(&m_hashtable, (uchar *)sp);
63 }
64
65 /**
66 Remove all elements from a stored routine cache if the current
67 number of elements exceeds the argument value.
68
69 @param[in] upper_limit_for_elements Soft upper limit of elements that
70 can be stored in the cache.
71 */
enforce_limit(ulong upper_limit_for_elements)72 void enforce_limit(ulong upper_limit_for_elements)
73 {
74 if (m_hashtable.records > upper_limit_for_elements)
75 my_hash_reset(&m_hashtable);
76 }
77
78 private:
79 void init();
80 void cleanup();
81
82 /* All routines in this cache */
83 HASH m_hashtable;
84 }; // class sp_cache
85
86 #ifdef HAVE_PSI_INTERFACE
87 static PSI_mutex_key key_Cversion_lock;
88
89 static PSI_mutex_info all_sp_cache_mutexes[]=
90 {
91 { &key_Cversion_lock, "Cversion_lock", PSI_FLAG_GLOBAL}
92 };
93
init_sp_cache_psi_keys(void)94 static void init_sp_cache_psi_keys(void)
95 {
96 const char* category= "sql";
97 int count;
98
99 count= array_elements(all_sp_cache_mutexes);
100 mysql_mutex_register(category, all_sp_cache_mutexes, count);
101 }
102 #endif
103
104 /* Initialize the SP caching once at startup */
105
sp_cache_init()106 void sp_cache_init()
107 {
108 #ifdef HAVE_PSI_INTERFACE
109 init_sp_cache_psi_keys();
110 #endif
111
112 mysql_mutex_init(key_Cversion_lock, &Cversion_lock, MY_MUTEX_INIT_FAST);
113 }
114
115
116 /*
117 Clear the cache *cp and set *cp to NULL.
118
119 SYNOPSIS
120 sp_cache_clear()
121 cp Pointer to cache to clear
122
123 NOTE
124 This function doesn't invalidate other caches.
125 */
126
sp_cache_clear(sp_cache ** cp)127 void sp_cache_clear(sp_cache **cp)
128 {
129 sp_cache *c= *cp;
130
131 if (c)
132 {
133 delete c;
134 *cp= NULL;
135 }
136 }
137
138
139 /*
140 Insert a routine into the cache.
141
142 SYNOPSIS
143 sp_cache_insert()
144 cp The cache to put routine into
145 sp Routine to insert.
146
147 TODO: Perhaps it will be more straightforward if in case we returned an
148 error from this function when we couldn't allocate sp_cache. (right
149 now failure to put routine into cache will cause a 'SP not found'
150 error to be reported at some later time)
151 */
152
sp_cache_insert(sp_cache ** cp,sp_head * sp)153 void sp_cache_insert(sp_cache **cp, sp_head *sp)
154 {
155 sp_cache *c;
156
157 if (!(c= *cp))
158 {
159 if (!(c= new sp_cache()))
160 return; // End of memory error
161 }
162 /* Reading a ulong variable with no lock. */
163 sp->set_sp_cache_version(Cversion);
164 DBUG_PRINT("info",("sp_cache: inserting: %.*s", (int) sp->m_qname.length,
165 sp->m_qname.str));
166 c->insert(sp);
167 *cp= c; // Update *cp if it was NULL
168 }
169
170
171 /*
172 Look up a routine in the cache.
173 SYNOPSIS
174 sp_cache_lookup()
175 cp Cache to look into
176 name Name of rutine to find
177
178 NOTE
179 An obsolete (but not more obsolete then since last
180 sp_cache_flush_obsolete call) routine may be returned.
181
182 RETURN
183 The routine or
184 NULL if the routine not found.
185 */
186
sp_cache_lookup(sp_cache ** cp,sp_name * name)187 sp_head *sp_cache_lookup(sp_cache **cp, sp_name *name)
188 {
189 sp_cache *c= *cp;
190 if (! c)
191 return NULL;
192 return c->lookup(name->m_qname.str, name->m_qname.length);
193 }
194
195
196 /*
197 Invalidate all routines in all caches.
198
199 SYNOPSIS
200 sp_cache_invalidate()
201
202 NOTE
203 This is called when a VIEW definition is created or modified (and in some
204 other contexts). We can't destroy sp_head objects here as one may modify
205 VIEW definitions from prelocking-free SPs.
206 */
207
sp_cache_invalidate()208 void sp_cache_invalidate()
209 {
210 DBUG_PRINT("info",("sp_cache: invalidating"));
211 thread_safe_increment(Cversion, &Cversion_lock);
212 }
213
214
215 /**
216 Remove an out-of-date SP from the cache.
217
218 @param[in] cp Cache to flush
219 @param[in] sp SP to remove.
220
221 @note This invalidates pointers to sp_head objects this thread
222 uses. In practice that means 'dont call this function when
223 inside SP'.
224 */
225
sp_cache_flush_obsolete(sp_cache ** cp,sp_head ** sp)226 void sp_cache_flush_obsolete(sp_cache **cp, sp_head **sp)
227 {
228 if ((*sp)->sp_cache_version() < Cversion && !(*sp)->is_invoked())
229 {
230 (*cp)->remove(*sp);
231 *sp= NULL;
232 }
233 }
234
235
236 /**
237 Return the current global version of the cache.
238 */
239
sp_cache_version()240 ulong sp_cache_version()
241 {
242 return Cversion;
243 }
244
245
246 /**
247 Enforce that the current number of elements in the cache don't exceed
248 the argument value by flushing the cache if necessary.
249
250 @param[in] c Cache to check
251 @param[in] upper_limit_for_elements Soft upper limit for number of sp_head
252 objects that can be stored in the cache.
253 */
254 void
sp_cache_enforce_limit(sp_cache * c,ulong upper_limit_for_elements)255 sp_cache_enforce_limit(sp_cache *c, ulong upper_limit_for_elements)
256 {
257 if (c)
258 c->enforce_limit(upper_limit_for_elements);
259 }
260
261 /*************************************************************************
262 Internal functions
263 *************************************************************************/
264
265 extern "C" uchar *hash_get_key_for_sp_head(const uchar *ptr, size_t *plen,
266 my_bool first);
267 extern "C" void hash_free_sp_head(void *p);
268
hash_get_key_for_sp_head(const uchar * ptr,size_t * plen,my_bool first)269 uchar *hash_get_key_for_sp_head(const uchar *ptr, size_t *plen,
270 my_bool first)
271 {
272 sp_head *sp= (sp_head *)ptr;
273 *plen= sp->m_qname.length;
274 return (uchar*) sp->m_qname.str;
275 }
276
277
hash_free_sp_head(void * p)278 void hash_free_sp_head(void *p)
279 {
280 sp_head *sp= (sp_head *)p;
281 delete sp;
282 }
283
284
sp_cache()285 sp_cache::sp_cache()
286 {
287 init();
288 }
289
290
~sp_cache()291 sp_cache::~sp_cache()
292 {
293 my_hash_free(&m_hashtable);
294 }
295
296
297 void
init()298 sp_cache::init()
299 {
300 my_hash_init(&m_hashtable, system_charset_info, 0, 0, 0,
301 hash_get_key_for_sp_head, hash_free_sp_head, 0);
302 }
303
304
305 void
cleanup()306 sp_cache::cleanup()
307 {
308 my_hash_free(&m_hashtable);
309 }
310