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
46str
47OPTemptybindImplementation(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
48{
49 int i,j, actions =0, extras= 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 */
310wrapup:
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