Make superuser.c maintain a simple one-entry cache holding the superuser
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 29 May 2005 20:38:06 +0000 (20:38 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sun, 29 May 2005 20:38:06 +0000 (20:38 +0000)
status of the most recently queried userid.  Since the common pattern is
many successive queries about the same user (ie, the current user) this
can save a lot of syscache probes.

src/backend/utils/misc/superuser.c

index ee6cc397e3476e9f687a9689b2801bf8f6d19792..520dc470cdcef7e6cab563bdc86a3e8425b1466b 100644 (file)
@@ -2,7 +2,11 @@
  *
  * superuser.c
  *   The superuser() function.  Determines if user has superuser privilege.
- *   Also, a function to check for the owner (datdba) of a database.
+ *
+ * All code should use either of these two functions to find out
+ * whether a given user is a superuser, rather than examining
+ * pg_shadow.usesuper directly, so that the escape hatch built in for
+ * the single-user case works.
  *
  *
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/utils/misc/superuser.c,v 1.30 2004/12/31 22:02:45 pgsql Exp $
+ *   $PostgreSQL: pgsql/src/backend/utils/misc/superuser.c,v 1.31 2005/05/29 20:38:06 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
 
-#include "access/heapam.h"
 #include "catalog/pg_shadow.h"
-#include "commands/dbcommands.h"
+#include "utils/inval.h"
 #include "utils/syscache.h"
 #include "miscadmin.h"
 
 
+/*
+ * In common cases the same userid (ie, the session or current ID) will
+ * be queried repeatedly.  So we maintain a simple one-entry cache for
+ * the status of the last requested userid.  The cache can be flushed
+ * at need by watching for cache update events on pg_shadow.
+ */
+static AclId   last_userid = 0;        /* 0 == cache not valid */
+static bool        last_userid_is_super = false;
+static bool        userid_callback_registered = false;
+
+static void UseridCallback(Datum arg, Oid relid);
+
+
 /*
  * The Postgres user running this command has Postgres superuser privileges
- *
- * All code should use either of these two functions to find out
- * whether a given user is a superuser, rather than evaluating
- * pg_shadow.usesuper directly, so that the escape hatch built in for
- * the single-user case works.
  */
 bool
 superuser(void)
@@ -38,16 +49,24 @@ superuser(void)
 }
 
 
+/*
+ * The specified userid has Postgres superuser privileges
+ */
 bool
 superuser_arg(AclId userid)
 {
-   bool        result = false;
+   bool        result;
    HeapTuple   utup;
 
+   /* Quick out for cache hit */
+   if (AclIdIsValid(last_userid) && last_userid == userid)
+       return last_userid_is_super;
+
    /* Special escape path in case you deleted all your users. */
    if (!IsUnderPostmaster && userid == BOOTSTRAP_USESYSID)
        return true;
 
+   /* OK, look up the information in pg_shadow */
    utup = SearchSysCache(SHADOWSYSID,
                          Int32GetDatum(userid),
                          0, 0, 0);
@@ -56,5 +75,35 @@ superuser_arg(AclId userid)
        result = ((Form_pg_shadow) GETSTRUCT(utup))->usesuper;
        ReleaseSysCache(utup);
    }
+   else
+   {
+       /* Report "not superuser" for invalid userids */
+       result = false;
+   }
+
+   /* If first time through, set up callback for cache flushes */
+   if (!userid_callback_registered)
+   {
+       CacheRegisterSyscacheCallback(SHADOWSYSID,
+                                     UseridCallback,
+                                     (Datum) 0);
+       userid_callback_registered = true;
+   }
+
+   /* Cache the result for next time */
+   last_userid = userid;
+   last_userid_is_super = result;
+
    return result;
 }
+
+/*
+ * UseridCallback
+ *     Syscache inval callback function
+ */
+static void
+UseridCallback(Datum arg, Oid relid)
+{
+   /* Invalidate our local cache in case user's superuserness changed */
+   last_userid = 0;
+}