From 6bd9a9a3b4dbb32ca01cc456ad5e5131c485b367 Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
Date: Wed, 31 Dec 2025 16:05:20 +0800
Subject: [PATCH v2 1/2] add Location to IndexElem
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Add location information to each IndexElem so that error reporting can precisely
identify the specific invalid element. This is particularly useful for
subsequent refactoring in ComputeIndexAttrs.

Author: Álvaro Herrera <alvherre(at)kurilemu(dot)de>

comitfest: https://commitfest.postgresql.org/patch/6322
discussion: https://postgr.es/m/CACJufxH3OgXF1hrzGAaWyNtye2jHEmk9JbtrtGv-KJK6tsGo5w@mail.gmail.com
---
 src/backend/bootstrap/bootparse.y  | 1 +
 src/backend/nodes/nodeFuncs.c      | 3 +++
 src/backend/parser/gram.y          | 5 +++++
 src/backend/parser/parse_clause.c  | 8 ++++----
 src/backend/parser/parse_utilcmd.c | 4 ++++
 src/include/nodes/parsenodes.h     | 1 +
 6 files changed, 18 insertions(+), 4 deletions(-)

diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y
index 9833f52c1be..da0e7dea497 100644
--- a/src/backend/bootstrap/bootparse.y
+++ b/src/backend/bootstrap/bootparse.y
@@ -415,6 +415,7 @@ boot_index_param:
 					n->opclass = list_make1(makeString($2));
 					n->ordering = SORTBY_DEFAULT;
 					n->nulls_ordering = SORTBY_NULLS_DEFAULT;
+					n->location = -1;
 					$$ = n;
 				}
 		;
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index 024a2b2fd84..89b7d80c0aa 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -1726,6 +1726,9 @@ exprLocation(const Node *expr)
 		case T_ColumnDef:
 			loc = ((const ColumnDef *) expr)->location;
 			break;
+		case T_IndexElem:
+			loc = ((const IndexElem *) expr)->location;
+			break;
 		case T_Constraint:
 			loc = ((const Constraint *) expr)->location;
 			break;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 28f4e11e30f..3f407e7d00f 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -8454,6 +8454,7 @@ index_elem_options:
 			$$->opclassopts = NIL;
 			$$->ordering = $3;
 			$$->nulls_ordering = $4;
+			$$->location = @1;
 		}
 	| opt_collate any_name reloptions opt_asc_desc opt_nulls_order
 		{
@@ -8466,6 +8467,7 @@ index_elem_options:
 			$$->opclassopts = $3;
 			$$->ordering = $4;
 			$$->nulls_ordering = $5;
+			$$->location = @1;
 		}
 	;
 
@@ -8478,16 +8480,19 @@ index_elem: ColId index_elem_options
 				{
 					$$ = $2;
 					$$->name = $1;
+					$$->location = @1;
 				}
 			| func_expr_windowless index_elem_options
 				{
 					$$ = $2;
 					$$->expr = $1;
+					$$->location = @1;
 				}
 			| '(' a_expr ')' index_elem_options
 				{
 					$$ = $4;
 					$$->expr = $2;
+					$$->location = @1;
 				}
 		;
 
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index 57609e2d55c..34693c8a9e6 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -3289,20 +3289,20 @@ resolve_unique_index_expr(ParseState *pstate, InferClause *infer,
 					 errmsg("%s is not allowed in ON CONFLICT clause",
 							"ASC/DESC"),
 					 parser_errposition(pstate,
-										exprLocation((Node *) infer))));
+										exprLocation((Node *) ielem))));
 		if (ielem->nulls_ordering != SORTBY_NULLS_DEFAULT)
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
 					 errmsg("%s is not allowed in ON CONFLICT clause",
 							"NULLS FIRST/LAST"),
 					 parser_errposition(pstate,
-										exprLocation((Node *) infer))));
+										exprLocation((Node *) ielem))));
 		if (ielem->opclassopts)
 			ereport(ERROR,
 					errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
 					errmsg("operator class options are not allowed in ON CONFLICT clause"),
 					parser_errposition(pstate,
-									   exprLocation((Node *) infer)));
+									   exprLocation((Node *) ielem)));
 
 		if (!ielem->expr)
 		{
@@ -3342,7 +3342,7 @@ resolve_unique_index_expr(ParseState *pstate, InferClause *infer,
 			pInfer->infercollid = InvalidOid;
 		else
 			pInfer->infercollid = LookupCollation(pstate, ielem->collation,
-												  exprLocation(pInfer->expr));
+												  exprLocation((Node *) ielem));
 
 		if (!ielem->opclass)
 			pInfer->inferopclass = InvalidOid;
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 2b7b084f216..535c4060dd4 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1883,6 +1883,7 @@ generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx,
 		int16		opt = source_idx->rd_indoption[keyno];
 
 		iparam = makeNode(IndexElem);
+		iparam->location = -1;
 
 		if (AttributeNumberIsValid(attnum))
 		{
@@ -1974,6 +1975,7 @@ generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx,
 											   keyno);
 
 		iparam = makeNode(IndexElem);
+		iparam->location = -1;
 
 		if (AttributeNumberIsValid(attnum))
 		{
@@ -2813,6 +2815,7 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
 			iparam->opclassopts = NIL;
 			iparam->ordering = SORTBY_DEFAULT;
 			iparam->nulls_ordering = SORTBY_NULLS_DEFAULT;
+			iparam->location = -1;
 			index->indexParams = lappend(index->indexParams, iparam);
 		}
 
@@ -2929,6 +2932,7 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
 		iparam->collation = NIL;
 		iparam->opclass = NIL;
 		iparam->opclassopts = NIL;
+		iparam->location = -1;
 		index->indexIncludingParams = lappend(index->indexIncludingParams, iparam);
 	}
 
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index bc7adba4a0f..b15b937660f 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -816,6 +816,7 @@ typedef struct IndexElem
 	List	   *opclassopts;	/* opclass-specific options, or NIL */
 	SortByDir	ordering;		/* ASC/DESC/default */
 	SortByNulls nulls_ordering; /* FIRST/LAST/default */
+	ParseLoc	location;		/* token location, or -1 if unknown */
 } IndexElem;
 
 /*
-- 
2.34.1

