Changeset: 9470fc0b626a for MonetDB
URL: https://dev.monetdb.org/hg/MonetDB/rev/9470fc0b626a
Modified Files:
        sql/backends/monet5/sql.c
        sql/backends/monet5/sql_cat.c
        sql/backends/monet5/sql_gencode.c
        sql/backends/monet5/sql_gencode.h
        sql/common/sql_types.c
        sql/include/sql_catalog.h
        sql/server/rel_psm.c
Branch: Jul2021
Log Message:

Fix for bug #7169

While replacing a UDF check if all parameters are equal, if so then there's no 
need to re-create it.

When a re-creation is needed, make sure it is done on the backend, otherwise a 
PLAN CREATE OR REPLACE query could make changes to the catalog, thus later 
conflict.

I did a small cleanup at the rel_create_func function, and it should be fine.


diffs (truncated from 587 to 300 lines):

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
@@ -5889,7 +5889,7 @@ static mel_func sql_init_funcs[] = {
  pattern("sqlcatalog", "drop_role", SQLdrop_role, false, "Catalog operation 
drop_role", args(0,2, arg("role",str),arg("action",int))),
  pattern("sqlcatalog", "drop_index", SQLdrop_index, false, "Catalog operation 
drop_index", args(0,3, arg("sname",str),arg("iname",str),arg("action",int))),
  pattern("sqlcatalog", "drop_function", SQLdrop_function, false, "Catalog 
operation drop_function", args(0,5, 
arg("sname",str),arg("fname",str),arg("fid",int),arg("type",int),arg("action",int))),
- pattern("sqlcatalog", "create_function", SQLcreate_function, false, "Catalog 
operation create_function", args(0,3, 
arg("sname",str),arg("fname",str),arg("fcn",ptr))),
+ pattern("sqlcatalog", "create_function", SQLcreate_function, false, "Catalog 
operation create_function", args(0,4, 
arg("sname",str),arg("fname",str),arg("fcn",ptr),arg("replace",int))),
  pattern("sqlcatalog", "create_trigger", SQLcreate_trigger, false, "Catalog 
operation create_trigger", args(0,10, 
arg("sname",str),arg("tname",str),arg("triggername",str),arg("time",int),arg("orientation",int),arg("event",int),arg("old",str),arg("new",str),arg("cond",str),arg("qry",str))),
  pattern("sqlcatalog", "drop_trigger", SQLdrop_trigger, false, "Catalog 
operation drop_trigger", args(0,3, 
arg("sname",str),arg("nme",str),arg("ifexists",int))),
  pattern("sqlcatalog", "alter_add_table", SQLalter_add_table, false, "Catalog 
operation alter_add_table", args(0,5, 
arg("sname",str),arg("mtnme",str),arg("psnme",str),arg("ptnme",str),arg("action",int))),
diff --git a/sql/backends/monet5/sql_cat.c b/sql/backends/monet5/sql_cat.c
--- a/sql/backends/monet5/sql_cat.c
+++ b/sql/backends/monet5/sql_cat.c
@@ -925,12 +925,23 @@ drop_func(mvc *sql, char *sname, char *n
        return MAL_SUCCEED;
 }
 
+static int
+args_cmp(sql_arg *a1, sql_arg *a2)
+{
+       if (a1->inout != a2->inout)
+               return -1;
+       if (strcmp(a1->name, a2->name) != 0)
+               return -1;
+       return subtype_cmp(&a1->type, &a2->type);
+}
+
 static char *
-create_func(mvc *sql, char *sname, char *fname, sql_func *f)
+create_func(mvc *sql, char *sname, char *fname, sql_func *f, int replace)
 {
        sql_func *nf;
+       sql_subfunc *sf;
        sql_schema *s = NULL;
-       int clientid = sql->clientid;
+       int clientid = sql->clientid, res = 0;
        char *F = NULL, *fn = NULL;
 
        FUNC_TYPE_STR(f->type, F, fn)
@@ -942,6 +953,62 @@ create_func(mvc *sql, char *sname, char 
                throw(SQL,"sql.create_func", SQLSTATE(42000) "CREATE %s: access 
denied for %s to schema '%s'", F, get_string_global_var(sql, "current_user"), 
s->base.name);
        if (strlen(fname) >= IDLENGTH)
                throw(SQL,"sql.create_func", SQLSTATE(42000) "CREATE %s: name 
'%s' too large for the backend", F, fname);
+
+       if (replace) {
+               list *tl = sa_list(sql->sa);
+               if (!list_empty(f->ops)) {
+                       for (node *n = f->ops->h ; n ; n = n->next ) {
+                               sql_arg *arg = n->data;
+
+                               list_append(tl, &arg->type);
+                       }
+               }
+
+               if ((sf = sql_bind_func_(sql, s->base.name, fname, tl, 
f->type)) != NULL) {
+                       sql_func *sff = sf->func;
+                       bool backend_ok = true;
+                       char *fimp = NULL;
+
+                       if (!sff->s || sff->system)
+                               throw(SQL,"sql.create_func", SQLSTATE(42000) 
"CREATE OR REPLACE %s: not allowed to replace system %s %s;", F, fn, 
sff->base.name);
+
+                       if (sff->lang == FUNC_LANG_MAL && 
!mal_function_find_implementation_address(&fimp, sql, sff)) {
+                               backend_ok = false;
+                               sql->session->status = 0; /* clean the error */
+                               sql->errstr[0] = '\0';
+                       }
+
+                       /* if all function parameters are the same, return */
+                       if (backend_ok && sff->lang == f->lang && sff->type == 
f->type &&
+                               sff->varres == f->varres && sff->vararg == 
f->vararg &&
+                               strcmp(sff->s->base.name, s->base.name) == 0 &&
+                               ((!sff->mod && !f->mod) || (sff->mod && f->mod 
&& strcmp(sff->mod, f->mod) == 0)) &&
+                               (sff->lang != FUNC_LANG_MAL || strcmp(fimp, 
f->imp) == 0) &&
+                               ((!sff->query && !f->query) || (sff->query && 
f->query && strcmp(sff->query, f->query) == 0)) &&
+                               list_cmp(sff->res, f->res, (fcmp) &args_cmp) == 
0 &&
+                               list_cmp(sff->ops, f->ops, (fcmp) &args_cmp) == 
0) {
+                               _DELETE(fimp);
+                               return MAL_SUCCEED;
+                       }
+                       _DELETE(fimp);
+
+                       if (mvc_check_dependency(sql, sff->base.id, 
!IS_PROC(sff) ? FUNC_DEPENDENCY : PROC_DEPENDENCY, NULL))
+                               throw(SQL,"sql.create_func", SQLSTATE(42000) 
"CREATE OR REPLACE %s: there are database objects dependent on %s %s;", F, fn, 
sff->base.name);
+                       switch ((res = mvc_drop_func(sql, s, sff, 0))) {
+                               case -1:
+                                       throw(SQL,"sql.create_func", 
SQLSTATE(HY013) MAL_MALLOC_FAIL);
+                               case -2:
+                               case -3:
+                                       throw(SQL,"sql.create_func", 
SQLSTATE(42000) "CREATE OR REPLACE %s: transaction conflict detected", F);
+                               default:
+                                       break;
+                       }
+               } else {
+                       sql->session->status = 0; /* if the function was not 
found clean the error */
+                       sql->errstr[0] = '\0';
+               }
+       }
+
        if (!(nf = mvc_create_func(sql, NULL, s, f->base.name, f->ops, f->res, 
f->type, f->lang, f->mod, f->imp, f->query, f->varres, f->vararg, f->system)))
                throw(SQL,"sql.create_func", SQLSTATE(42000) "CREATE %s: 
transaction conflict detected", F);
        switch (nf->lang) {
@@ -1740,9 +1807,10 @@ SQLcreate_function(Client cntxt, MalBlkP
        str sname = *getArgReference_str(stk, pci, 1);
        str fname = *getArgReference_str(stk, pci, 2);
        sql_func *f = *(sql_func **) getArgReference(stk, pci, 3);
+       int replace = *getArgReference_int(stk, pci, 4);
 
        initcontext();
-       msg = create_func(sql, sname, fname, f);
+       msg = create_func(sql, sname, fname, f, replace);
        return msg;
 }
 
diff --git a/sql/backends/monet5/sql_gencode.c 
b/sql/backends/monet5/sql_gencode.c
--- a/sql/backends/monet5/sql_gencode.c
+++ b/sql/backends/monet5/sql_gencode.c
@@ -1113,8 +1113,8 @@ backend_create_c_func(backend *be, sql_f
 }
 
 /* Parse the SQL query from the function, and extract the MAL function from 
the generated abstract syntax tree */
-static int
-mal_function_find_implementation_address(mvc *m, sql_func *f)
+int
+mal_function_find_implementation_address(str *res, mvc *m, sql_func *f)
 {
        mvc *o = m;
        buffer *b = NULL;
@@ -1131,9 +1131,12 @@ mal_function_find_implementation_address
        }
        m->type = Q_PARSE;
        m->user_id = m->role_id = USER_MONETDB;
-
-       m->session = sql_session_create(m->store, m->pa, 0);
-       if (!m->session) {
+       m->store = o->store;
+       if (!(m->pa = sa_create(NULL))) {
+               (void) sql_error(o, 02, SQLSTATE(HY013) MAL_MALLOC_FAIL);
+               goto bailout;
+       }
+       if (!(m->session = sql_session_create(m->store, m->pa, 0))) {
                (void) sql_error(o, 02, SQLSTATE(HY013) MAL_MALLOC_FAIL);
                goto bailout;
        }
@@ -1170,8 +1173,8 @@ mal_function_find_implementation_address
        assert(m->sym->token == SQL_CREATE_FUNC);
        l = m->sym->data.lval;
        ext_name = l->h->next->next->next->data.lval;
-       f->imp = sa_strdup(f->sa, qname_schema_object(ext_name)); /* found the 
implementation, set it */
-
+       if (!(*res = _STRDUP(qname_schema_object(ext_name)))) /* found the 
implementation, set it */
+               (void) sql_error(o, 02, SQLSTATE(HY013) MAL_MALLOC_FAIL);
 bailout:
        if (m) {
                bstream_destroy(m->scanner.rs);
@@ -1209,8 +1212,13 @@ backend_create_sql_func(backend *be, sql
        }
        /* nothing to do for internal and ready (not recompiling) functions, 
besides finding respective MAL implementation */
        if (!f->sql && (f->lang == FUNC_LANG_INT || f->lang == FUNC_LANG_MAL)) {
-               if (f->lang == FUNC_LANG_MAL && !f->imp && 
!mal_function_find_implementation_address(m, f))
-                       return -1;
+               if (f->lang == FUNC_LANG_MAL && !f->imp) {
+                       char *imp = NULL;
+                       if (!mal_function_find_implementation_address(&imp, m, 
f))
+                               return -1;
+                       f->imp = sa_strdup(f->sa, imp);
+                       _DELETE(imp);
+               }
                if (!backend_resolve_function(&clientid, f)) {
                        if (f->lang == FUNC_LANG_INT)
                                (void) sql_error(m, 02, SQLSTATE(HY005) 
"Implementation for function %s.%s not found", f->mod, f->imp);
diff --git a/sql/backends/monet5/sql_gencode.h 
b/sql/backends/monet5/sql_gencode.h
--- a/sql/backends/monet5/sql_gencode.h
+++ b/sql/backends/monet5/sql_gencode.h
@@ -23,6 +23,7 @@ extern int monet5_has_module(ptr M, char
 extern int monet5_resolve_function(ptr M, sql_func *f);
 extern int backend_create_func(backend *be, sql_func *f, list *restypes, list 
*ops);
 extern int backend_create_subfunc(backend *be, sql_subfunc *f, list *ops);
+extern int mal_function_find_implementation_address(str *res, mvc *m, sql_func 
*f);
 
 extern int monet5_create_relational_function(mvc *m, const char *mod, const 
char *name, sql_rel *rel, stmt *call, list *rel_ops, int inline_func);
 
diff --git a/sql/common/sql_types.c b/sql/common/sql_types.c
--- a/sql/common/sql_types.c
+++ b/sql/common/sql_types.c
@@ -665,7 +665,6 @@ sql_create_func_(sql_allocator *sa, cons
                t->res = list_append(SA_LIST(sa, (fdestroy) &arg_destroy), 
fres);
        } else
                t->res = NULL;
-       t->nr = list_length(funcs);
        t->sql = 0;
        t->lang = FUNC_LANG_INT;
        t->semantics = semantics;
diff --git a/sql/include/sql_catalog.h b/sql/include/sql_catalog.h
--- a/sql/include/sql_catalog.h
+++ b/sql/include/sql_catalog.h
@@ -485,7 +485,6 @@ typedef struct sql_func {
        sql_ftype type;
        list *ops;      /* param list */
        list *res;      /* list of results */
-       int nr;
        int sql;        /* 0 native implementation
                           1 sql
                           2 sql instantiated proc
diff --git a/sql/server/rel_psm.c b/sql/server/rel_psm.c
--- a/sql/server/rel_psm.c
+++ b/sql/server/rel_psm.c
@@ -760,7 +760,7 @@ create_type_list(mvc *sql, dlist *params
 }
 
 static sql_rel*
-rel_create_function(sql_allocator *sa, const char *sname, sql_func *f)
+rel_create_function(sql_allocator *sa, const char *sname, sql_func *f, int 
replace)
 {
        sql_rel *rel = rel_create(sa);
        list *exps = new_exp_list(sa);
@@ -771,6 +771,7 @@ rel_create_function(sql_allocator *sa, c
        if (f)
                append(exps, exp_atom_clob(sa, f->base.name));
        append(exps, exp_atom_ptr(sa, f));
+       append(exps, exp_atom_int(sa, replace));
        rel->l = NULL;
        rel->r = NULL;
        rel->op = op_ddl;
@@ -789,14 +790,14 @@ rel_create_func(sql_query *query, dlist 
        const char *sname = qname_schema(qname);
        sql_schema *s = cur_schema(sql);
        sql_func *f = NULL;
-       sql_subfunc *sf;
+       sql_subfunc *sf = NULL;
        dnode *n;
-       list *type_list = NULL, *restype = NULL;
+       list *type_list = NULL, *restype = NULL, *l = NULL;
        int instantiate = (sql->emode == m_instantiate);
        int deps = (sql->emode == m_deps);
        int create = (!instantiate && !deps);
        bit vararg = FALSE, union_err = 0;
-       char *F = NULL, *fn = NULL, is_func;
+       char *F = NULL, *fn = NULL, is_func, *q = QUERY(sql->scanner);
 
        if (res && res->token == SQL_TABLE) {
                if (type == F_FUNC)
@@ -830,58 +831,32 @@ rel_create_func(sql_query *query, dlist 
 
        if (sname && !(s = mvc_bind_schema(sql, sname)))
                return sql_error(sql, ERR_NOTFOUND, SQLSTATE(3F000) "CREATE %s: 
no such schema '%s'", F, sname);
+       if (create && !mvc_schema_privs(sql, s))
+               return sql_error(sql, 02, SQLSTATE(42000) "CREATE %s: 
insufficient privileges for user '%s' in schema '%s'", F,
+                                                get_string_global_var(sql, 
"current_user"), s->base.name); 
 
        type_list = create_type_list(sql, params, 1);
 
-       if ((sf = sql_bind_func_(sql, s->base.name, fname, type_list, type)) != 
NULL && create) {
-               if (replace) {
-                       int res = 0;
-                       sql_func *func = sf->func;
-                       if (!mvc_schema_privs(sql, s)) {
-                               list_destroy(type_list);
-                               return sql_error(sql, 02, SQLSTATE(42000) 
"CREATE OR REPLACE %s: access denied for %s to schema '%s'", F, 
get_string_global_var(sql, "current_user"), s->base.name);
+       if ((sf = sql_bind_func_(sql, s->base.name, fname, type_list, type)) != 
NULL && create && !replace) {
+               if (params) {
+                       char *arg_list = NULL;
+                       node *n;
+
+                       for (n = type_list->h; n; n = n->next) {
+                               char *tpe =  sql_subtype_string(sql->ta, 
(sql_subtype *) n->data);
+
+                               if (arg_list) {
+                                       arg_list = sa_message(sql->ta, "%s, 
%s", arg_list, tpe);
+                               } else {
+                                       arg_list = tpe;
+                               }
                        }
-                       if (mvc_check_dependency(sql, func->base.id, 
!IS_PROC(func) ? FUNC_DEPENDENCY : PROC_DEPENDENCY, NULL)) {
-                               list_destroy(type_list);
-                               return sql_error(sql, 02, SQLSTATE(42000) 
"CREATE OR REPLACE %s: there are database objects dependent on %s %s;", F, fn, 
func->base.name);
-                       }
-                       if (!func->s) {
-                               list_destroy(type_list);
-                               return sql_error(sql, 02, SQLSTATE(42000) 
"CREATE OR REPLACE %s: not allowed to replace system %s %s;", F, fn, 
func->base.name);
-                       }
-                       res = mvc_drop_func(sql, s, func, 0);
+                       (void)sql_error(sql, 02, SQLSTATE(42000) "CREATE %s: 
name '%s' (%s) already in use", F, fname, arg_list ? arg_list : "");
                        list_destroy(type_list);
-                       switch (res) {
-                               case -1:
-                                       return sql_error(sql, 02, 
SQLSTATE(HY013) MAL_MALLOC_FAIL);
-                               case -2:
-                               case -3:
-                                       return sql_error(sql, 02, 
SQLSTATE(42000) "CREATE OR REPLACE %s: transaction conflict detected", F);
-                               default:
-                                       break;
-                       }
-                       sf = NULL;
+                       return NULL;
                } else {
-                       if (params) {
-                               char *arg_list = NULL;
_______________________________________________
checkin-list mailing list
[email protected]
https://www.monetdb.org/mailman/listinfo/checkin-list

Reply via email to