| 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 | |