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