0002-idea.patch

application/octet-stream

Filename: 0002-idea.patch
Type: application/octet-stream
Part: 0
Message: Re: BUG #15182: Canceling authentication due to timeout aka Denial of Service Attack

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: unified
Series: patch 0002
File+
src/backend/commands/vacuum.c 85 57
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index 5736f12b8f..d7e79794cd 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -30,11 +30,13 @@
 #include "access/multixact.h"
 #include "access/transam.h"
 #include "access/xact.h"
+#include "catalog/catalog.h"
 #include "catalog/namespace.h"
 #include "catalog/pg_database.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/pg_namespace.h"
 #include "commands/cluster.h"
+#include "commands/tablecmds.h"
 #include "commands/vacuum.h"
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
@@ -47,6 +49,7 @@
 #include "utils/acl.h"
 #include "utils/fmgroids.h"
 #include "utils/guc.h"
+#include "utils/lsyscache.h"
 #include "utils/memutils.h"
 #include "utils/snapmgr.h"
 #include "utils/syscache.h"
@@ -68,6 +71,7 @@ static BufferAccessStrategy vac_strategy;
 
 
 /* non-export function prototypes */
+static bool vacuum_skip_rel(Oid relid, Form_pg_class reltuple, int elevel);
 static List *expand_vacuum_rel(VacuumRelation *vrel);
 static List *get_all_vacuum_rels(void);
 static void vac_truncate_clog(TransactionId frozenXID,
@@ -408,6 +412,60 @@ vacuum(int options, List *relations, VacuumParams *params,
 	vac_context = NULL;
 }
 
+/*
+ * Check if a given relation can be safely vacuumed or not.  If the
+ * relation needs to be skipped, issue a log message and return true to
+ * let the caller know what to do with this relation.
+ */
+static bool
+vacuum_skip_rel(Oid relid, Form_pg_class reltuple, int elevel)
+{
+	/*
+	 * Check permissions.
+	 *
+	 * We allow the user to vacuum a table if he is superuser, the table
+	 * owner, or the database owner (but in the latter case, only if it's not
+	 * a shared relation).  pg_class_ownercheck includes the superuser case.
+	 *
+	 * Note we choose to treat permissions failure as a WARNING and keep
+	 * trying to vacuum the rest of the DB --- is this appropriate?
+	 */
+	if (!(pg_class_ownercheck(relid, GetUserId()) ||
+		  (pg_database_ownercheck(MyDatabaseId, GetUserId()) && !reltuple->relisshared)))
+	{
+		if (reltuple->relisshared)
+			ereport(elevel,
+					(errmsg("skipping \"%s\" --- only superuser can vacuum it",
+							NameStr(reltuple->relname))));
+		else if (reltuple->relnamespace == PG_CATALOG_NAMESPACE)
+			ereport(elevel,
+					(errmsg("skipping \"%s\" --- only superuser or database owner can vacuum it",
+							NameStr(reltuple->relname))));
+		else
+			ereport(elevel,
+					(errmsg("skipping \"%s\" --- only table or database owner can vacuum it",
+							NameStr(reltuple->relname))));
+		return true;
+	}
+
+	/*
+	 * Check that it's of a vacuumable relkind.
+	 */
+	if (reltuple->relkind != RELKIND_RELATION &&
+		reltuple->relkind != RELKIND_MATVIEW &&
+		reltuple->relkind != RELKIND_TOASTVALUE &&
+		reltuple->relkind != RELKIND_PARTITIONED_TABLE)
+	{
+		ereport(elevel,
+				(errmsg("skipping \"%s\" --- cannot vacuum non-tables or special system tables",
+						NameStr(reltuple->relname))));
+		return true;
+	}
+
+	return false;
+}
+
+
 /*
  * Given a VacuumRelation, fill in the table OID if it wasn't specified,
  * and optionally add VacuumRelations for partitions of the table.
@@ -456,15 +514,6 @@ expand_vacuum_rel(VacuumRelation *vrel)
 		 */
 		relid = RangeVarGetRelid(vrel->relation, AccessShareLock, false);
 
-		/*
-		 * Make a returnable VacuumRelation for this rel.
-		 */
-		oldcontext = MemoryContextSwitchTo(vac_context);
-		vacrels = lappend(vacrels, makeVacuumRelation(vrel->relation,
-													  relid,
-													  vrel->va_cols));
-		MemoryContextSwitchTo(oldcontext);
-
 		/*
 		 * To check whether the relation is a partitioned table, fetch its
 		 * syscache entry.
@@ -473,9 +522,27 @@ expand_vacuum_rel(VacuumRelation *vrel)
 		if (!HeapTupleIsValid(tuple))
 			elog(ERROR, "cache lookup failed for relation %u", relid);
 		classForm = (Form_pg_class) GETSTRUCT(tuple);
+
+		/* check permissions of relation */
+		if (vacuum_skip_rel(relid, classForm, WARNING))
+		{
+			ReleaseSysCache(tuple);
+			UnlockRelationOid(relid, AccessShareLock);
+			return vacrels;
+		}
+
 		include_parts = (classForm->relkind == RELKIND_PARTITIONED_TABLE);
 		ReleaseSysCache(tuple);
 
+		/*
+		 * Make a returnable VacuumRelation for this rel.
+		 */
+		oldcontext = MemoryContextSwitchTo(vac_context);
+		vacrels = lappend(vacrels, makeVacuumRelation(vrel->relation,
+													  relid,
+													  vrel->va_cols));
+		MemoryContextSwitchTo(oldcontext);
+
 		/*
 		 * If it is, make relation list entries for its partitions.  Note that
 		 * the list returned by find_all_inheritors() includes the passed-in
@@ -545,15 +612,10 @@ get_all_vacuum_rels(void)
 	{
 		Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
 		MemoryContext oldcontext;
+		Oid			relid = HeapTupleGetOid(tuple);
 
-		/*
-		 * We include partitioned tables here; depending on which operation is
-		 * to be performed, caller will decide whether to process or ignore
-		 * them.
-		 */
-		if (classForm->relkind != RELKIND_RELATION &&
-			classForm->relkind != RELKIND_MATVIEW &&
-			classForm->relkind != RELKIND_PARTITIONED_TABLE)
+		/* check permissions of relation */
+		if (vacuum_skip_rel(relid, classForm, DEBUG1))
 			continue;
 
 		/*
@@ -563,7 +625,7 @@ get_all_vacuum_rels(void)
 		 */
 		oldcontext = MemoryContextSwitchTo(vac_context);
 		vacrels = lappend(vacrels, makeVacuumRelation(NULL,
-													  HeapTupleGetOid(tuple),
+													  relid,
 													  NIL));
 		MemoryContextSwitchTo(oldcontext);
 	}
@@ -1436,47 +1498,13 @@ vacuum_rel(Oid relid, RangeVar *relation, int options, VacuumParams *params)
 	}
 
 	/*
-	 * Check permissions.
-	 *
-	 * We allow the user to vacuum a table if he is superuser, the table
-	 * owner, or the database owner (but in the latter case, only if it's not
-	 * a shared relation).  pg_class_ownercheck includes the superuser case.
-	 *
-	 * Note we choose to treat permissions failure as a WARNING and keep
-	 * trying to vacuum the rest of the DB --- is this appropriate?
-	 */
-	if (!(pg_class_ownercheck(RelationGetRelid(onerel), GetUserId()) ||
-		  (pg_database_ownercheck(MyDatabaseId, GetUserId()) && !onerel->rd_rel->relisshared)))
-	{
-		if (onerel->rd_rel->relisshared)
-			ereport(WARNING,
-					(errmsg("skipping \"%s\" --- only superuser can vacuum it",
-							RelationGetRelationName(onerel))));
-		else if (onerel->rd_rel->relnamespace == PG_CATALOG_NAMESPACE)
-			ereport(WARNING,
-					(errmsg("skipping \"%s\" --- only superuser or database owner can vacuum it",
-							RelationGetRelationName(onerel))));
-		else
-			ereport(WARNING,
-					(errmsg("skipping \"%s\" --- only table or database owner can vacuum it",
-							RelationGetRelationName(onerel))));
-		relation_close(onerel, lmode);
-		PopActiveSnapshot();
-		CommitTransactionCommand();
-		return false;
-	}
-
-	/*
-	 * Check that it's of a vacuumable relkind.
+	 * Check if relation needs to be skipped.  This check happens as well
+	 * when building the relation list to VACUUM for a manual operation,
+	 * and needs to be done here as well as this could be spawned across
+	 * multiple transactions.
 	 */
-	if (onerel->rd_rel->relkind != RELKIND_RELATION &&
-		onerel->rd_rel->relkind != RELKIND_MATVIEW &&
-		onerel->rd_rel->relkind != RELKIND_TOASTVALUE &&
-		onerel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
+	if (vacuum_skip_rel(RelationGetRelid(onerel), onerel->rd_rel, WARNING))
 	{
-		ereport(WARNING,
-				(errmsg("skipping \"%s\" --- cannot vacuum non-tables or special system tables",
-						RelationGetRelationName(onerel))));
 		relation_close(onerel, lmode);
 		PopActiveSnapshot();
 		CommitTransactionCommand();