From edf52acd1f6b08c33d54e1e4378428c47ed9a67c Mon Sep 17 00:00:00 2001 From: Richard Guo Date: Mon, 1 Dec 2025 16:14:53 +0900 Subject: [PATCH v3 3/3] Teach expr_is_nonnullable() to handle more expression types --- src/backend/optimizer/util/clauses.c | 105 +++++++++++++++++++++++---- 1 file changed, 90 insertions(+), 15 deletions(-) diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 933ac38d62e..712f071a389 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -4333,24 +4333,99 @@ var_is_nonnullable(PlannerInfo *root, Var *var, bool use_rel_info) bool expr_is_nonnullable(PlannerInfo *root, Expr *expr, bool use_rel_info) { - if (IsA(expr, Var) && root) - return var_is_nonnullable(root, (Var *) expr, use_rel_info); - if (IsA(expr, Const)) - return !((Const *) expr)->constisnull; - if (IsA(expr, CoalesceExpr)) + switch (nodeTag(expr)) { - /* - * A CoalesceExpr returns NULL if and only if all its arguments are - * NULL. Therefore, we can determine that a CoalesceExpr cannot be - * NULL if at least one of its arguments can be proven non-nullable. - */ - CoalesceExpr *coalesceexpr = (CoalesceExpr *) expr; + case T_Var: + { + if (root) + return var_is_nonnullable(root, (Var *) expr, use_rel_info); + } + break; + case T_Const: + return !((Const *) expr)->constisnull; + case T_CoalesceExpr: + { + /* + * A CoalesceExpr returns NULL if and only if all its + * arguments are NULL. Therefore, we can determine that a + * CoalesceExpr cannot be NULL if at least one of its + * arguments can be proven non-nullable. + */ + CoalesceExpr *coalesceexpr = (CoalesceExpr *) expr; - foreach_ptr(Expr, arg, coalesceexpr->args) - { - if (expr_is_nonnullable(root, arg, use_rel_info)) + foreach_ptr(Expr, arg, coalesceexpr->args) + { + if (expr_is_nonnullable(root, arg, use_rel_info)) + return true; + } + } + break; + case T_MinMaxExpr: + { + /* + * Like CoalesceExpr, a MinMaxExpr returns NULL only if all + * its arguments evaluate to NULL. + */ + MinMaxExpr *minmaxexpr = (MinMaxExpr *) expr; + + foreach_ptr(Expr, arg, minmaxexpr->args) + { + if (expr_is_nonnullable(root, arg, use_rel_info)) + return true; + } + } + break; + case T_ArrayExpr: + { + /* + * An ARRAY[] expression always returns a valid Array object, + * even if it is empty (ARRAY[]) or contains NULLs + * (ARRAY[NULL]). It never evaluates to a SQL NULL. + */ return true; - } + } + case T_NullTest: + { + /* + * An IS NULL / IS NOT NULL expression always returns a + * boolean value. It never returns SQL NULL. + */ + return true; + } + case T_BooleanTest: + { + /* + * A BooleanTest expression always evaluates to a boolean + * value. It never returns SQL NULL. + */ + return true; + } + case T_CaseExpr: + { + /* + * A CASE expression is non-nullable if all branch results are + * non-nullable. We must also verify that the default result + * (ELSE) exists and is non-nullable. + */ + CaseExpr *caseexpr = (CaseExpr *) expr; + + /* The default result must be present and non-nullable */ + if (caseexpr->defresult == NULL || + !expr_is_nonnullable(root, caseexpr->defresult, use_rel_info)) + return false; + + /* All branch results must be non-nullable */ + foreach_ptr(CaseWhen, casewhen, caseexpr->args) + { + if (!expr_is_nonnullable(root, casewhen->result, use_rel_info)) + return false; + } + + return true; + } + break; + default: + break; } return false; -- 2.39.5 (Apple Git-154)