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 1vkiVH-001Fa8-0K for pgsql-hackers@arkaria.postgresql.org; Tue, 27 Jan 2026 12:49:07 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1vkiVE-00DoN6-36 for pgsql-hackers@arkaria.postgresql.org; Tue, 27 Jan 2026 12:49:05 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1vkiVE-00DoMx-1i for pgsql-hackers@lists.postgresql.org; Tue, 27 Jan 2026 12:49:04 +0000 Received: from mail-pg1-x529.google.com ([2607:f8b0:4864:20::529]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.96) (envelope-from ) id 1vkiV8-002Z5j-2v for pgsql-hackers@lists.postgresql.org; Tue, 27 Jan 2026 12:49:03 +0000 Received: by mail-pg1-x529.google.com with SMTP id 41be03b00d2f7-c634c20be90so1916920a12.1 for ; Tue, 27 Jan 2026 04:48:59 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1769518139; cv=none; d=google.com; s=arc-20240605; b=K2L87jFBdlBl+CWw7WAJnLq3QEVhYB/7T0cHi+XFYCGQq+EbZxPXsdp9ON8MVgkdba XxT604T9OdadpyvqXshmGZrgmcxKFjCrWLiTH3I6sLgwa2VHKEjA1msejBg7cL21FtWP XPcGQIF9lvI64pBvOe5G4MIYkkm/oGhKQJI1vx6qD6odmt9T/lSJYMH5SKdiaC8pkJgN 8InEJgQTEWjBZ8J79RcVUtbaMBDQShkPyfRXQpNy1YZA5alPhAIQkClXwyK/Mgw8BfWr dmp36OidrFURiP5+DR95zXKNsNP2kS6fmMn/joe+2EU4cwQ+4OUg5kdkAsKvcnfRnXwX oSuQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:dkim-signature; bh=kB4tMBgBB6685UgFA0+F4Lh48fxqzBs+SFWW9yKomhc=; fh=DGwJWUxa+QXi7XfmDtRiNm+sDCkpcF2TVjwS2Zfsk5o=; b=OaqblnlzaiGeNbGn/73ys7gHHyfDgErEl4XZaiTrtoqG9+nT5MvC1JmA9wBzOW0V/6 4gZataS/6fBqdwLG6ROZmUh+UFWGycp0eth/sJph8TH2YVcfL+0q0XrKggiLzWW3NmXR mKwKUj8roTQhly9B1X8+jLRzhAgfIwelxTBdqp801hlTYdwCQgLpqfViJgdPijd+0B6t IGJEiAigZLDVsEu2RIjumr2ZI7cKQocwx8IquX8xC1ATen9/xr+N0sUFVYsLTo3ihg7c ey3Av2eus3DA08d5g+e4UV9g2Q0gSNvLyQcKly76pTkGJAA8I9NYedB3zmKRFMNzNZPq v6Yg==; 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=1769518139; x=1770122939; darn=lists.postgresql.org; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:from:to:cc:subject:date :message-id:reply-to; bh=kB4tMBgBB6685UgFA0+F4Lh48fxqzBs+SFWW9yKomhc=; b=hcVWuIwlfHNZwMIHFgA/caGOOpFmzWeXqu8KAXGvGWkNwtI/Id/sZwMJxzCjkvl4Mj /MjJ9XXb6GfEP52wfiRR3PZHdzmzRIS1kLXSO5znsDNXEcph+ejRSYvOaPI0VuwVUGt/ VUMIaPnCmtnFXIWxN+DqE47szCJEt2vfWxIJmxxUISLExc+kl8i36KuJ9uD95ye5BrIH lvrwY7QUOkOag87T7xucizJyfReaFtFY/0hjewOCzdxxaaXhwxAIhms3jcTSlfMafiAT OeW5A/TSJwkFjUon3dIZbFHx/zUceMCd1k5D3wE9gvB1kjH8Z0t9Uy9YY9UYOIp3PQsH Wy+Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1769518139; x=1770122939; h=content-transfer-encoding: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=kB4tMBgBB6685UgFA0+F4Lh48fxqzBs+SFWW9yKomhc=; b=kZSm/NsZA6eoDjTr4kyqkhFQ5kw/zAu1IiXNFop7OeVsMENrQUGjpWu/yEksgFqNDK BuCTiTebIC8Sbodar4wTJpaiygSmDQ2i45CC2Ki2Br3GkLLWaqhkoexiAueWM92tdwvu EGGbP5bIXpvSsXBTrwrVkBXX8KtxDSXosY+aJ9gGqTN1aMvf+MIH0+0HYEHuDa0xCzQl QOs2bzMItyKzjRdZ0HlKp2td10JylTxwSoV/su+54Lw5Xl+6kj6KzrU8Naf01me15eFA BwJGeeZq90dg4o4r6nIElY0heTMXFJbcwjCMcsw17/myO+2sFIta5M0yphke1jADxP9S WSRA== X-Forwarded-Encrypted: i=1; AJvYcCXBn8BHrxGxN4hjqIai+AuUeCfVAUgBJJH15+PxgO/E776h+7x69iThIrBmBrFBDoSi2Cj96V2HSkelXMpI@lists.postgresql.org X-Gm-Message-State: AOJu0Yx6qfYskDKXJVrlzJEFYWb6nbqnXtwLNujWAiZz/NSyqWeRE0K9 MbwkIPnVIQ04/6oVgHfLS+mYfBVFgAOgdsyGgX9tVv+bPfebOT5eRNLN8gfP8NifbHqtt/mnoNp yb5DcFo7X/WZI2dsQma/YOdfziM3R7jk= X-Gm-Gg: AZuq6aIv7B1dk1pf/H8ZUbhZOOjF0aTh4tsyf+kLQd/ZYDeFLIrUYwN4KsCy2mC8HU9 0REWCgtsTrdcYC3XXKNf7UGhO271n9FVRGc2lYj1vawvRjw+RtBO4yLTWYN3S3toDNrJvEj+Cti pZJX8quDNh6Kj7khU7o6qISO6uUieIhq+3M3gbMrot5Iaoml7Z68r+MJLV/3QSGUiLcDIcDSekE RNDehTA1Yr1/IWdQAnMKdryj8LEfsw4rHnAT05VlctlCSoyC1b1oEwSkxjXpFOjEa9SoF1GEQ== X-Received: by 2002:a17:90b:268a:b0:353:6373:590b with SMTP id 98e67ed59e1d1-353fecc6720mr1856782a91.7.1769518138504; Tue, 27 Jan 2026 04:48:58 -0800 (PST) MIME-Version: 1.0 References: In-Reply-To: From: Ajay Pal Date: Tue, 27 Jan 2026 18:18:46 +0530 X-Gm-Features: AZwV_Qg2-SSAxPDecY4_cEWiKrRjLC4TwrxwjTnGxd1el4TlXdU2rt_b975mvLo Message-ID: Subject: Re: pg_plan_advice To: Robert Haas Cc: Jakub Wartak , Lukas Fittl , Jacob Champion , Dian Fay , Matheus Alcantara , PostgreSQL Hackers Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk Thank you Robert for clarification. On Tue, Jan 27, 2026 at 6:02=E2=80=AFPM Robert Haas = wrote: > > On Tue, Jan 27, 2026 at 2:49=E2=80=AFAM Ajay Pal w= rote: > > #1 Grouped Hash Join, This forces the join of dim1 and dim2 to happen > > first, and then places that resulting set on the inner side of a Hash > > Join against fact. > > but the planner partially matches the generated advice. > > > > -- We want (dim1 JOIN dim2) to be the inner side of a Hash Join > > SET LOCAL pg_plan_advice.advice =3D 'HASH_JOIN((dim1 dim2))'; > > > > postgres=3D*# EXPLAIN (COSTS OFF, PLAN_ADVICE) > > SELECT * FROM fact > > JOIN dim1 ON fact.d1_id =3D dim1.id > > JOIN dim2 ON fact.d2_id =3D dim2.id; > > QUERY PLAN > > ----------------------------------------------------------- > > Nested Loop > > Disabled: true > > -> Nested Loop > > Disabled: true > > -> Seq Scan on fact > > -> Index Scan using dim1_pkey on dim1 > > Index Cond: (id =3D fact.d1_id) > > -> Index Scan using dim2_pkey on dim2 > > Index Cond: (id =3D fact.d2_id) > > Supplied Plan Advice: > > HASH_JOIN((dim1 dim2)) /* partially matched */ > > Generated Plan Advice: > > JOIN_ORDER(fact dim1 dim2) > > NESTED_LOOP_PLAIN(dim1 dim2) > > SEQ_SCAN(fact) > > INDEX_SCAN(dim1 public.dim1_pkey dim2 public.dim2_pkey) > > NO_GATHER(fact dim1 dim2) > > (17 rows) > > Thanks for the report, but this is actually correct behavior. There's > no join clause between dim1 and dim2, so the planner doesn't consider > a dim1-dim2 join. This is a good example of the phenomenon described > in the documentation: you can't force the planner to create an > arbitrary plan that it wouldn't otherwise have considered. I might > tweak the documentation wording a little to try to mention that this > is another way "partially matched" can happen, but there's no bug > here. > > > #2 Multiple Instances of Same Table in Subqueries, here target the > > second instance of dim1 inside the subquery 'sq'. both seq_scan and > > index_scan advices are not matching. > > > > SET LOCAL pg_plan_advice.advice =3D 'SEQ_SCAN(dim1#2@sq) > > INDEX_SCAN(dim1@sq dim1_pkey)'; > > > > postgres=3D*# EXPLAIN (COSTS OFF, PLAN_ADVICE) > > SELECT * FROM fact > > JOIN ( > > SELECT a.id FROM dim1 a > > JOIN dim1 b ON a.id =3D b.id > > OFFSET 0 > > ) sq ON fact.d1_id =3D sq.id; > > QUERY PLAN > > --------------------------------------------------- > > Hash Join > > Hash Cond: (fact.d1_id =3D b.id) > > -> Seq Scan on fact > > -> Hash > > -> Seq Scan on dim1 b > > Supplied Plan Advice: > > SEQ_SCAN(dim1#2@sq) /* not matched */ > > INDEX_SCAN(dim1@sq dim1_pkey) /* not matched */ > > Generated Plan Advice: > > JOIN_ORDER(fact sq) > > HASH_JOIN(sq) > > SEQ_SCAN(b@sq fact) > > NO_GATHER(fact b@sq) > > (13 rows) > > I'm not sure what why you expected this to work. You can see what the > correct relation identifiers are from the generated plan advice, and > you've used something else, so it doesn't match. It's documented in > both the SGML documentation and the README that relation identifiers > are based on the relation alias, not the relation name. > > In general, this seems like a good to reiterate that this is first and > foremost a plan stability feature. More than anything, these examples > show that if you try to write your own plan advice from scratch to > force a novel plan that the planner has never produced itself, you may > not have much luck. If you do want to try to produce a novel plan, you > should at least look at the generated plan advice and adapt it instead > of starting from scratch. And if you find, when trying to produce a > novel plan, that it doesn't work, you need to consider the possibility > that this is because the optimizer did not ever consider that plan, > and that is why pg_plan_advice is unable to induce the planner to > prefer it. That's not to say there can't be any remaining bugs in > pg_plan_advice; there probably are. But it also is absolutely not a > "write your own plan and do anything you like" feature. > > -- > Robert Haas > EDB: http://www.enterprisedb.com