LCOV - code coverage report
Current view: top level - monetdb5/optimizer - opt_bincopyfrom.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 90 95 94.7 %
Date: 2021-10-13 02:24:04 Functions: 3 3 100.0 %

          Line data    Source code
       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 - 2021 MonetDB B.V.
       7             :  */
       8             : 
       9             : /* author Joeri van Ruth
      10             :  * This optimizer replaces calls to sql.importTable with a series of calls to
      11             :  * sql.importColumn.
      12             :  */
      13             : #include "monetdb_config.h"
      14             : #include "mal_builder.h"
      15             : #include "opt_bincopyfrom.h"
      16             : 
      17             : static str transform(MalBlkPtr mb, InstrPtr importTable);
      18             : static int extract_column(MalBlkPtr mb, InstrPtr old, int idx, str proto_path, int proto_bat_var, int count_var, bool byteswap);
      19             : 
      20             : str
      21      356706 : OPTbincopyfromImplementation(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
      22             : {
      23             :         str msg = MAL_SUCCEED;
      24             :         InstrPtr *old_mb_stmt = NULL;
      25             :         int actions = 0;
      26             :         size_t old_ssize = 0;
      27             :         size_t old_stop = 0;
      28             : 
      29             :         (void)stk;
      30             : 
      31             :         int found_at = -1;
      32    23352420 :         for (int i = 0; i < mb->stop; i++) {
      33    22995767 :                 InstrPtr p = getInstrPtr(mb, i);
      34    22995767 :                 if (p->modname == sqlRef && p->fcnname == importTableRef) {
      35             :                         found_at = i;
      36             :                         break;
      37             :                 }
      38             :         }
      39      356706 :         if (found_at == -1)
      40             :                 // we didn't find a reason to modify the plan
      41      356653 :                 goto wrapup;
      42             : 
      43          53 :         old_mb_stmt = mb->stmt;
      44          53 :         old_ssize = mb->ssize;
      45          53 :         old_stop = mb->stop;
      46          53 :         if (newMalBlkStmt(mb, mb->stop + getInstrPtr(mb, found_at)->argc) < 0) 
      47           0 :                 throw(MAL, "optimizer.bincopyfrom", SQLSTATE(HY013) MAL_MALLOC_FAIL);
      48             : 
      49        2319 :         for (size_t i = 0; i < old_stop; i++) {
      50        2266 :                 InstrPtr p = old_mb_stmt[i];
      51        2266 :                 if (p->modname == sqlRef && p->fcnname == importTableRef) {
      52          53 :                         msg = transform(mb, p);
      53          53 :                         actions++;
      54             :                 } else {
      55        2213 :                         pushInstruction(mb, p);
      56             :                 }
      57        2266 :                 if (msg != MAL_SUCCEED)
      58           0 :                         goto end;
      59             :         }
      60             : 
      61          53 : end:
      62          53 :         if (old_mb_stmt) {
      63       11355 :                 for (size_t i = old_stop; i < old_ssize; i++)
      64       11302 :                         if (old_mb_stmt[i])
      65           0 :                                 pushInstruction(mb, old_mb_stmt[i]);
      66          53 :                 GDKfree(old_mb_stmt);
      67             :         }
      68             : 
      69             :         /* Defense line against incorrect plans */
      70          53 :         if (actions > 0 && msg == MAL_SUCCEED) {
      71             :                 if (!msg)
      72          53 :                         msg = chkTypes(cntxt->usermodule, mb, FALSE);
      73          53 :                 if (!msg)
      74          53 :                         msg = chkFlow(mb);
      75          53 :                 if (!msg)
      76          53 :                         msg = chkDeclarations(mb);
      77             :         }
      78             :         /* keep all actions taken as a post block comment */
      79           0 : wrapup:
      80             :         /* keep actions taken as a fake argument*/
      81      356706 :         (void) pushInt(mb,pci,actions);
      82      356706 :         return msg;
      83             : }
      84             : 
      85             : static str
      86          53 : transform(MalBlkPtr mb, InstrPtr old)
      87             : {
      88             :         // prototype: (bat1, .., batN) := sql.importTable(schema, table, onclient, path1 , .. ,pathN);
      89          53 :         int onclient_arg = *(int*)getVarValue(mb, getArg(old, old->retc + 2));
      90             :         bool onserver = !onclient_arg;
      91             :         bool onclient = !onserver;
      92          53 :         bool byteswap = *(bit*)getVarValue(mb, getArg(old, old->retc + 3));
      93             : 
      94             :         // In the following loop we pick a "prototype column".
      95             :         // This is always a column with a non-nil path and will be the first column for
      96             :         // which we emit code. We prefer a prototype column that is quick to import
      97             :         // because ON SERVER, all other columns can be loaded in parallel once we've
      98             :         // loaded the first one.
      99             :         //
     100             :         // Both ON SERVER and ON CLIENT, the prototype column is also used when emitting the
     101             :         // columns with a nil path, by projecting nil values over it.
     102             :         int prototype_idx = -1;
     103             :         int prototype_type = TYPE_any;
     104             :         str prototype_path = NULL;
     105         227 :         for (int i = 0; i < old->retc; i++) {
     106         174 :                 int var = getArg(old, i);
     107         174 :                 int var_type = getVarType(mb, var);
     108         174 :                 int tail_type = ATOMstorage(getBatType(var_type));
     109         174 :                 if (tail_type >= prototype_type)
     110         112 :                         continue;
     111          62 :                 int path_idx = old->retc + 4 + i;
     112          62 :                 int path_var = getArg(old, path_idx);
     113          62 :                 if (VALisnil(&getVarConstant(mb, path_var)))
     114           3 :                         continue;
     115             :                 // this is the best so far
     116             :                 prototype_idx = i;
     117             :                 prototype_type = tail_type;
     118          59 :                 prototype_path = (str)getVarValue(mb, path_var);
     119             :         }
     120          53 :         if (prototype_idx < 0)
     121           0 :                 return createException(MAL, "optimizer.bincopyfrom", SQLSTATE(42000) "all paths are nil");
     122             : 
     123             :         // Always emit the prototype column first
     124          53 :         int prototype_count_var = extract_column(mb, old, prototype_idx, NULL, -1, -1, byteswap);
     125          53 :         assert(mb->stop > 0);
     126          53 :         int prototype_bat_var = getArg(getInstrPtr(mb, mb->stop - 1), 0);
     127          53 :         assert(prototype_count_var == getArg(getInstrPtr(mb, mb->stop - 1), 1));
     128             : 
     129             :         // Then emit the rest of the columns
     130             : 
     131             :         int row_count_var = prototype_count_var;
     132         227 :         for (int i = 0; i < old->retc; i++) {
     133         174 :                 if (i == prototype_idx)
     134          53 :                         continue;
     135         121 :                 int new_row_count_var = extract_column(mb, old, i, prototype_path, prototype_bat_var, row_count_var, byteswap);
     136         121 :                 if (onclient)
     137             :                         row_count_var = new_row_count_var; // chain the importColumn statements
     138             :         }
     139             : 
     140          53 :         freeInstruction(old);
     141             : 
     142          53 :         return MAL_SUCCEED;
     143             : }
     144             : 
     145             : static int
     146         174 : extract_column(MalBlkPtr mb, InstrPtr old, int idx, str proto_path, int proto_bat_var, int count_var, bool byteswap)
     147             : {
     148         174 :         int var = getArg(old, idx);
     149         174 :         int var_type = getVarType(mb, var);
     150             : 
     151             :         // The sql.importColumn operator takes a 'method' string to determine how to
     152             :         // load the data. This leaves the door open to have multiple loaders for the
     153             :         // same backend type, for example nul- and newline terminated strings.
     154             :         // For the time being we just use the name of the storage type as the method
     155             :         // name.
     156         174 :         str method = ATOMname(getBatType(var_type));
     157             : 
     158         174 :         int onclient = *(int*)getVarValue(mb, getArg(old, old->retc + 2));
     159             : 
     160         174 :         int path_idx = old->retc + 4 + idx;
     161         174 :         int path_var = getArg(old, path_idx);
     162         174 :         str path = (str)getVarValue(mb, path_var);
     163             : 
     164         174 :         if (!strNil(path)) {
     165         167 :                 if (proto_path != NULL && strcmp(proto_path, path) == 0) {
     166             :                         // Same data as in the prototype column so reuse that var
     167          11 :                         InstrPtr p = newInstructionArgs(mb, NULL, NULL, 2);
     168          11 :                         p = pushArgument(mb, p, proto_bat_var);
     169          11 :                         setReturnArgument(p, old->argv[idx]);
     170          11 :                         pushInstruction(mb, p);
     171          11 :                         return count_var;
     172             :                 } else {
     173             :                         // Emit a new importColumn call
     174         156 :                         InstrPtr p = newFcnCall(mb, sqlRef, importColumnRef);
     175         156 :                         setReturnArgument(p, old->argv[idx]);
     176         156 :                         int new_count_var = newTmpVariable(mb, TYPE_oid);
     177         156 :                         pushReturn(mb, p, new_count_var);
     178         156 :                         p = pushStr(mb, p, method);
     179         156 :                         p = pushBit(mb, p, byteswap);
     180         156 :                         p = pushStr(mb, p, path);
     181         156 :                         p = pushInt(mb, p, onclient);
     182         156 :                         if (count_var < 0)
     183          53 :                                 p = pushOid(mb, p, 0);
     184             :                         else
     185         103 :                                 p = pushArgument(mb, p, count_var);
     186         156 :                         return new_count_var;
     187             :                 }
     188             :         } else {
     189             :                 // create an empty column by projecting the prototype
     190           7 :                 InstrPtr p = newFcnCall(mb, algebraRef, projectRef);
     191           7 :                 setReturnArgument(p, old->argv[idx]);
     192           7 :                 p = pushArgument(mb, p, proto_bat_var);
     193           7 :                 int proto_bat_type = getVarType(mb, var);
     194           7 :                 int proto_elem_type = getBatType(proto_bat_type);
     195           7 :                 p = pushNil(mb, p, proto_elem_type);
     196           7 :                 return count_var;
     197             :         }
     198             : }

Generated by: LCOV version 1.14