From 867dbc9e866ce149b07ff55631598e0843046b61 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E4=B8=80=E6=8C=83?= <yizhi.fzh@alibaba-inc.com>
Date: Sat, 3 Jul 2021 21:06:32 +0800
Subject: [PATCH v4 3/6] add the not null attrs for RelOptInfo. Here is how it
 works.

For baserel, it records the notnull attrs as a bitmapset and store it to
RelOptInfo->notnull_attrs[0].  As for the joinrel, suppose the relids is {1,3,
5}, then the notnull_attrs[1/3/5] will be used to store notnull_attrs bitmapset
for relation 1,3,5 separately. I don't handle this stuff for all kinds of upper
relation and subquery so far since it doesn't pass the design review yet.
---
 notnulltest.sql                       | 24 ++++++++
 src/backend/nodes/bitmapset.c         | 14 +++++
 src/backend/optimizer/path/allpaths.c | 36 ++++++++++++
 src/backend/optimizer/util/plancat.c  |  9 +++
 src/backend/optimizer/util/relnode.c  | 80 +++++++++++++++++++++++++++
 src/include/nodes/bitmapset.h         |  1 +
 src/include/nodes/pathnodes.h         |  6 ++
 7 files changed, 170 insertions(+)
 create mode 100644 notnulltest.sql

diff --git a/notnulltest.sql b/notnulltest.sql
new file mode 100644
index 0000000000..2a36bd0c7f
--- /dev/null
+++ b/notnulltest.sql
@@ -0,0 +1,24 @@
+create table t1(a int, b int not null, c int, d int);
+create table t2(a int, b int not null, c int, d int);
+
+-- single rel
+select * from t1;
+select * from t1 where a > 1;
+select * from t2 where a > 1 or c > 1;
+
+-- partitioned relation.
+create table p (a int, b int, c int not null) partition by range(a);
+create table p_1 partition of p for values from (0) to (10000) partition by list(b);
+create table p_1_1(b int,  c int not null, a int);
+alter table p_1 attach partition p_1_1 for values in (1);
+
+
+select * from p;
+select * from p where a > 1;
+
+
+-- test join:
+select * from t1, t2 where t1.a = t2.c;
+select t1.a, t2.b, t2.c from t1 left join t2 on t1.a = t2.c;
+select * from t1 full join t2 on t1.a = t2.c;
+
diff --git a/src/backend/nodes/bitmapset.c b/src/backend/nodes/bitmapset.c
index 649478b0d4..fa71f36aaf 100644
--- a/src/backend/nodes/bitmapset.c
+++ b/src/backend/nodes/bitmapset.c
@@ -663,6 +663,20 @@ bms_num_members(const Bitmapset *a)
 	return result;
 }
 
+/*
+ * bms_max_member - the max member in this bitmap.
+ */
+int
+bms_max_member(const Bitmapset *a)
+{
+	int result;
+	if (a == NULL || bms_is_empty(a))
+		elog(ERROR, "Must be an non-empty bitmapset.");
+	result = (a->nwords - 1) * BITS_PER_BITMAPWORD;
+	result += bmw_leftmost_one_pos(a->words[a->nwords - 1]);
+	return result;
+}
+
 /*
  * bms_membership - does a set have zero, one, or multiple members?
  *
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 296dd75c1b..a63a3dfe00 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -354,6 +354,40 @@ set_base_rel_pathlists(PlannerInfo *root)
 	}
 }
 
+/*
+ * set_baserel_notnull_attrs
+ *
+ *	Set baserel's notnullattrs based on baserestrictinfo
+ */
+static void
+set_baserel_notnull_attrs(RelOptInfo *rel)
+{
+	List *clauses = extract_actual_clauses(rel->baserestrictinfo, false);
+	ListCell	*lc;
+	foreach(lc, find_nonnullable_vars((Node *)clauses))
+	{
+		Var *var = (Var *) lfirst(lc);
+		if (var->varno != rel->relid)
+		{
+			/* Lateral Join */
+			continue;
+		}
+		Assert(var->varno == rel->relid);
+		rel->notnull_attrs[0] = bms_add_member(rel->notnull_attrs[0],
+											   var->varattno - FirstLowInvalidHeapAttributeNumber);
+	}
+
+	/* Debug Only, Will be removed at last. */
+	if (false)
+	{
+		elog(INFO, "FirstLowInvalidHeapAttributeNumber = %d, BaseRel(%d), notnull_attrs = %s",
+			 FirstLowInvalidHeapAttributeNumber,
+			 rel->relid,
+			 bmsToString(rel->notnull_attrs[0])
+			);
+	}
+}
+
 /*
  * set_rel_size
  *	  Set size estimates for a base relation
@@ -457,6 +491,8 @@ set_rel_size(PlannerInfo *root, RelOptInfo *rel,
 		}
 	}
 
+	set_baserel_notnull_attrs(rel);
+
 	/*
 	 * We insist that all non-dummy rels have a nonzero rowcount estimate.
 	 */
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index c5194fdbbf..7d3b40090e 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -117,6 +117,7 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
 	Relation	relation;
 	bool		hasindex;
 	List	   *indexinfos = NIL;
+	int		i;
 
 	/*
 	 * We need not lock the relation since it was already locked, either by
@@ -471,6 +472,14 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
 	if (inhparent && relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
 		set_relation_partition_info(root, rel, relation);
 
+	for (i = 0; i < relation->rd_att->natts; i++)
+	{
+		FormData_pg_attribute attr = relation->rd_att->attrs[i];
+		if (attr.attnotnull)
+			rel->notnull_attrs[0] = bms_add_member(rel->notnull_attrs[0],
+												   attr.attnum - FirstLowInvalidHeapAttributeNumber);
+	}
+
 	table_close(relation, NoLock);
 
 	/*
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 47769cea45..f017a7d52d 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -16,6 +16,7 @@
 
 #include <limits.h>
 
+#include "access/sysattr.h"
 #include "miscadmin.h"
 #include "nodes/nodeFuncs.h"
 #include "optimizer/appendinfo.h"
@@ -259,6 +260,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 	rel->all_partrels = NULL;
 	rel->partexprs = NULL;
 	rel->nullable_partexprs = NULL;
+	rel->notnull_attrs = palloc0(sizeof(Bitmapset *) * 1);
 
 	/*
 	 * Pass assorted information down the inheritance hierarchy.
@@ -557,6 +559,81 @@ add_join_rel(PlannerInfo *root, RelOptInfo *joinrel)
 	}
 }
 
+static void
+copy_notnull_attrs_to_joinrel(RelOptInfo *joinrel, RelOptInfo *rel)
+{
+	int relid;
+	if (bms_get_singleton_member(rel->relids, &relid))
+		joinrel->notnull_attrs[relid] = bms_copy(rel->notnull_attrs[0]);
+	else
+	{
+		relid = -1;
+		while ((relid = bms_next_member(rel->relids, relid)) >= 0)
+			joinrel->notnull_attrs[relid] = bms_copy(rel->notnull_attrs[relid]);
+	}
+}
+
+/*
+ *
+ */
+static void
+set_joinrel_notnull_attrs(RelOptInfo *joinrel,
+						  RelOptInfo *outer_rel,
+						  RelOptInfo *inner_rel,
+						  List *restrictlist,
+						  SpecialJoinInfo *sjinfo)
+{
+	if (sjinfo->jointype == JOIN_FULL)
+		/* Both sides are nullable. */
+		return;
+	/* If it is not FULL join, the outer side is not changed. */
+	copy_notnull_attrs_to_joinrel(joinrel, outer_rel);
+	switch(sjinfo->jointype)
+	{
+		case JOIN_ANTI:
+		case JOIN_SEMI:
+		case JOIN_INNER:
+			copy_notnull_attrs_to_joinrel(joinrel, inner_rel);
+			{
+				ListCell	*lc;
+				List *clauses = extract_actual_clauses(restrictlist, false);
+				foreach(lc, find_nonnullable_vars((Node *) clauses))
+				{
+					Var *var = lfirst_node(Var, lc);
+					if (!bms_is_member(var->varno, joinrel->relids))
+					{
+						/* lateral join */
+						continue;
+					}
+					joinrel->notnull_attrs[var->varno] = bms_add_member(
+						joinrel->notnull_attrs[var->varno],
+						var->varattno - FirstLowInvalidHeapAttributeNumber);
+				}
+			}
+			break;
+		case JOIN_LEFT:
+			break;
+		default:
+			elog(ERROR, "Unexpected join type %d", sjinfo->jointype);
+	}
+	/* Debug Only, will be removed at last. */
+	if (false)
+	{
+		int relid = -1;
+		int eLevel = INFO;
+		elog(eLevel, "Dump notnull for JoinRel(%s)", bmsToString(joinrel->relids));
+		while((relid = bms_next_member(joinrel->relids, relid)) >= 0)
+		{
+			Bitmapset *notnullattrs = joinrel->notnull_attrs[relid];
+			if (notnullattrs != NULL)
+				elog(eLevel, "FirstLowInvalidHeapAttributeNumber = %d, RELID = (%d), notnull_attrs: %s",
+					 FirstLowInvalidHeapAttributeNumber,
+					 relid,
+					 bmsToString(notnullattrs));
+		}
+	}
+
+}
 /*
  * build_join_rel
  *	  Returns relation entry corresponding to the union of two given rels,
@@ -674,6 +751,7 @@ build_join_rel(PlannerInfo *root,
 	joinrel->all_partrels = NULL;
 	joinrel->partexprs = NULL;
 	joinrel->nullable_partexprs = NULL;
+	joinrel->notnull_attrs = palloc0(sizeof(Bitmapset *) * (bms_max_member(joinrel->relids) + 1));
 
 	/* Compute information relevant to the foreign relations. */
 	set_foreign_rel_properties(joinrel, outer_rel, inner_rel);
@@ -765,6 +843,8 @@ build_join_rel(PlannerInfo *root,
 			lappend(root->join_rel_level[root->join_cur_level], joinrel);
 	}
 
+	set_joinrel_notnull_attrs(joinrel, outer_rel, inner_rel, restrictlist, sjinfo);
+
 	return joinrel;
 }
 
diff --git a/src/include/nodes/bitmapset.h b/src/include/nodes/bitmapset.h
index 1fd12de698..303873a546 100644
--- a/src/include/nodes/bitmapset.h
+++ b/src/include/nodes/bitmapset.h
@@ -94,6 +94,7 @@ extern bool bms_nonempty_difference(const Bitmapset *a, const Bitmapset *b);
 extern int	bms_singleton_member(const Bitmapset *a);
 extern bool bms_get_singleton_member(const Bitmapset *a, int *member);
 extern int	bms_num_members(const Bitmapset *a);
+extern int	bms_max_member(const Bitmapset *a);
 
 /* optimized tests when we don't need to know exact membership count: */
 extern BMS_Membership bms_membership(const Bitmapset *a);
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 6e068f2c8b..7bf1896e12 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -686,6 +686,12 @@ typedef struct RelOptInfo
 	/* default result targetlist for Paths scanning this relation */
 	struct PathTarget *reltarget;	/* list of Vars/Exprs, cost, width */
 
+	Bitmapset	**notnull_attrs; /* The attno which is not null after evalating
+								  * all the quals on this relation, for baserel,
+								  * the len would always 1. and for others the array
+								  * index is relid from relids.
+								  */
+
 	/* materialization information */
 	List	   *pathlist;		/* Path structures */
 	List	   *ppilist;		/* ParamPathInfos used in pathlist */
-- 
2.21.0

