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 906067 : SQLgetColumnSize(sql_trans *tr, sql_column *c, int access)
32 : {
33 906067 : sqlstore *store = tr->store;
34 1812113 : return store->storage_api.count_col(tr, c, access);
35 : }
36 :
37 : static lng
38 6405 : SQLgetIdxSize(sql_trans *tr, sql_idx *i, int access)
39 : {
40 6405 : sqlstore *store = tr->store;
41 6405 : 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 degration on SF100 Q3 with SSD(>6x)
55 : */
56 :
57 : static lng
58 548471 : SQLgetSpace(mvc *m, MalBlkPtr mb, int prepare)
59 : {
60 548471 : sql_trans *tr = m->session->tr;
61 548471 : lng size,space = 0, i;
62 548471 : str lasttable = 0;
63 :
64 13331110 : for (i = 0; i < mb->stop; i++) {
65 12782665 : 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 12782665 : if (getModuleId(p) == sqlRef && getFunctionId(p) == bindRef && p->retc <= 2){
70 906071 : char *sname = getVarConstant(mb, getArg(p, 1 + p->retc)).val.sval;
71 906071 : char *tname = getVarConstant(mb, getArg(p, 2 + p->retc)).val.sval;
72 906071 : char *cname = getVarConstant(mb, getArg(p, 3 + p->retc)).val.sval;
73 906071 : int access = getVarConstant(mb, getArg(p, 4 + p->retc)).val.ival;
74 906071 : sql_schema *s = mvc_bind_schema(m, sname);
75 906088 : sql_table *t = 0;
76 906088 : sql_column *c = 0;
77 :
78 906088 : if (!s)
79 0 : continue;
80 906088 : t = mvc_bind_table(m, s, tname);
81 906094 : if (!t || isDeclaredTable(t))
82 0 : continue;
83 906094 : c = mvc_bind_column(m, t, cname);
84 906066 : if (!c)
85 0 : continue;
86 :
87 : /* we have to sum the cost of all three components of a BAT */
88 906066 : if (c && isTable(c->t) && (lasttable == 0 || strcmp(lasttable,tname)==0)) {
89 906067 : size = SQLgetColumnSize(tr, c, access);
90 906046 : space += size; // accumulate once per table
91 : //lasttable = tname; invalidate this attempt
92 906046 : if( !prepare && size == 0 && ! t->system){
93 282131 : setFunctionId(p, emptybindRef);
94 : }
95 : }
96 : }
97 12782639 : if (getModuleId(p) == sqlRef && (getFunctionId(p) == bindidxRef)) {
98 6405 : char *sname = getVarConstant(mb, getArg(p, 1 + p->retc)).val.sval;
99 : //char *tname = getVarConstant(mb, getArg(p, 2 + p->retc)).val.sval;
100 6405 : char *idxname = getVarConstant(mb, getArg(p, 3 + p->retc)).val.sval;
101 6405 : int access = getVarConstant(mb, getArg(p, 4 + p->retc)).val.ival;
102 6405 : sql_schema *s = mvc_bind_schema(m, sname);
103 :
104 6405 : if (getFunctionId(p) == bindidxRef) {
105 6405 : sql_idx *i = mvc_bind_idx(m, s, idxname);
106 :
107 6405 : if (i && isTable(i->t)) {
108 6405 : size = SQLgetIdxSize(tr, i, access);
109 :
110 6405 : if( !prepare && size == 0 && ! i->t->system){
111 3132 : setFunctionId(p, emptybindidxRef);
112 : }
113 : }
114 : }
115 : }
116 : }
117 548445 : return space;
118 : }
119 :
120 : /* gather the optimizer pipeline defined in the current session */
121 : str
122 794536 : getSQLoptimizer(mvc *m)
123 : {
124 794536 : char *opt = get_string_global_var(m, "optimizer");
125 796270 : char *pipe = "default_pipe";
126 :
127 796270 : if (opt)
128 796270 : pipe = opt;
129 796270 : return pipe;
130 : }
131 :
132 : static str
133 548463 : addOptimizers(Client c, MalBlkPtr mb, char *pipe, int prepare)
134 : {
135 548463 : int i;
136 548463 : InstrPtr q;
137 548463 : backend *be;
138 548463 : str msg= MAL_SUCCEED;
139 :
140 548463 : be = (backend *) c->sqlcontext;
141 548463 : assert(be && be->mvc); /* SQL clients should always have their state set */
142 :
143 548463 : (void) SQLgetSpace(be->mvc, mb, prepare); // detect empty bats.
144 548461 : pipe = pipe? pipe: "default_pipe";
145 548461 : msg = addOptimizerPipe(c, mb, pipe);
146 548671 : if (msg){
147 : return msg;
148 : }
149 548664 : if (be->no_mitosis) {
150 4183439 : for (i = mb->stop - 1; i > 0; i--) {
151 4183439 : q = getInstrPtr(mb, i);
152 4183439 : if (q->token == ENDsymbol)
153 : break;
154 3953116 : if (getFunctionId(q) == mitosisRef)
155 126171 : 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 791 : SQLoptimizeFunction(Client c, MalBlkPtr mb)
167 : {
168 791 : str msg;
169 791 : str pipe;
170 791 : backend *be = (backend *) c->sqlcontext;
171 791 : assert(be && be->mvc); /* SQL clients should always have their state set */
172 :
173 791 : pipe = getSQLoptimizer(be->mvc);
174 791 : msg = addOptimizers(c, mb, pipe, TRUE);
175 791 : if (msg)
176 : return msg;
177 791 : msg = optimizeMALBlock(c, mb);
178 791 : return msg;
179 : }
180 :
181 : str
182 546489 : SQLoptimizeQuery(Client c, MalBlkPtr mb)
183 : {
184 546489 : backend *be;
185 546489 : str msg = 0, pipe = 0;
186 546489 : bool free_pipe = false;
187 546489 : InstrPtr p = mb->stmt[mb->stop -1];
188 :
189 546489 : if (p && mb->stop > 0 && getModuleId(p) == optimizerRef)
190 : return MAL_SUCCEED; /* already optimized */
191 :
192 546489 : be = (backend *) c->sqlcontext;
193 546489 : assert(be && be->mvc); /* SQL clients should always have their state set */
194 :
195 546489 : c->blkmode = 0;
196 546489 : 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 547019 : if (msg != MAL_SUCCEED || mb->errors) {
204 13 : if (c->listing)
205 0 : printFunction(c->fdout, mb, 0, c->listing);
206 13 : 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 13 : str nmsg = createException(MAL, "optimizer.optimizeQuery", "%s", mb->errors ? mb->errors : msg);
211 13 : freeException(msg);
212 13 : return nmsg;
213 : }
214 :
215 547006 : pipe = getSQLoptimizer(be->mvc);
216 547732 : 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 547732 : msg = addOptimizers(c, mb, pipe, FALSE);
223 547856 : if (free_pipe)
224 15 : GDKfree(pipe);
225 547856 : if (msg)
226 : return msg;
227 547851 : msg = optimizeMALBlock(c, mb);
228 547851 : return msg;
229 : }
230 :
231 : /* queries are added to the MAL catalog under the client session namespace */
232 : void
233 692 : SQLaddQueryToCache(Client c)
234 : {
235 692 : insertSymbol(c->usermodule, c->curprg);
236 691 : }
237 :
238 : void
239 0 : SQLremoveQueryFromCache(Client c)
240 : {
241 0 : deleteSymbol(c->usermodule, c->curprg);
242 0 : }
|