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 1000019 : SQLgetColumnSize(sql_trans *tr, sql_column *c, int access)
31 : {
32 1000019 : sqlstore *store = tr->store;
33 2000038 : 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 570168 : SQLgetSpace(mvc *m, MalBlkPtr mb, int prepare)
58 : {
59 570168 : sql_trans *tr = m->session->tr;
60 570168 : lng size,space = 0, i;
61 570168 : str lasttable = 0;
62 :
63 14352591 : for (i = 0; i < mb->stop; i++) {
64 13782464 : 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 13782464 : if (getModuleId(p) == sqlRef && getFunctionId(p) == bindRef && p->retc <= 2){
69 1000050 : char *sname = getVarConstant(mb, getArg(p, 1 + p->retc)).val.sval;
70 1000050 : char *tname = getVarConstant(mb, getArg(p, 2 + p->retc)).val.sval;
71 1000050 : char *cname = getVarConstant(mb, getArg(p, 3 + p->retc)).val.sval;
72 1000050 : int access = getVarConstant(mb, getArg(p, 4 + p->retc)).val.ival;
73 1000050 : sql_schema *s = mvc_bind_schema(m, sname);
74 1000059 : sql_table *t = 0;
75 1000059 : sql_column *c = 0;
76 :
77 1000059 : if (!s)
78 0 : continue;
79 1000059 : t = mvc_bind_table(m, s, tname);
80 1000051 : if (!t || isDeclaredTable(t))
81 0 : continue;
82 1000051 : c = mvc_bind_column(m, t, cname);
83 1000008 : if (!c)
84 0 : continue;
85 :
86 : /* we have to sum the cost of all three components of a BAT */
87 1000008 : if (c && isTable(c->t) && (lasttable == 0 || strcmp(lasttable,tname)==0)) {
88 1000019 : size = SQLgetColumnSize(tr, c, access);
89 1000019 : space += size; // accumulate once per table
90 : //lasttable = tname; invalidate this attempt
91 1000019 : if( !prepare && size == 0 && ! t->system){
92 286396 : setFunctionId(p, emptybindRef);
93 : }
94 : }
95 : }
96 13782423 : 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 570127 : return space;
117 : }
118 :
119 : /* gather the optimizer pipeline defined in the current session */
120 : str
121 823951 : getSQLoptimizer(mvc *m)
122 : {
123 823951 : char *opt = get_string_global_var(m, "optimizer");
124 826252 : char *pipe = "default_pipe";
125 826252 : char *rec_pipe = "recursive_pipe";
126 :
127 826252 : if (m->recursive)
128 : return rec_pipe;
129 :
130 826125 : if (opt)
131 826125 : pipe = opt;
132 : return pipe;
133 : }
134 :
135 : static str
136 570219 : addOptimizers(Client c, MalBlkPtr mb, char *pipe, int prepare)
137 : {
138 570219 : int i;
139 570219 : InstrPtr q;
140 570219 : backend *be;
141 570219 : str msg= MAL_SUCCEED;
142 :
143 570219 : be = (backend *) c->sqlcontext;
144 570219 : assert(be && be->mvc); /* SQL clients should always have their state set */
145 :
146 570219 : (void) SQLgetSpace(be->mvc, mb, prepare); // detect empty bats.
147 569906 : pipe = pipe? pipe: "default_pipe";
148 569906 : msg = addOptimizerPipe(c, mb, pipe);
149 570312 : if (msg){
150 : return msg;
151 : }
152 570313 : if (be->no_mitosis) {
153 4265967 : for (i = mb->stop - 1; i > 0; i--) {
154 4265968 : q = getInstrPtr(mb, i);
155 4265968 : if (q->token == ENDsymbol)
156 : break;
157 4033050 : if (getFunctionId(q) == mitosisRef)
158 128825 : 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 826 : SQLoptimizeFunction(Client c, MalBlkPtr mb)
170 : {
171 826 : str msg;
172 826 : str pipe;
173 826 : backend *be = (backend *) c->sqlcontext;
174 826 : assert(be && be->mvc); /* SQL clients should always have their state set */
175 :
176 826 : pipe = getSQLoptimizer(be->mvc);
177 826 : msg = addOptimizers(c, mb, pipe, TRUE);
178 826 : if (msg)
179 : return msg;
180 826 : msg = optimizeMALBlock(c, mb);
181 826 : return msg;
182 : }
183 :
184 : str
185 567942 : SQLoptimizeQuery(Client c, MalBlkPtr mb)
186 : {
187 567942 : backend *be;
188 567942 : str msg = 0, pipe = 0;
189 567942 : bool free_pipe = false;
190 567942 : InstrPtr p = mb->stmt[mb->stop -1];
191 :
192 567942 : if (p && mb->stop > 0 && getModuleId(p) == optimizerRef)
193 : return MAL_SUCCEED; /* already optimized */
194 :
195 567942 : be = (backend *) c->sqlcontext;
196 567942 : assert(be && be->mvc); /* SQL clients should always have their state set */
197 :
198 567942 : c->blkmode = 0;
199 567942 : 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 568813 : 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 568812 : pipe = getSQLoptimizer(be->mvc);
219 569378 : 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 569378 : msg = addOptimizers(c, mb, pipe, FALSE);
226 569483 : if (free_pipe)
227 15 : GDKfree(pipe);
228 569483 : if (msg)
229 : return msg;
230 569485 : msg = optimizeMALBlock(c, mb);
231 569485 : 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 702 : }
240 :
241 : void
242 0 : SQLremoveQueryFromCache(Client c)
243 : {
244 0 : deleteSymbol(c->usermodule, c->curprg);
245 0 : }
|