From 4a0e610157b0d7c6885339d9e64ff19cde14a862 Mon Sep 17 00:00:00 2001
From: "Chao Li (Evan)" <lic@highgo.com>
Date: Mon, 20 Jul 2026 12:40:20 +0800
Subject: [PATCH v1] Fix empty FOREIGN_JOIN sublist validation

FOREIGN_JOIN target sublists must contain at least two relation identifiers.
However, the parser checked only for sublists with exactly one identifier, so
FOREIGN_JOIN(()) was accepted.

Reject sublists with fewer than two relation identifiers, and add regression
coverage.

Author: Chao Li <lic@highgo.com>
---
 contrib/pg_plan_advice/expected/syntax.out | 3 +++
 contrib/pg_plan_advice/pgpa_parser.y       | 2 +-
 contrib/pg_plan_advice/sql/syntax.sql      | 1 +
 3 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/contrib/pg_plan_advice/expected/syntax.out b/contrib/pg_plan_advice/expected/syntax.out
index 3b57bb2bf57..d53d9598634 100644
--- a/contrib/pg_plan_advice/expected/syntax.out
+++ b/contrib/pg_plan_advice/expected/syntax.out
@@ -209,6 +209,9 @@ DETAIL:  Could not parse advice: FOREIGN_JOIN targets must contain more than one
 SET pg_plan_advice.advice = 'FOREIGN_JOIN((a))';
 ERROR:  invalid value for parameter "pg_plan_advice.advice": "FOREIGN_JOIN((a))"
 DETAIL:  Could not parse advice: FOREIGN_JOIN targets must contain more than one relation identifier at or near ")"
+SET pg_plan_advice.advice = 'FOREIGN_JOIN(())';
+ERROR:  invalid value for parameter "pg_plan_advice.advice": "FOREIGN_JOIN(())"
+DETAIL:  Could not parse advice: FOREIGN_JOIN targets must contain more than one relation identifier at or near ")"
 -- Tag keywords used as alias names work fine, because the 'identifier'
 -- nonterminal accepts all token types.
 SET pg_plan_advice.advice = 'SEQ_SCAN(hash_join)';
diff --git a/contrib/pg_plan_advice/pgpa_parser.y b/contrib/pg_plan_advice/pgpa_parser.y
index 5811a6e5e56..295f16ad064 100644
--- a/contrib/pg_plan_advice/pgpa_parser.y
+++ b/contrib/pg_plan_advice/pgpa_parser.y
@@ -135,7 +135,7 @@ advice_item: TOK_TAG_JOIN_ORDER '(' join_order_target_list ')'
 				foreach_ptr(pgpa_advice_target, target, $3)
 				{
 					if (target->ttype == PGPA_TARGET_IDENTIFIER ||
-						list_length(target->children) == 1)
+						list_length(target->children) < 2)
 							pgpa_yyerror(result, parse_error_msg_p, yyscanner,
 										 "FOREIGN_JOIN targets must contain more than one relation identifier");
 				}
diff --git a/contrib/pg_plan_advice/sql/syntax.sql b/contrib/pg_plan_advice/sql/syntax.sql
index 5af7607db42..de11e8c6fc2 100644
--- a/contrib/pg_plan_advice/sql/syntax.sql
+++ b/contrib/pg_plan_advice/sql/syntax.sql
@@ -73,6 +73,7 @@ SET pg_plan_advice.advice = '/*/* stuff */*/';
 -- Foreign join requires multiple relation identifiers.
 SET pg_plan_advice.advice = 'FOREIGN_JOIN(a)';
 SET pg_plan_advice.advice = 'FOREIGN_JOIN((a))';
+SET pg_plan_advice.advice = 'FOREIGN_JOIN(())';
 
 -- Tag keywords used as alias names work fine, because the 'identifier'
 -- nonterminal accepts all token types.
-- 
2.50.1 (Apple Git-155)

