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 'dont 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(&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