From 867dbc9e866ce149b07ff55631598e0843046b61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=80=E6=8C=83?= 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 +#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