summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/backend/commands/user.c2
-rw-r--r--src/test/regress/expected/roleattributes.out236
-rw-r--r--src/test/regress/parallel_schedule2
-rw-r--r--src/test/regress/serial_schedule1
-rw-r--r--src/test/regress/sql/roleattributes.sql85
5 files changed, 324 insertions, 2 deletions
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index afbf2763be..295e0b0fdf 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -493,7 +493,7 @@ AlterRole(AlterRoleStmt *stmt)
char *validUntil = NULL; /* time the login is valid until */
Datum validUntil_datum; /* same, as timestamptz Datum */
bool validUntil_null;
- bool bypassrls = -1;
+ int bypassrls = -1;
DefElem *dpassword = NULL;
DefElem *dissuper = NULL;
DefElem *dinherit = NULL;
diff --git a/src/test/regress/expected/roleattributes.out b/src/test/regress/expected/roleattributes.out
new file mode 100644
index 0000000000..b7fded9355
--- /dev/null
+++ b/src/test/regress/expected/roleattributes.out
@@ -0,0 +1,236 @@
+-- default for superuser is false
+CREATE ROLE test_def_superuser;
+SELECT * FROM pg_authid WHERE rolname = 'test_def_superuser';
+ rolname | rolsuper | rolinherit | rolcreaterole | rolcreatedb | rolcanlogin | rolreplication | rolbypassrls | rolconnlimit | rolpassword | rolvaliduntil
+--------------------+----------+------------+---------------+-------------+-------------+----------------+--------------+--------------+-------------+---------------
+ test_def_superuser | f | t | f | f | f | f | f | -1 | |
+(1 row)
+
+CREATE ROLE test_superuser WITH SUPERUSER;
+SELECT * FROM pg_authid WHERE rolname = 'test_superuser';
+ rolname | rolsuper | rolinherit | rolcreaterole | rolcreatedb | rolcanlogin | rolreplication | rolbypassrls | rolconnlimit | rolpassword | rolvaliduntil
+----------------+----------+------------+---------------+-------------+-------------+----------------+--------------+--------------+-------------+---------------
+ test_superuser | t | t | f | f | f | f | f | -1 | |
+(1 row)
+
+ALTER ROLE test_superuser WITH NOSUPERUSER;
+SELECT * FROM pg_authid WHERE rolname = 'test_superuser';
+ rolname | rolsuper | rolinherit | rolcreaterole | rolcreatedb | rolcanlogin | rolreplication | rolbypassrls | rolconnlimit | rolpassword | rolvaliduntil
+----------------+----------+------------+---------------+-------------+-------------+----------------+--------------+--------------+-------------+---------------
+ test_superuser | f | t | f | f | f | f | f | -1 | |
+(1 row)
+
+ALTER ROLE test_superuser WITH SUPERUSER;
+SELECT * FROM pg_authid WHERE rolname = 'test_superuser';
+ rolname | rolsuper | rolinherit | rolcreaterole | rolcreatedb | rolcanlogin | rolreplication | rolbypassrls | rolconnlimit | rolpassword | rolvaliduntil
+----------------+----------+------------+---------------+-------------+-------------+----------------+--------------+--------------+-------------+---------------
+ test_superuser | t | t | f | f | f | f | f | -1 | |
+(1 row)
+
+-- default for inherit is true
+CREATE ROLE test_def_inherit;
+SELECT * FROM pg_authid WHERE rolname = 'test_def_inherit';
+ rolname | rolsuper | rolinherit | rolcreaterole | rolcreatedb | rolcanlogin | rolreplication | rolbypassrls | rolconnlimit | rolpassword | rolvaliduntil
+------------------+----------+------------+---------------+-------------+-------------+----------------+--------------+--------------+-------------+---------------
+ test_def_inherit | f | t | f | f | f | f | f | -1 | |
+(1 row)
+
+CREATE ROLE test_inherit WITH NOINHERIT;
+SELECT * FROM pg_authid WHERE rolname = 'test_inherit';
+ rolname | rolsuper | rolinherit | rolcreaterole | rolcreatedb | rolcanlogin | rolreplication | rolbypassrls | rolconnlimit | rolpassword | rolvaliduntil
+--------------+----------+------------+---------------+-------------+-------------+----------------+--------------+--------------+-------------+---------------
+ test_inherit | f | f | f | f | f | f | f | -1 | |
+(1 row)
+
+ALTER ROLE test_inherit WITH INHERIT;
+SELECT * FROM pg_authid WHERE rolname = 'test_inherit';
+ rolname | rolsuper | rolinherit | rolcreaterole | rolcreatedb | rolcanlogin | rolreplication | rolbypassrls | rolconnlimit | rolpassword | rolvaliduntil
+--------------+----------+------------+---------------+-------------+-------------+----------------+--------------+--------------+-------------+---------------
+ test_inherit | f | t | f | f | f | f | f | -1 | |
+(1 row)
+
+ALTER ROLE test_inherit WITH NOINHERIT;
+SELECT * FROM pg_authid WHERE rolname = 'test_inherit';
+ rolname | rolsuper | rolinherit | rolcreaterole | rolcreatedb | rolcanlogin | rolreplication | rolbypassrls | rolconnlimit | rolpassword | rolvaliduntil
+--------------+----------+------------+---------------+-------------+-------------+----------------+--------------+--------------+-------------+---------------
+ test_inherit | f | f | f | f | f | f | f | -1 | |
+(1 row)
+
+-- default for create role is false
+CREATE ROLE test_def_createrole;
+SELECT * FROM pg_authid WHERE rolname = 'test_def_createrole';
+ rolname | rolsuper | rolinherit | rolcreaterole | rolcreatedb | rolcanlogin | rolreplication | rolbypassrls | rolconnlimit | rolpassword | rolvaliduntil
+---------------------+----------+------------+---------------+-------------+-------------+----------------+--------------+--------------+-------------+---------------
+ test_def_createrole | f | t | f | f | f | f | f | -1 | |
+(1 row)
+
+CREATE ROLE test_createrole WITH CREATEROLE;
+SELECT * FROM pg_authid WHERE rolname = 'test_createrole';
+ rolname | rolsuper | rolinherit | rolcreaterole | rolcreatedb | rolcanlogin | rolreplication | rolbypassrls | rolconnlimit | rolpassword | rolvaliduntil
+-----------------+----------+------------+---------------+-------------+-------------+----------------+--------------+--------------+-------------+---------------
+ test_createrole | f | t | t | f | f | f | f | -1 | |
+(1 row)
+
+ALTER ROLE test_createrole WITH NOCREATEROLE;
+SELECT * FROM pg_authid WHERE rolname = 'test_createrole';
+ rolname | rolsuper | rolinherit | rolcreaterole | rolcreatedb | rolcanlogin | rolreplication | rolbypassrls | rolconnlimit | rolpassword | rolvaliduntil
+-----------------+----------+------------+---------------+-------------+-------------+----------------+--------------+--------------+-------------+---------------
+ test_createrole | f | t | f | f | f | f | f | -1 | |
+(1 row)
+
+ALTER ROLE test_createrole WITH CREATEROLE;
+SELECT * FROM pg_authid WHERE rolname = 'test_createrole';
+ rolname | rolsuper | rolinherit | rolcreaterole | rolcreatedb | rolcanlogin | rolreplication | rolbypassrls | rolconnlimit | rolpassword | rolvaliduntil
+-----------------+----------+------------+---------------+-------------+-------------+----------------+--------------+--------------+-------------+---------------
+ test_createrole | f | t | t | f | f | f | f | -1 | |
+(1 row)
+
+-- default for create database is false
+CREATE ROLE test_def_createdb;
+SELECT * FROM pg_authid WHERE rolname = 'test_def_createdb';
+ rolname | rolsuper | rolinherit | rolcreaterole | rolcreatedb | rolcanlogin | rolreplication | rolbypassrls | rolconnlimit | rolpassword | rolvaliduntil
+-------------------+----------+------------+---------------+-------------+-------------+----------------+--------------+--------------+-------------+---------------
+ test_def_createdb | f | t | f | f | f | f | f | -1 | |
+(1 row)
+
+CREATE ROLE test_createdb WITH CREATEDB;
+SELECT * FROM pg_authid WHERE rolname = 'test_createdb';
+ rolname | rolsuper | rolinherit | rolcreaterole | rolcreatedb | rolcanlogin | rolreplication | rolbypassrls | rolconnlimit | rolpassword | rolvaliduntil
+---------------+----------+------------+---------------+-------------+-------------+----------------+--------------+--------------+-------------+---------------
+ test_createdb | f | t | f | t | f | f | f | -1 | |
+(1 row)
+
+ALTER ROLE test_createdb WITH NOCREATEDB;
+SELECT * FROM pg_authid WHERE rolname = 'test_createdb';
+ rolname | rolsuper | rolinherit | rolcreaterole | rolcreatedb | rolcanlogin | rolreplication | rolbypassrls | rolconnlimit | rolpassword | rolvaliduntil
+---------------+----------+------------+---------------+-------------+-------------+----------------+--------------+--------------+-------------+---------------
+ test_createdb | f | t | f | f | f | f | f | -1 | |
+(1 row)
+
+ALTER ROLE test_createdb WITH CREATEDB;
+SELECT * FROM pg_authid WHERE rolname = 'test_createdb';
+ rolname | rolsuper | rolinherit | rolcreaterole | rolcreatedb | rolcanlogin | rolreplication | rolbypassrls | rolconnlimit | rolpassword | rolvaliduntil
+---------------+----------+------------+---------------+-------------+-------------+----------------+--------------+--------------+-------------+---------------
+ test_createdb | f | t | f | t | f | f | f | -1 | |
+(1 row)
+
+-- default for can login is false for role
+CREATE ROLE test_def_role_canlogin;
+SELECT * FROM pg_authid WHERE rolname = 'test_def_role_canlogin';
+ rolname | rolsuper | rolinherit | rolcreaterole | rolcreatedb | rolcanlogin | rolreplication | rolbypassrls | rolconnlimit | rolpassword | rolvaliduntil
+------------------------+----------+------------+---------------+-------------+-------------+----------------+--------------+--------------+-------------+---------------
+ test_def_role_canlogin | f | t | f | f | f | f | f | -1 | |
+(1 row)
+
+CREATE ROLE test_role_canlogin WITH LOGIN;
+SELECT * FROM pg_authid WHERE rolname = 'test_role_canlogin';
+ rolname | rolsuper | rolinherit | rolcreaterole | rolcreatedb | rolcanlogin | rolreplication | rolbypassrls | rolconnlimit | rolpassword | rolvaliduntil
+--------------------+----------+------------+---------------+-------------+-------------+----------------+--------------+--------------+-------------+---------------
+ test_role_canlogin | f | t | f | f | t | f | f | -1 | |
+(1 row)
+
+ALTER ROLE test_role_canlogin WITH NOLOGIN;
+SELECT * FROM pg_authid WHERE rolname = 'test_role_canlogin';
+ rolname | rolsuper | rolinherit | rolcreaterole | rolcreatedb | rolcanlogin | rolreplication | rolbypassrls | rolconnlimit | rolpassword | rolvaliduntil
+--------------------+----------+------------+---------------+-------------+-------------+----------------+--------------+--------------+-------------+---------------
+ test_role_canlogin | f | t | f | f | f | f | f | -1 | |
+(1 row)
+
+ALTER ROLE test_role_canlogin WITH LOGIN;
+SELECT * FROM pg_authid WHERE rolname = 'test_role_canlogin';
+ rolname | rolsuper | rolinherit | rolcreaterole | rolcreatedb | rolcanlogin | rolreplication | rolbypassrls | rolconnlimit | rolpassword | rolvaliduntil
+--------------------+----------+------------+---------------+-------------+-------------+----------------+--------------+--------------+-------------+---------------
+ test_role_canlogin | f | t | f | f | t | f | f | -1 | |
+(1 row)
+
+-- default for can login is true for user
+CREATE USER test_def_user_canlogin;
+SELECT * FROM pg_authid WHERE rolname = 'test_def_user_canlogin';
+ rolname | rolsuper | rolinherit | rolcreaterole | rolcreatedb | rolcanlogin | rolreplication | rolbypassrls | rolconnlimit | rolpassword | rolvaliduntil
+------------------------+----------+------------+---------------+-------------+-------------+----------------+--------------+--------------+-------------+---------------
+ test_def_user_canlogin | f | t | f | f | t | f | f | -1 | |
+(1 row)
+
+CREATE USER test_user_canlogin WITH NOLOGIN;
+SELECT * FROM pg_authid WHERE rolname = 'test_user_canlogin';
+ rolname | rolsuper | rolinherit | rolcreaterole | rolcreatedb | rolcanlogin | rolreplication | rolbypassrls | rolconnlimit | rolpassword | rolvaliduntil
+--------------------+----------+------------+---------------+-------------+-------------+----------------+--------------+--------------+-------------+---------------
+ test_user_canlogin | f | t | f | f | f | f | f | -1 | |
+(1 row)
+
+ALTER USER test_user_canlogin WITH LOGIN;
+SELECT * FROM pg_authid WHERE rolname = 'test_user_canlogin';
+ rolname | rolsuper | rolinherit | rolcreaterole | rolcreatedb | rolcanlogin | rolreplication | rolbypassrls | rolconnlimit | rolpassword | rolvaliduntil
+--------------------+----------+------------+---------------+-------------+-------------+----------------+--------------+--------------+-------------+---------------
+ test_user_canlogin | f | t | f | f | t | f | f | -1 | |
+(1 row)
+
+ALTER USER test_user_canlogin WITH NOLOGIN;
+SELECT * FROM pg_authid WHERE rolname = 'test_user_canlogin';
+ rolname | rolsuper | rolinherit | rolcreaterole | rolcreatedb | rolcanlogin | rolreplication | rolbypassrls | rolconnlimit | rolpassword | rolvaliduntil
+--------------------+----------+------------+---------------+-------------+-------------+----------------+--------------+--------------+-------------+---------------
+ test_user_canlogin | f | t | f | f | f | f | f | -1 | |
+(1 row)
+
+-- default for replication is false
+CREATE ROLE test_def_replication;
+SELECT * FROM pg_authid WHERE rolname = 'test_def_replication';
+ rolname | rolsuper | rolinherit | rolcreaterole | rolcreatedb | rolcanlogin | rolreplication | rolbypassrls | rolconnlimit | rolpassword | rolvaliduntil
+----------------------+----------+------------+---------------+-------------+-------------+----------------+--------------+--------------+-------------+---------------
+ test_def_replication | f | t | f | f | f | f | f | -1 | |
+(1 row)
+
+CREATE ROLE test_replication WITH REPLICATION;
+SELECT * FROM pg_authid WHERE rolname = 'test_replication';
+ rolname | rolsuper | rolinherit | rolcreaterole | rolcreatedb | rolcanlogin | rolreplication | rolbypassrls | rolconnlimit | rolpassword | rolvaliduntil
+------------------+----------+------------+---------------+-------------+-------------+----------------+--------------+--------------+-------------+---------------
+ test_replication | f | t | f | f | f | t | f | -1 | |
+(1 row)
+
+ALTER ROLE test_replication WITH NOREPLICATION;
+SELECT * FROM pg_authid WHERE rolname = 'test_replication';
+ rolname | rolsuper | rolinherit | rolcreaterole | rolcreatedb | rolcanlogin | rolreplication | rolbypassrls | rolconnlimit | rolpassword | rolvaliduntil
+------------------+----------+------------+---------------+-------------+-------------+----------------+--------------+--------------+-------------+---------------
+ test_replication | f | t | f | f | f | f | f | -1 | |
+(1 row)
+
+ALTER ROLE test_replication WITH REPLICATION;
+SELECT * FROM pg_authid WHERE rolname = 'test_replication';
+ rolname | rolsuper | rolinherit | rolcreaterole | rolcreatedb | rolcanlogin | rolreplication | rolbypassrls | rolconnlimit | rolpassword | rolvaliduntil
+------------------+----------+------------+---------------+-------------+-------------+----------------+--------------+--------------+-------------+---------------
+ test_replication | f | t | f | f | f | t | f | -1 | |
+(1 row)
+
+-- default for bypassrls is false
+CREATE ROLE test_def_bypassrls;
+SELECT * FROM pg_authid WHERE rolname = 'test_def_bypassrls';
+ rolname | rolsuper | rolinherit | rolcreaterole | rolcreatedb | rolcanlogin | rolreplication | rolbypassrls | rolconnlimit | rolpassword | rolvaliduntil
+--------------------+----------+------------+---------------+-------------+-------------+----------------+--------------+--------------+-------------+---------------
+ test_def_bypassrls | f | t | f | f | f | f | f | -1 | |
+(1 row)
+
+CREATE ROLE test_bypassrls WITH BYPASSRLS;
+SELECT * FROM pg_authid WHERE rolname = 'test_bypassrls';
+ rolname | rolsuper | rolinherit | rolcreaterole | rolcreatedb | rolcanlogin | rolreplication | rolbypassrls | rolconnlimit | rolpassword | rolvaliduntil
+----------------+----------+------------+---------------+-------------+-------------+----------------+--------------+--------------+-------------+---------------
+ test_bypassrls | f | t | f | f | f | f | t | -1 | |
+(1 row)
+
+ALTER ROLE test_bypassrls WITH NOBYPASSRLS;
+SELECT * FROM pg_authid WHERE rolname = 'test_bypassrls';
+ rolname | rolsuper | rolinherit | rolcreaterole | rolcreatedb | rolcanlogin | rolreplication | rolbypassrls | rolconnlimit | rolpassword | rolvaliduntil
+----------------+----------+------------+---------------+-------------+-------------+----------------+--------------+--------------+-------------+---------------
+ test_bypassrls | f | t | f | f | f | f | f | -1 | |
+(1 row)
+
+ALTER ROLE test_bypassrls WITH BYPASSRLS;
+SELECT * FROM pg_authid WHERE rolname = 'test_bypassrls';
+ rolname | rolsuper | rolinherit | rolcreaterole | rolcreatedb | rolcanlogin | rolreplication | rolbypassrls | rolconnlimit | rolpassword | rolvaliduntil
+----------------+----------+------------+---------------+-------------+-------------+----------------+--------------+--------------+-------------+---------------
+ test_bypassrls | f | t | f | f | f | f | t | -1 | |
+(1 row)
+
+-- remove the one role with LOGIN rights
+DROP ROLE test_role_canlogin;
+-- other roles not removed to test pg_dumpall role dump through
+-- pg_upgrade
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 4df15def6e..6fc5d1e632 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -60,7 +60,7 @@ test: create_index create_view
# ----------
# Another group of parallel tests
# ----------
-test: create_aggregate create_function_3 create_cast constraints triggers inherit create_table_like typed_table vacuum drop_if_exists updatable_views rolenames
+test: create_aggregate create_function_3 create_cast constraints triggers inherit create_table_like typed_table vacuum drop_if_exists updatable_views rolenames roleattributes
# ----------
# sanity_check does a vacuum, affecting the sort order of SELECT *
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 15d74d4e6e..2ae51cf078 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -73,6 +73,7 @@ test: vacuum
test: drop_if_exists
test: updatable_views
test: rolenames
+test: roleattributes
test: sanity_check
test: errors
test: select
diff --git a/src/test/regress/sql/roleattributes.sql b/src/test/regress/sql/roleattributes.sql
new file mode 100644
index 0000000000..9f9dd9cf81
--- /dev/null
+++ b/src/test/regress/sql/roleattributes.sql
@@ -0,0 +1,85 @@
+-- default for superuser is false
+CREATE ROLE test_def_superuser;
+SELECT * FROM pg_authid WHERE rolname = 'test_def_superuser';
+CREATE ROLE test_superuser WITH SUPERUSER;
+SELECT * FROM pg_authid WHERE rolname = 'test_superuser';
+ALTER ROLE test_superuser WITH NOSUPERUSER;
+SELECT * FROM pg_authid WHERE rolname = 'test_superuser';
+ALTER ROLE test_superuser WITH SUPERUSER;
+SELECT * FROM pg_authid WHERE rolname = 'test_superuser';
+
+-- default for inherit is true
+CREATE ROLE test_def_inherit;
+SELECT * FROM pg_authid WHERE rolname = 'test_def_inherit';
+CREATE ROLE test_inherit WITH NOINHERIT;
+SELECT * FROM pg_authid WHERE rolname = 'test_inherit';
+ALTER ROLE test_inherit WITH INHERIT;
+SELECT * FROM pg_authid WHERE rolname = 'test_inherit';
+ALTER ROLE test_inherit WITH NOINHERIT;
+SELECT * FROM pg_authid WHERE rolname = 'test_inherit';
+
+-- default for create role is false
+CREATE ROLE test_def_createrole;
+SELECT * FROM pg_authid WHERE rolname = 'test_def_createrole';
+CREATE ROLE test_createrole WITH CREATEROLE;
+SELECT * FROM pg_authid WHERE rolname = 'test_createrole';
+ALTER ROLE test_createrole WITH NOCREATEROLE;
+SELECT * FROM pg_authid WHERE rolname = 'test_createrole';
+ALTER ROLE test_createrole WITH CREATEROLE;
+SELECT * FROM pg_authid WHERE rolname = 'test_createrole';
+
+-- default for create database is false
+CREATE ROLE test_def_createdb;
+SELECT * FROM pg_authid WHERE rolname = 'test_def_createdb';
+CREATE ROLE test_createdb WITH CREATEDB;
+SELECT * FROM pg_authid WHERE rolname = 'test_createdb';
+ALTER ROLE test_createdb WITH NOCREATEDB;
+SELECT * FROM pg_authid WHERE rolname = 'test_createdb';
+ALTER ROLE test_createdb WITH CREATEDB;
+SELECT * FROM pg_authid WHERE rolname = 'test_createdb';
+
+-- default for can login is false for role
+CREATE ROLE test_def_role_canlogin;
+SELECT * FROM pg_authid WHERE rolname = 'test_def_role_canlogin';
+CREATE ROLE test_role_canlogin WITH LOGIN;
+SELECT * FROM pg_authid WHERE rolname = 'test_role_canlogin';
+ALTER ROLE test_role_canlogin WITH NOLOGIN;
+SELECT * FROM pg_authid WHERE rolname = 'test_role_canlogin';
+ALTER ROLE test_role_canlogin WITH LOGIN;
+SELECT * FROM pg_authid WHERE rolname = 'test_role_canlogin';
+
+-- default for can login is true for user
+CREATE USER test_def_user_canlogin;
+SELECT * FROM pg_authid WHERE rolname = 'test_def_user_canlogin';
+CREATE USER test_user_canlogin WITH NOLOGIN;
+SELECT * FROM pg_authid WHERE rolname = 'test_user_canlogin';
+ALTER USER test_user_canlogin WITH LOGIN;
+SELECT * FROM pg_authid WHERE rolname = 'test_user_canlogin';
+ALTER USER test_user_canlogin WITH NOLOGIN;
+SELECT * FROM pg_authid WHERE rolname = 'test_user_canlogin';
+
+-- default for replication is false
+CREATE ROLE test_def_replication;
+SELECT * FROM pg_authid WHERE rolname = 'test_def_replication';
+CREATE ROLE test_replication WITH REPLICATION;
+SELECT * FROM pg_authid WHERE rolname = 'test_replication';
+ALTER ROLE test_replication WITH NOREPLICATION;
+SELECT * FROM pg_authid WHERE rolname = 'test_replication';
+ALTER ROLE test_replication WITH REPLICATION;
+SELECT * FROM pg_authid WHERE rolname = 'test_replication';
+
+-- default for bypassrls is false
+CREATE ROLE test_def_bypassrls;
+SELECT * FROM pg_authid WHERE rolname = 'test_def_bypassrls';
+CREATE ROLE test_bypassrls WITH BYPASSRLS;
+SELECT * FROM pg_authid WHERE rolname = 'test_bypassrls';
+ALTER ROLE test_bypassrls WITH NOBYPASSRLS;
+SELECT * FROM pg_authid WHERE rolname = 'test_bypassrls';
+ALTER ROLE test_bypassrls WITH BYPASSRLS;
+SELECT * FROM pg_authid WHERE rolname = 'test_bypassrls';
+
+-- remove the one role with LOGIN rights
+DROP ROLE test_role_canlogin;
+
+-- other roles not removed to test pg_dumpall role dump through
+-- pg_upgrade