Changeset: 44006041fccf for MonetDB
URL: https://dev.monetdb.org/hg/MonetDB?cmd=changeset;node=44006041fccf
Modified Files:
        monetdb5/optimizer/opt_append.c
        sql/backends/monet5/sql.c
Branch: copybinary
Log Message:

Let sql.append_prep handle multiple columns at once


diffs (273 lines):

diff --git a/monetdb5/optimizer/opt_append.c b/monetdb5/optimizer/opt_append.c
--- a/monetdb5/optimizer/opt_append.c
+++ b/monetdb5/optimizer/opt_append.c
@@ -14,13 +14,21 @@
 #include "mal_builder.h"
 #include "opt_append.h"
 
-static str transform(MalBlkPtr mb, InstrPtr importTable);
+typedef struct parstate {
+       InstrPtr prep_stmt;
+} parstate;
+
+static str transform(parstate *state, MalBlkPtr mb, InstrPtr importTable);
+static int setup_append_prep(parstate *state, MalBlkPtr mb, InstrPtr old);
+
 
 str
 OPTparappendImplementation(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr 
pci)
 {
        str msg = MAL_SUCCEED;
        InstrPtr *old_mb_stmt = NULL;
+       parstate state = { NULL };
+
        (void)cntxt;
        (void)mb;
        (void)stk;
@@ -47,8 +55,11 @@ OPTparappendImplementation(Client cntxt,
        for (int i = 0; i < old_stop; i++) {
                InstrPtr p = old_mb_stmt[i];
                if (p->modname == sqlRef && p->fcnname == appendRef && 
isaBatType(getArgType(mb, p, 5))) {
-                               msg = transform(mb, p);
-                       } else {
+                       msg = transform(&state, mb, p);
+               } else {
+                       if (mayhaveSideEffects(cntxt, mb, p, false)) {
+                               state = (parstate) { NULL };
+                       }
                        pushInstruction(mb, p);
                }
                if (msg != MAL_SUCCEED)
@@ -62,14 +73,11 @@ end:
 }
 
 static str
-transform(MalBlkPtr mb, InstrPtr old)
+transform(parstate *state, MalBlkPtr mb, InstrPtr old)
 {
-
        // take the old instruction apart
        assert(old->retc == 1);
        assert(old->argc == 1 + 5);
-       int chain_out_var = getArg(old, 0);
-       int chain_in_var = getArg(old, 1);
        int sname_var = getArg(old, 2);
        int tname_var = getArg(old, 3);
        int cname_var = getArg(old, 4);
@@ -85,16 +93,8 @@ transform(MalBlkPtr mb, InstrPtr old)
                return MAL_SUCCEED;
        }
 
-       int cookie_var = newTmpVariable(mb, TYPE_ptr);
 
-       str append_prepRef = putName("append_prep");
-       InstrPtr p = newFcnCall(mb, sqlRef, append_prepRef);
-       setReturnArgument(p, chain_out_var);
-       pushReturn(mb, p, cookie_var);
-       pushArgument(mb, p, chain_in_var);
-       pushArgument(mb, p, sname_var);
-       pushArgument(mb, p, tname_var);
-       pushArgument(mb, p, cname_var);
+       int cookie_var = setup_append_prep(state, mb, old);
 
        str append_execRef = putName("append_exec");
        InstrPtr e = newFcnCall(mb, sqlRef, append_execRef);
@@ -105,3 +105,84 @@ transform(MalBlkPtr mb, InstrPtr old)
 
        return MAL_SUCCEED;
 }
+
+static int
+setup_append_prep(parstate *state, MalBlkPtr mb, InstrPtr old)
+{
+       // take the old instruction apart
+       assert(old->retc == 1);
+       assert(old->argc == 1 + 5);
+       int chain_out_var = getArg(old, 0);
+       int chain_in_var = getArg(old, 1);
+       int sname_var = getArg(old, 2);
+       int tname_var = getArg(old, 3);
+       int cname_var = getArg(old, 4);
+
+       // check if the state refers to a sql.append_prep statement that can be
+       // reused.
+       InstrPtr prep_stmt = NULL;
+       do {
+               if (state->prep_stmt == NULL)
+                       break;
+
+               InstrPtr prev = state->prep_stmt;
+
+               int existing_sname_var = getArg(prev, prev->retc + 1);
+               int existing_tname_var = getArg(prev, prev->retc + 2);
+
+               const char *existing_sname = getVarConstant(mb, 
existing_sname_var).val.sval;
+               const char *incoming_sname = getVarConstant(mb, 
sname_var).val.sval;
+               if (strcmp(existing_sname, incoming_sname) != 0)
+                       break;
+
+               const char *existing_tname = getVarConstant(mb, 
existing_tname_var).val.sval;
+               const char *incoming_tname = getVarConstant(mb, 
tname_var).val.sval;
+               if (strcmp(existing_tname, incoming_tname) != 0)
+                       break;
+
+               const char *incoming_cname = getVarConstant(mb, 
cname_var).val.sval;
+               int existing_cols = prev->retc - 1;
+               for (int i = prev->argc - existing_cols; i < prev->argc; i++) {
+                       int var = getArg(prev, i);
+                       const char *existing_cname = getVarConstant(mb, 
var).val.sval;
+                       if (strcmp(existing_cname, incoming_cname) == 0) {
+                               // We're not prepared for the complications 
that may arise
+                               // when there are multiple appends to the same 
column.
+                               // In particular we would have to track down 
where the previous
+                               // cookie is used and make sure we execute the 
next append
+                               // after that use.
+                               // This is unlikely to occur in practice, so 
instead we just start over.
+                               break;
+                       }
+               }
+
+               // It seems there is no objection to reusing the existing 
sql.append_prep.
+               prep_stmt = prev;
+       } while (0);
+
+       int cookie_var = newTmpVariable(mb, TYPE_ptr);
+       if (prep_stmt == NULL) {
+               str append_prepRef = putName("append_prep");
+               InstrPtr p = newFcnCall(mb, sqlRef, append_prepRef);
+               setReturnArgument(p, chain_out_var);
+               pushReturn(mb, p, cookie_var);
+               pushArgument(mb, p, chain_in_var);
+               pushArgument(mb, p, sname_var);
+               pushArgument(mb, p, tname_var);
+               pushArgument(mb, p, cname_var);
+               state->prep_stmt = p;
+       } else {
+               // Append to existing first, to ensure there is room
+               pushArgument(mb, prep_stmt, cname_var);
+               pushArgument(mb, prep_stmt, cookie_var);
+               // Now move the cookie_var to its proper location
+               for (int i = prep_stmt->argc - 1; i > prep_stmt->retc; i--)
+                       setArg(prep_stmt, i, getArg(prep_stmt, i - 1));
+               setArg(prep_stmt, prep_stmt->retc, cookie_var);
+               prep_stmt->retc += 1;
+               // Always use the chain_out of the latest sql_append:
+               setArg(prep_stmt, 0, chain_out_var);
+       }
+
+       return cookie_var;
+}
diff --git a/sql/backends/monet5/sql.c b/sql/backends/monet5/sql.c
--- a/sql/backends/monet5/sql.c
+++ b/sql/backends/monet5/sql.c
@@ -1764,34 +1764,34 @@ mvc_append_bat_wrap(Client cntxt, MalBlk
        return MAL_SUCCEED;
 }
 
-// tmp_1, cookie_1 := sql.append_prep(chain_0, s, t, c_1);
+// chain_out, cookie_1, ..., cookie_N := sql.append_prep(chain_in, s, t, c_1, 
... c_N);
 str
 mvc_append_prep_wrap(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
 {
        int *chain_out = getArgReference_int(stk, pci, 0);
-       ptr *cookie_out = getArgReference_ptr(stk, pci, 1);
-       int chain_in = *getArgReference_int(stk, pci, 2);
+       int chain_in = *getArgReference_int(stk, pci, pci->retc);
        mvc *m = NULL;
        str msg;
-       const char *sname = *getArgReference_str(stk, pci, 3);
-       const char *tname = *getArgReference_str(stk, pci, 4);
-       const char *cname = *getArgReference_str(stk, pci, 5);
+       const char *sname = *getArgReference_str(stk, pci, pci->retc + 1);
+       const char *tname = *getArgReference_str(stk, pci, pci->retc + 2);
        sql_schema *s;
        sql_table *t;
        sql_column *c;
 
+       // for N columns, we ought to have N + 1 return values and N + 3 
parameters.
+       int first_col = pci->retc + 3;
+       int first_ret = 1;
+       int ncolumns = pci->retc - first_ret;
+       if (pci->argc - first_col != ncolumns)
+               throw(SQL, "sql.append_prep",
+                       SQLSTATE(42000) "sql.append_prep inconsistent argument 
count argc=%d retc=%d", pci->argc, pci->retc);
+
        *chain_out = chain_in;
-       *cookie_out = NULL;
 
        if (strNil(sname))
-               throw(SQL, "sql.append_bat", SQLSTATE(42000) "sql.append_bat 
schema name is nil");
+               throw(SQL, "sql.append_prep", SQLSTATE(42000) "schema name is 
nil");
        if (strNil(tname))
-               throw(SQL, "sql.append_bat", SQLSTATE(42000) "sql.append_bat 
table name is nil");
-       if (strNil(cname))
-               throw(SQL, "sql.append_bat", SQLSTATE(42000) "sql.append_bat 
column name is nil");
-
-       if (cname[0] == '%')
-               throw(SQL, "sql.append_bat", SQLSTATE(42000) "sql.append_bat 
not intended for indices: %s.%s.%s", sname, tname, cname);
+               throw(SQL, "sql.append_prep", SQLSTATE(42000) "table name is 
nil");
 
        if ((msg = getSQLContext(cntxt, mb, &m, NULL)) != NULL)
                return msg;
@@ -1799,17 +1799,29 @@ mvc_append_prep_wrap(Client cntxt, MalBl
                return msg;
        s = mvc_bind_schema(m, sname);
        if (s == NULL)
-               throw(SQL, "sql.append_bat", SQLSTATE(3F000) "Schema missing 
%s", sname);
+               throw(SQL, "sql.append_prep", SQLSTATE(3F000) "Schema missing 
%s", sname);
        t = mvc_bind_table(m, s, tname);
        if (t == NULL)
-               throw(SQL, "sql.append_bat", SQLSTATE(42S02) "Table missing 
%s.%s", sname, tname);
-       c = mvc_bind_column(m, t, cname);
-       if (c == NULL)
-               throw(SQL, "sql.append_bat", SQLSTATE(42S02) "Column missing 
%s.%s.%s", sname, tname, cname);
-
-       void *cookie = store_funcs.append_col_prep(m->session->tr, c);
-
-       *cookie_out = cookie;
+               throw(SQL, "sql.append_prep", SQLSTATE(42S02) "Table missing 
%s.%s", sname, tname);
+
+       for (int i = 0; i < ncolumns; i++) {
+               const char *cname = *getArgReference_str(stk, pci, first_col + 
i);
+               ptr *cookie_out = getArgReference_ptr(stk, pci, first_ret + i);
+
+               if (strNil(cname))
+                       throw(SQL, "sql.append_prep", SQLSTATE(42000) "column 
name %d is nil", i);
+               if (cname[0] == '%')
+                       throw(SQL, "sql.append_prep", SQLSTATE(42000) 
"sql.append_prep not intended for indices: %s.%s.%s", sname, tname, cname);
+
+               c = mvc_bind_column(m, t, cname);
+               if (c == NULL)
+                       throw(SQL, "sql.append_prep", SQLSTATE(42S02) "Column 
missing %s.%s.%s", sname, tname, cname);
+
+               void *cookie = store_funcs.append_col_prep(m->session->tr, c);
+
+               *cookie_out = cookie;
+       }
+
        return MAL_SUCCEED;
 }
 
@@ -5392,7 +5404,10 @@ static mel_func sql_init_funcs[] = {
  pattern("sql", "grow", mvc_grow_wrap, false, "Resize the tid column of a 
declared table.", args(1,3, arg("",int),batarg("tid",oid),argany("",1))),
 
 
- pattern("sql", "append", mvc_append_wrap, false, "Append to the column 
tname.cname (possibly optimized to replace the insert bat of tname.cname. 
Returns sequence number for order dependence.", args(1,6, 
arg("",int),arg("mvc",int),arg("sname",str),arg("tname",str),arg("cname",str),argany("ins",0))),
+ pattern("sql", "append", mvc_append_wrap, false, "Append to the column 
tname.cname (possibly optimized to replace the insert bat of tname.cname. 
Returns sequence number for order dependence.",
+       args(1,6,
+               arg("",int),
+               
arg("mvc",int),arg("sname",str),arg("tname",str),arg("cname",str),argany("ins",0))),
 
  pattern("sql", "append_bat", mvc_append_bat_wrap, false, "Append to the 
column tname.cname (possibly optimized to replace the insert bat of 
tname.cname. Returns sequence number for order dependence.",
     args(1,6, 
arg("",int),arg("mvc",int),arg("sname",str),arg("tname",str),arg("cname",str),batargany("ins",1))),
@@ -5400,8 +5415,8 @@ static mel_func sql_init_funcs[] = {
  pattern("sql", "append_prep", mvc_append_prep_wrap, false,
        "Prepare to append to the column. Return new mvc state and cookie to 
pass to append_exec",
     args(2,6,
-               arg("",int),arg("",ptr),
-               
arg("mvc",int),arg("sname",str),arg("tname",str),arg("cname",str))),
+               arg("",int),vararg("",ptr),
+               
arg("mvc",int),arg("sname",str),arg("tname",str),vararg("cname",str))),
  pattern("sql", "append_exec", mvc_append_exec_wrap, false, "Perform the 
actual append",
     args(1,3,
                arg("",void),
_______________________________________________
checkin-list mailing list
checkin-list@monetdb.org
https://www.monetdb.org/mailman/listinfo/checkin-list

Reply via email to