Thread

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

    BharatDB <bharatdbpg@gmail.com> — 2025-08-28T13:01:55Z

    *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