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 | |
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 | */ |
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 | |
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 | |
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 | |
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 | |
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 | 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 | |
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 | |
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 | |
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 | |
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 | |
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 |
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 | |
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 | |
283 | void hash_free_sp_head(void *p) |
284 | { |
285 | sp_head *sp= (sp_head *)p; |
286 | delete sp; |
287 | } |
288 | |
289 | |
290 | sp_cache::sp_cache() |
291 | { |
292 | init(); |
293 | } |
294 | |
295 | |
296 | sp_cache::~sp_cache() |
297 | { |
298 | my_hash_free(&m_hashtable); |
299 | } |
300 | |
301 | |
302 | void |
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 |
311 | sp_cache::cleanup() |
312 | { |
313 | my_hash_free(&m_hashtable); |
314 | } |
315 | |