From f27e84d4ea6de2525b1e312209295b72ac1d14ae Mon Sep 17 00:00:00 2001
From: Greg Sabino Mullane <greg@turnstep.com>
Date: Mon, 24 Aug 2026 21:10:26 -0400
Subject: [PATCH] Provide limited support for trailing commas

---
 src/backend/parser/gram.y            | 16 +++++++++++++---
 src/test/regress/expected/select.out |  7 +++++++
 src/test/regress/sql/select.sql      |  3 +++
 3 files changed, 23 insertions(+), 3 deletions(-)

diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 17035fb4d15..974644cc553 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -322,6 +322,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 				simple_select values_clause
 				PLpgSQL_Expr PLAssignStmt
 
+%type <node>		opt_trailing_comma
 %type <str>			opt_single_name
 %type <list>		opt_qualified_name
 %type <boolean>		opt_concurrently opt_usingindex
@@ -439,7 +440,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 				qualified_name_list any_name any_name_list type_name_list
 				any_operator expr_list attrs
 				distinct_clause opt_distinct_clause
-				target_list opt_target_list insert_column_list set_target_list
+				target_list target_list_items opt_target_list insert_column_list set_target_list
 				merge_values_clause
 				set_clause_list set_clause
 				def_list operator_def_list indirection opt_indirection
@@ -1182,6 +1183,11 @@ stmt:
 /*
  * Generic supporting productions for DDL
  */
+opt_trailing_comma:
+			 ','							{ $$ = NULL; }
+			| /* EMPTY */					{ $$ = NULL; }
+			;
+
 opt_single_name:
 			ColId							{ $$ = $1; }
 			| /* EMPTY */					{ $$ = NULL; }
@@ -18465,9 +18471,13 @@ opt_target_list: target_list						{ $$ = $1; }
 		;
 
 target_list:
+			target_list_items  opt_trailing_comma	{ $$ = $1; }
+			;
+
+target_list_items:
 			target_el								{ $$ = list_make1($1); }
-			| target_list ',' target_el				{ $$ = lappend($1, $3); }
-		;
+			| target_list_items ',' target_el		{ $$ = lappend($1, $3); }
+			;
 
 target_el:	a_expr AS ColLabel
 				{
diff --git a/src/test/regress/expected/select.out b/src/test/regress/expected/select.out
index 34f040beecc..45aa0f910c5 100644
--- a/src/test/regress/expected/select.out
+++ b/src/test/regress/expected/select.out
@@ -983,3 +983,10 @@ explain (costs off) select * from list_parted_tbl;
 (3 rows)
 
 drop table list_parted_tbl;
+-- Test trailing commas
+select 1,2,3, from pg_class limit 1;
+ ?column? | ?column? | ?column? 
+----------+----------+----------
+        1 |        2 |        3
+(1 row)
+
diff --git a/src/test/regress/sql/select.sql b/src/test/regress/sql/select.sql
index 2dfe88d2054..695271fd6df 100644
--- a/src/test/regress/sql/select.sql
+++ b/src/test/regress/sql/select.sql
@@ -271,3 +271,6 @@ create table list_parted_tbl1 partition of list_parted_tbl
   for values in (1) partition by list(b);
 explain (costs off) select * from list_parted_tbl;
 drop table list_parted_tbl;
+
+-- Test trailing commas
+select 1,2,3, from pg_class limit 1;
-- 
2.47.3

