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