Changeset: df7bf5ceb480 for MonetDB
URL: https://dev.monetdb.org/hg/MonetDB/rev/df7bf5ceb480
Modified Files:
        sql/server/rel_unnest.c
        sql/server/sql_parser.y
        sql/test/BugTracker-2025/Tests/All
Branch: default
Log Message:

merged with mar2025


diffs (287 lines):

diff --git a/sql/server/rel_unnest.c b/sql/server/rel_unnest.c
--- a/sql/server/rel_unnest.c
+++ b/sql/server/rel_unnest.c
@@ -2557,6 +2557,7 @@ rel_set_type(visitor *v, sql_rel *rel)
        if (!rel)
                return rel;
        if (is_project(rel->op) && rel->l) {
+               bool clear_hash = false;
                if (is_set(rel->op)) {
                        sql_rel *l = rel->l, *r = rel->r;
                        list *exps = l->exps;
@@ -2565,13 +2566,18 @@ rel_set_type(visitor *v, sql_rel *rel)
                                        sql_exp *e = n->data;
                                        sql_subtype *t = exp_subtype(e);
 
-                                       if (t && !t->type->localtype)
+                                       if (t && !t->type->localtype) {
                                                n->data = exp_set_type(v->sql, 
m->data, e);
+                                               clear_hash = true;
+                                       }
                                }
+                               if (clear_hash)
+                                       list_hash_clear(exps);
                                if (exps != r->exps)
                                        exps = r->exps;
                                else
                                        exps = NULL;
+                               clear_hash = false;
                        }
                } else if (is_munion(rel->op)) {
                        list *l = rel->l;
@@ -2582,9 +2588,13 @@ rel_set_type(visitor *v, sql_rel *rel)
                                        sql_exp *e = n->data;
                                        sql_subtype *t = exp_subtype(e);
 
-                                       if (t && !t->type->localtype)
+                                       if (t && !t->type->localtype) {
                                                n->data = exp_set_type(v->sql, 
m->data, e);
+                                               clear_hash = true;
+                                       }
                                }
+                               if (clear_hash)
+                                       list_hash_clear(exps);
                        }
                } else if ((is_simple_project(rel->op) || is_groupby(rel->op)) 
&& rel->l) {
                        list *exps = rel->exps;
@@ -2615,6 +2625,7 @@ rel_set_type(visitor *v, sql_rel *rel)
                                                                                
                int label = e->alias.label;
                                                                                
                n->data = e = exp_convert(v->sql, e, t, exp_subtype(te));
                                                                                
                e->alias.label = label;
+                                                                               
                list_hash_clear(l->exps);
                                                                                
                break;
                                                                                
        }
                                                                                
}
diff --git a/sql/server/sql_parser.y b/sql/server/sql_parser.y
--- a/sql/server/sql_parser.y
+++ b/sql/server/sql_parser.y
@@ -1033,7 +1033,7 @@ set_statement:
                { dlist *l = L();
                  sql_subtype t;
                sql_find_subtype(&t, "char", UTF8_strlen($5), 0 );
-               append_list(l, append_string(append_string(L(), sa_strdup(SA, 
"sys")), sa_strdup(SA, "current_user")));
+               append_list(l, append_string(append_string(L(), sa_strdup(SA, 
"sys")), sa_strdup(SA, "current_role")));
                append_symbol(l, _newAtomNode( _atom_string(&t, $5)) );
                $$ = _symbol_create_list( SQL_SET, l); }
   | set session_schema ident
@@ -1047,7 +1047,7 @@ set_statement:
                { dlist *l = L();
                  sql_subtype t;
                sql_find_subtype(&t, "char", UTF8_strlen($4), 0 );
-               append_list(l, append_string(append_string(L(), sa_strdup(SA, 
"sys")), sa_strdup(SA, "current_user")));
+               append_list(l, append_string(append_string(L(), sa_strdup(SA, 
"sys")), sa_strdup(SA, "current_role")));
                append_symbol(l, _newAtomNode( _atom_string(&t, $4)) );
                $$ = _symbol_create_list( SQL_SET, l); }
   | set ROLE ident
diff --git a/sql/server/sql_privileges.c b/sql/server/sql_privileges.c
--- a/sql/server/sql_privileges.c
+++ b/sql/server/sql_privileges.c
@@ -405,6 +405,24 @@ sql_create_auth_id(mvc *m, sqlid id, str
        return true;
 }
 
+static bool
+role_granting_privs(mvc *m, oid role_rid, sqlid role_id, sqlid grantor_id)
+{
+       sql_schema *sys = find_sql_schema(m->session->tr, "sys");
+       sql_table *auths = find_sql_table(m->session->tr, sys, "auths");
+       sql_column *auths_grantor = find_sql_column(auths, "grantor");
+       sqlid owner_id;
+       sqlstore *store = m->session->tr->store;
+
+       owner_id = store->table_api.column_find_sqlid(m->session->tr, 
auths_grantor, role_rid);
+       if (owner_id == grantor_id)
+               return true;
+       if (sql_privilege(m, grantor_id, role_id, PRIV_ROLE_ADMIN) == 
PRIV_ROLE_ADMIN)
+               return true;
+       /* check for grant rights in the privs table */
+       return false;
+}
+
 str
 sql_create_role(mvc *m, str auth, sqlid grantor)
 {
@@ -443,6 +461,16 @@ sql_drop_role(mvc *m, str auth)
        rid = store->table_api.column_find_row(tr, find_sql_column(auths, 
"name"), auth, NULL);
        if (is_oid_nil(rid))
                throw(SQL, "sql.drop_role", SQLSTATE(0P000) "DROP ROLE: no such 
role '%s'", auth);
+
+       if (!admin_privs(m->user_id) &&
+               !admin_privs(m->role_id) &&
+               !role_granting_privs(m, rid, role_id, m->user_id) &&
+               !role_granting_privs(m, rid, role_id, m->role_id))
+               throw(SQL,"sql.drop_role", SQLSTATE(0P000) "Insufficient 
privileges to drop role '%s'", auth);
+
+       if (!is_oid_nil(backend_find_user(m, auth)))
+               throw(SQL,"sql.drop_role", SQLSTATE(M1M05) "DROP: '%s' is a 
USER not a ROLE", auth);
+
        if ((log_res = store->table_api.table_delete(m->session->tr, auths, 
rid)) != LOG_OK)
                throw(SQL, "sql.drop_role", SQLSTATE(42000) "DROP ROLE: 
failed%s", log_res == LOG_CONFLICT ? " due to conflict with another 
transaction" : "");
 
@@ -555,21 +583,15 @@ execute_priv(mvc *m, sql_func *f)
 }
 
 static bool
-role_granting_privs(mvc *m, oid role_rid, sqlid role_id, sqlid grantor_id)
+sql_user_has_role(mvc *m, sqlid user_id, sqlid role_id)
 {
+       if (user_id == role_id)
+               return true;
        sql_schema *sys = find_sql_schema(m->session->tr, "sys");
-       sql_table *auths = find_sql_table(m->session->tr, sys, "auths");
-       sql_column *auths_grantor = find_sql_column(auths, "grantor");
-       sqlid owner_id;
+       sql_table *roles = find_sql_table(m->session->tr, sys, "user_role");
        sqlstore *store = m->session->tr->store;
-
-       owner_id = store->table_api.column_find_sqlid(m->session->tr, 
auths_grantor, role_rid);
-       if (owner_id == grantor_id)
-               return true;
-       if (sql_privilege(m, grantor_id, role_id, PRIV_ROLE_ADMIN) == 
PRIV_ROLE_ADMIN)
-               return true;
-       /* check for grant rights in the privs table */
-       return false;
+       oid rid = store->table_api.column_find_row(m->session->tr, 
find_sql_column(roles, "login_id"), &user_id, find_sql_column(roles, 
"role_id"), &role_id, NULL);
+       return (!is_oid_nil(rid));
 }
 
 char *
@@ -877,6 +899,7 @@ sql_drop_granted_users(mvc *sql, sqlid u
        if (!list_find(deleted_users, &user_id, (fcmp) &id_cmp)) {
                if (mvc_check_dependency(sql, user_id, OWNER_DEPENDENCY, NULL))
                        throw(SQL,"sql.drop_user",SQLSTATE(M1M05) "DROP USER: 
'%s' owns a schema", user);
+
                if (backend_drop_user(sql, user) == FALSE)
                        throw(SQL,"sql.drop_user",SQLSTATE(M0M27) "%s", 
sql->errstr);
 
@@ -947,10 +970,22 @@ sql_drop_granted_users(mvc *sql, sqlid u
 char *
 sql_drop_user(mvc *sql, char *user)
 {
-       sqlid user_id = sql_find_auth(sql, user);
        list *deleted = list_create(NULL);
        str msg = NULL;
 
+       sqlid user_id = sql_find_auth(sql, user);
+       sql_schema *sys = find_sql_schema(sql->session->tr, "sys");
+       sql_table *auths = find_sql_table(sql->session->tr, sys, "auths");
+       sql_trans *tr = sql->session->tr;
+       sqlstore *store = sql->session->tr->store;
+       oid rid = store->table_api.column_find_row(tr, find_sql_column(auths, 
"name"), user, NULL);
+
+       if (!admin_privs(sql->user_id) &&
+           !admin_privs(sql->role_id) &&
+           !role_granting_privs(sql, rid, user_id, sql->user_id) &&
+           !role_granting_privs(sql, rid, user_id, sql->role_id))
+               throw(SQL,"sql.drop_role", SQLSTATE(0P000) "Insufficient 
privileges to drop user '%s'", user);
+
        if (!deleted)
                throw(SQL, "sql.drop_user", SQLSTATE(HY013) MAL_MALLOC_FAIL);
        msg = sql_drop_granted_users(sql, user_id, user, deleted);
@@ -983,6 +1018,12 @@ sql_alter_user(mvc *sql, char *user, cha
 
        if (user != NULL && is_oid_nil(backend_find_user(sql, user)))
                throw(SQL,"sql.alter_user", SQLSTATE(42M32) "ALTER USER: no 
such user '%s'", user);
+
+       sqlid user_id = !user ? sql->user_id : sql_find_auth(sql, user);
+       bool has_role = role_id && sql_user_has_role(sql, user_id, role_id);
+       if (!admin_privs(sql->user_id) && !admin_privs(sql->role_id) && role_id 
&& !has_role)
+               throw(SQL,"sql.alter_user", SQLSTATE(M1M05) "User has not been 
granted the role '%s'", role);
+
        if (schema) {
                if (!(s = find_sql_schema(sql->session->tr, schema)))
                        throw(SQL,"sql.alter_user", SQLSTATE(3F000) "ALTER 
USER: no such schema '%s'", schema);
@@ -992,6 +1033,18 @@ sql_alter_user(mvc *sql, char *user, cha
        }
        if (backend_alter_user(sql, user, passwd, enc, schema_id, schema_path, 
oldpasswd, role_id, max_memory, max_workers) == FALSE)
                throw(SQL,"sql.alter_user", SQLSTATE(M0M27) "%s", sql->errstr);
+
+       /* the default role must explicitly granted to the new user */
+       if (role_id && !has_role) {
+               str r;
+               /* - we don't grant the default role WITH ADMIN OPTION hence 
admin=0
+                * - we should use sql->role_id instead of sql->user_id 
otherwise a user with high privs
+                * (e.g. sysadmin) might not be able to GRANT the DEFAULT ROLE 
(see sql_grant_role() impl)
+                */
+               if ((r = sql_grant_role(sql, user, role, sql->role_id, 0)))
+                       return r;
+       }
+
        return NULL;
 }
 
diff --git a/sql/test/BugTracker-2025/Tests/7662-drop-role-user.test 
b/sql/test/BugTracker-2025/Tests/7662-drop-role-user.test
new file mode 100644
--- /dev/null
+++ b/sql/test/BugTracker-2025/Tests/7662-drop-role-user.test
@@ -0,0 +1,19 @@
+statement ok
+CREATE USER test WITH PASSWORD 'test' NAME 'test'
+
+statement ok
+CREATE USER alice WITH PASSWORD 'alice' NAME 'alice' SCHEMA sys
+
+@connection(id=test, username=test, password=test)
+query T
+select CURRENT_ROLE
+----
+test
+
+@connection(id=test)
+statement error
+DROP ROLE alice
+
+@connection(id=test)
+statement error
+DROP USER alice
diff --git a/sql/test/BugTracker-2025/Tests/7663-alter-user-role.test 
b/sql/test/BugTracker-2025/Tests/7663-alter-user-role.test
new file mode 100644
--- /dev/null
+++ b/sql/test/BugTracker-2025/Tests/7663-alter-user-role.test
@@ -0,0 +1,14 @@
+statement ok
+CREATE USER test WITH PASSWORD 'test' NAME 'test'
+
+@connection(id=test, username=test, password=test)
+statement ok
+SET SCHEMA sys
+
+@connection(id=test)
+statement error 42000!CREATE TABLE: insufficient privileges for user 'test' in 
schema 'sys'
+CREATE TABLE x (y INT)
+
+@connection(id=test)
+statement error M1M05!User has not been granted the role 'monetdb'
+ALTER USER test DEFAULT ROLE monetdb;
diff --git a/sql/test/BugTracker-2025/Tests/7664-session-user.test 
b/sql/test/BugTracker-2025/Tests/7664-session-user.test
new file mode 100644
--- /dev/null
+++ b/sql/test/BugTracker-2025/Tests/7664-session-user.test
@@ -0,0 +1,14 @@
+statement ok
+CREATE USER test WITH PASSWORD 'test' NAME 'test';
+
+@connection(id=test, username=test, password=test)
+statement error M1M05!Insufficient privileges to change user 'monetdb'
+ALTER USER monetdb MAX_WORKERS 10
+
+@connection(id=test)
+statement error HY009!Role (monetdb) missing
+SET SESSION AUTHORIZATION monetdb;
+
+@connection(id=test)
+statement error M1M05!Insufficient privileges to change user 'monetdb'
+ALTER USER monetdb MAX_WORKERS 10;
diff --git a/sql/test/BugTracker-2025/Tests/All 
b/sql/test/BugTracker-2025/Tests/All
--- a/sql/test/BugTracker-2025/Tests/All
+++ b/sql/test/BugTracker-2025/Tests/All
@@ -24,6 +24,9 @@ 7654_non_monetdb_user_remote_table_exec
 7656_incorrect_error
 7659_trigger_crashes
 7661_trigger_crash
+7662-drop-role-user
+7663-alter-user-role
+7664-session-user
 7671-lag-over-empty-bat
 7674-rel_find_designated_index_crash
 7680-union-all
_______________________________________________
checkin-list mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to