diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c index b31d892121..277314c8cb 100644 --- a/src/backend/optimizer/plan/initsplan.c +++ b/src/backend/optimizer/plan/initsplan.c @@ -2618,6 +2618,50 @@ check_redundant_nullability_qual(PlannerInfo *root, Node *clause) return false; } +/* + * add_baserestrictinfo_to_rel + * Add 'restrictinfo' as a baserestrictinfo to the base relation denoted by + * 'relid' with some prechecks to try to determine if the qual is always + * true, in which case we ignore it rather than add it. + */ +static void +add_baserestrictinfo_to_rel(PlannerInfo *root, Index relid, + RestrictInfo *restrictinfo) +{ + RelOptInfo *rel = find_base_rel(root, relid); + + Assert(bms_membership(restrictinfo->required_relids) == BMS_SINGLETON); + + /* Any IS NOT NULL qual on a NOT NULL column is always true */ + if (IsA(restrictinfo->clause, NullTest)) + { + NullTest *nulltest = (NullTest *) restrictinfo->clause; + + if (nulltest->nulltesttype == IS_NOT_NULL && IsA(nulltest->arg, Var)) + { + Var *var = (Var *) nulltest->arg; + + Assert(var->varno == rel->relid); + + /* don't add the qual as system columns cannot be NULL */ + if (var->varattno < 0) + return; + + /* don't add the IS NOT NULL when the column is defined NOT NULL */ + if (var->varattno > 0 && + bms_is_member(var->varattno, rel->notnullattnums)) + return; + } + } + + /* Add clause to rel's restriction list */ + rel->baserestrictinfo = lappend(rel->baserestrictinfo, restrictinfo); + + /* Update security level info */ + rel->baserestrict_min_security = Min(rel->baserestrict_min_security, + restrictinfo->security_level); +} + /* * distribute_restrictinfo_to_rels * Push a completed RestrictInfo into the proper restriction or join @@ -2633,57 +2677,39 @@ distribute_restrictinfo_to_rels(PlannerInfo *root, { Relids relids = restrictinfo->required_relids; RelOptInfo *rel; + int relid; - switch (bms_membership(relids)) + if (relids == NULL) { - case BMS_SINGLETON: - - /* - * There is only one relation participating in the clause, so it - * is a restriction clause for that relation. - */ - rel = find_base_rel(root, bms_singleton_member(relids)); - - /* Add clause to rel's restriction list */ - rel->baserestrictinfo = lappend(rel->baserestrictinfo, - restrictinfo); - /* Update security level info */ - rel->baserestrict_min_security = Min(rel->baserestrict_min_security, - restrictinfo->security_level); - break; - case BMS_MULTIPLE: - - /* - * The clause is a join clause, since there is more than one rel - * in its relid set. - */ - - /* - * Check for hashjoinable operators. (We don't bother setting the - * hashjoin info except in true join clauses.) - */ - check_hashjoinable(restrictinfo); + /* + * clause references no rels, and therefore we have no place to + * attach it. Shouldn't get here if callers are working properly. + */ + elog(ERROR, "cannot cope with variable-free clause"); + } + else if (bms_get_singleton_member(relids, &relid)) + add_baserestrictinfo_to_rel(root, relid, restrictinfo); + else + { + /* + * The clause is a join clause, since there is more than one rel in + * its relid set. + */ - /* - * Likewise, check if the clause is suitable to be used with a - * Memoize node to cache inner tuples during a parameterized - * nested loop. - */ - check_memoizable(restrictinfo); + /* + * Check for hashjoinable operators. (We don't bother setting the + * hashjoin info except in true join clauses.) + */ + check_hashjoinable(restrictinfo); - /* - * Add clause to the join lists of all the relevant relations. - */ - add_join_clause_to_rels(root, restrictinfo, relids); - break; - default: + /* + * Likewise, check if the clause is suitable to be used with a Memoize + * node to cache inner tuples during a parameterized nested loop. + */ + check_memoizable(restrictinfo); - /* - * clause references no rels, and therefore we have no place to - * attach it. Shouldn't get here if callers are working properly. - */ - elog(ERROR, "cannot cope with variable-free clause"); - break; + /* Add clause to the join lists of all the relevant relations. */ + add_join_clause_to_rels(root, restrictinfo, relids); } } diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index 39932d3c2d..d810ad8127 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -163,6 +163,15 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent, rel->attr_widths = (int32 *) palloc0((rel->max_attr - rel->min_attr + 1) * sizeof(int32)); + /* record which columns are defined as NOT NULL */ + for (int i = 0; i < relation->rd_att->natts; i++) + { + FormData_pg_attribute *attr = &relation->rd_att->attrs[i]; + + if (attr->attnotnull) + rel->notnullattnums = bms_add_member(rel->notnullattnums, attr->attnum); + } + /* * Estimate relation size --- unless it's an inheritance parent, in which * case the size we want is not the rel's own size but the size of its diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h index c17b53f7ad..851a4513a9 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -913,6 +913,8 @@ typedef struct RelOptInfo Relids *attr_needed pg_node_attr(read_write_ignore); /* array indexed [min_attr .. max_attr] */ int32 *attr_widths pg_node_attr(read_write_ignore); + /* zero-based set containing attnums of NOT NULL columns */ + Bitmapset *notnullattnums; /* relids of outer joins that can null this baserel */ Relids nulling_relids; /* LATERAL Vars and PHVs referenced by rel */