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