From c545ab7c6373a8602a377509616b13b778e34ecd Mon Sep 17 00:00:00 2001
From: Dhruv Chauhan <chauhandhruv351@gmail.com>
Date: Mon, 27 Jul 2026 21:11:03 +0530
Subject: [PATCH v1] Avoid generating an empty name for an expression index
 column

When an index column is an expression, its name is built from the
Vars, Consts, and function names found in the expression. Some
expressions contain none of these -- for example a whole-row
reference, or a constant made up entirely of punctuation -- so the
generated name came out empty. That produces an empty column name
and an awkward index name such as "t__idx".

Fall back to "expr" when the expression yields no usable text, so a
non-empty name is always produced.
---
 src/backend/commands/indexcmds.c           |  3 +++
 src/test/regress/expected/create_index.out | 22 ++++++++++++++++++++++
 src/test/regress/sql/create_index.sql      | 17 +++++++++++++++++
 3 files changed, 42 insertions(+)

diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 713bb5d10f..834c9e2275 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -2874,6 +2874,9 @@ ChooseIndexExpressionName(Relation rel, Node *indexExpr)
 	context.buf = &buf;
 	/* Walk the tree, stopping when we have enough text */
 	(void) ChooseIndexExpressionName_walker(indexExpr, &context);
+	/* Fall back to "expr" if the walk found nothing, to avoid an empty name */
+	if (buf.len == 0)
+		appendStringInfoString(&buf, "expr");
 	/* Ensure generated names are shorter than NAMEDATALEN */
 	nlen = pg_mbcliplen(buf.data, buf.len, NAMEDATALEN - 1);
 	buf.data[nlen] = '\0';
diff --git a/src/test/regress/expected/create_index.out b/src/test/regress/expected/create_index.out
index 1145d57726..d75683d37b 100644
--- a/src/test/regress/expected/create_index.out
+++ b/src/test/regress/expected/create_index.out
@@ -1648,6 +1648,28 @@ CREATE INDEX ON syscol_table (a) WHERE ctid >= '(1000,0)';
 ERROR:  index creation on system columns is not supported
 DROP TABLE syscol_table;
 --
+-- Generated index column names must never be empty; fall back to "expr".
+--
+CREATE TABLE gen_exprname (a int, b int);
+-- whole-row expression yields no usable text -> "expr"
+CREATE INDEX ON gen_exprname ((gen_exprname IS NOT NULL));
+-- normal expression still gets a descriptive name
+CREATE INDEX ON gen_exprname ((a + b));
+SELECT c.relname AS index_name, a.attname AS column_name
+  FROM pg_class c
+  JOIN pg_attribute a ON a.attrelid = c.oid AND a.attnum > 0
+  WHERE c.relkind = 'i'
+    AND c.relnamespace = 'public'::regnamespace
+    AND c.relname LIKE 'gen_exprname%'
+  ORDER BY c.relname, a.attnum;
+      index_name       | column_name 
+-----------------------+-------------
+ gen_exprname_a_b_idx  | a_b
+ gen_exprname_expr_idx | expr
+(2 rows)
+
+DROP TABLE gen_exprname;
+--
 -- Tests for IS NULL/IS NOT NULL with b-tree indexes
 --
 CREATE TABLE onek_with_null AS SELECT unique1, unique2 FROM onek;
diff --git a/src/test/regress/sql/create_index.sql b/src/test/regress/sql/create_index.sql
index 8e59f6bcd0..5d0e6520da 100644
--- a/src/test/regress/sql/create_index.sql
+++ b/src/test/regress/sql/create_index.sql
@@ -649,6 +649,23 @@ CREATE INDEX ON syscol_table (a) WHERE ctid >= '(1000,0)';
 
 DROP TABLE syscol_table;
 
+--
+-- Generated index column names must never be empty; fall back to "expr".
+--
+CREATE TABLE gen_exprname (a int, b int);
+-- whole-row expression yields no usable text -> "expr"
+CREATE INDEX ON gen_exprname ((gen_exprname IS NOT NULL));
+-- normal expression still gets a descriptive name
+CREATE INDEX ON gen_exprname ((a + b));
+SELECT c.relname AS index_name, a.attname AS column_name
+  FROM pg_class c
+  JOIN pg_attribute a ON a.attrelid = c.oid AND a.attnum > 0
+  WHERE c.relkind = 'i'
+    AND c.relnamespace = 'public'::regnamespace
+    AND c.relname LIKE 'gen_exprname%'
+  ORDER BY c.relname, a.attnum;
+DROP TABLE gen_exprname;
+
 --
 -- Tests for IS NULL/IS NOT NULL with b-tree indexes
 --
-- 
2.34.1

