From 32447566c1c3bf5201e36141590f4654d144f777 Mon Sep 17 00:00:00 2001
From: amit <amitlangote09@gmail.com>
Date: Tue, 28 Feb 2017 13:43:59 +0900
Subject: [PATCH 1/3] Avoid useless partitioned table ops

Also, recursively perform VACUUM and ANALYZE on partitions when the
command is applied to a partitioned table.
---
 doc/src/sgml/ddl.sgml            | 47 ++++++++++++++++++------------------
 doc/src/sgml/ref/analyze.sgml    |  7 ++++--
 doc/src/sgml/ref/vacuum.sgml     |  4 +++-
 src/backend/commands/analyze.c   | 37 +++++++++++++++++++----------
 src/backend/commands/tablecmds.c | 15 +++++++++---
 src/backend/commands/vacuum.c    | 51 +++++++++++++++++++++++++++++++++++++---
 6 files changed, 116 insertions(+), 45 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index ef0f7cf727..09b5b3ff70 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -3792,8 +3792,7 @@ UNION ALL SELECT * FROM measurement_y2008m01;
    <title>Caveats</title>
 
    <para>
-    The following caveats apply to partitioned tables implemented using either
-    method (unless noted otherwise):
+    The following caveats apply to using inheritance to implement partitioning:
    <itemizedlist>
     <listitem>
      <para>
@@ -3803,13 +3802,6 @@ UNION ALL SELECT * FROM measurement_y2008m01;
       partitions and creates and/or modifies associated objects than
       to write each by hand.
      </para>
-
-     <para>
-      This is not a problem with partitioned tables though, as trying to
-      create a partition that overlaps with one of the existing partitions
-      results in an error, so it is impossible to end up with partitions
-      that overlap one another.
-     </para>
     </listitem>
 
     <listitem>
@@ -3822,14 +3814,6 @@ UNION ALL SELECT * FROM measurement_y2008m01;
       on the partition tables, but it makes management of the structure
       much more complicated.
      </para>
-
-     <para>
-      This problem exists even for partitioned tables.  An <command>UPDATE</>
-      that causes a row to move from one partition to another fails, because
-      the new value of the row fails to satisfy the implicit partition
-      constraint of the original partition.  This might change in future
-      releases.
-     </para>
     </listitem>
 
     <listitem>
@@ -3840,8 +3824,7 @@ UNION ALL SELECT * FROM measurement_y2008m01;
 <programlisting>
 ANALYZE measurement;
 </programlisting>
-      will only process the master table.  This is true even for partitioned
-      tables.
+      will only process the master table.
      </para>
     </listitem>
 
@@ -3852,11 +3835,27 @@ ANALYZE measurement;
       action is only taken in case of unique violations on the specified
       target relation, not its child relations.
      </para>
+    </listitem>
+   </itemizedlist>
+   </para>
 
+   <para>
+    The following caveats apply to partitioned tables created with the
+    explicit syntax:
+   <itemizedlist>
+    <listitem>
+     <para>
+      An <command>UPDATE</> that causes a row to move from one partition to
+      another fails, because the new value of the row fails to satisfy the
+      implicit partition constraint of the original partition.  This might
+      change in future releases.
+     </para>
+    </listitem>
+
+    <listitem>
      <para>
       <command>INSERT</command> statements with <literal>ON CONFLICT</>
-      clause are currently not allowed on partitioned tables, that is,
-      cause error when specified.
+      clause are currently not allowed on partitioned tables.
      </para>
     </listitem>
 
@@ -3864,7 +3863,8 @@ ANALYZE measurement;
    </para>
 
    <para>
-    The following caveats apply to constraint exclusion:
+    The following caveats apply to constraint exclusion, which is currently
+    used by both inheritance and partitioned tables:
 
    <itemizedlist>
     <listitem>
@@ -3898,8 +3898,7 @@ ANALYZE measurement;
       during constraint exclusion, so large numbers of partitions are likely
       to increase query planning time considerably.  Partitioning using
       these techniques will work well with up to perhaps a hundred partitions;
-      don't try to use many thousands of partitions. This restriction applies
-      both to inheritance and explicit partitioning syntax.
+      don't try to use many thousands of partitions.
      </para>
     </listitem>
 
diff --git a/doc/src/sgml/ref/analyze.sgml b/doc/src/sgml/ref/analyze.sgml
index 27ab4fca42..620d2e43ff 100644
--- a/doc/src/sgml/ref/analyze.sgml
+++ b/doc/src/sgml/ref/analyze.sgml
@@ -63,8 +63,11 @@ ANALYZE [ VERBOSE ] [ <replaceable class="PARAMETER">table_name</replaceable> [
     <listitem>
      <para>
       The name (possibly schema-qualified) of a specific table to
-      analyze.  If omitted, all regular tables (but not foreign tables)
-      in the current database are analyzed.
+      analyze.  If omitted, all regular tables, partitioned tables (but not
+      foreign tables), and materialized views in the current database are
+      analyzed.  If the specified table is a partitioned table, both the
+      inheritance statistics of the partitioned table as a whole and
+      statistics of the individual partitions are updated.
      </para>
     </listitem>
    </varlistentry>
diff --git a/doc/src/sgml/ref/vacuum.sgml b/doc/src/sgml/ref/vacuum.sgml
index f18180a2fa..543ebcf649 100644
--- a/doc/src/sgml/ref/vacuum.sgml
+++ b/doc/src/sgml/ref/vacuum.sgml
@@ -153,7 +153,9 @@ VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] ANALYZE [ <replaceable class="PARAMETER">
     <listitem>
      <para>
       The name (optionally schema-qualified) of a specific table to
-      vacuum. Defaults to all tables in the current database.
+      vacuum.  If omitted, all regular tables and materialized views in the
+      current database are vacuumed.  If the specified table is a partitioned
+      table, all of its leaf partitions are vacuumed.
      </para>
     </listitem>
    </varlistentry>
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index ed3acb1673..a70c760341 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -201,8 +201,7 @@ analyze_rel(Oid relid, RangeVar *relation, int options,
 	 * locked the relation.
 	 */
 	if (onerel->rd_rel->relkind == RELKIND_RELATION ||
-		onerel->rd_rel->relkind == RELKIND_MATVIEW ||
-		onerel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+		onerel->rd_rel->relkind == RELKIND_MATVIEW)
 	{
 		/* Regular table, so we'll use the regular row acquisition function */
 		acquirefunc = acquire_sample_rows;
@@ -234,6 +233,12 @@ analyze_rel(Oid relid, RangeVar *relation, int options,
 			return;
 		}
 	}
+	else if (onerel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+	{
+		/*
+		 * For partitioned tables, we want to do the recursive ANALYZE below.
+		 */
+	}
 	else
 	{
 		/* No need for a WARNING if we already complained during VACUUM */
@@ -253,10 +258,12 @@ analyze_rel(Oid relid, RangeVar *relation, int options,
 	LWLockRelease(ProcArrayLock);
 
 	/*
-	 * Do the normal non-recursive ANALYZE.
+	 * Do the normal non-recursive ANALYZE.  We can skip this for partitioned
+	 * tables, which don't contain any rows.
 	 */
-	do_analyze_rel(onerel, options, params, va_cols, acquirefunc, relpages,
-				   false, in_outer_xact, elevel);
+	if (onerel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
+		do_analyze_rel(onerel, options, params, va_cols, acquirefunc,
+					   relpages, false, in_outer_xact, elevel);
 
 	/*
 	 * If there are child tables, do recursive ANALYZE.
@@ -1260,6 +1267,7 @@ acquire_inherited_sample_rows(Relation onerel, int elevel,
 				nrels,
 				i;
 	ListCell   *lc;
+	bool 		has_child;
 
 	/*
 	 * Find all members of inheritance set.  We only need AccessShareLock on
@@ -1297,6 +1305,7 @@ acquire_inherited_sample_rows(Relation onerel, int elevel,
 	relblocks = (double *) palloc(list_length(tableOIDs) * sizeof(double));
 	totalblocks = 0;
 	nrels = 0;
+	has_child = false;
 	foreach(lc, tableOIDs)
 	{
 		Oid			childOID = lfirst_oid(lc);
@@ -1318,8 +1327,7 @@ acquire_inherited_sample_rows(Relation onerel, int elevel,
 
 		/* Check table type (MATVIEW can't happen, but might as well allow) */
 		if (childrel->rd_rel->relkind == RELKIND_RELATION ||
-			childrel->rd_rel->relkind == RELKIND_MATVIEW ||
-			childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+			childrel->rd_rel->relkind == RELKIND_MATVIEW)
 		{
 			/* Regular table, so use the regular row acquisition function */
 			acquirefunc = acquire_sample_rows;
@@ -1351,13 +1359,17 @@ acquire_inherited_sample_rows(Relation onerel, int elevel,
 		}
 		else
 		{
-			/* ignore, but release the lock on it */
-			Assert(childrel != onerel);
-			heap_close(childrel, AccessShareLock);
+			/*
+			 * ignore, but release the lock on it.  could be a partitioned
+			 * table.
+			 */
+			if (childrel != onerel)
+				heap_close(childrel, AccessShareLock);
 			continue;
 		}
 
 		/* OK, we'll process this child */
+		has_child = true;
 		rels[nrels] = childrel;
 		acquirefuncs[nrels] = acquirefunc;
 		relblocks[nrels] = (double) relpages;
@@ -1366,9 +1378,10 @@ acquire_inherited_sample_rows(Relation onerel, int elevel,
 	}
 
 	/*
-	 * If we don't have at least two tables to consider, fail.
+	 * If we don't have at least one child table to consider, fail.  If the
+	 * relation is a partitioned table, it's not counted as a child table.
 	 */
-	if (nrels < 2)
+	if (!has_child)
 	{
 		ereport(elevel,
 				(errmsg("skipping analyze of \"%s.%s\" inheritance tree --- this inheritance tree contains no analyzable child tables",
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 3cea220421..317012068b 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -1349,6 +1349,10 @@ ExecuteTruncate(TruncateStmt *stmt)
 	{
 		Relation	rel = (Relation) lfirst(cell);
 
+		/* Skip partitioned tables as there is nothing to do */
+		if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+			continue;
+
 		/*
 		 * Normally, we need a transaction-safe truncation here.  However, if
 		 * the table was either created in the current (sub)transaction or has
@@ -1459,7 +1463,11 @@ truncate_check_rel(Relation rel)
 {
 	AclResult	aclresult;
 
-	/* Only allow truncate on regular tables */
+	/*
+	 * Only allow truncate on regular tables and partitioned tables (although,
+	 * the latter are only being included here for the following checks; no
+	 * physical truncation will occur in their case.)
+	 */
 	if (rel->rd_rel->relkind != RELKIND_RELATION &&
 		rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
 		ereport(ERROR,
@@ -4006,8 +4014,9 @@ ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode)
 	{
 		AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
 
-		/* Foreign tables have no storage. */
-		if (tab->relkind == RELKIND_FOREIGN_TABLE)
+		/* Foreign tables have no storage, nor do partitioned tables. */
+		if (tab->relkind == RELKIND_FOREIGN_TABLE ||
+			tab->relkind == RELKIND_PARTITIONED_TABLE)
 			continue;
 
 		/*
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index 812fb4a48f..3a9b965266 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -32,6 +32,7 @@
 #include "access/xact.h"
 #include "catalog/namespace.h"
 #include "catalog/pg_database.h"
+#include "catalog/pg_inherits_fn.h"
 #include "catalog/pg_namespace.h"
 #include "commands/cluster.h"
 #include "commands/vacuum.h"
@@ -394,6 +395,9 @@ get_rel_oids(Oid relid, const RangeVar *vacrel)
 	{
 		/* Process a specific relation */
 		Oid			relid;
+		HeapTuple	tuple;
+		Form_pg_class classForm;
+		bool		include_parts;
 
 		/*
 		 * Since we don't take a lock here, the relation might be gone, or the
@@ -406,9 +410,29 @@ get_rel_oids(Oid relid, const RangeVar *vacrel)
 		 */
 		relid = RangeVarGetRelid(vacrel, NoLock, false);
 
-		/* Make a relation list entry for this guy */
+		/*
+		 * To check whether the relation is a partitioned table, fetch its
+		 * syscache entry.
+		 */
+		tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
+		if (!HeapTupleIsValid(tuple))
+			elog(ERROR, "cache lookup failed for relation %u", relid);
+		classForm = (Form_pg_class) GETSTRUCT(tuple);
+		include_parts = (classForm->relkind == RELKIND_PARTITIONED_TABLE);
+		ReleaseSysCache(tuple);
+
+		/*
+		 * Make relation list entries for this guy and its partitions, if any.
+		 * Note that the list returned by find_all_inheritors() include the
+		 * passed-in OID at its head.  Also note that we did not request a
+		 * lock to be taken to match what would be done otherwise.
+		 */
 		oldcontext = MemoryContextSwitchTo(vac_context);
-		oid_list = lappend_oid(oid_list, relid);
+		if (include_parts)
+			oid_list = list_concat(oid_list,
+								   find_all_inheritors(relid, NoLock, NULL));
+		else
+			oid_list = lappend_oid(oid_list, relid);
 		MemoryContextSwitchTo(oldcontext);
 	}
 	else
@@ -429,8 +453,14 @@ get_rel_oids(Oid relid, const RangeVar *vacrel)
 		{
 			Form_pg_class classForm = (Form_pg_class) GETSTRUCT(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_MATVIEW &&
+				classForm->relkind != RELKIND_PARTITIONED_TABLE)
 				continue;
 
 			/* Make a relation list entry for this guy */
@@ -1350,6 +1380,21 @@ vacuum_rel(Oid relid, RangeVar *relation, int options, VacuumParams *params)
 	}
 
 	/*
+	 * Ignore partitioned tables as there is no work to be done.  Since we
+	 * release the lock here, it's possible that any partitions added from
+	 * this point on will not get processed, but that seems harmless.
+	 */
+	if (onerel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+	{
+		relation_close(onerel, lmode);
+		PopActiveSnapshot();
+		CommitTransactionCommand();
+
+		/* It's OK for other commands to look at this table */
+		return true;
+	}
+
+	/*
 	 * Get a session-level lock too. This will protect our access to the
 	 * relation across multiple transactions, so that we can vacuum the
 	 * relation's TOAST table (if any) secure in the knowledge that no one is
-- 
2.11.0

