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 1vkiFG-001DS0-0D for pgsql-hackers@arkaria.postgresql.org; Tue, 27 Jan 2026 12:32:34 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1vkiFE-00Dm2O-2R for pgsql-hackers@arkaria.postgresql.org; Tue, 27 Jan 2026 12:32:33 +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 1vkiFE-00Dm2F-1N for pgsql-hackers@lists.postgresql.org; Tue, 27 Jan 2026 12:32:32 +0000 Received: from mail-ej1-x62f.google.com ([2a00:1450:4864:20::62f]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1vkiFA-00000000hiw-0hPA for pgsql-hackers@lists.postgresql.org; Tue, 27 Jan 2026 12:32:32 +0000 Received: by mail-ej1-x62f.google.com with SMTP id a640c23a62f3a-b8845cb580bso981962866b.3 for ; Tue, 27 Jan 2026 04:32:27 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1769517145; cv=none; d=google.com; s=arc-20240605; b=g51YVEKOsIvJG/L7xVDx6zDVzsxn4KTD4MgN9sqSbfRm8TFXYgi+cL52XKu6pdRFu5 GP+NSaIwayynwG+W4fCOk9UmD8w4tk3T/qUwDHRxh9F/c3QmuzhRnfs2uAoPK0ECWXSQ Ig4ZmSpFNxMTL/Zw0wjlb0g3diqMzIKYW+o66TP0470jLOC5DgW9H21iDnrS+Rhdtps8 VXtdgrZBzcURh/HmCMNfFy6MapaSg9N9CRRKgQxLipNA86a/U8DNsJ4nt2OYx18mEKPn WtLx6gneMA+b+ZnADvwLF2WQ9OszZWAVJOcE3ORraCKnfd+ZtMwAjhAVoBlg6E00hAhs 50zA== 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=DoD8cASBrlBSiT1O082dSzrTB0pxVphAgQmumMwIQUA=; fh=9qIxLp98JFMrnrfWIvFVFvxbxaLs0Hvwbk7jE4K4JbU=; b=bOWHfjL7juC+1U0KwCElnb95aPEcvq9P8fkbhpo5YzGAo+OWGcxQ5GeBN5JVfNI9Ac N/SsfrMWVUk+ll746ODD1a444oyt4i+xNSfsGgRUoThTqnZUaNZI03ESn96YNWgck59G RRxxb5Xmtvkl8XEB80YY14cIGu9+flzjEz7zux72g63pL22fVCmI4moVW23z5sBrcHRo 48tVUYQSEUFPbdQs8OKEd9Zihe8iRU0+tsWbUkHSpzhJce6jjPUL+DHE0iyY4nzWfHT5 OqZLgJ1qGsxgcIUHvxzMoCwgDjFojbLZj8LxPbbhY8IQDooLLlr33qYH4LodymQIWYKY wlkQ==; 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=1769517145; x=1770121945; 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=DoD8cASBrlBSiT1O082dSzrTB0pxVphAgQmumMwIQUA=; b=U/wwRv28GiwsZvT/QCXj+56SXoqeVYOLtvT8SyFmHbhZlSs3j+qdDrwNkSru15tZsZ U6s1wJnGF1LYYoJPG6wGX+kqMn1nswC7+hMV8iw1M5GpLnh0MQa/Czl3vt2uj8dhYEBs YFhaBIXVbQV5ORIsN9OO+ZaHQGF6lt9CAw5fStgNiiF7uR7bC/brakgvmwXeGSz0/ipj zpcYZVAgafojCrxc/KW2y3ha7RzKeSey7OFSi02kgTXF5FmygoznOc6wBG2ga5XxeD4r Mqzb87y5nwLpZ9ibUzQ9Xh02IowUctnoWlwGcTt6rHijaC8YYdYx1tRudqGzIZPtRJ51 ML8Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1769517145; x=1770121945; 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=DoD8cASBrlBSiT1O082dSzrTB0pxVphAgQmumMwIQUA=; b=O20OVrmedFG01JZQYAFHBiIXF+8rZiEo3iBjRBHd83WzQwVWfXWg67TmKHc0JhVVEq j62/SDEUSpYdVgGIbpFRkXhHSBVJEWWtFkSDgma+yvV/O4CjJ3qoj1w2aH5Lox9TyZrZ o9eBU+aRgDS7QAVYY0l0SVdClRxlRgdgNAks0G5KbHvQ/nBeoTcgFBi1Pcy9rcKA9Z8r U6xBMP9IRwGK3e01IUEchXZfeEv1QKao1Qo3VUTwcUC9JwTBQf1snAR1PfTvJmEnbvaf wd5LPk6KgqTvt8wxN9dt3c5y0fucqxddjlbdg3lxwjdsnIbLcJxuT8B/e7K9+Np+czzg 4oUw== X-Forwarded-Encrypted: i=1; AJvYcCWDEOa+UPzOMFIr0VMNBN6OJun2OYHFkenjHarDAuhfeEcBq4A0QWL0y41kd231uvW0g1sXCd7Or1jVa20C@lists.postgresql.org X-Gm-Message-State: AOJu0YxQX+clkYPxLjn+zz+qMRSjcss21Gr4cCXKbyGO8qt8Mb1mRcpF qgqWAlim4EE0t3h72N98YsIPYgdVF79hEpASfOV5/ym4kEi4PDWmK72qD+hDpZg3YVD/lMelLSn euqlEJyd+RGN2l0pInPz9wjSg8xD3Z/E= X-Gm-Gg: AZuq6aInTOTbyRgMghw4nVqL+5WEHWK8VfpRyizIOfI+qrzjVFY6Ax1MYMztDPTaw+J D4xBOPHM6JAK9n7X9GQjSOkRkWU844jQ4qwxYADXFVqbS5Ln3AKdttClRV99GOdDBB9NUvC/czj mPt4NleSn6UdkM7tEBr4ZqQPLKoI6mmD0ICMbK5LbfldTLTa8TDhRlC962fv2KMuVn7Q75Nx+lI dbWZw2RcVyVFJlaXc1uUL+J9b3osUcW4zRNKVMB2hTkjkuVC2ygonxI2Xf75z68aN3QZ5irgr33 6x6TZeZwQw4fK+qTyJqqaAfFC9k= X-Received: by 2002:a17:907:9622:b0:b87:117f:b6f1 with SMTP id a640c23a62f3a-b8dab152abbmr129614566b.2.1769517145091; Tue, 27 Jan 2026 04:32:25 -0800 (PST) MIME-Version: 1.0 References: In-Reply-To: From: Robert Haas Date: Tue, 27 Jan 2026 07:32:11 -0500 X-Gm-Features: AZwV_QgTbxte1OH_KYV6Hk6KMt-okV8Lq1v0WVrZmw4DIcR5N9HPRHvSf-r4qmU Message-ID: Subject: Re: pg_plan_advice To: Ajay Pal 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 On Tue, Jan 27, 2026 at 2:49=E2=80=AFAM Ajay Pal wro= te: > #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. --=20 Robert Haas EDB: http://www.enterprisedb.com