Reduce "Var IS [NOT] NULL" quals during constant folding

From: BharatDB <bharatdbpg(at)gmail(dot)com>
To: pgsql-hackers(at)lists(dot)postgresql(dot)org, pgsql-hackers(at)postgresql(dot)org
Subject: Reduce "Var IS [NOT] NULL" quals during constant folding
Date: 2025-08-28 13:01:55
Message-ID: CAAh00ETEMEXntw1gxp=xP+4sqrz80tK1R4VEhTpqH9CJpxs-wA@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

*Subject:* Contribution Interest – Bug Fix on Reducing "Var IS [NOT] NULL"
Quals During Constant Folding

Dear Team,

I hope this message finds you well.

With reference to the conversation ongioing in message ID:
(CAMbWs487wxHq0fUu+Cew7Y+n+wpY_B--PT-61syXrE40uZqErw
<https://www.postgresql.org/message-id/CAMbWs487wxHq0fUu%2BCew7Y%2Bn%2BwpY_B--PT-61syXrE40uZqErw%40mail.gmail.com>
,
I am writing to express my interest in contributing to the ongoing work on
fixing the bug related to reducing “Var IS [NOT] NULL” quals during
constant folding. As part of my initial efforts, I have been exploring the
planner code paths and introducing an approach to gather early catalog
information for base relations.

Specifically, I have implemented the following:

-

Preprocessing Relation RTEs
-

Added a new static function preprocess_relation_rtes() in
src/backend/optimizer/plan/planner.c to collect attribute-level metadata
(e.g., NOT NULL, generated columns, inheritance flags) at an early stage.
-

Hash Table for Attribute Info
-

Defined a new planner-local hash table (relattrinfo_htab) for storing
per-attribute information.
-

Created a supporting header (src/include/optimizer/relattrinfo.h) and
implementation file (src/backend/optimizer/util/relattrinfo.c).
-

Planner Integration
-

Initialized the attribute info hash table in subquery_planner().
-

Ensured preprocessing is invoked before pull_up_sublinks().
-

Optimization Step
-

Extended the constant folding path to check stored attribute metadata
during IS [NOT] NULL qual simplification, allowing safe reduction to
constant true/false where applicable.

1. Declare - *preprocess_relation_rtes()*

File: in subquery_planner() inside *src/backend/optimizer/plan/planner.c*.

-

Insert your new step *before* pull_up_sublinks().

*Snippet:*

/* * Do preprocessing of RTEs before we pull up sublinks */

preprocess_relation_rtes(root);

2.Create a new static function *preprocess_relation_rtes()* in planner.c:

File: *src/backend/optimizer/plan/planner.c*

*Snippet:*

/* * preprocess_relation_rtes * Gather early catalog info for each
base relation * (NOT NULL attrs, generated cols, inheritance flags).
*/static void preprocess_relation_rtes(PlannerInfo *root){ ListCell
*lc; Relation rel;
foreach(lc, root->parse->rtable) { RangeTblEntry *rte =
(RangeTblEntry *) lfirst(lc);
if (rte->rtekind == RTE_RELATION) { rel =
table_open(rte->relid, AccessShareLock);
/* Collect attnotnull info */ TupleDesc tupdesc
= RelationGetDescr(rel); for (int attno = 0; attno <
tupdesc->natts; attno++) { Form_pg_attribute
attr = TupleDescAttr(tupdesc, attno); if
(!attr->attisdropped) { if
(attr->attnotnull) store_notnull_info(root,
rte->relid, attno + 1);
if (attr->attgenerated)
store_generated_info(root, rte->relid, attno + 1); }
}
/* Inheritance flag */ if
(rel->rd_rel->relhassubclass) store_inh_info(root,
rte->relid);
table_close(rel, AccessShareLock); } }

}

3. Filename* – **src/include/nodes/pathnodes.h*

*Snippet:*

typedef struct PlannerInfo

{

HTAB *relattrinfo_htab; /* plannerlocal cache of attr metadata */

} PlannerInfo;

4. Create a new header.

Location: *src/include/optimizer/relattrinfo.h*

*Snippet: *

#ifndef RELATTRINFO_H

#define RELATTRINFO_H

#include "postgres.h"

#include "utils/hsearch.h"

/* key for hash table */

typedef struct RelAttrInfoKey

{

Oid relid; /* relation OID */

AttrNumber attno; /* attribute number (1-based) */

} RelAttrInfoKey;

/* value stored in hash table */

typedef struct RelAttrInfoEntry

{

RelAttrInfoKey key;

bool notnull;

bool generated;

} RelAttrInfoEntry;

extern HTAB *create_relattrinfo_htab(void);

#endif /* RELATTRINFO_H */

5. Created a new file.

Location: *src/backend/optimizer/util/relattrinfo.c*

*Snippet:*

#include "postgres.h"

#include "optimizer/relattrinfo.h"

HTAB *create_relattrinfo_htab(void)

{

HASHCTL ctl;

memset(&ctl, 0, sizeof(ctl));

ctl.keysize = sizeof(RelAttrInfoKey);

ctl.entrysize = sizeof(RelAttrInfoEntry);

return hash_create("Planner relattr info cache",

128, /* initial size */

&ctl,

HASH_ELEM | HASH_BLOBS);

}

6. File: *src/backend/optimizer/plan/planner.c* in *subquery_planner()*
(this runs before most rewrites).

*Snippet:*

*/** Create hash table for early attr info */

root->relattrinfo_htab = create_relattrinfo_htab();

/* Collect early info before pull_up_sublinks */

preprocess_relation_rtes(root);

7. *C**reate**d** a hash table*

File: *src/backend/optimizer/plan/planner.c*

*Example:*

typedef struct RelAttrInfoKey{ Oid relid; AttrNumber attno;}
RelAttrInfoKey;
typedef struct RelAttrInfoEntry{ RelAttrInfoKey key; bool
notnull; bool generated;} RelAttrInfoEntry;

Initialize it in *subquery_planner()*:

root->relattrinfo_htab = create_relattrinfo_htab();

8. Constant folding

if (IsA(arg, Var)){ Var *var = (Var *) arg; if
(lookup_notnull_info(root, var->varno, var->varattno)) { if
(ntest->nulltesttype == IS_NOT_NULL) return (Expr *)
makeBoolConst(true, false); else return (Expr *)
makeBoolConst(false, false); }}

With this groundwork, my plan is to continue refining the approach,
validating correctness against inheritance cases, and ensuring
compliance with planner invariants. I would also like to engage with
reviewers to align this with the broader design direction.

Please let me know how best I can proceed with submitting my patch for
review and contributing further to this fix.

Thank you for your time and consideration.

Best Regards,

Soumya

Browse pgsql-hackers by date

  From Date Subject
Next Message Andrew Jackson 2025-08-28 13:05:34 Re: Adding pg_dump flag for parallel export to pipes
Previous Message Konstantin Knizhnik 2025-08-28 12:57:27 Re: Non-reproducible AIO failure