diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 478a216dbb..07bb333a3b 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -23660,6 +23660,20 @@ SELECT * FROM pg_ls_dir('.') WITH ORDINALITY AS t(ls,n);
+
+
+
+ system_user
+
+ system_user
+ name
+
+
+ Returns the identity (if any) that the user presented during the
+ authentication cycle, before they were assigned a database role.
+
+
+
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index e44ad68cda..63cf3d8bb7 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -2543,6 +2543,11 @@ ExecEvalSQLValueFunction(ExprState *state, ExprEvalStep *op)
*op->resvalue = session_user(fcinfo);
*op->resnull = fcinfo->isnull;
break;
+ case SVFOP_SYSTEM_USER:
+ InitFunctionCallInfoData(*fcinfo, NULL, 0, InvalidOid, NULL, NULL);
+ *op->resvalue = system_user(fcinfo);
+ *op->resnull = fcinfo->isnull;
+ break;
case SVFOP_CURRENT_CATALOG:
InitFunctionCallInfoData(*fcinfo, NULL, 0, InvalidOid, NULL, NULL);
*op->resvalue = current_database(fcinfo);
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 969c9c158f..29dcc77724 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -842,7 +842,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
SEQUENCE SEQUENCES SERIALIZABLE SERVER SESSION SESSION_USER SET SETS SETOF
SHARE SHOW SIMILAR SIMPLE SKIP SMALLINT SNAPSHOT SOME SQL_P STABLE STANDALONE_P
START STATEMENT STATISTICS STDIN STDOUT STORAGE STORED STRICT_P STRING STRIP_P
- SUBSCRIPTION SUBSTRING SUPPORT SYMMETRIC SYSID SYSTEM_P
+ SUBSCRIPTION SUBSTRING SUPPORT SYMMETRIC SYSID SYSTEM_P SYSTEM_USER
TABLE TABLES TABLESAMPLE TABLESPACE TEMP TEMPLATE TEMPORARY TEXT_P THEN
TIES TIME TIMESTAMP TO TRAILING TRANSACTION TRANSFORM
@@ -15457,6 +15457,10 @@ func_expr_common_subexpr:
{
$$ = makeSQLValueFunction(SVFOP_SESSION_USER, -1, @1);
}
+ | SYSTEM_USER
+ {
+ $$ = makeSQLValueFunction(SVFOP_SYSTEM_USER, -1, @1);
+ }
| USER
{
$$ = makeSQLValueFunction(SVFOP_USER, -1, @1);
@@ -18173,6 +18177,7 @@ reserved_keyword:
| SESSION_USER
| SOME
| SYMMETRIC
+ | SYSTEM_USER
| TABLE
| THEN
| TO
@@ -18578,6 +18583,7 @@ bare_label_keyword:
| SYMMETRIC
| SYSID
| SYSTEM_P
+ | SYSTEM_USER
| TABLE
| TABLES
| TABLESAMPLE
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 0dc2fc472e..8256cc177d 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -2299,6 +2299,7 @@ transformSQLValueFunction(ParseState *pstate, SQLValueFunction *svf)
case SVFOP_CURRENT_USER:
case SVFOP_USER:
case SVFOP_SESSION_USER:
+ case SVFOP_SYSTEM_USER:
case SVFOP_CURRENT_CATALOG:
case SVFOP_CURRENT_SCHEMA:
svf->type = NAMEOID;
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index 2a1d44b813..8ad50e31d2 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -1917,6 +1917,9 @@ FigureColnameInternal(Node *node, char **name)
case SVFOP_SESSION_USER:
*name = "session_user";
return 2;
+ case SVFOP_SYSTEM_USER:
+ *name = "system_user";
+ return 2;
case SVFOP_CURRENT_CATALOG:
*name = "current_catalog";
return 2;
diff --git a/src/backend/utils/adt/name.c b/src/backend/utils/adt/name.c
index e8bba3670c..f8e69fb7ec 100644
--- a/src/backend/utils/adt/name.c
+++ b/src/backend/utils/adt/name.c
@@ -257,7 +257,7 @@ namestrcmp(Name name, const char *str)
/*
- * SQL-functions CURRENT_USER, SESSION_USER
+ * SQL-functions CURRENT_USER, SESSION_USER, SYSTEM_USER
*/
Datum
current_user(PG_FUNCTION_ARGS)
@@ -271,6 +271,16 @@ session_user(PG_FUNCTION_ARGS)
PG_RETURN_DATUM(DirectFunctionCall1(namein, CStringGetDatum(GetUserNameFromId(GetSessionUserId(), false))));
}
+Datum
+system_user(PG_FUNCTION_ARGS)
+{
+ const char *sysuser = GetSystemUser();
+
+ if (sysuser)
+ PG_RETURN_DATUM(DirectFunctionCall1(namein, CStringGetDatum(GetSystemUser())));
+ else
+ return session_user(fcinfo);
+}
/*
* SQL-functions CURRENT_SCHEMA, CURRENT_SCHEMAS
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index c3937a60fd..ffdd818f1e 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -9319,6 +9319,9 @@ get_rule_expr(Node *node, deparse_context *context,
case SVFOP_SESSION_USER:
appendStringInfoString(buf, "SESSION_USER");
break;
+ case SVFOP_SYSTEM_USER:
+ appendStringInfoString(buf, "SYSTEM_USER");
+ break;
case SVFOP_CURRENT_CATALOG:
appendStringInfoString(buf, "CURRENT_CATALOG");
break;
diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c
index b25bd0e583..4984da23a7 100644
--- a/src/backend/utils/init/miscinit.c
+++ b/src/backend/utils/init/miscinit.c
@@ -473,6 +473,7 @@ static Oid AuthenticatedUserId = InvalidOid;
static Oid SessionUserId = InvalidOid;
static Oid OuterUserId = InvalidOid;
static Oid CurrentUserId = InvalidOid;
+static const char *SystemUser = NULL;
/* We also have to remember the superuser state of some of these levels */
static bool AuthenticatedUserIsSuperuser = false;
@@ -544,6 +545,16 @@ SetSessionUserId(Oid userid, bool is_superuser)
CurrentUserId = userid;
}
+/*
+ * Return the system user (the authenticated identity), which may be different
+ * from the session user.
+ */
+const char *
+GetSystemUser(void)
+{
+ return SystemUser;
+}
+
/*
* GetAuthenticatedUserId - get the authenticated user ID
*/
@@ -814,6 +825,19 @@ InitializeSessionUserIdStandalone(void)
SetSessionUserId(BOOTSTRAP_SUPERUSERID, true);
}
+/*
+ * Initialize the system user during normal backend startup.
+ */
+void
+InitializeSystemUser(const char *system_user)
+{
+ /* call only once */
+ Assert(SystemUser == NULL);
+
+ /* If system_user is not NULL, store it in long-lived storage */
+ if (system_user)
+ SystemUser = MemoryContextStrdup(TopMemoryContext, system_user);
+}
/*
* Change session auth ID while running
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 6b9082604f..a3199a17ea 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -835,6 +835,7 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
Assert(MyProcPort != NULL);
PerformAuthentication(MyProcPort);
InitializeSessionUserId(username, useroid);
+ InitializeSystemUser(MyProcPort->authn_id);
am_superuser = superuser();
}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 87aa571a33..4406396055 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -1508,6 +1508,9 @@
{ oid => '746', descr => 'session user name',
proname => 'session_user', provolatile => 's', prorettype => 'name',
proargtypes => '', prosrc => 'session_user' },
+{ oid => '786', descr => 'system user name',
+ proname => 'system_user', provolatile => 's', prorettype => 'name',
+ proargtypes => '', prosrc => 'system_user' },
{ oid => '744',
proname => 'array_eq', prorettype => 'bool',
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 0af130fbc5..de173c1387 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -364,6 +364,8 @@ extern void InitializeSessionUserIdStandalone(void);
extern void SetSessionAuthorization(Oid userid, bool is_superuser);
extern Oid GetCurrentRoleId(void);
extern void SetCurrentRoleId(Oid roleid, bool is_superuser);
+extern void InitializeSystemUser(const char *system_user);
+extern const char* GetSystemUser(void);
/* in utils/misc/superuser.c */
extern bool superuser(void); /* current user is superuser */
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 51505eee85..c23b0f2507 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -1186,6 +1186,7 @@ typedef enum SQLValueFunctionOp
SVFOP_CURRENT_USER,
SVFOP_USER,
SVFOP_SESSION_USER,
+ SVFOP_SYSTEM_USER,
SVFOP_CURRENT_CATALOG,
SVFOP_CURRENT_SCHEMA
} SQLValueFunctionOp;
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index ae35f03251..e309988b26 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -434,6 +434,7 @@ PG_KEYWORD("support", SUPPORT, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("symmetric", SYMMETRIC, RESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("sysid", SYSID, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("system", SYSTEM_P, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("system_user", SYSTEM_USER, RESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("table", TABLE, RESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("tables", TABLES, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("tablesample", TABLESAMPLE, TYPE_FUNC_NAME_KEYWORD, BARE_LABEL)
diff --git a/src/test/kerberos/t/001_auth.pl b/src/test/kerberos/t/001_auth.pl
index 62e0542639..2f6e6e4d38 100644
--- a/src/test/kerberos/t/001_auth.pl
+++ b/src/test/kerberos/t/001_auth.pl
@@ -4,8 +4,8 @@
# Sets up a KDC and then runs a variety of tests to make sure that the
# GSSAPI/Kerberos authentication and encryption are working properly,
# that the options in pg_hba.conf and pg_ident.conf are handled correctly,
-# and that the server-side pg_stat_gssapi view reports what we expect to
-# see for each test.
+# that the server-side pg_stat_gssapi view reports what we expect to
+# see for each test and that SYSTEM_USER returns what we expect to see.
#
# Since this requires setting up a full KDC, it doesn't make much sense
# to have multiple test scripts (since they'd have to also create their
@@ -307,6 +307,15 @@ test_query(
'gssencmode=require',
'sending 100K lines works');
+# Test that SYSTEM_USER works.
+test_query(
+ $node,
+ 'test1',
+ 'SELECT CURRENT_USER,SYSTEM_USER;',
+ qr/^test1|test1\@$realm$/s,
+ 'gssencmode=require',
+ 'testing system_user');
+
unlink($node->data_dir . '/pg_hba.conf');
$node->append_conf('pg_hba.conf',
qq{hostgssenc all all $hostaddr/32 gss map=mymap});