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
45qc *
46qc_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
59static void
60cq_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
75void
76qc_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
94void
95qc_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
116void
117qc_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
129static int
130param_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
152static int
153param_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
210cq *
211qc_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
224cq *
225qc_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
240cq *
241qc_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
290int
291qc_isaquerytemplate(str name){
292 int i,j;
293 return sscanf(name, "s%d_%d", &i,&j) == 2;
294}
295
296int
297qc_isapreparedquerytemplate(str name){
298 int i,j;
299 return sscanf(name, "p%d_%d", &i,&j) == 2;
300}
301
302int
303qc_size(qc *cache)
304{
305 return cache->nr;
306}
307