diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 5b657a3f13..f39f3dec8a 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -68,6 +68,7 @@
 #include "utils/timeout.h"
 
 static HeapTuple GetDatabaseTuple(const char *dbname);
+static HeapTuple GetDatabaseTupleInternal(Relation relation, const char *dbname);
 static HeapTuple GetDatabaseTupleByOid(Oid dboid);
 static void PerformAuthentication(Port *port);
 static void CheckMyDatabase(const char *name, bool am_superuser, bool override_allow_connections);
@@ -97,12 +98,75 @@ static void process_settings(Oid databaseid, Oid roleid);
  * descriptors for both pg_database and its indexes from the shared relcache
  * cache file, and so we can do an indexscan.  criticalSharedRelcachesBuilt
  * tells whether we got the cached descriptors.
+ *
+ * This function also deals with the trickier-than-it-sounds issue of
+ * truncating an overlength incoming database name.  We do not know which
+ * encoding the name is in, so we can't apply pg_encoding_mbcliplen() as
+ * CREATE DATABASE would have done.  We use the heuristic of truncating one
+ * byte at a time until we find a match in pg_database, relying on the
+ * assumption that the name as stored in pg_database is valid according to
+ * some server-side encoding.  This lets us constrain the search using the
+ * fact that all bytes of a multibyte character have their high bit set.
  */
 static HeapTuple
 GetDatabaseTuple(const char *dbname)
 {
-	HeapTuple	tuple;
+	HeapTuple	tuple = NULL;
 	Relation	relation;
+	char		tname[NAMEDATALEN];
+
+	relation = table_open(DatabaseRelationId, AccessShareLock);
+
+	/* Fast path if name is short enough, use it directly */
+	if (strlen(dbname) < NAMEDATALEN) {
+		tuple = GetDatabaseTupleInternal(relation, dbname);
+
+		if (HeapTupleIsValid(tuple)) {
+			table_close(relation, AccessShareLock);
+			return tuple;
+		}
+	}
+
+	/* Need to try truncation */
+	for (int i = NAMEDATALEN - 1;
+		 i >= NAMEDATALEN - MAX_MULTIBYTE_CHAR_LEN;
+		 i--)
+	{
+		strncpy(tname, dbname, sizeof(tname));
+		tname[i] = '\0';
+
+		/*
+		 * Take the first match.  It's theoretically possible that we could
+		 * find more than one match if we continued to try shorter truncations
+		 * (which is what makes this a heuristic).  This is unlikely though.
+		 * We considered failing if there are multiple candidate matches, but
+		 * that cure seems worse than the disease: it might reject cases that
+		 * worked fine before v17.
+		 */
+		tuple = GetDatabaseTupleInternal(relation, tname);
+		if (HeapTupleIsValid(tuple))
+			break;
+
+		/*
+		 * If the bytes at the truncation point are ASCII characters, we know
+		 * that they cannot both be part of the same character, so the
+		 * original truncation by CREATE/ALTER DATABASE would have gone no
+		 * further.
+		 */
+		if (!IS_HIGHBIT_SET(dbname[i]) || !IS_HIGHBIT_SET(dbname[i - 1]))
+			break;
+	}
+
+	table_close(relation, AccessShareLock);
+
+	return tuple;
+}
+
+/* One tuple fetch for GetDatabaseTuple */
+static HeapTuple
+GetDatabaseTupleInternal(Relation relation, const char *dbname)
+{
+	HeapTuple	tuple;
 	SysScanDesc scan;
 	ScanKeyData key[1];
 
@@ -115,11 +179,10 @@ GetDatabaseTuple(const char *dbname)
 				CStringGetDatum(dbname));
 
 	/*
-	 * Open pg_database and fetch a tuple.  Force heap scan if we haven't yet
-	 * built the critical shared relcache entries (i.e., we're starting up
-	 * without a shared relcache cache file).
+	 * Try to fetch the tuple.  Force heap scan if we haven't yet built the
+	 * critical shared relcache entries (i.e., we're starting up without a
+	 * shared relcache cache file).
 	 */
-	relation = table_open(DatabaseRelationId, AccessShareLock);
 	scan = systable_beginscan(relation, DatabaseNameIndexId,
 							  criticalSharedRelcachesBuilt,
 							  NULL,
@@ -133,7 +196,6 @@ GetDatabaseTuple(const char *dbname)
 
 	/* all done */
 	systable_endscan(scan);
-	table_close(relation, AccessShareLock);
 
 	return tuple;
 }
@@ -1011,6 +1073,8 @@ InitPostgres(const char *in_dbname, Oid dboid,
 					 errmsg("database \"%s\" does not exist", in_dbname)));
 		dbform = (Form_pg_database) GETSTRUCT(tuple);
 		dboid = dbform->oid;
+		/* Save the real (possibly truncated) database name */
+		strlcpy(dbname, NameStr(dbform->datname), sizeof(dbname));
 	}
 	else if (!OidIsValid(dboid))
 	{
@@ -1066,8 +1130,9 @@ InitPostgres(const char *in_dbname, Oid dboid,
 		if (HeapTupleIsValid(tuple))
 			datform = (Form_pg_database) GETSTRUCT(tuple);
 
+		/* Note comparison here must be against the truncated DB name */
 		if (!HeapTupleIsValid(tuple) ||
-			(in_dbname && namestrcmp(&datform->datname, in_dbname)))
+			(in_dbname && namestrcmp(&datform->datname, dbname) != 0))
 		{
 			if (in_dbname)
 				ereport(FATAL,
