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