1 /* Copyright (c) 2002, 2012, 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 as published by
5 the Free Software Foundation; version 2 of the License.
6
7 This program is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 GNU General Public License for more details.
11
12 You should have received a copy of the GNU General Public License
13 along with this program; if not, write to the Free Software
14 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
15
16 #include "mariadb.h"
17 #include "sql_priv.h"
18 #include "unireg.h"
19 #ifdef USE_PRAGMA_IMPLEMENTATION
20 #pragma implementation
21 #endif
22 #include "sp_cache.h"
23 #include "sp_head.h"
24
25 static mysql_mutex_t Cversion_lock;
26 static ulong volatile Cversion= 1;
27
28
29 /*
30 Cache of stored routines.
31 */
32
33 class sp_cache
34 {
35 public:
36 sp_cache();
37 ~sp_cache();
38
39 /**
40 Inserts a sp_head object into a hash table.
41
42 @returns Success status
43 @return TRUE Failure
44 @return FALSE Success
45 */
insert(sp_head * sp)46 inline bool insert(sp_head *sp)
47 {
48 return my_hash_insert(&m_hashtable, (const uchar *)sp);
49 }
50
lookup(char * name,size_t namelen)51 inline sp_head *lookup(char *name, size_t namelen)
52 {
53 return (sp_head *) my_hash_search(&m_hashtable, (const uchar *)name,
54 namelen);
55 }
56
remove(sp_head * sp)57 inline void remove(sp_head *sp)
58 {
59 my_hash_delete(&m_hashtable, (uchar *)sp);
60 }
61
62 /**
63 Remove all elements from a stored routine cache if the current
64 number of elements exceeds the argument value.
65
66 @param[in] upper_limit_for_elements Soft upper limit of elements that
67 can be stored in the cache.
68 */
enforce_limit(ulong upper_limit_for_elements)69 void enforce_limit(ulong upper_limit_for_elements)
70 {
71 if (m_hashtable.records > upper_limit_for_elements)
72 my_hash_reset(&m_hashtable);
73 }
74
75 private:
76 void init();
77 void cleanup();
78
79 /* All routines in this cache */
80 HASH m_hashtable;
81 }; // class sp_cache
82
83 #ifdef HAVE_PSI_INTERFACE
84 static PSI_mutex_key key_Cversion_lock;
85
86 static PSI_mutex_info all_sp_cache_mutexes[]=
87 {
88 { &key_Cversion_lock, "Cversion_lock", PSI_FLAG_GLOBAL}
89 };
90
init_sp_cache_psi_keys(void)91 static void init_sp_cache_psi_keys(void)
92 {
93 const char* category= "sql";
94 int count;
95
96 if (PSI_server == NULL)
97 return;
98
99 count= array_elements(all_sp_cache_mutexes);
100 PSI_server->register_mutex(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
sp_cache_end()139 void sp_cache_end()
140 {
141 mysql_mutex_destroy(&Cversion_lock);
142 }
143
144
145 /*
146 Insert a routine into the cache.
147
148 SYNOPSIS
149 sp_cache_insert()
150 cp The cache to put routine into
151 sp Routine to insert.
152
153 TODO: Perhaps it will be more straightforward if in case we returned an
154 error from this function when we couldn't allocate sp_cache. (right
155 now failure to put routine into cache will cause a 'SP not found'
156 error to be reported at some later time)
157 */
158
sp_cache_insert(sp_cache ** cp,sp_head * sp)159 void sp_cache_insert(sp_cache **cp, sp_head *sp)
160 {
161 sp_cache *c;
162
163 if (!(c= *cp))
164 {
165 if (!(c= new sp_cache()))
166 return; // End of memory error
167 }
168 /* Reading a ulong variable with no lock. */
169 sp->set_sp_cache_version(Cversion);
170 DBUG_PRINT("info",("sp_cache: inserting: %s", ErrConvDQName(sp).ptr()));
171 c->insert(sp);
172 *cp= c; // Update *cp if it was NULL
173 }
174
175
176 /*
177 Look up a routine in the cache.
178 SYNOPSIS
179 sp_cache_lookup()
180 cp Cache to look into
181 name Name of rutine to find
182
183 NOTE
184 An obsolete (but not more obsolete then since last
185 sp_cache_flush_obsolete call) routine may be returned.
186
187 RETURN
188 The routine or
189 NULL if the routine not found.
190 */
191
sp_cache_lookup(sp_cache ** cp,const Database_qualified_name * name)192 sp_head *sp_cache_lookup(sp_cache **cp, const Database_qualified_name *name)
193 {
194 char buf[NAME_LEN * 2 + 2];
195 sp_cache *c= *cp;
196 if (! c)
197 return NULL;
198 return c->lookup(buf, name->make_qname(buf, sizeof(buf)));
199 }
200
201
202 /*
203 Invalidate all routines in all caches.
204
205 SYNOPSIS
206 sp_cache_invalidate()
207
208 NOTE
209 This is called when a VIEW definition is created or modified (and in some
210 other contexts). We can't destroy sp_head objects here as one may modify
211 VIEW definitions from prelocking-free SPs.
212 */
213
sp_cache_invalidate()214 void sp_cache_invalidate()
215 {
216 DBUG_PRINT("info",("sp_cache: invalidating"));
217 thread_safe_increment(Cversion, &Cversion_lock);
218 }
219
220
221 /**
222 Remove an out-of-date SP from the cache.
223
224 @param[in] cp Cache to flush
225 @param[in] sp SP to remove.
226
227 @note This invalidates pointers to sp_head objects this thread
228 uses. In practice that means don't call this function when
229 inside SP'.
230 */
231
sp_cache_flush_obsolete(sp_cache ** cp,sp_head ** sp)232 void sp_cache_flush_obsolete(sp_cache **cp, sp_head **sp)
233 {
234 if ((*sp)->sp_cache_version() < Cversion && !(*sp)->is_invoked())
235 {
236 (*cp)->remove(*sp);
237 *sp= NULL;
238 }
239 }
240
241 /**
242 Return the current global version of the cache.
243 */
244
sp_cache_version()245 ulong sp_cache_version()
246 {
247 return Cversion;
248 }
249
250
251 /**
252 Enforce that the current number of elements in the cache don't exceed
253 the argument value by flushing the cache if necessary.
254
255 @param[in] c Cache to check
256 @param[in] upper_limit_for_elements Soft upper limit for number of sp_head
257 objects that can be stored in the cache.
258 */
259 void
sp_cache_enforce_limit(sp_cache * c,ulong upper_limit_for_elements)260 sp_cache_enforce_limit(sp_cache *c, ulong upper_limit_for_elements)
261 {
262 if (c)
263 c->enforce_limit(upper_limit_for_elements);
264 }
265
266 /*************************************************************************
267 Internal functions
268 *************************************************************************/
269
270 extern "C" uchar *hash_get_key_for_sp_head(const uchar *ptr, size_t *plen,
271 my_bool first);
272 extern "C" void hash_free_sp_head(void *p);
273
hash_get_key_for_sp_head(const uchar * ptr,size_t * plen,my_bool first)274 uchar *hash_get_key_for_sp_head(const uchar *ptr, size_t *plen,
275 my_bool first)
276 {
277 sp_head *sp= (sp_head *)ptr;
278 *plen= sp->m_qname.length;
279 return (uchar*) sp->m_qname.str;
280 }
281
282
hash_free_sp_head(void * p)283 void hash_free_sp_head(void *p)
284 {
285 sp_head *sp= (sp_head *)p;
286 sp_head::destroy(sp);
287 }
288
289
sp_cache()290 sp_cache::sp_cache()
291 {
292 init();
293 }
294
295
~sp_cache()296 sp_cache::~sp_cache()
297 {
298 my_hash_free(&m_hashtable);
299 }
300
301
302 void
init()303 sp_cache::init()
304 {
305 my_hash_init(key_memory_sp_cache, &m_hashtable, system_charset_info, 0, 0, 0,
306 hash_get_key_for_sp_head, hash_free_sp_head, 0);
307 }
308
309
310 void
cleanup()311 sp_cache::cleanup()
312 {
313 my_hash_free(&m_hashtable);
314 }
315