1 | /* |
2 | * This Source Code Form is subject to the terms of the Mozilla Public |
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
5 | * |
6 | * Copyright 1997 - July 2008 CWI, August 2008 - 2019 MonetDB B.V. |
7 | */ |
8 | |
9 | /* author M.Kersten |
10 | * This optimizer hunts for the empty persistent tables accessed and propagates them. |
11 | * |
12 | * Patterns to look for: |
13 | * X_13 := algebra.projection(X_1,X_4); |
14 | * where either argument is empty |
15 | * |
16 | */ |
17 | #include "monetdb_config.h" |
18 | #include "opt_emptybind.h" |
19 | #include "opt_aliases.h" |
20 | #include "opt_deadcode.h" |
21 | #include "mal_builder.h" |
22 | |
23 | #define addresult(I) \ |
24 | do { \ |
25 | int tpe = getVarType(mb,getArg(p,I)); \ |
26 | q= newStmt(mb, batRef, newRef); \ |
27 | getArg(q,0)= getArg(p,I); \ |
28 | q = pushType(mb, q, getBatType(tpe)); \ |
29 | empty[getArg(q,0)]= i; \ |
30 | } while (0) |
31 | |
32 | #define emptyresult(I) \ |
33 | do { \ |
34 | int tpe = getVarType(mb,getArg(p,I)); \ |
35 | clrFunction(p); \ |
36 | setModuleId(p,batRef); \ |
37 | setFunctionId(p,newRef); \ |
38 | p->argc = p->retc; \ |
39 | p = pushType(mb,p, getBatType(tpe)); \ |
40 | setVarType(mb, getArg(p,0), tpe); \ |
41 | setVarFixed(mb, getArg(p,0)); \ |
42 | empty[getArg(p,0)]= i; \ |
43 | } while (0) |
44 | |
45 | |
46 | str |
47 | OPTemptybindImplementation(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci) |
48 | { |
49 | int i,j, actions =0, = 0; |
50 | int *empty; |
51 | int limit = mb->stop, slimit = mb->ssize; |
52 | InstrPtr p, q, *old = mb->stmt, *updated; |
53 | char buf[256]; |
54 | lng usec = GDKusec(); |
55 | str sch,tbl; |
56 | int etop= 0, esize= 256; |
57 | str msg = MAL_SUCCEED; |
58 | |
59 | (void) stk; |
60 | (void) cntxt; |
61 | (void) pci; |
62 | |
63 | //if ( optimizerIsApplied(mb,"emptybind") ) |
64 | //return 0; |
65 | // use an instruction reference table to keep |
66 | |
67 | for( i=0; i< mb->stop; i++) |
68 | if( getFunctionId(getInstrPtr(mb,i)) == emptybindRef || getFunctionId(getInstrPtr(mb,i)) == emptybindidxRef) |
69 | extras += getInstrPtr(mb,i)->argc; |
70 | if( extras == 0) |
71 | goto wrapup; |
72 | |
73 | // track of where 'emptybind' results are produced |
74 | // reserve space for maximal number of emptybat variables created |
75 | empty = (int *) GDKzalloc((mb->vsize + extras) * sizeof(int)); |
76 | if ( empty == NULL) |
77 | throw(MAL,"optimizer.emptybind" , SQLSTATE(HY001) MAL_MALLOC_FAIL); |
78 | |
79 | updated= (InstrPtr *) GDKzalloc(esize * sizeof(InstrPtr)); |
80 | if( updated == 0){ |
81 | GDKfree(empty); |
82 | return 0; |
83 | } |
84 | |
85 | if( OPTdebug & OPTemptybind){ |
86 | fprintf(stderr, "#Optimize Query Emptybind\n" ); |
87 | fprintFunction(stderr, mb, 0, LIST_MAL_ALL); |
88 | } |
89 | |
90 | if ( newMalBlkStmt(mb, mb->ssize) < 0) { |
91 | GDKfree(empty); |
92 | GDKfree(updated); |
93 | throw(MAL,"optimizer.emptybind" , SQLSTATE(HY001) MAL_MALLOC_FAIL); |
94 | } |
95 | |
96 | /* Symbolic evaluation of instructions with empty BAT variables */ |
97 | actions = 0; |
98 | for (i = 0; i < limit; i++) { |
99 | p = old[i]; |
100 | |
101 | pushInstruction(mb,p); |
102 | if (p->token == ENDsymbol){ |
103 | for(i++; i<limit; i++) |
104 | if (old[i]) |
105 | pushInstruction(mb,old[i]); |
106 | break; |
107 | } |
108 | |
109 | /* |
110 | * The bulk of the intelligence lies in inspecting calling |
111 | * sequences to filter and replace results |
112 | */ |
113 | if ( getModuleId(p) == batRef && getFunctionId(p) == newRef){ |
114 | if( OPTdebug & OPTemptybind){ |
115 | fprintf(stderr, "#empty bat pc %d var %d\n" ,i , getArg(p,0) ); |
116 | } |
117 | empty[getArg(p,0)] = i; |
118 | continue; |
119 | } |
120 | |
121 | // any of these instructions leave a non-empty BAT behind |
122 | if(p && getModuleId(p) == sqlRef && isUpdateInstruction(p)){ |
123 | if ( etop == esize){ |
124 | InstrPtr *tmp = updated; |
125 | updated = (InstrPtr*) GDKrealloc( updated, (esize += 256) * sizeof(InstrPtr)); |
126 | if( updated == NULL){ |
127 | GDKfree(tmp); |
128 | GDKfree(empty); |
129 | goto wrapup; |
130 | } |
131 | } |
132 | updated[etop++]= p; |
133 | } |
134 | |
135 | /* restore the naming, dropping the runtime property 'empty' |
136 | * Keep the bind operation, because it is cheap, rather focus on their re-use |
137 | */ |
138 | |
139 | if (getFunctionId(p) == emptybindRef) { |
140 | if( OPTdebug & OPTemptybind){ |
141 | fprintf(stderr, "#empty bind pc %d var %d\n" ,i , getArg(p,0) ); |
142 | } |
143 | setFunctionId(p,bindRef); |
144 | p->typechk= TYPE_UNKNOWN; |
145 | empty[getArg(p,0)] = i; |
146 | if( p->retc == 2){ |
147 | empty[getArg(p,1)] = i; |
148 | if( OPTdebug & OPTemptybind){ |
149 | fprintf(stderr, "#empty update bind pc %d var %d\n" ,i , getArg(p,1) ); |
150 | } |
151 | } |
152 | // replace the call into a empty bat creation unless the table was updated already in the same query |
153 | sch = getVarConstant(mb,getArg(p,2 + (p->retc==2))).val.sval; |
154 | tbl = getVarConstant(mb,getArg(p,3 + (p->retc==2))).val.sval; |
155 | for(j= 0; j< etop; j++){ |
156 | q= updated[j]; |
157 | if(q && getModuleId(q) == sqlRef && isUpdateInstruction(q)){ |
158 | if ( strcmp(getVarConstant(mb,getArg(q,2)).val.sval, sch) == 0 && |
159 | strcmp(getVarConstant(mb,getArg(q,3)).val.sval, tbl) == 0 ){ |
160 | if( OPTdebug & OPTemptybind){ |
161 | fprintf(stderr, "#reset mark empty variable pc %d var %d\n" ,i , getArg(p,0) ); |
162 | } |
163 | empty[getArg(p,0)] = 0; |
164 | if( p->retc == 2){ |
165 | if( OPTdebug & OPTemptybind){ |
166 | fprintf(stderr, "#reset mark empty variable pc %d var %d\n" ,i , getArg(p,1) ); |
167 | } |
168 | empty[getArg(p,1)] = 0; |
169 | } |
170 | break; |
171 | } |
172 | } |
173 | if(q && getModuleId(q) == sqlcatalogRef){ |
174 | if ( strcmp(getVarConstant(mb,getArg(q,2)).val.sval, sch) == 0 ){ |
175 | empty[getArg(p,0)] = 0; |
176 | if( p->retc == 2){ |
177 | if( OPTdebug & OPTemptybind){ |
178 | fprintf(stderr, "#reset mark empty variable pc %d var %d\n" ,i , getArg(p,1) ); |
179 | } |
180 | empty[getArg(p,1)] = 0; |
181 | } |
182 | break; |
183 | } |
184 | } |
185 | } |
186 | continue; |
187 | } |
188 | |
189 | if (getFunctionId(p) == emptybindidxRef) { |
190 | if( OPTdebug & OPTemptybind){ |
191 | fprintf(stderr, "#empty bindidx pc %d var %d\n" ,i , getArg(p,0) ); |
192 | } |
193 | setFunctionId(p,bindidxRef); |
194 | p->typechk= TYPE_UNKNOWN; |
195 | empty[getArg(p,0)] = i; |
196 | // replace the call into a empty bat creation unless the table was updated already in the same query |
197 | sch = getVarConstant(mb,getArg(p,2 + (p->retc==2))).val.sval; |
198 | tbl = getVarConstant(mb,getArg(p,3 + (p->retc==2))).val.sval; |
199 | for(j= 0; j< etop; j++){ |
200 | q= updated[j]; |
201 | if(q && getModuleId(q) == sqlRef && (getFunctionId(q) == appendRef || getFunctionId(q) == updateRef )){ |
202 | if ( strcmp(getVarConstant(mb,getArg(q,2)).val.sval, sch) == 0 && |
203 | strcmp(getVarConstant(mb,getArg(q,3)).val.sval, tbl) == 0 ){ |
204 | if( OPTdebug & OPTemptybind){ |
205 | fprintf(stderr, "#reset mark empty variable pc %d var %d\n" ,i , getArg(p,0) ); |
206 | } |
207 | empty[getArg(p,0)] = 0; |
208 | if( p->retc == 2){ |
209 | if( OPTdebug & OPTemptybind){ |
210 | fprintf(stderr, "#reset mark empty variable pc %d var %d\n" ,i , getArg(p,1) ); |
211 | } |
212 | empty[getArg(p,1)] = 0; |
213 | } |
214 | break; |
215 | } |
216 | } |
217 | if(q && getModuleId(q) == sqlcatalogRef){ |
218 | if ( strcmp(getVarConstant(mb,getArg(q,2)).val.sval, sch) == 0 ){ |
219 | empty[getArg(p,0)] = 0; |
220 | break; |
221 | } |
222 | } |
223 | } |
224 | continue; |
225 | } |
226 | |
227 | // delta operations without updates+ insert can be replaced by an assignment |
228 | if (getModuleId(p)== sqlRef && getFunctionId(p) == deltaRef && p->argc ==5){ |
229 | if( empty[getArg(p,2)] && empty[getArg(p,3)] && empty[getArg(p,4)] ){ |
230 | if( OPTdebug & OPTemptybind){ |
231 | fprintf(stderr, "#empty delta pc %d var %d,%d,%d\n" ,i ,empty[getArg(p,2)], empty[getArg(p,3)], empty[getArg(p,4)] ); |
232 | fprintf(stderr, "#empty delta pc %d var %d\n" ,i , getArg(p,0) ); |
233 | } |
234 | actions++; |
235 | clrFunction(p); |
236 | p->argc = 2; |
237 | if ( empty[getArg(p,1)] ){ |
238 | empty[getArg(p,0)] = i; |
239 | } |
240 | } |
241 | continue; |
242 | } |
243 | |
244 | if (getModuleId(p)== sqlRef && getFunctionId(p) == projectdeltaRef) { |
245 | if( empty[getArg(p,3)] && empty[getArg(p,4)] ){ |
246 | if( OPTdebug & OPTemptybind){ |
247 | fprintf(stderr, "#empty projectdelta pc %d var %d\n" ,i , getArg(p,0) ); |
248 | } |
249 | actions++; |
250 | setModuleId(p,algebraRef); |
251 | setFunctionId(p,projectionRef); |
252 | p->argc = 3; |
253 | p->typechk= TYPE_UNKNOWN; |
254 | } |
255 | continue; |
256 | } |
257 | if (getModuleId(p)== algebraRef){ |
258 | if( getFunctionId(p) == projectionRef) { |
259 | if( empty[getArg(p,1)] || empty[getArg(p,2)] ){ |
260 | if( OPTdebug & OPTemptybind){ |
261 | fprintf(stderr, "#empty projection pc %d var %d\n" ,i , getArg(p,0) ); |
262 | } |
263 | actions++; |
264 | emptyresult(0); |
265 | } |
266 | } |
267 | if( getFunctionId(p) == thetaselectRef || getFunctionId(p) == selectRef) { |
268 | if( empty[getArg(p,1)] || empty[getArg(p,2)] ){ |
269 | if( OPTdebug & OPTemptybind){ |
270 | fprintf(stderr, "#empty selection pc %d var %d\n" ,i , getArg(p,0) ); |
271 | } |
272 | actions++; |
273 | emptyresult(0); |
274 | } |
275 | } |
276 | } |
277 | if( getModuleId(p) == batstrRef){ |
278 | if( empty[getArg(p,1)] || empty[getArg(p,2)] ){ |
279 | |
280 | if( OPTdebug & OPTemptybind){ |
281 | fprintf(stderr, "#empty string operation pc %d var %d\n" ,i , getArg(p,0) ); |
282 | } |
283 | actions++; |
284 | emptyresult(0); |
285 | } |
286 | } |
287 | if (getModuleId(p)== batRef && isUpdateInstruction(p)){ |
288 | if( empty[getArg(p,1)] && empty[getArg(p,2)]){ |
289 | emptyresult(0); |
290 | } else |
291 | if( empty[getArg(p,2)]){ |
292 | actions++; |
293 | clrFunction(p); |
294 | p->argc = 2; |
295 | } |
296 | } |
297 | } |
298 | |
299 | for(; i<slimit; i++) |
300 | if( old[i]) |
301 | freeInstruction(old[i]); |
302 | GDKfree(old); |
303 | GDKfree(empty); |
304 | GDKfree(updated); |
305 | /* Defense line against incorrect plans */ |
306 | chkTypes(cntxt->usermodule, mb, FALSE); |
307 | chkFlow(mb); |
308 | chkDeclarations(mb); |
309 | /* keep all actions taken as a post block comment */ |
310 | wrapup: |
311 | usec = GDKusec()- usec; |
312 | snprintf(buf,256,"%-20s actions=%2d time=" LLFMT " usec" ,"emptybind" ,actions, usec); |
313 | newComment(mb,buf); |
314 | if( actions >= 0) |
315 | addtoMalBlkHistory(mb); |
316 | |
317 | if( OPTdebug & OPTemptybind){ |
318 | fprintf(stderr, "#EMPTYBIND optimizer exit\n" ); |
319 | fprintFunction(stderr, mb, 0, LIST_MAL_ALL); |
320 | } |
321 | return msg; |
322 | } |
323 | |