From f2c741e5a52cd71268891c3d7877878a2a2ce260 Mon Sep 17 00:00:00 2001
From: Ayush Tiwari <ayushtiwari.slg01@gmail.com>
Date: Fri, 21 Aug 2026 15:37:01 +0530
Subject: [PATCH v2] Fix internal errors reachable from SQL

currtid_for_view() ended with an elog() when it could not resolve the
view's ctid column to a base relation, which happens for example when
the view groups by ctid.  Ordinary SQL can reach that, so it should not
be reported as XX000 (internal_error).  The other unsupported-view
checks in the same function report ERRCODE_FEATURE_NOT_SUPPORTED, so
match them.  (bug #19631)

While looking for other instances of the same problem, a few more
errors reachable from SQL turned out to have no error code:

* varlena.c: unicode_assigned() rejects non-UTF8 server encodings;
  report that as ERRCODE_FEATURE_NOT_SUPPORTED, since the operation is
  not supported for the current database encoding.

* pg_controldata.c: the pg_control_*() functions raise a control file
  CRC mismatch, which is ERRCODE_DATA_CORRUPTED.

* gist.c: an inner tuple left invalid by a pre-9.1 crash is a corrupt
  index, so use ERRCODE_INDEX_CORRUPTED, like the checks in gistutil.c.

* collationcmds.c: refusing ALTER COLLATION ... REFRESH VERSION for the
  default collation is ERRCODE_WRONG_OBJECT_TYPE.

Add regression coverage for the currtid_for_view() fallback, using
SQLSTATE-only error verbosity to verify that it reports 0A000 rather
than XX000.  No error messages change.

Author: Ayush Tiwari <ayushtiwari.slg01@gmail.com>
Bug: #19631
Reported-by: Zheng Wang <hackerzheng666@gmail.com>
Reported-by: Yanjie Zhao
Reported-by: Yiyang Liu
Discussion: https://postgr.es/m/19631-b443dd6cd8d4e40b@postgresql.org
---
Changes in v2:
* Use ERRCODE_FEATURE_NOT_SUPPORTED for unicode_assigned().
* Add a regression test for the currtid_for_view() error code.

 src/backend/access/gist/gist.c          |  3 ++-
 src/backend/commands/collationcmds.c    |  3 ++-
 src/backend/utils/adt/tid.c             |  4 +++-
 src/backend/utils/adt/varlena.c         |  3 ++-
 src/backend/utils/misc/pg_controldata.c | 12 ++++++++----
 src/test/regress/expected/tid.out       |  8 ++++++++
 src/test/regress/sql/tid.sql            |  7 +++++++
 7 files changed, 32 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
index 8565e225be7..44597793433 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -762,7 +762,8 @@ gistdoinsert(Relation r, IndexTuple itup, Size freespace,
 			 */
 			if (GistTupleIsInvalid(idxtuple))
 				ereport(ERROR,
-						(errmsg("index \"%s\" contains an inner tuple marked as invalid",
+						(errcode(ERRCODE_INDEX_CORRUPTED),
+						 errmsg("index \"%s\" contains an inner tuple marked as invalid",
 								RelationGetRelationName(r)),
 						 errdetail("This is caused by an incomplete page split at crash recovery before upgrading to PostgreSQL 9.1."),
 						 errhint("Please REINDEX it.")));
diff --git a/src/backend/commands/collationcmds.c b/src/backend/commands/collationcmds.c
index cfa0e4610d9..92faa60a750 100644
--- a/src/backend/commands/collationcmds.c
+++ b/src/backend/commands/collationcmds.c
@@ -443,7 +443,8 @@ AlterCollation(AlterCollationStmt *stmt)
 
 	if (collOid == DEFAULT_COLLATION_OID)
 		ereport(ERROR,
-				(errmsg("cannot refresh version of default collation"),
+				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+				 errmsg("cannot refresh version of default collation"),
 		/* translator: %s is an SQL command */
 				 errhint("Use %s instead.",
 						 "ALTER DATABASE ... REFRESH COLLATION VERSION")));
diff --git a/src/backend/utils/adt/tid.c b/src/backend/utils/adt/tid.c
index a97873f91ba..9b2cca0db4d 100644
--- a/src/backend/utils/adt/tid.c
+++ b/src/backend/utils/adt/tid.c
@@ -432,7 +432,9 @@ currtid_for_view(Relation viewrel, const ItemPointerData *tid)
 			break;
 		}
 	}
-	elog(ERROR, "currtid cannot handle this view");
+	ereport(ERROR,
+			errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			errmsg("currtid cannot handle this view"));
 	return NULL;
 }
 
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index f6a41e709ae..1288705baeb 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -5482,7 +5482,8 @@ unicode_assigned(PG_FUNCTION_ARGS)
 
 	if (GetDatabaseEncoding() != PG_UTF8)
 		ereport(ERROR,
-				(errmsg("Unicode categorization can only be performed if server encoding is UTF8")));
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("Unicode categorization can only be performed if server encoding is UTF8")));
 
 	/* convert to char32_t */
 	size = pg_mbstrlen_with_len(VARDATA_ANY(input), VARSIZE_ANY_EXHDR(input));
diff --git a/src/backend/utils/misc/pg_controldata.c b/src/backend/utils/misc/pg_controldata.c
index 1f47302fe2a..4eac0b33cd1 100644
--- a/src/backend/utils/misc/pg_controldata.c
+++ b/src/backend/utils/misc/pg_controldata.c
@@ -47,7 +47,8 @@ pg_control_system(PG_FUNCTION_ARGS)
 	LWLockRelease(ControlFileLock);
 	if (!crc_ok)
 		ereport(ERROR,
-				(errmsg("calculated CRC checksum does not match value stored in file")));
+				(errcode(ERRCODE_DATA_CORRUPTED),
+				 errmsg("calculated CRC checksum does not match value stored in file")));
 
 	values[0] = Int32GetDatum(ControlFile->pg_control_version);
 	nulls[0] = false;
@@ -87,7 +88,8 @@ pg_control_checkpoint(PG_FUNCTION_ARGS)
 	LWLockRelease(ControlFileLock);
 	if (!crc_ok)
 		ereport(ERROR,
-				(errmsg("calculated CRC checksum does not match value stored in file")));
+				(errcode(ERRCODE_DATA_CORRUPTED),
+				 errmsg("calculated CRC checksum does not match value stored in file")));
 
 	/*
 	 * Calculate name of the WAL file containing the latest checkpoint's REDO
@@ -184,7 +186,8 @@ pg_control_recovery(PG_FUNCTION_ARGS)
 	LWLockRelease(ControlFileLock);
 	if (!crc_ok)
 		ereport(ERROR,
-				(errmsg("calculated CRC checksum does not match value stored in file")));
+				(errcode(ERRCODE_DATA_CORRUPTED),
+				 errmsg("calculated CRC checksum does not match value stored in file")));
 
 	values[0] = LSNGetDatum(ControlFile->minRecoveryPoint);
 	nulls[0] = false;
@@ -225,7 +228,8 @@ pg_control_init(PG_FUNCTION_ARGS)
 	LWLockRelease(ControlFileLock);
 	if (!crc_ok)
 		ereport(ERROR,
-				(errmsg("calculated CRC checksum does not match value stored in file")));
+				(errcode(ERRCODE_DATA_CORRUPTED),
+				 errmsg("calculated CRC checksum does not match value stored in file")));
 
 	values[0] = Int32GetDatum(ControlFile->maxAlign);
 	nulls[0] = false;
diff --git a/src/test/regress/expected/tid.out b/src/test/regress/expected/tid.out
index 45cc61fb1ca..d2dc23fbdeb 100644
--- a/src/test/regress/expected/tid.out
+++ b/src/test/regress/expected/tid.out
@@ -192,4 +192,12 @@ CREATE VIEW tid_view_fake_ctid AS SELECT 1 AS ctid, 2 AS a;
 SELECT currtid2('tid_view_fake_ctid'::text, '(0,1)'::tid); -- fails
 ERROR:  ctid isn't of type TID
 DROP VIEW tid_view_fake_ctid;
+-- ctid cannot be traced to a base relation
+CREATE VIEW tid_view_grouped_ctid AS
+  SELECT ctid, a FROM tid_tab GROUP BY ctid, a;
+\set VERBOSITY sqlstate
+SELECT currtid2('tid_view_grouped_ctid'::text, '(0,1)'::tid); -- fails
+ERROR:  0A000
+\set VERBOSITY default
+DROP VIEW tid_view_grouped_ctid;
 DROP TABLE tid_tab CASCADE;
diff --git a/src/test/regress/sql/tid.sql b/src/test/regress/sql/tid.sql
index 51d00b92074..5176503951b 100644
--- a/src/test/regress/sql/tid.sql
+++ b/src/test/regress/sql/tid.sql
@@ -91,5 +91,12 @@ TRUNCATE tid_tab;
 CREATE VIEW tid_view_fake_ctid AS SELECT 1 AS ctid, 2 AS a;
 SELECT currtid2('tid_view_fake_ctid'::text, '(0,1)'::tid); -- fails
 DROP VIEW tid_view_fake_ctid;
+-- ctid cannot be traced to a base relation
+CREATE VIEW tid_view_grouped_ctid AS
+  SELECT ctid, a FROM tid_tab GROUP BY ctid, a;
+\set VERBOSITY sqlstate
+SELECT currtid2('tid_view_grouped_ctid'::text, '(0,1)'::tid); -- fails
+\set VERBOSITY default
+DROP VIEW tid_view_grouped_ctid;
 
 DROP TABLE tid_tab CASCADE;

base-commit: 5624de9904c29224737574f039ba073bc02fba82
-- 
2.34.1
