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