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-1301 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
25static mysql_mutex_t Cversion_lock;
26static ulong volatile Cversion= 1;
27
28
29/*
30 Cache of stored routines.
31*/
32
33class sp_cache
34{
35public:
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 */
46 inline bool insert(sp_head *sp)
47 {
48 return my_hash_insert(&m_hashtable, (const uchar *)sp);
49 }
50
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
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 */
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
75private:
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
84static PSI_mutex_key key_Cversion_lock;
85
86static PSI_mutex_info all_sp_cache_mutexes[]=
87{
88 { &key_Cversion_lock, "Cversion_lock", PSI_FLAG_GLOBAL}
89};
90
91static 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
106void 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
127void 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
139void 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
159void 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
192sp_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
214void 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 'dont call this function when
229 inside SP'.
230*/
231
232void 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
245ulong 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*/
259void
260sp_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
270extern "C" uchar *hash_get_key_for_sp_head(const uchar *ptr, size_t *plen,
271 my_bool first);
272extern "C" void hash_free_sp_head(void *p);
273
274uchar *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
283void hash_free_sp_head(void *p)
284{
285 sp_head *sp= (sp_head *)p;
286 delete sp;
287}
288
289
290sp_cache::sp_cache()
291{
292 init();
293}
294
295
296sp_cache::~sp_cache()
297{
298 my_hash_free(&m_hashtable);
299}
300
301
302void
303sp_cache::init()
304{
305 my_hash_init(&m_hashtable, system_charset_info, 0, 0, 0,
306 hash_get_key_for_sp_head, hash_free_sp_head, 0);
307}
308
309
310void
311sp_cache::cleanup()
312{
313 my_hash_free(&m_hashtable);
314}
315