>From 693c9b0f67a0fabc38212e436453a29f9ae275c5 Mon Sep 17 00:00:00 2001
From: Kyotaro Horiguchi <horiguchi.kyotaro@lab.ntt.co.jp>
Date: Thu, 6 Aug 2015 16:59:47 +0900
Subject: [PATCH 4/6] Syntactical part of multivariate coefficient.

This adds new two syntax

 ALTER TABLE <name> (ADD|DROP) STATISTICS (<type>,..) ON (<col>,..)

To avoid shift/reduce conflict caused by adding these syntaxes, two
existing rules are splitted into two rules each. They simply add or
remove one definition tuple into/from pg_mvcoefficeint and currently
only one definition for one relation is allowed. The tuples are
removed on deletion of the target relation or covering columns.
---
 src/backend/catalog/heap.c       |  49 ++++++++++
 src/backend/commands/tablecmds.c | 191 +++++++++++++++++++++++++++++++++++++++
 src/backend/parser/gram.y        |  46 +++++++++-
 src/include/catalog/heap.h       |   1 +
 src/include/nodes/parsenodes.h   |   2 +
 5 files changed, 287 insertions(+), 2 deletions(-)

diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index d04e94d..32fafb7 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -46,6 +46,7 @@
 #include "catalog/pg_constraint.h"
 #include "catalog/pg_foreign_table.h"
 #include "catalog/pg_inherits.h"
+#include "catalog/pg_mvcoefficient.h"
 #include "catalog/pg_namespace.h"
 #include "catalog/pg_statistic.h"
 #include "catalog/pg_tablespace.h"
@@ -2647,6 +2648,51 @@ cookConstraint(ParseState *pstate,
 }
 
 
+void
+RemoveMvStatistics(Oid relid, AttrNumber attnum)
+{
+	Relation pgmvcoef;
+	SysScanDesc scan;
+	ScanKeyData key[1];
+	HeapTuple	tuple;
+
+	pgmvcoef = heap_open(MvCoefficientRelationId, RowExclusiveLock);
+
+	ScanKeyInit(&key[0],
+				Anum_pg_mvcoefficient_mvcreloid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(relid));
+
+	scan = systable_beginscan(pgmvcoef, MvCoefficientIndexId, true,
+							  NULL, 1, key);
+
+	while (HeapTupleIsValid(tuple = systable_getnext(scan)))
+	{
+		if (attnum != 0)
+		{
+			/* Check if this attnum is contained in this mvc entry */
+			int i;
+			Form_pg_mvcoefficient mvc =
+				(Form_pg_mvcoefficient) GETSTRUCT (tuple);
+
+			for (i = 0 ; i < mvc->mvcnattrs ; i++)
+			{
+				if (mvc->mvcattrs.values[i] == attnum)
+					break;
+			}
+
+			/* This mvcoef entry has no relation with this attribute set */
+			if (i == mvc->mvcnattrs)
+				continue;
+		}
+
+		simple_heap_delete(pgmvcoef, &tuple->t_self);
+	}
+
+	systable_endscan(scan);
+
+	heap_close(pgmvcoef, RowExclusiveLock);
+}
 /*
  * RemoveStatistics --- remove entries in pg_statistic for a rel or column
  *
@@ -2690,6 +2736,9 @@ RemoveStatistics(Oid relid, AttrNumber attnum)
 	systable_endscan(scan);
 
 	heap_close(pgstatistic, RowExclusiveLock);
+
+	/* Remove mutivariate coefficient entry */
+	RemoveMvStatistics(relid, attnum);
 }
 
 
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 126b119..843a14e 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -36,6 +36,7 @@
 #include "catalog/pg_inherits.h"
 #include "catalog/pg_inherits_fn.h"
 #include "catalog/pg_namespace.h"
+#include "catalog/pg_mvcoefficient.h"
 #include "catalog/pg_opclass.h"
 #include "catalog/pg_tablespace.h"
 #include "catalog/pg_trigger.h"
@@ -402,6 +403,7 @@ static bool ATPrepChangePersistence(Relation rel, bool toLogged);
 static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
 					char *tablespacename, LOCKMODE lockmode);
 static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode);
+static void	ATExecAddDropMvStatistics(Relation rel, List *param, bool drop);
 static void ATExecSetRelOptions(Relation rel, List *defList,
 					AlterTableType operation,
 					LOCKMODE lockmode);
@@ -3015,6 +3017,8 @@ AlterTableGetLockLevel(List *cmds)
 				 * applies: we don't currently allow concurrent catalog
 				 * updates.
 				 */
+			case AT_AddMvStatistics:
+			case AT_DropMvStatistics:
 			case AT_SetStatistics:		/* Uses MVCC in getTableAttrs() */
 			case AT_ClusterOn:	/* Uses MVCC in getIndexes() */
 			case AT_DropCluster:		/* Uses MVCC in getIndexes() */
@@ -3175,6 +3179,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
 			break;
 		case AT_SetOptions:		/* ALTER COLUMN SET ( options ) */
 		case AT_ResetOptions:	/* ALTER COLUMN RESET ( options ) */
+		case AT_AddMvStatistics:
+		case AT_DropMvStatistics:
 			ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW | ATT_INDEX | ATT_FOREIGN_TABLE);
 			/* This command never recurses */
 			pass = AT_PASS_MISC;
@@ -3591,6 +3597,12 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
 			 * Nothing to do here; Phase 3 does the work
 			 */
 			break;
+		case AT_AddMvStatistics: /* ADD STATISTICS */
+			ATExecAddDropMvStatistics(rel, (List *)cmd->def, false);
+			break;
+		case AT_DropMvStatistics: /* DROP STATISTICS */
+			ATExecAddDropMvStatistics(rel, (List *)cmd->def, true);
+			break;
 		case AT_SetRelOptions:	/* SET (...) */
 		case AT_ResetRelOptions:		/* RESET (...) */
 		case AT_ReplaceRelOptions:		/* replace entire option list */
@@ -9300,6 +9312,185 @@ ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, char *tablespacename, L
 	tab->newTableSpace = tablespaceId;
 }
 
+static int
+cmpint16(const void *p1, const void *p2)
+{
+	int a = *(int16 *)p1;
+	int b = *(int16 *)p2;
+
+	return a - b;
+}
+
+static void
+ATExecAddDropMvStatistics(Relation rel, List * params, bool drop)
+{
+	Oid			relid = RelationGetRelid(rel);
+	int			ncols = 0;
+	int16		colids[MVCOEF_MAX_COLS];
+	Relation 	mvcrel;
+	HeapTuple	oldtup, newtup;
+	ScanKeyData	scankey;
+	SysScanDesc	sysscan;
+	Datum	values[Natts_pg_mvcoefficient];
+	bool	nulls[Natts_pg_mvcoefficient];
+	bool	replaces[Natts_pg_mvcoefficient];
+	List	   *stattypes = (List*) linitial(params);
+	List	   *colnames  = (List*) lsecond(params);
+	char	   *stattype = strVal(linitial(stattypes));
+	ListCell   *lc;
+	int i;
+
+	if (list_length(stattypes) > 1)
+		ereport(ERROR,
+				(errcode(ERRCODE_SYNTAX_ERROR),
+				 errmsg("multiple stat types specfied")));
+	
+	if(strcasecmp(stattype, "mvndistinct") != 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_SYNTAX_ERROR),
+				 errmsg("unknown multivariate stat type: %s", stattype)));
+
+	if (colnames && list_length(colnames) < 2)
+		ereport(ERROR,
+				(errcode(ERRCODE_SYNTAX_ERROR),
+				 errmsg("at least two columns needed")));
+
+	if (list_length(colnames) > MVCOEF_MAX_COLS)
+		ereport(ERROR,
+				(errcode(ERRCODE_TOO_MANY_COLUMNS),
+				 errmsg("number of columns (%d) exceeds limit (%d)",
+						list_length(colnames), MVCOEF_MAX_COLS)));
+
+	foreach (lc, colnames)
+	{
+		char	   *attname = strVal(lfirst (lc));
+		HeapTuple	atttup;
+		int16		colid;
+
+		atttup = SearchSysCacheAttName(relid, attname);
+		if (!HeapTupleIsValid(atttup))
+			ereport(ERROR,
+					(errcode(ERRCODE_UNDEFINED_COLUMN),
+					 errmsg("column \"%s\" of relation \"%s\" does not exist",
+							attname, get_rel_name(relid))));
+		colid = ((Form_pg_attribute) GETSTRUCT(atttup))->attnum;
+		ReleaseSysCache(atttup);
+
+		if (!AttrNumberIsForUserDefinedAttr(colid))
+			ereport(ERROR,
+					(errcode(ERRCODE_UNDEFINED_COLUMN),
+					 errmsg("system columns cannot be specified")));
+			
+		for (i = 0 ; i < ncols ; i++)
+		{
+			if (colids[i] == colid)
+				ereport(ERROR,
+						(errcode(ERRCODE_DUPLICATE_COLUMN),
+						 errmsg("column \"%s\" specified more than once",
+							 attname)));
+		}
+
+		colids[ncols++] = colid;
+	}
+
+	mvcrel = heap_open(MvCoefficientRelationId, RowExclusiveLock);
+
+	ScanKeyInit(&scankey,
+				Anum_pg_mvcoefficient_mvcreloid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(relid));
+	sysscan = systable_beginscan(mvcrel, MvCoefficientIndexId, true,
+								 NULL, 1, &scankey);
+
+	/* colum list is required to be sorted in comparison */
+	qsort(colids, ncols, sizeof(int16), cmpint16);
+
+	if (drop)
+	{
+		bool dropped = false;
+
+		while (HeapTupleIsValid(oldtup = systable_getnext(sysscan)))
+		{
+			Form_pg_mvcoefficient mvc =
+				(Form_pg_mvcoefficient) GETSTRUCT (oldtup);
+			int i;
+
+			/* Fast exit, number of columns don't match  */
+			if (mvc->mvcnattrs != ncols)
+				continue;
+
+			/*
+			 * Check for individual columns. This assumes that both of the
+			 * column vector in mvc and colids are sorted.
+			 */
+			for (i = 0 ; i < ncols ; i++)
+			{
+				if (mvc->mvcattrs.values[i] != colids[i])
+					break;
+			}
+
+			/*
+			 * Remove it if match. Unconditionally do this for the case of
+			 * ncols == 0.
+			 */
+			if (i == ncols)
+			{
+				simple_heap_delete(mvcrel, &oldtup->t_self);
+				dropped = true;
+			}
+		}
+
+		if (!dropped)
+			ereport(ERROR,
+					(errcode(ERRCODE_NO_DATA_FOUND),
+					 errmsg("no matching statistics found")));
+	}
+	else
+	{
+		int2vector *colvec;
+
+		oldtup = systable_getnext(sysscan);
+
+		for (i = 0; i < Natts_pg_mvcoefficient ; ++i)
+		{
+			nulls[i] = false;
+			values[i] = false;
+			replaces[i] = false;
+		}
+
+		colvec = buildint2vector(colids, ncols);
+
+		values[Anum_pg_mvcoefficient_mvcreloid - 1] = ObjectIdGetDatum(relid);
+		values[Anum_pg_mvcoefficient_mvcnattrs - 1] = Int16GetDatum(ncols);
+		values[Anum_pg_mvcoefficient_mvccoefficient - 1]  = Float8GetDatum(1.0);
+		values[Anum_pg_mvcoefficient_mvcattrs - 1]  = PointerGetDatum(colvec);
+
+		if (HeapTupleIsValid(oldtup))
+		{
+			replaces[Anum_pg_mvcoefficient_mvcnattrs - 1] = true;
+			replaces[Anum_pg_mvcoefficient_mvccoefficient - 1] = true;
+			replaces[Anum_pg_mvcoefficient_mvcattrs - 1] = true;
+			newtup = heap_modify_tuple(oldtup, RelationGetDescr(mvcrel),
+									   values, nulls, replaces);
+			simple_heap_update(mvcrel, &oldtup->t_self, newtup);
+
+			elog(NOTICE,
+				 "multivariate distinctness for relation %s is cleard",
+				 get_rel_name(relid));
+		}
+		else
+		{
+			newtup = heap_form_tuple(RelationGetDescr(mvcrel), values, nulls);
+			simple_heap_insert(mvcrel, newtup);
+		}
+		CatalogUpdateIndexes(mvcrel, newtup);
+	}
+
+	systable_endscan(sysscan);
+
+	heap_close(mvcrel, RowExclusiveLock);
+}
+
 /*
  * Set, reset, or replace reloptions.
  */
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 1efc6d6..a637df0 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -2033,7 +2033,7 @@ alter_table_cmd:
 					$$ = (Node *)n;
 				}
 			/* ALTER TABLE <name> DROP [COLUMN] IF EXISTS <colname> [RESTRICT|CASCADE] */
-			| DROP opt_column IF_P EXISTS ColId opt_drop_behavior
+			| DROP COLUMN IF_P EXISTS ColId opt_drop_behavior
 				{
 					AlterTableCmd *n = makeNode(AlterTableCmd);
 					n->subtype = AT_DropColumn;
@@ -2042,8 +2042,17 @@ alter_table_cmd:
 					n->missing_ok = TRUE;
 					$$ = (Node *)n;
 				}
+			| DROP IF_P EXISTS ColId opt_drop_behavior
+				{
+					AlterTableCmd *n = makeNode(AlterTableCmd);
+					n->subtype = AT_DropColumn;
+					n->name = $4;
+					n->behavior = $5;
+					n->missing_ok = TRUE;
+					$$ = (Node *)n;
+				}
 			/* ALTER TABLE <name> DROP [COLUMN] <colname> [RESTRICT|CASCADE] */
-			| DROP opt_column ColId opt_drop_behavior
+			| DROP COLUMN ColId opt_drop_behavior
 				{
 					AlterTableCmd *n = makeNode(AlterTableCmd);
 					n->subtype = AT_DropColumn;
@@ -2052,6 +2061,15 @@ alter_table_cmd:
 					n->missing_ok = FALSE;
 					$$ = (Node *)n;
 				}
+			| DROP ColId opt_drop_behavior
+				{
+					AlterTableCmd *n = makeNode(AlterTableCmd);
+					n->subtype = AT_DropColumn;
+					n->name = $2;
+					n->behavior = $3;
+					n->missing_ok = FALSE;
+					$$ = (Node *)n;
+				}
 			/*
 			 * ALTER TABLE <name> ALTER [COLUMN] <colname> [SET DATA] TYPE <typename>
 			 *		[ USING <expression> ]
@@ -2087,6 +2105,14 @@ alter_table_cmd:
 					n->def = $2;
 					$$ = (Node *)n;
 				}
+			/* ALTER TABLE <name> ADD STATISTICS (...) ON ( <cols> )  */
+			| ADD_P STATISTICS '(' name_list ')' ON '(' columnList ')'
+				{
+					AlterTableCmd *n = makeNode(AlterTableCmd);
+					n->subtype = AT_AddMvStatistics;
+					n->def = (Node*) list_make2((Node*) $4, (Node*) $8);
+					$$ = (Node *)n;
+				}
 			/* ALTER TABLE <name> ALTER CONSTRAINT ... */
 			| ALTER CONSTRAINT name ConstraintAttributeSpec
 				{
@@ -2130,6 +2156,22 @@ alter_table_cmd:
 					n->missing_ok = FALSE;
 					$$ = (Node *)n;
 				}
+			/* ALTER TABLE <name> DROP STATISTICS (...) ON ( <cols> )  */
+			| DROP STATISTICS '(' name_list ')' ON '(' columnList ')'
+				{
+					AlterTableCmd *n = makeNode(AlterTableCmd);
+					n->subtype = AT_DropMvStatistics;
+					n->def = (Node*) list_make2((Node*) $4, (Node*) $8);
+					$$ = (Node *)n;
+				}
+			/* ALTER TABLE <name> DROP STATISTICS (...) */
+			| DROP STATISTICS '(' name_list ')'
+				{
+					AlterTableCmd *n = makeNode(AlterTableCmd);
+					n->subtype = AT_DropMvStatistics;
+					n->def = (Node*) list_make2((Node*) $4, NULL);
+					$$ = (Node *)n;
+				}
 			/* ALTER TABLE <name> SET WITH OIDS  */
 			| SET WITH OIDS
 				{
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
index e6ac394..30d2acd 100644
--- a/src/include/catalog/heap.h
+++ b/src/include/catalog/heap.h
@@ -119,6 +119,7 @@ extern void RemoveAttrDefault(Oid relid, AttrNumber attnum,
 				  DropBehavior behavior, bool complain, bool internal);
 extern void RemoveAttrDefaultById(Oid attrdefId);
 extern void RemoveStatistics(Oid relid, AttrNumber attnum);
+extern void RemoveMvStatistics(Oid relid, AttrNumber attnum);
 
 extern Form_pg_attribute SystemAttributeDefinition(AttrNumber attno,
 						  bool relhasoids);
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index f0dcd2f..479b8bc 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1513,6 +1513,8 @@ typedef enum AlterTableType
 	AT_ReplicaIdentity,			/* REPLICA IDENTITY */
 	AT_EnableRowSecurity,		/* ENABLE ROW SECURITY */
 	AT_DisableRowSecurity,		/* DISABLE ROW SECURITY */
+	AT_AddMvStatistics,			/* ADD STATISTICS (...) ON (...)  */
+	AT_DropMvStatistics,		/* DROP STATISTICS (...) ON (...)  */
 	AT_GenericOptions			/* OPTIONS (...) */
 } AlterTableType;
 
-- 
1.8.3.1

