v6-attempt-multibyte-aware-truncation.patch
text/x-diff
Filename: v6-attempt-multibyte-aware-truncation.patch
Type: text/x-diff
Part: 0
Patch
Format: unified
Series: patch v6
| File | + | − |
|---|---|---|
| src/backend/utils/init/postinit.c | 72 | 7 |
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,