Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1vxzZh-00HKS3-0N for pgsql-hackers@arkaria.postgresql.org; Thu, 05 Mar 2026 03:40:33 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1vxzZf-00FbZR-10 for pgsql-hackers@arkaria.postgresql.org; Thu, 05 Mar 2026 03:40:31 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1vxzZe-00FbZI-2q for pgsql-hackers@lists.postgresql.org; Thu, 05 Mar 2026 03:40:31 +0000 Received: from mail-ej1-x629.google.com ([2a00:1450:4864:20::629]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1vxzZc-00000000iwP-27LZ for pgsql-hackers@lists.postgresql.org; Thu, 05 Mar 2026 03:40:30 +0000 Received: by mail-ej1-x629.google.com with SMTP id a640c23a62f3a-b8fbb24a9a9so1267666366b.0 for ; Wed, 04 Mar 2026 19:40:28 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1772682026; cv=none; d=google.com; s=arc-20240605; b=XuM+NqgGIxDmlvvXfyZeFTvkqDj5bMbYNtYjAcTVfMQBn7ZYDMwCzbPNDeJjSz6xGb kjC2YjK0gFSJu3v5oornNqWqDnoRy1OijEULks+p90A3piD0TsuyAbz1R6Hf2DBEzMD0 l1kBZMEt8euEy1eIRhBGzEpLpJK/mTJY31jzhM73gyrJCCx5oz0B0GSq1E7lPOezW2tQ TqJoUFPzDHLAEMPDdFLFxI+GdzrV3mgExqPm5nCxfy+hz8XPnqH2oOLaYFC0yk/NUcxs eTFWujqI8G41ZtUOB8TpNWEkSVK0VD/v7kIrzcJ9s2DoGtjovu0XG+JVpKtpvSpbr73t tiSA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:dkim-signature; bh=HOHK1eTCHmMVDnlm6REnR4JJEykehCDsQJ2ma5FGViU=; fh=4UU3neGfKNq47dNpGkLYJsrytfBRGLWIZlNuvbiZYj4=; b=AfbC75SeXgBhnZruv+g0Hp6XPEuAn9lmOpS53ylo9z0Vh6wlYK575kHk9HJQWccPji dhzI1fDZcklLxihlnlJKla9a4ywAC0gjpIA2nYuKHtZC5F64aaUUT6QOjYBYHcI31R1/ +XX2wQZ81enOtkGrwrvqfm3xhT9L2bTF/aJcGaZeIoP9r8VusRZ4vU+f2kDflovX1X2O zOkJQTE534ZaYExW2Wq7gOijkGY7jIcEuCUGggEsB5IC9MRfMa3Lgk95CYR5015SPiXX nztoWM0R4v4n5m1yHlesbClaWTt1r6Q6u6PNprdd6KRTIPi7K3GRZD1RrnEadhoYw+YC CNhQ==; darn=lists.postgresql.org ARC-Authentication-Results: i=1; mx.google.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1772682026; x=1773286826; darn=lists.postgresql.org; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=HOHK1eTCHmMVDnlm6REnR4JJEykehCDsQJ2ma5FGViU=; b=HDYyVQefFFyGbA0T+bBuU/icoSvzJ1XZjICAUxBwoI2uc/pe6mF53tdSqscSmTV6yf KdtCLV6Hgt8lNMhaaF68aTvSeU3lGF/+pEYDxj3Zia4wfvnI3AWy5uftTLMC8lRGtv5+ gKD0LaTs7AadlX4u0zfOLi0uJHZOHZyFtPmeLgY9GcwffOMAfJldHIgXpU7sHSjAMXrs F70c4eKha7dZwBL0VS/K60Y6rfhlkqt2kDzNuTiB/h/YspjiByYiGfTxd8vFXWIObBgR zFPFFTNUMMwA9gmUEmAgVGGooKQzA98fZc7i2SOLFrMnBXfHW+N0nAAvUDP5JwIN/Bql bWCw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772682026; x=1773286826; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=HOHK1eTCHmMVDnlm6REnR4JJEykehCDsQJ2ma5FGViU=; b=jYqwX8lUJ+6jh+SDzMyQBub7z51m90C2Dh7WtfItT2vV7DVD7I2ULqHrMOPwFOQFuN oJ/ZMV8Su9+3NtkriHQeP7YpMORdKJGph1GTNtduTSR75GMWiAIRbzmeCvpSJrzRux8k V+Nr76nqdKylkN35elDGoznQ31k6du4P1WT92EO8asvl8d3OOFAltaY9h4W/7Fq96N1q CR+Hfrer+gaS0mDUVpvja2ZHizeFE4F43jqAKAN6VJNb5tQkU8f1aDLYI8ngv2ju9WDZ Px27Hh/3tXouN0EOK8QOpK/a6YNuPDaayxEW6Koa6lUg8ak9OQTXKKwBKPxVfaZlnz/+ lRTg== X-Forwarded-Encrypted: i=1; AJvYcCUHuXwCo2g4mDeSz20JEvalcrE1TOQFxCn98mwnaqvbgJ2lnSxw/0btLBZEjUTpgAiYr56U9ww8tjQnKyef@lists.postgresql.org X-Gm-Message-State: AOJu0YzHbuqCEDdnCCQsLUb6CWZfIcAfDGoDY6MWahY7gfALJblRrkQI 8nDnQZo1EArtT9hpS3PRt7dRVlO9ZgBmPanLl2HewWvo5e2kr5n/QddIUHe20uhk8Vr+KHgwpqK LF4ec522Wqsj/z/4PPdiM6bFUeU0s2Ug= X-Gm-Gg: ATEYQzzMhG1IFlZeE3ao/z3lhcWCcEfE3GrEMCu/TiqRP+XqdFgQmnmedj72Qj89IBD eZWGGx2tdBGt2LPhyzNIu5WirxgtS/mLdDG4wa/4itn3fE2Lrgd5afhSVLrcff6/HMmVuZ6KNoL RHpy6XewMFPYKf4nRBK14FlDSdWYim2yHGGMQND81DUH7YUaLiBm/mWih6NqcbHIqok6g5KaDbv dHru016Cs2UKwE/iSY+HphaDKRb0diwDjG0vKNeDN4q7bPCjDJ82JxtpWhO0X2qzsCxs/JqVi2x NUTTsukmNJ45smcHi7t2gOBVepkePrzX1cG4Cw== X-Received: by 2002:a17:907:2d90:b0:b8f:6f94:a618 with SMTP id a640c23a62f3a-b9409def9edmr59407766b.17.1772682025416; Wed, 04 Mar 2026 19:40:25 -0800 (PST) MIME-Version: 1.0 References: In-Reply-To: From: wenhui qiu Date: Thu, 5 Mar 2026 11:40:13 +0800 X-Gm-Features: AaiRm52IwTeMy3muCegyKjb34vtLG0wZLcdr5WPymGAXHFh2JLBUFuqU-B6IxAY Message-ID: Subject: Re: Convert NOT IN sublinks to anti-joins when safe To: Japin Li Cc: Richard Guo , Pg Hackers Content-Type: multipart/alternative; boundary="0000000000006cbb9a064c3eb25d" List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --0000000000006cbb9a064c3eb25d Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable HI Japin > Just a quick note: I think `foreach_ptr` is more appropriate here than `foreach`. Thank you review this path , I think using foreach_node should be safe ``` #define foreach_ptr(type, var, lst) foreach_internal(type, *, var, lst, lfirst) #define foreach_int(var, lst) foreach_internal(int, , var, lst, lfirst_int) #define foreach_oid(var, lst) foreach_internal(Oid, , var, lst, lfirst_oid) #define foreach_xid(var, lst) foreach_internal(TransactionId, , var, lst, lfirst_xid) /* * The internal implementation of the above macros. Do not use directly. * * This macro actually generates two loops in order to declare two variables of * different types. The outer loop only iterates once, so we expect optimizing * compilers will unroll it, thereby optimizing it away. */ #define foreach_internal(type, pointer, var, lst, func) \ for (type pointer var =3D 0, pointer var##__outerloop =3D (type pointer) 1;= \ var##__outerloop; \ var##__outerloop =3D 0) \ for (ForEachState var##__state =3D {(lst), 0}; \ (var##__state.l !=3D NIL && \ var##__state.i < var##__state.l->length && \ (var =3D (type pointer) func(&var##__state.l->elements[var##__state.i]), tr= ue)); \ var##__state.i++) /* * foreach_node - * The same as foreach_ptr, but asserts that the element is of the specified * node type. */ #define foreach_node(type, var, lst) \ for (type * var =3D 0, *var##__outerloop =3D (type *) 1; \ var##__outerloop; \ var##__outerloop =3D 0) \ for (ForEachState var##__state =3D {(lst), 0}; \ (var##__state.l !=3D NIL && \ var##__state.i < var##__state.l->length && \ (var =3D lfirst_node(type, &var##__state.l->elements[var##__state.i]), true= )); \ var##__state.i++) ``` On Thu, Mar 5, 2026 at 9:58=E2=80=AFAM Japin Li wrote= : > > Hi, Richard > > On Wed, 04 Mar 2026 at 18:52, Richard Guo wrote: > > On Sat, Feb 14, 2026 at 4:37=E2=80=AFPM Richard Guo > wrote: > >> On Thu, Feb 5, 2026 at 3:51=E2=80=AFPM Richard Guo > wrote: > >> > Attached is the updated patch, which adds the check requiring the > >> > operator to be a member of a btree or hash opfamily. > > > >> Attached is another updated patch rebased on current master, with the > >> addition of support for RowCompareExpr to handle multi-column ordering > >> operations; otherwise unchanged. > > > > Attached is another updated patch rebased on current master, with some > > minor cosmetic adjustments; nothing essential has changed. > > > > Thank you for working on this! > > Just a quick note: I think `foreach_ptr` is more appropriate here than > `foreach`. > > diff --git a/src/backend/optimizer/plan/subselect.c > b/src/backend/optimizer/plan/subselect.c > index 299b3354f6d..0d31861da7f 100644 > --- a/src/backend/optimizer/plan/subselect.c > +++ b/src/backend/optimizer/plan/subselect.c > @@ -1484,7 +1484,6 @@ sublink_testexpr_is_not_nullable(PlannerInfo *root, > SubLink *sublink) > { > Node *testexpr =3D sublink->testexpr; > List *outer_exprs =3D NIL; > - ListCell *lc; > > /* Punt if sublink is not in the expected format */ > if (sublink->subLinkType !=3D ANY_SUBLINK || testexpr =3D=3D NULL= ) > @@ -1514,10 +1513,8 @@ sublink_testexpr_is_not_nullable(PlannerInfo *root= , > SubLink *sublink) > /* multi-column equality or inequality checks */ > BoolExpr *bexpr =3D (BoolExpr *) testexpr; > > - foreach(lc, bexpr->args) > + foreach_ptr(OpExpr, opexpr, bexpr->args) > { > - OpExpr *opexpr =3D (OpExpr *) lfirst(lc); > - > if (!IsA(opexpr, OpExpr)) > return false; > > @@ -1537,10 +1534,8 @@ sublink_testexpr_is_not_nullable(PlannerInfo *root= , > SubLink *sublink) > /* multi-column ordering checks */ > RowCompareExpr *rcexpr =3D (RowCompareExpr *) testexpr; > > - foreach(lc, rcexpr->opnos) > + foreach_oid(opno, rcexpr->opnos) > { > - Oid opno =3D lfirst_oid(lc); > - > /* verify operator safety; see comment above */ > if (!op_is_safe_index_member(opno)) > return false; > @@ -1566,10 +1561,8 @@ sublink_testexpr_is_not_nullable(PlannerInfo *root= , > SubLink *sublink) > flatten_join_alias_vars(root, root->parse, (Node *) > outer_exprs); > > /* Check that every outer expression is non-nullable */ > - foreach(lc, outer_exprs) > + foreach_ptr(Expr, expr, outer_exprs) > { > - Expr *expr =3D (Expr *) lfirst(lc); > - > /* > * We have already collected relation-level not-null > constraints for > * the outer query, so we can consult the global hash > table for > diff --git a/src/backend/optimizer/util/clauses.c > b/src/backend/optimizer/util/clauses.c > index c47c9da4a9b..3f3baf2149a 100644 > --- a/src/backend/optimizer/util/clauses.c > +++ b/src/backend/optimizer/util/clauses.c > @@ -2052,7 +2052,6 @@ query_outputs_are_not_nullable(Query *query) > List *safe_quals =3D NIL; > List *nonnullable_vars =3D NIL; > bool computed_nonnullable_vars =3D false; > - ListCell *tl; > > /* > * If the query contains set operations, punt. The set ops > themselves > @@ -2083,9 +2082,8 @@ query_outputs_are_not_nullable(Query *query) > /* > * Examine each targetlist entry to prove that it can't produce > NULL. > */ > - foreach(tl, query->targetList) > + foreach_ptr(TargetEntry, tle, query->targetList) > { > - TargetEntry *tle =3D (TargetEntry *) lfirst(tl); > Expr *expr =3D tle->expr; > > /* Resjunk columns can be ignored: they don't produce > output values */ > @@ -2194,11 +2192,10 @@ find_subquery_safe_quals(Node *jtnode, List > **safe_quals) > else if (IsA(jtnode, FromExpr)) > { > FromExpr *f =3D (FromExpr *) jtnode; > - ListCell *lc; > > /* All elements of the FROM list are allowable */ > - foreach(lc, f->fromlist) > - find_subquery_safe_quals((Node *) lfirst(lc), > safe_quals); > + foreach_ptr(Node, node, f->fromlist) > + find_subquery_safe_quals(node, safe_quals); > /* ... and its WHERE quals are too */ > if (f->quals) > *safe_quals =3D lappend(*safe_quals, f->quals); > > > > - Richard > > > > [2. text/x-diff; > v4-0001-Convert-NOT-IN-sublinks-to-anti-joins-when-safe.patch]... > > -- > Regards, > Japin Li > ChengDu WenWu Information Technology Co., Ltd. > > > --0000000000006cbb9a064c3eb25d Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
HI Japin
> Just a quick note: I think `foreach_ptr`= is more appropriate here than `foreach`.

Than= k you review this path , I think using=C2=A0foreach_node should be safe
```
#define foreach_ptr(<= span style=3D"color:rgb(156,220,254)">type, var, lst) foreach_internal(type, *= , var, lst, lfirst)
#define foreach_int(var, lst) f= oreach_internal(int, , var, ls= t, lfirst_int)
#define foreach_oid(var, lst) foreach_internal(Oid, , var, lst, lfirst_oid= )
#define foreach_xid(var, lst) foreach_internal(TransactionId, , var, lst, lfirst_xid)

=
/*
* The internal implementation of the above macr= os. Do not use directly.
*
* This mac= ro actually generates two loops in order to declare two variables of=
* different types. The o= uter loop only iterates once, so we expect optimizing
* compilers will unroll it, thereby opti= mizing it away.
*/<= /span>
#define foreach_internal(type, pointer, var, lst, func) = \
for (type pointer var =3D 0, pointer var#= #__outerloop =3D (type pointer) 1; <= span style=3D"color:rgb(215,186,125)">\
var##__outerloop; \
= var##__outerloop =3D= 0) \
for= (ForEachState = var##__state =3D {(lst), 0}; \
(var##__state.l = !=3D NIL && <= span style=3D"color:rgb(215,186,125)">\
var##__state.i < var##__state.l-= >length &= amp;& \
(var = =3D (type pointer) func(&var##__state.l->elements[var##__state.i]), true)); \
var= ##__state.i= ++)

/*
* foreach_no= de -
* The same = as foreach_ptr, but asserts that the element is of the specified
* node type.
<= div> */
#define foreach_node(type, var, lst) \
for (type * var =3D 0, *var##__outerloop =3D (type *)= 1; \<= /div>
var##__outerloop; = \
var##__outerloop =3D <= /span>0) \
for (= ForEachState var##__state =3D {(lst), 0}; \
<= div> (var##__state.l != =3D NIL = && \<= /div>
var##<= span style=3D"color:rgb(156,220,254)">__state.i < var##__state.l->length && \
<= span style=3D"color:rgb(86,156,214)"> (var =3D lfirst_node(type, &var##__state.l->elements[var##__state.i]), true)); \
var##_= _state.i++)

```
=


On Thu, Mar 5, 2026 at 9:58=E2=80=AFAM Japin Li = <japinli@hotmai= l.com> wrote:

Hi, Richard

On Wed, 04 Mar 2026 at 18:52, Richard Guo <guofenglinux@gmail.com> wrote:
> On Sat, Feb 14, 2026 at 4:37=E2=80=AFPM Richard Guo <guofenglinux@gmail.com>= ; wrote:
>> On Thu, Feb 5, 2026 at 3:51=E2=80=AFPM Richard Guo <guofenglinux@gmail.com= > wrote:
>> > Attached is the updated patch, which adds the check requiring= the
>> > operator to be a member of a btree or hash opfamily.
>
>> Attached is another updated patch rebased on current master, with = the
>> addition of support for RowCompareExpr to handle multi-column orde= ring
>> operations; otherwise unchanged.
>
> Attached is another updated patch rebased on current master, with some=
> minor cosmetic adjustments; nothing essential has changed.
>

Thank you for working on this!

Just a quick note: I think `foreach_ptr` is more appropriate here than `for= each`.

diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer= /plan/subselect.c
index 299b3354f6d..0d31861da7f 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -1484,7 +1484,6 @@ sublink_testexpr_is_not_nullable(PlannerInfo *root, S= ubLink *sublink)
=C2=A0{
=C2=A0 =C2=A0 =C2=A0 =C2=A0 Node=C2=A0 =C2=A0 =C2=A0 =C2=A0*testexpr =3D su= blink->testexpr;
=C2=A0 =C2=A0 =C2=A0 =C2=A0 List=C2=A0 =C2=A0 =C2=A0 =C2=A0*outer_exprs =3D= NIL;
-=C2=A0 =C2=A0 =C2=A0 =C2=A0ListCell=C2=A0 =C2=A0*lc;

=C2=A0 =C2=A0 =C2=A0 =C2=A0 /* Punt if sublink is not in the expected forma= t */
=C2=A0 =C2=A0 =C2=A0 =C2=A0 if (sublink->subLinkType !=3D ANY_SUBLINK ||= testexpr =3D=3D NULL)
@@ -1514,10 +1513,8 @@ sublink_testexpr_is_not_nullable(PlannerInfo *root, = SubLink *sublink)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 /* multi-column equ= ality or inequality checks */
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 BoolExpr=C2=A0 =C2= =A0*bexpr =3D (BoolExpr *) testexpr;

-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0foreach(lc, bexpr-&= gt;args)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0foreach_ptr(OpExpr,= opexpr, bexpr->args)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 {
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0OpExpr=C2=A0 =C2=A0 =C2=A0*opexpr =3D (OpExpr *) lfirst(lc);
-
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 if (!IsA(opexpr, OpExpr))
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return false;

@@ -1537,10 +1534,8 @@ sublink_testexpr_is_not_nullable(PlannerInfo *root, = SubLink *sublink)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 /* multi-column ord= ering checks */
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 RowCompareExpr *rce= xpr =3D (RowCompareExpr *) testexpr;

-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0foreach(lc, rcexpr-= >opnos)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0foreach_oid(opno, r= cexpr->opnos)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 {
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0Oid=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= =C2=A0 =C2=A0opno =3D lfirst_oid(lc);
-
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 /* verify operator safety; see comment above */
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 if (!op_is_safe_index_member(opno))
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return false;
@@ -1566,10 +1561,8 @@ sublink_testexpr_is_not_nullable(PlannerInfo *root, = SubLink *sublink)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 flatten_join_alias_= vars(root, root->parse, (Node *) outer_exprs);

=C2=A0 =C2=A0 =C2=A0 =C2=A0 /* Check that every outer expression is non-nul= lable */
-=C2=A0 =C2=A0 =C2=A0 =C2=A0foreach(lc, outer_exprs)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0foreach_ptr(Expr, expr, outer_exprs)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 {
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0Expr=C2=A0 =C2=A0 = =C2=A0 =C2=A0*expr =3D (Expr *) lfirst(lc);
-
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 /*
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* We have alr= eady collected relation-level not-null constraints for
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* the outer q= uery, so we can consult the global hash table for
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/u= til/clauses.c
index c47c9da4a9b..3f3baf2149a 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -2052,7 +2052,6 @@ query_outputs_are_not_nullable(Query *query)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 List=C2=A0 =C2=A0 =C2=A0 =C2=A0*safe_quals =3D = NIL;
=C2=A0 =C2=A0 =C2=A0 =C2=A0 List=C2=A0 =C2=A0 =C2=A0 =C2=A0*nonnullable_var= s =3D NIL;
=C2=A0 =C2=A0 =C2=A0 =C2=A0 bool=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 c= omputed_nonnullable_vars =3D false;
-=C2=A0 =C2=A0 =C2=A0 =C2=A0ListCell=C2=A0 =C2=A0*tl;

=C2=A0 =C2=A0 =C2=A0 =C2=A0 /*
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* If the query contains set operations, p= unt.=C2=A0 The set ops themselves
@@ -2083,9 +2082,8 @@ query_outputs_are_not_nullable(Query *query)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 /*
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* Examine each targetlist entry to prove = that it can't produce NULL.
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0*/
-=C2=A0 =C2=A0 =C2=A0 =C2=A0foreach(tl, query->targetList)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0foreach_ptr(TargetEntry, tle, query->targetL= ist)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 {
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0TargetEntry *tle = =3D (TargetEntry *) lfirst(tl);
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 Expr=C2=A0 =C2=A0 = =C2=A0 =C2=A0*expr =3D tle->expr;

=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 /* Resjunk columns = can be ignored: they don't produce output values */
@@ -2194,11 +2192,10 @@ find_subquery_safe_quals(Node *jtnode, List **safe_= quals)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 else if (IsA(jtnode, FromExpr))
=C2=A0 =C2=A0 =C2=A0 =C2=A0 {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 FromExpr=C2=A0 =C2= =A0*f =3D (FromExpr *) jtnode;
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ListCell=C2=A0 =C2= =A0*lc;

=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 /* All elements of = the FROM list are allowable */
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0foreach(lc, f->f= romlist)
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0find_subquery_safe_quals((Node *) lfirst(lc), safe_quals);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0foreach_ptr(Node, n= ode, f->fromlist)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0find_subquery_safe_quals(node, safe_quals);
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 /* ... and its WHER= E quals are too */
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (f->quals) =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 *safe_quals =3D lappend(*safe_quals, f->quals);


> - Richard
>
> [2. text/x-diff; v4-0001-Convert-NOT-IN-sublinks-to-anti-joins-when-sa= fe.patch]...

--
Regards,
Japin Li
ChengDu WenWu Information Technology Co., Ltd.


--0000000000006cbb9a064c3eb25d--