Line data Source code
1 : /*
2 : * SPDX-License-Identifier: MPL-2.0
3 : *
4 : * This Source Code Form is subject to the terms of the Mozilla Public
5 : * License, v. 2.0. If a copy of the MPL was not distributed with this
6 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 : *
8 : * Copyright 2024 MonetDB Foundation;
9 : * Copyright August 2008 - 2023 MonetDB B.V.;
10 : * Copyright 1997 - July 2008 CWI.
11 : */
12 :
13 : /*
14 : * N. Nes, M.L. Kersten
15 : * The queries are stored in the user cache after they have been
16 : * type checked and optimized.
17 : */
18 : #include "monetdb_config.h"
19 : #include "mal_builder.h"
20 : #include "opt_prelude.h"
21 : #include "sql_mvc.h"
22 : #include "sql_optimizer.h"
23 : #include "sql_scenario.h"
24 : #include "sql_gencode.h"
25 : #include "opt_pipes.h"
26 :
27 : /* calculate the footprint for optimizer pipe line choices
28 : * and identify empty columns upfront for just in time optimizers.
29 : */
30 : static lng
31 980444 : SQLgetColumnSize(sql_trans *tr, sql_column *c, int access)
32 : {
33 980444 : sqlstore *store = tr->store;
34 1960891 : return store->storage_api.count_col(tr, c, access);
35 : }
36 :
37 : static lng
38 6467 : SQLgetIdxSize(sql_trans *tr, sql_idx *i, int access)
39 : {
40 6467 : sqlstore *store = tr->store;
41 6467 : return store->storage_api.count_idx(tr, i, access);
42 : }
43 :
44 :
45 : /*
46 : * The maximal space occupied by a query is calculated
47 : * under the assumption that the complete database should fit in memory.
48 : * The assumption is that the plan does not contain duplicate bind operations.
49 : * Calculation of the precise footprint is much more complex
50 : * and can not deal with intermediate structures, or fast
51 : * access using sorted probing.
52 : *
53 : * A run where we only take the size of a table only once,
54 : * caused major degradation on SF100 Q3 with SSD(>6x)
55 : */
56 :
57 : static lng
58 566404 : SQLgetSpace(mvc *m, MalBlkPtr mb, int prepare)
59 : {
60 566404 : sql_trans *tr = m->session->tr;
61 566404 : lng size,space = 0, i;
62 566404 : str lasttable = 0;
63 :
64 14223133 : for (i = 0; i < mb->stop; i++) {
65 13656725 : InstrPtr p = mb->stmt[i];
66 :
67 : /* now deal with the update binds, it is only necessary to identify that there are updates
68 : * The actual size is not that important */
69 13656725 : if (getModuleId(p) == sqlRef && getFunctionId(p) == bindRef && p->retc <= 2){
70 980443 : char *sname = getVarConstant(mb, getArg(p, 1 + p->retc)).val.sval;
71 980443 : char *tname = getVarConstant(mb, getArg(p, 2 + p->retc)).val.sval;
72 980443 : char *cname = getVarConstant(mb, getArg(p, 3 + p->retc)).val.sval;
73 980443 : int access = getVarConstant(mb, getArg(p, 4 + p->retc)).val.ival;
74 980443 : sql_schema *s = mvc_bind_schema(m, sname);
75 980449 : sql_table *t = 0;
76 980449 : sql_column *c = 0;
77 :
78 980449 : if (!s)
79 0 : continue;
80 980449 : t = mvc_bind_table(m, s, tname);
81 980450 : if (!t || isDeclaredTable(t))
82 0 : continue;
83 980450 : c = mvc_bind_column(m, t, cname);
84 980444 : if (!c)
85 0 : continue;
86 :
87 : /* we have to sum the cost of all three components of a BAT */
88 980444 : if (c && isTable(c->t) && (lasttable == 0 || strcmp(lasttable,tname)==0)) {
89 980444 : size = SQLgetColumnSize(tr, c, access);
90 980447 : space += size; // accumulate once per table
91 : //lasttable = tname; invalidate this attempt
92 980447 : if( !prepare && size == 0 && ! t->system){
93 285333 : setFunctionId(p, emptybindRef);
94 : }
95 : }
96 : }
97 13656729 : if (getModuleId(p) == sqlRef && (getFunctionId(p) == bindidxRef)) {
98 6467 : char *sname = getVarConstant(mb, getArg(p, 1 + p->retc)).val.sval;
99 : //char *tname = getVarConstant(mb, getArg(p, 2 + p->retc)).val.sval;
100 6467 : char *idxname = getVarConstant(mb, getArg(p, 3 + p->retc)).val.sval;
101 6467 : int access = getVarConstant(mb, getArg(p, 4 + p->retc)).val.ival;
102 6467 : sql_schema *s = mvc_bind_schema(m, sname);
103 :
104 6467 : if (getFunctionId(p) == bindidxRef) {
105 6467 : sql_idx *i = mvc_bind_idx(m, s, idxname);
106 :
107 6467 : if (i && isTable(i->t)) {
108 6467 : size = SQLgetIdxSize(tr, i, access);
109 :
110 6467 : if( !prepare && size == 0 && ! i->t->system){
111 3180 : setFunctionId(p, emptybindidxRef);
112 : }
113 : }
114 : }
115 : }
116 : }
117 566408 : return space;
118 : }
119 :
120 : /* gather the optimizer pipeline defined in the current session */
121 : str
122 821524 : getSQLoptimizer(mvc *m)
123 : {
124 821524 : char *opt = get_string_global_var(m, "optimizer");
125 821484 : char *pipe = "default_pipe";
126 :
127 821484 : if (opt)
128 821484 : pipe = opt;
129 821484 : return pipe;
130 : }
131 :
132 : static str
133 566403 : addOptimizers(Client c, MalBlkPtr mb, char *pipe, int prepare)
134 : {
135 566403 : int i;
136 566403 : InstrPtr q;
137 566403 : backend *be;
138 566403 : str msg= MAL_SUCCEED;
139 :
140 566403 : be = (backend *) c->sqlcontext;
141 566403 : assert(be && be->mvc); /* SQL clients should always have their state set */
142 :
143 566403 : (void) SQLgetSpace(be->mvc, mb, prepare); // detect empty bats.
144 566398 : pipe = pipe? pipe: "default_pipe";
145 566398 : msg = addOptimizerPipe(c, mb, pipe);
146 566412 : if (msg){
147 : return msg;
148 : }
149 566412 : if (be->no_mitosis) {
150 4248206 : for (i = mb->stop - 1; i > 0; i--) {
151 4248206 : q = getInstrPtr(mb, i);
152 4248206 : if (q->token == ENDsymbol)
153 : break;
154 4015857 : if (getFunctionId(q) == mitosisRef)
155 128249 : q->token = REMsymbol; /* they are ignored */
156 : }
157 : }
158 : return msg;
159 : }
160 :
161 : /* Queries that should rely on the latest consolidated state
162 : * are not allowed to remove sql.binds operations.
163 : */
164 :
165 : str
166 822 : SQLoptimizeFunction(Client c, MalBlkPtr mb)
167 : {
168 822 : str msg;
169 822 : str pipe;
170 822 : backend *be = (backend *) c->sqlcontext;
171 822 : assert(be && be->mvc); /* SQL clients should always have their state set */
172 :
173 822 : pipe = getSQLoptimizer(be->mvc);
174 822 : msg = addOptimizers(c, mb, pipe, TRUE);
175 822 : if (msg)
176 : return msg;
177 822 : msg = optimizeMALBlock(c, mb);
178 822 : return msg;
179 : }
180 :
181 : str
182 565571 : SQLoptimizeQuery(Client c, MalBlkPtr mb)
183 : {
184 565571 : backend *be;
185 565571 : str msg = 0, pipe = 0;
186 565571 : bool free_pipe = false;
187 565571 : InstrPtr p = mb->stmt[mb->stop -1];
188 :
189 565571 : if (p && mb->stop > 0 && getModuleId(p) == optimizerRef)
190 : return MAL_SUCCEED; /* already optimized */
191 :
192 565571 : be = (backend *) c->sqlcontext;
193 565571 : assert(be && be->mvc); /* SQL clients should always have their state set */
194 :
195 565571 : c->blkmode = 0;
196 565571 : msg = chkProgram(c->usermodule, mb);
197 :
198 : /*
199 : * An error in the compilation should be reported to the user.
200 : * And if the debugging option is set, the debugger is called
201 : * to allow inspection.
202 : */
203 565585 : if (msg != MAL_SUCCEED || mb->errors) {
204 0 : if (c->listing)
205 0 : printFunction(c->fdout, mb, 0, c->listing);
206 0 : if (mb->errors && msg && msg != mb->errors) { /* if both set, throw mb->errors as the earliest one */
207 0 : freeException(msg);
208 0 : msg = MAL_SUCCEED;
209 : }
210 0 : str nmsg = createException(MAL, "optimizer.optimizeQuery", "%s", mb->errors ? mb->errors : msg);
211 0 : freeException(msg);
212 0 : return nmsg;
213 : }
214 :
215 565585 : pipe = getSQLoptimizer(be->mvc);
216 565585 : if( strcmp(pipe, "default_pipe") == 0 && strcmp(c->optimizer, "default_pipe") != 0) {
217 15 : if (!(pipe = GDKstrdup(c->optimizer)))
218 0 : throw(MAL, "sql.optimizeQuery", SQLSTATE(HY013) MAL_MALLOC_FAIL);
219 : free_pipe = true;
220 : }
221 :
222 565585 : msg = addOptimizers(c, mb, pipe, FALSE);
223 565590 : if (free_pipe)
224 15 : GDKfree(pipe);
225 565590 : if (msg)
226 : return msg;
227 565590 : msg = optimizeMALBlock(c, mb);
228 565590 : return msg;
229 : }
230 :
231 : /* queries are added to the MAL catalog under the client session namespace */
232 : void
233 702 : SQLaddQueryToCache(Client c)
234 : {
235 702 : insertSymbol(c->usermodule, c->curprg);
236 702 : }
237 :
238 : void
239 0 : SQLremoveQueryFromCache(Client c)
240 : {
241 0 : deleteSymbol(c->usermodule, c->curprg);
242 0 : }
|