diff --git a/src/backend/catalog/pg_db_role_setting.c b/src/backend/catalog/pg_db_role_setting.c index cf14ea1..8df4ac5 100644 --- a/src/backend/catalog/pg_db_role_setting.c +++ b/src/backend/catalog/pg_db_role_setting.c @@ -21,18 +21,18 @@ #include "utils/tqual.h" void -AlterSetting(Oid databaseid, Oid roleid, VariableSetStmt *setstmt) +AlterSetting(Oid databaseid, Oid roleid, List *setstmt, int action) { - char *valuestr; HeapTuple tuple; Relation rel; ScanKeyData scankey[2]; SysScanDesc scan; - - valuestr = ExtractSetVariableArgs(setstmt); + ListCell *l; + Datum datum; + bool isnull; + ArrayType *a = NULL; /* Get the old tuple, if any. */ - rel = heap_open(DbRoleSettingRelationId, RowExclusiveLock); ScanKeyInit(&scankey[0], Anum_pg_db_role_setting_setdatabase, @@ -46,6 +46,13 @@ AlterSetting(Oid databaseid, Oid roleid, VariableSetStmt *setstmt) NULL, 2, scankey); tuple = systable_getnext(scan); + if (HeapTupleIsValid(tuple)) + { + datum = heap_getattr(tuple, Anum_pg_db_role_setting_setconfig, + RelationGetDescr(rel), &isnull); + a = isnull ? NULL :DatumGetArrayTypeP(datum); + } + /* * There are three cases: * @@ -58,72 +65,52 @@ AlterSetting(Oid databaseid, Oid roleid, VariableSetStmt *setstmt) * - otherwise, insert a new pg_db_role_setting tuple, but only if the * command is not RESET */ - if (setstmt->kind == VAR_RESET_ALL) + foreach(l, setstmt) { - if (HeapTupleIsValid(tuple)) - { - ArrayType *new = NULL; - Datum datum; - bool isnull; + VariableSetStmt *stmt = (VariableSetStmt *) lfirst(l); + char *valuestr = NULL; - datum = heap_getattr(tuple, Anum_pg_db_role_setting_setconfig, - RelationGetDescr(rel), &isnull); + /* Set kind of variable set by action */ + if (action == +1) + stmt->kind = VAR_SET_VALUE; /* +1 = set variable */ + else if (action == -1) + stmt->kind = VAR_RESET; /* -1 = reset variable */ - if (!isnull) - new = GUCArrayReset(DatumGetArrayTypeP(datum)); - - if (new) - { - Datum repl_val[Natts_pg_db_role_setting]; - bool repl_null[Natts_pg_db_role_setting]; - bool repl_repl[Natts_pg_db_role_setting]; - HeapTuple newtuple; - - memset(repl_repl, false, sizeof(repl_repl)); - - repl_val[Anum_pg_db_role_setting_setconfig - 1] = - PointerGetDatum(new); - repl_repl[Anum_pg_db_role_setting_setconfig - 1] = true; - repl_null[Anum_pg_db_role_setting_setconfig - 1] = false; - - newtuple = heap_modify_tuple(tuple, RelationGetDescr(rel), - repl_val, repl_null, repl_repl); - simple_heap_update(rel, &tuple->t_self, newtuple); + /* Extract old value of setconfig */ + valuestr = ExtractSetVariableArgs(stmt); - /* Update indexes */ - CatalogUpdateIndexes(rel, newtuple); - } + if (stmt->kind == VAR_RESET_ALL) + { + if (HeapTupleIsValid(tuple) && !isnull) + a = GUCArrayReset(a); + break; + } + else if (HeapTupleIsValid(tuple)) + { + /* Update (valuestr is NULL in RESET cases) */ + if (valuestr) + a = GUCArrayAdd(a, stmt->name, valuestr); else - simple_heap_delete(rel, &tuple->t_self); + a = GUCArrayDelete(a, stmt->name); } + else if (valuestr) + a = GUCArrayAdd(a, stmt->name, valuestr); } - else if (HeapTupleIsValid(tuple)) - { - Datum repl_val[Natts_pg_db_role_setting]; - bool repl_null[Natts_pg_db_role_setting]; - bool repl_repl[Natts_pg_db_role_setting]; - HeapTuple newtuple; - Datum datum; - bool isnull; - ArrayType *a; - - memset(repl_repl, false, sizeof(repl_repl)); - repl_repl[Anum_pg_db_role_setting_setconfig - 1] = true; - repl_null[Anum_pg_db_role_setting_setconfig - 1] = false; - - /* Extract old value of setconfig */ - datum = heap_getattr(tuple, Anum_pg_db_role_setting_setconfig, - RelationGetDescr(rel), &isnull); - a = isnull ? NULL : DatumGetArrayTypeP(datum); - - /* Update (valuestr is NULL in RESET cases) */ - if (valuestr) - a = GUCArrayAdd(a, setstmt->name, valuestr); - else - a = GUCArrayDelete(a, setstmt->name); + /* Update system catalog */ + if (HeapTupleIsValid(tuple)) + { + /* Update or delete tuple */ if (a) { + Datum repl_val[Natts_pg_db_role_setting]; + bool repl_null[Natts_pg_db_role_setting]; + bool repl_repl[Natts_pg_db_role_setting]; + HeapTuple newtuple; + + memset(repl_repl, false, sizeof(repl_repl)); + repl_repl[Anum_pg_db_role_setting_setconfig - 1] = true; + repl_null[Anum_pg_db_role_setting_setconfig - 1] = false; repl_val[Anum_pg_db_role_setting_setconfig - 1] = PointerGetDatum(a); @@ -137,17 +124,12 @@ AlterSetting(Oid databaseid, Oid roleid, VariableSetStmt *setstmt) else simple_heap_delete(rel, &tuple->t_self); } - else if (valuestr) + else if (a) { /* non-null valuestr means it's not RESET, so insert a new tuple */ - HeapTuple newtuple; Datum values[Natts_pg_db_role_setting]; bool nulls[Natts_pg_db_role_setting]; - ArrayType *a; - - memset(nulls, false, sizeof(nulls)); - - a = GUCArrayAdd(NULL, setstmt->name, valuestr); + HeapTuple newtuple; values[Anum_pg_db_role_setting_setdatabase - 1] = ObjectIdGetDatum(databaseid); diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c index 6cbe65e..e3c45d4 100644 --- a/src/backend/commands/dbcommands.c +++ b/src/backend/commands/dbcommands.c @@ -1553,7 +1553,7 @@ AlterDatabaseSet(AlterDatabaseSetStmt *stmt) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, stmt->dbname); - AlterSetting(datid, InvalidOid, stmt->setstmt); + AlterSetting(datid, InvalidOid, list_make1(stmt->setstmt), stmt->action); UnlockSharedObject(DatabaseRelationId, datid, 0, AccessShareLock); diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c index 2961ccb..7726182 100644 --- a/src/backend/commands/user.c +++ b/src/backend/commands/user.c @@ -854,6 +854,19 @@ AlterRoleSet(AlterRoleSetStmt *stmt) HeapTuple roletuple; Oid databaseid = InvalidOid; Oid roleid = InvalidOid; + ListCell *l; + + /* Syntax check for case where RESET multiple parameters */ + if (stmt->action == -1) + { + foreach(l, stmt->setstmt) + { + if (((VariableSetStmt *) lfirst(l))->args != NULL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("RESET must not include values for parameteres."))); + } + } if (stmt->role) { @@ -916,7 +929,7 @@ AlterRoleSet(AlterRoleSetStmt *stmt) errmsg("must be superuser to alter settings globally"))); } - AlterSetting(databaseid, roleid, stmt->setstmt); + AlterSetting(databaseid, roleid, stmt->setstmt, stmt->action); return roleid; } diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 223ef17..4ebb319 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -346,6 +346,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); OptTableElementList TableElementList OptInherit definition OptTypedTableElementList TypedTableElementList reloptions opt_reloptions + useroptions useroption_list OptWith distinct_clause opt_all_clause opt_definition func_args func_args_list func_args_with_defaults func_args_with_defaults_list aggr_args aggr_args_list @@ -459,6 +460,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); %type relation_expr_opt_alias %type tablesample_clause opt_repeatable_clause %type target_el single_set_clause set_target insert_column_item +%type useroption_elem; %type generic_option_name %type generic_option_arg @@ -1074,7 +1076,26 @@ AlterRoleSetStmt: AlterRoleSetStmt *n = makeNode(AlterRoleSetStmt); n->role = $3; n->database = $4; - n->setstmt = $5; + n->setstmt = list_make1($5); + n->action = 0; + $$ = (Node *)n; + } + | ALTER ROLE RoleSpec opt_in_database SET useroptions + { + AlterRoleSetStmt *n = makeNode(AlterRoleSetStmt); + n->role = $3; + n->database = NULL; + n->setstmt = $6; + n->action = +1; + $$ = (Node *)n; + } + | ALTER ROLE RoleSpec opt_in_database RESET useroptions + { + AlterRoleSetStmt *n = makeNode(AlterRoleSetStmt); + n->role = $3; + n->database = NULL; + n->setstmt = $6; + n->action = -1; $$ = (Node *)n; } | ALTER ROLE ALL opt_in_database SetResetClause @@ -1082,7 +1103,8 @@ AlterRoleSetStmt: AlterRoleSetStmt *n = makeNode(AlterRoleSetStmt); n->role = NULL; n->database = $4; - n->setstmt = $5; + n->setstmt = list_make1($5); + n->action = 0; $$ = (Node *)n; } ; @@ -1105,19 +1127,63 @@ AlterUserStmt: } ; - AlterUserSetStmt: ALTER USER RoleSpec SetResetClause { AlterRoleSetStmt *n = makeNode(AlterRoleSetStmt); n->role = $3; n->database = NULL; - n->setstmt = $4; + n->setstmt = list_make1($4); + n->action = 0; $$ = (Node *)n; } - ; + | ALTER USER RoleSpec SET useroptions + { + AlterRoleSetStmt *n = makeNode(AlterRoleSetStmt); + n->role = $3; + n->database = NULL; + n->setstmt = $5; + n->action = +1; + $$ = (Node *)n; + } + | ALTER USER RoleSpec RESET useroptions + { + AlterRoleSetStmt *n = makeNode(AlterRoleSetStmt); + n->role = $3; + n->database = NULL; + n->setstmt = $5; + n->action= -1; + $$ = (Node *)n; + } + ; + +useroptions: + '(' useroption_list ')' { $$ = $2; } + ; +useroption_list: + useroption_elem { $$ = list_make1($1); } + | useroption_list ',' useroption_elem { $$ = lappend($1, $3); } + ; +useroption_elem: + var_name '=' var_value + { + VariableSetStmt *n = makeNode(VariableSetStmt); + n->kind = VAR_SET_VALUE; + n->name = $1; + n->args = list_make1($3); + $$ = (void *)n; + } + | var_name + { + VariableSetStmt *n = makeNode(VariableSetStmt); + n->kind = VAR_RESET; + n->name = $1; + n->args = NULL; + $$ = (void *)n; + } + ; /***************************************************************************** * * Drop a postgresql DBMS role @@ -8923,7 +8989,8 @@ AlterDatabaseSetStmt: { AlterDatabaseSetStmt *n = makeNode(AlterDatabaseSetStmt); n->dbname = $3; - n->setstmt = $4; + n->setstmt = list_make1($4); + n->action = 0; $$ = (Node *)n; } ; diff --git a/src/include/catalog/pg_db_role_setting.h b/src/include/catalog/pg_db_role_setting.h index 9717569..aaf742b 100644 --- a/src/include/catalog/pg_db_role_setting.h +++ b/src/include/catalog/pg_db_role_setting.h @@ -61,7 +61,7 @@ typedef FormData_pg_db_role_setting *Form_pg_db_role_setting; /* * prototypes for functions in pg_db_role_setting.h */ -extern void AlterSetting(Oid databaseid, Oid roleid, VariableSetStmt *setstmt); +extern void AlterSetting(Oid databaseid, Oid roleid, List *setstmt, int action); extern void DropSetting(Oid databaseid, Oid roleid); extern void ApplySetting(Snapshot snapshot, Oid databaseid, Oid roleid, Relation relsetting, GucSource source); diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index abd4dd1..09c7067 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -2173,7 +2173,8 @@ typedef struct AlterRoleSetStmt NodeTag type; Node *role; /* role */ char *database; /* database name, or NULL */ - VariableSetStmt *setstmt; /* SET or RESET subcommand */ + List *setstmt; /* list of VariableSetStmt */ + int action; /* +1 = set, -1 = reset, 0 = follows each element */ } AlterRoleSetStmt; typedef struct DropRoleStmt @@ -2743,7 +2744,8 @@ typedef struct AlterDatabaseSetStmt { NodeTag type; char *dbname; /* database name */ - VariableSetStmt *setstmt; /* SET or RESET subcommand */ + List *setstmt; /* list of VariableSetStmt */ + int action; /* +1 = set, -1 = reset, 0 = follows each element */ } AlterDatabaseSetStmt; /* ---------------------- diff --git a/src/test/regress/expected/rolenames.out b/src/test/regress/expected/rolenames.out index 8f88c02..adf2b0a 100644 --- a/src/test/regress/expected/rolenames.out +++ b/src/test/regress/expected/rolenames.out @@ -324,31 +324,31 @@ SELECT * FROM chksetconfig(); (0 rows) ALTER ROLE CURRENT_USER SET application_name to 'FOO'; -ALTER ROLE SESSION_USER SET application_name to 'BAR'; +ALTER ROLE SESSION_USER SET (application_name = 'BAR', log_min_duration_statement = 100); ALTER ROLE "current_user" SET application_name to 'FOOFOO'; ALTER ROLE "Public" SET application_name to 'BARBAR'; ALTER ROLE ALL SET application_name to 'SLAP'; SELECT * FROM chksetconfig(); - db | role | rolkeyword | setconfig ------+--------------+--------------+--------------------------- + db | role | rolkeyword | setconfig +-----+--------------+--------------+------------------------------------------------------- ALL | Public | - | {application_name=BARBAR} ALL | current_user | - | {application_name=FOOFOO} - ALL | testrol1 | session_user | {application_name=BAR} + ALL | testrol1 | session_user | {application_name=BAR,log_min_duration_statement=100} ALL | testrol2 | current_user | {application_name=FOO} (4 rows) ALTER ROLE testrol1 SET application_name to 'SLAM'; SELECT * FROM chksetconfig(); - db | role | rolkeyword | setconfig ------+--------------+--------------+--------------------------- + db | role | rolkeyword | setconfig +-----+--------------+--------------+-------------------------------------------------------- ALL | Public | - | {application_name=BARBAR} ALL | current_user | - | {application_name=FOOFOO} - ALL | testrol1 | session_user | {application_name=SLAM} + ALL | testrol1 | session_user | {application_name=SLAM,log_min_duration_statement=100} ALL | testrol2 | current_user | {application_name=FOO} (4 rows) ALTER ROLE CURRENT_USER RESET application_name; -ALTER ROLE SESSION_USER RESET application_name; +ALTER ROLE SESSION_USER RESET (application_name, log_min_duration_statement, log_duration); ALTER ROLE "current_user" RESET application_name; ALTER ROLE "Public" RESET application_name; ALTER ROLE ALL RESET application_name; @@ -376,7 +376,7 @@ SELECT * FROM chksetconfig(); (0 rows) ALTER USER CURRENT_USER SET application_name to 'FOO'; -ALTER USER SESSION_USER SET application_name to 'BAR'; +ALTER USER SESSION_USER SET (application_name = 'BAR', log_min_duration_statement = 100); ALTER USER "current_user" SET application_name to 'FOOFOO'; ALTER USER "Public" SET application_name to 'BARBAR'; ALTER USER ALL SET application_name to 'SLAP'; @@ -384,26 +384,26 @@ ERROR: syntax error at or near "ALL" LINE 1: ALTER USER ALL SET application_name to 'SLAP'; ^ SELECT * FROM chksetconfig(); - db | role | rolkeyword | setconfig ------+--------------+--------------+--------------------------- + db | role | rolkeyword | setconfig +-----+--------------+--------------+------------------------------------------------------- ALL | Public | - | {application_name=BARBAR} ALL | current_user | - | {application_name=FOOFOO} - ALL | testrol1 | session_user | {application_name=BAR} + ALL | testrol1 | session_user | {application_name=BAR,log_min_duration_statement=100} ALL | testrol2 | current_user | {application_name=FOO} (4 rows) ALTER USER testrol1 SET application_name to 'SLAM'; SELECT * FROM chksetconfig(); - db | role | rolkeyword | setconfig ------+--------------+--------------+--------------------------- + db | role | rolkeyword | setconfig +-----+--------------+--------------+-------------------------------------------------------- ALL | Public | - | {application_name=BARBAR} ALL | current_user | - | {application_name=FOOFOO} - ALL | testrol1 | session_user | {application_name=SLAM} + ALL | testrol1 | session_user | {application_name=SLAM,log_min_duration_statement=100} ALL | testrol2 | current_user | {application_name=FOO} (4 rows) ALTER USER CURRENT_USER RESET application_name; -ALTER USER SESSION_USER RESET application_name; +ALTER USER SESSION_USER RESET (application_name, log_min_duration_statement, log_duration); ALTER USER "current_user" RESET application_name; ALTER USER "Public" RESET application_name; ALTER USER ALL RESET application_name; diff --git a/src/test/regress/sql/rolenames.sql b/src/test/regress/sql/rolenames.sql index e8c6b33..fae0b3b 100644 --- a/src/test/regress/sql/rolenames.sql +++ b/src/test/regress/sql/rolenames.sql @@ -127,7 +127,7 @@ ALTER USER nonexistent WITH NOREPLICATION; -- error -- ALTER ROLE SET/RESET SELECT * FROM chksetconfig(); ALTER ROLE CURRENT_USER SET application_name to 'FOO'; -ALTER ROLE SESSION_USER SET application_name to 'BAR'; +ALTER ROLE SESSION_USER SET (application_name = 'BAR', log_min_duration_statement = 100); ALTER ROLE "current_user" SET application_name to 'FOOFOO'; ALTER ROLE "Public" SET application_name to 'BARBAR'; ALTER ROLE ALL SET application_name to 'SLAP'; @@ -135,7 +135,7 @@ SELECT * FROM chksetconfig(); ALTER ROLE testrol1 SET application_name to 'SLAM'; SELECT * FROM chksetconfig(); ALTER ROLE CURRENT_USER RESET application_name; -ALTER ROLE SESSION_USER RESET application_name; +ALTER ROLE SESSION_USER RESET (application_name, log_min_duration_statement, log_duration); ALTER ROLE "current_user" RESET application_name; ALTER ROLE "Public" RESET application_name; ALTER ROLE ALL RESET application_name; @@ -150,7 +150,7 @@ ALTER ROLE nonexistent SET application_name to 'BOMB'; -- error -- ALTER USER SET/RESET SELECT * FROM chksetconfig(); ALTER USER CURRENT_USER SET application_name to 'FOO'; -ALTER USER SESSION_USER SET application_name to 'BAR'; +ALTER USER SESSION_USER SET (application_name = 'BAR', log_min_duration_statement = 100); ALTER USER "current_user" SET application_name to 'FOOFOO'; ALTER USER "Public" SET application_name to 'BARBAR'; ALTER USER ALL SET application_name to 'SLAP'; @@ -158,7 +158,7 @@ SELECT * FROM chksetconfig(); ALTER USER testrol1 SET application_name to 'SLAM'; SELECT * FROM chksetconfig(); ALTER USER CURRENT_USER RESET application_name; -ALTER USER SESSION_USER RESET application_name; +ALTER USER SESSION_USER RESET (application_name, log_min_duration_statement, log_duration); ALTER USER "current_user" RESET application_name; ALTER USER "Public" RESET application_name; ALTER USER ALL RESET application_name;