From: Nathan Bossart <nathan@postgresql.org>
Subject: [PATCH v8 1/1] fix database name truncation
Date: Wed, 27 Nov 2024 09:19:03 -0600
---
src/backend/tcop/backend_startup.c | 35 +++++++++-----
src/backend/tcop/postgres.c | 17 ++++++-
src/backend/utils/init/postinit.c | 76 +++++++++++++++++++++++++++---
src/include/tcop/backend_startup.h | 1 +
4 files changed, 108 insertions(+), 21 deletions(-)
inline patch 1: src/backend/tcop/backend_startup.c, src/backend/tcop/postgres.c, src/backend/utils/init/postinit.c and 1 more (+108/-21 lines) download inline-1.patchdiff --git a/src/backend/tcop/backend_startup.c b/src/backend/tcop/backend_startup.cindex 2a96c81f92..096c798df5 100644--- a/src/backend/tcop/backend_startup.c+++ b/src/backend/tcop/backend_startup.c@@ -128,7 +128,7 @@ BackendInitialize(ClientSocket *client_sock, CAC_state cac)
Port *port;
char remote_host[NI_MAXHOST];
char remote_port[NI_MAXSERV];
- StringInfoData ps_data;+ char *ps_data;
MemoryContext oldcontext;
/* Tell fd.c about the long-lived FD associated with the client_sock */
@@ -351,18 +351,9 @@ BackendInitialize(ClientSocket *client_sock, CAC_state cac)
* Now that we have the user and database name, we can set the process
* title for ps. It's good to do this as early as possible in startup.
*/
- initStringInfo(&ps_data);- if (am_walsender)- appendStringInfo(&ps_data, "%s ", GetBackendTypeDesc(B_WAL_SENDER));- appendStringInfo(&ps_data, "%s ", port->user_name);- if (port->database_name[0] != '\0')- appendStringInfo(&ps_data, "%s ", port->database_name);- appendStringInfoString(&ps_data, port->remote_host);- if (port->remote_port[0] != '\0')- appendStringInfo(&ps_data, "(%s)", port->remote_port);-- init_ps_display(ps_data.data);- pfree(ps_data.data);+ ps_data = get_ps_data(port);+ init_ps_display(ps_data);+ pfree(ps_data);
set_ps_display("initializing");
}
@@ -915,3 +906,21 @@ StartupPacketTimeoutHandler(void)
{
_exit(1);
}
++char *+get_ps_data(Port *port)+{+ StringInfoData ps_data;++ initStringInfo(&ps_data);+ if (am_walsender)+ appendStringInfo(&ps_data, "%s ", GetBackendTypeDesc(B_WAL_SENDER));+ appendStringInfo(&ps_data, "%s ", port->user_name);+ if (port->database_name[0] != '\0')+ appendStringInfo(&ps_data, "%s ", port->database_name);+ appendStringInfoString(&ps_data, port->remote_host);+ if (port->remote_port[0] != '\0')+ appendStringInfo(&ps_data, "(%s)", port->remote_port);++ return ps_data.data;+}diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.cindex bd42476717..24b9bc0cc0 100644--- a/src/backend/tcop/postgres.c+++ b/src/backend/tcop/postgres.c@@ -66,6 +66,7 @@
#include "storage/proc.h"
#include "storage/procsignal.h"
#include "storage/sinval.h"
+#include "tcop/backend_startup.h"
#include "tcop/fastpath.h"
#include "tcop/pquery.h"
#include "tcop/tcopprot.h"
@@ -4264,6 +4265,7 @@ void
PostgresMain(const char *dbname, const char *username)
{
sigjmp_buf local_sigjmp_buf;
+ char out_dbname[NAMEDATALEN];
/* these must be volatile to ensure state is preserved across longjmp: */
volatile bool send_ready_for_query = true;
@@ -4365,7 +4367,20 @@ PostgresMain(const char *dbname, const char *username)
InitPostgres(dbname, InvalidOid, /* database to connect to */
username, InvalidOid, /* role to connect as */
(!am_walsender) ? INIT_PG_LOAD_SESSION_LIBS : 0,
- NULL); /* no out_dbname */+ out_dbname);++ if (MyProcPort && unlikely(strcmp(dbname, out_dbname) != 0))+ {+ char *ps_data;++ strcpy(MyProcPort->database_name, out_dbname);++ ps_data = get_ps_data(MyProcPort);+ init_ps_display(ps_data);+ pfree(ps_data);++ set_ps_display("startup");+ }
/*
* If the PostmasterContext is still around, recycle the space; we don't
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.cindex 5b657a3f13..017a6aa7cf 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,72 @@ 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);++ 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;++ /*+ * On the first iteration, check if we actually truncated anything. If+ * not, then we don't need to bother with the successive-truncation+ * heuristic.+ */+ if (i == NAMEDATALEN - 1 && strlen(dbname) < NAMEDATALEN)+ 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 +176,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 +193,6 @@ GetDatabaseTuple(const char *dbname)
/* all done */
systable_endscan(scan);
- table_close(relation, AccessShareLock);
return tuple;
}
@@ -1011,6 +1070,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 +1127,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,
diff --git a/src/include/tcop/backend_startup.h b/src/include/tcop/backend_startup.hindex 993b013afd..6e26c00002 100644--- a/src/include/tcop/backend_startup.h+++ b/src/include/tcop/backend_startup.h@@ -40,5 +40,6 @@ typedef struct BackendStartupData
} BackendStartupData;
extern void BackendMain(char *startup_data, size_t startup_data_len) pg_attribute_noreturn();
+extern char *get_ps_data(Port *port);
#endif /* BACKEND_STARTUP_H */
--
2.39.5 (Apple Git-154)
--c7nARe+9MIG3DUN3--
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: pgsql-bugs@postgresql.org
Cc: nathan@postgresql.org
Subject: Re: [PATCH v8 1/1] fix database name truncation
In-Reply-To: <no-message-id-25447@localhost>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox