1 | /* |
2 | * This Source Code Form is subject to the terms of the Mozilla Public |
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
5 | * |
6 | * Copyright 1997 - July 2008 CWI, August 2008 - 2019 MonetDB B.V. |
7 | */ |
8 | |
9 | /* |
10 | * The query cache |
11 | * =============== |
12 | * |
13 | * An effective scheme to speedup processing of an SQL engine is |
14 | * to keep a cache of recently executed queries around. |
15 | * This cache can be inspected by simply searching for an |
16 | * identical query string, or a simple pattern match against |
17 | * the sql query tree. Finding an element saves code generation. |
18 | * |
19 | * The scheme used here is based on keeping a hash-key around for |
20 | * the original text and subsequently perform a parse-tree comparison. |
21 | * This means that only text-identical queries are captured. |
22 | * |
23 | * The upper layers should consider the cache as an auxiliary |
24 | * structure. There is no guarantee that elements remain in |
25 | * the cache forever, nor control features to assure this. |
26 | * |
27 | * Entries in the cache obtain a unique(?) cache entry number. |
28 | * It can be used as an external name. |
29 | * |
30 | * [todo] |
31 | * The information retain for each cached item is back-end specific. |
32 | * It should have a hook to update the initialize the cache entry |
33 | * and a method to destroy it. |
34 | * |
35 | * The optimization/processing cost should be kept around and the re-use of |
36 | * a cache entry. |
37 | */ |
38 | |
39 | #include "monetdb_config.h" |
40 | |
41 | #include "sql_qc.h" |
42 | #include "sql_mvc.h" |
43 | #include "sql_atom.h" |
44 | |
45 | qc * |
46 | qc_create(int clientid, int seqnr) |
47 | { |
48 | qc *r = MNEW(qc); |
49 | if(!r) |
50 | return NULL; |
51 | r->clientid = clientid; |
52 | r->id = seqnr; |
53 | r->nr = 0; |
54 | |
55 | r->q = NULL; |
56 | return r; |
57 | } |
58 | |
59 | static void |
60 | cq_delete(int clientid, cq *q) |
61 | { |
62 | if (q->code) |
63 | backend_freecode(clientid, q->code, q->stk, q->id, q->name); |
64 | if (q->stk) |
65 | backend_freestack(clientid, q->stk); |
66 | if (q->codestring) |
67 | _DELETE(q->codestring); |
68 | |
69 | /* params and name are allocated using sa, ie need to be delete last */ |
70 | if (q->sa) |
71 | sa_destroy(q->sa); |
72 | _DELETE(q); |
73 | } |
74 | |
75 | void |
76 | qc_delete(qc *cache, cq *q) |
77 | { |
78 | cq *n, *p = NULL; |
79 | |
80 | for (n = cache->q; n; p = n, n = n->next) { |
81 | if (n == q) { |
82 | if (p) { |
83 | p->next = q->next; |
84 | } else { |
85 | cache->q = q->next; |
86 | } |
87 | cq_delete(cache->clientid, q); |
88 | cache->nr--; |
89 | break; |
90 | } |
91 | } |
92 | } |
93 | |
94 | void |
95 | qc_clean(qc *cache) |
96 | { |
97 | cq *n, *q, *p = NULL; |
98 | |
99 | for (q = cache->q; q; ) { |
100 | if (!q->prepared) { |
101 | n = q->next; |
102 | if (p) |
103 | p->next = n; |
104 | else |
105 | cache->q = n; |
106 | cq_delete(cache->clientid, q); |
107 | cache->nr--; |
108 | q = n; |
109 | continue; |
110 | } |
111 | p = q; |
112 | q = q->next; |
113 | } |
114 | } |
115 | |
116 | void |
117 | qc_destroy(qc *cache) |
118 | { |
119 | cq *q, *n; |
120 | for (q = cache->q; q; q = n) { |
121 | n = q->next; |
122 | |
123 | cq_delete(cache->clientid, q); |
124 | cache->nr--; |
125 | } |
126 | _DELETE(cache); |
127 | } |
128 | |
129 | static int |
130 | param_cmp(sql_subtype *t1, sql_subtype *t2) |
131 | { |
132 | int res; |
133 | |
134 | if (t1->scale != t2->scale) |
135 | return -1; |
136 | |
137 | if (t1->type->eclass == EC_NUM && t2->type->eclass == EC_NUM && |
138 | t1->digits >= t2->digits) |
139 | return 0; |
140 | |
141 | res = is_subtype(t2, t1); |
142 | if (!res) |
143 | return -1; |
144 | /* |
145 | if ((t1->digits > 0 && t1->scale == 0 && t1->digits < t2->digits) || (t1->scale > 0 && t1->digits > 0 && t1->digits - t1->scale < t2->digits - t2->scale)) { |
146 | return -1; |
147 | } |
148 | */ |
149 | return 0; |
150 | } |
151 | |
152 | static int |
153 | param_list_cmp(sql_subtype *typelist, atom **atoms, int plen, sql_query_t type) |
154 | { |
155 | int i; |
156 | |
157 | if (!plen && !typelist) |
158 | return 0; |
159 | |
160 | if (!typelist || !atoms) |
161 | return -1; |
162 | for (i=0; i < plen; i++) { |
163 | sql_subtype *tp = typelist + i; |
164 | atom *a = atoms[i]; |
165 | |
166 | if (atom_null(a) && type != Q_UPDATE) |
167 | return -1; |
168 | |
169 | /* NULL values match any type */ |
170 | if (!atom_null(a) && param_cmp(tp, atom_type(a)) != 0) { |
171 | sql_subtype *at = atom_type(a); |
172 | |
173 | if (tp->type->eclass == EC_CHAR && |
174 | at->type->eclass == EC_CHAR && |
175 | (!tp->digits || tp->digits == at->digits)) |
176 | continue; |
177 | if (tp->type->eclass == EC_STRING && |
178 | at->type->eclass == EC_CHAR && |
179 | (!tp->digits || tp->digits >= at->digits)) |
180 | continue; |
181 | if (type != Q_UPDATE) |
182 | return -1; |
183 | /* FLT == DEC/NUM and DEC/NUM are equal */ |
184 | if ((!((at->type->eclass == EC_DEC || |
185 | at->type->eclass == EC_NUM) && |
186 | tp->type->eclass == EC_FLT)) && |
187 | (!(EC_VARCHAR(tp->type->eclass) && |
188 | EC_VARCHAR(at->type->eclass) && |
189 | (!tp->digits || |
190 | tp->digits >= at->digits))) && |
191 | (!(tp->type->eclass == EC_DEC && |
192 | at->type->eclass == EC_NUM && |
193 | tp->type->localtype >= at->type->localtype && |
194 | atom_num_digits(a)+tp->scale <= tp->digits)) && |
195 | /* |
196 | (!(tp->type->eclass == EC_DEC && |
197 | at->type->eclass == EC_DEC && |
198 | tp->type->localtype >= at->type->localtype && |
199 | at->digits <= tp->digits && |
200 | at->scale <= tp->scale)) && |
201 | */ |
202 | (!(at->type->eclass == EC_NUM && tp->type->eclass == EC_NUM && |
203 | at->type->localtype <= tp->type->localtype))) |
204 | return -1; |
205 | } |
206 | } |
207 | return 0; |
208 | } |
209 | |
210 | cq * |
211 | qc_find(qc *cache, int id) |
212 | { |
213 | cq *q; |
214 | |
215 | for (q = cache->q; q; q = q->next) { |
216 | if (q->id == id) { |
217 | q->count++; |
218 | return q; |
219 | } |
220 | } |
221 | return NULL; |
222 | } |
223 | |
224 | cq * |
225 | qc_match(qc *cache, mvc *sql, symbol *s, atom **params, int plen, int key) |
226 | { |
227 | cq *q; |
228 | |
229 | for (q = cache->q; q; q = q->next) { |
230 | if (q->key == key) { |
231 | if (q->paramlen == plen && param_list_cmp(q->params, params, plen, q->type) == 0 && symbol_cmp(sql, q->s, s) == 0) { |
232 | q->count++; |
233 | return q; |
234 | } |
235 | } |
236 | } |
237 | return NULL; |
238 | } |
239 | |
240 | cq * |
241 | qc_insert(qc *cache, sql_allocator *sa, sql_rel *r, char *qname, symbol *s, atom **params, int paramlen, int key, sql_query_t type, char *cmd, int no_mitosis, int prepared) |
242 | { |
243 | int i, namelen; |
244 | cq *n = MNEW(cq); |
245 | if(!n) |
246 | return NULL; |
247 | |
248 | n->id = cache->id++; |
249 | cache->nr++; |
250 | |
251 | n->sa = sa; |
252 | n->rel = r; |
253 | n->s = s; |
254 | |
255 | n->params = NULL; |
256 | n->paramlen = paramlen; |
257 | if (paramlen) { |
258 | n->params = SA_NEW_ARRAY(sa, sql_subtype,paramlen); |
259 | if(!n->params) { |
260 | _DELETE(n); |
261 | return NULL; |
262 | } |
263 | for (i = 0; i < paramlen; i++) { |
264 | atom *a = params[i]; |
265 | |
266 | n->params[i] = *(atom_type(a)); |
267 | } |
268 | } |
269 | n->prepared = prepared; |
270 | n->next = cache->q; |
271 | n->stk = 0; |
272 | n->code = NULL; |
273 | n->type = type; |
274 | n->key = key; |
275 | n->codestring = cmd; |
276 | n->count = 1; |
277 | namelen = 5 + ((n->id+7)>>3) + ((cache->clientid+7)>>3); |
278 | n->name = sa_alloc(sa, namelen); |
279 | n->no_mitosis = no_mitosis; |
280 | if(!n->name) { |
281 | _DELETE(n->params); |
282 | _DELETE(n); |
283 | return NULL; |
284 | } |
285 | strcpy(n->name, qname); |
286 | cache->q = n; |
287 | return n; |
288 | } |
289 | |
290 | int |
291 | qc_isaquerytemplate(str name){ |
292 | int i,j; |
293 | return sscanf(name, "s%d_%d" , &i,&j) == 2; |
294 | } |
295 | |
296 | int |
297 | qc_isapreparedquerytemplate(str name){ |
298 | int i,j; |
299 | return sscanf(name, "p%d_%d" , &i,&j) == 2; |
300 | } |
301 | |
302 | int |
303 | qc_size(qc *cache) |
304 | { |
305 | return cache->nr; |
306 | } |
307 | |