pg.ddx.io  pgsql-bugs@postgresql.org mailing list archive  
help / color / mirror / Atom feed
From: Nathan Bossart <nathan@postgresql.org>
Subject: [PATCH v2] attempt multibyte-aware truncation of database names
Date: Wed, 20 Nov 2024 16:30:11 -0600

---
 src/backend/utils/init/postinit.c | 76 +++++++++++++++++++++++++++++--
 1 file changed, 73 insertions(+), 3 deletions(-)
 100.0% src/backend/utils/init/

inline patch 1: src/backend/utils/init/postinit.c (+73/-3 lines) download inline-1.patchdiff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c index 5b657a3f13..a594c0d4f3 100644 --- a/src/backend/utils/init/postinit.c +++ b/src/backend/utils/init/postinit.c @@ -1003,14 +1003,84 @@ InitPostgres(const char *in_dbname, Oid dboid, { HeapTuple tuple; Form_pg_database dbform; + char trunc_dbname[NAMEDATALEN]; + + /* truncate to NAMEDATALEN-1 bytes first */ + strncpy(trunc_dbname, in_dbname, NAMEDATALEN - 1); + trunc_dbname[NAMEDATALEN - 1] = '\0'; + + tuple = GetDatabaseTuple(trunc_dbname); + if (HeapTupleIsValid(tuple)) + strcpy(dbname, trunc_dbname); + + /* + * If the original name is too long and we see two consecutive bytes + * with their high bits set at the truncation point, we might have + * truncated in the middle of a multibyte character. In multibyte + * encodings, every byte of a multibyte character has its high bit + * set. So if IS_HIGHBIT_SET is true for both NAMEDATALEN-1 and + * NAMEDATALEN-2, we know we're in the middle of a multibyte + * character. We need to try truncating one more byte back to find the + * start of the next character. + */ + if (strlen(in_dbname) >= NAMEDATALEN && + IS_HIGHBIT_SET(in_dbname[NAMEDATALEN - 1]) && + IS_HIGHBIT_SET(in_dbname[NAMEDATALEN - 2])) + { + /* + * Try progressively shorter truncations to find a character + * boundary. We check at most MAX_MULTIBYTE_CHAR_LEN-1 bytes back. + */ + for (int i = 1; i < MAX_MULTIBYTE_CHAR_LEN - 1; i++) + { + HeapTuple tmp; + + /* truncate one more byte back */ + trunc_dbname[NAMEDATALEN - 1 - i] = '\0'; + tmp = GetDatabaseTuple(trunc_dbname); + + /* + * If we already had a match then the name is ambiguous and we + * must fail. + */ + if (HeapTupleIsValid(tmp)) + { + if (HeapTupleIsValid(tuple)) + ereport(FATAL, + (errmsg("ambiguous database name"))); + tuple = tmp; + strcpy(dbname, trunc_dbname); + } + + /* + * If we've hit a byte with high bit clear (an ASCII byte), we + * know we can't be in the middle of a multibyte character, + * because all bytes of a multibyte character must have their + * high bits set. Any following byte must therefore be the + * start of a new character, so we can stop looking for + * earlier truncation points. + */ + if (!IS_HIGHBIT_SET(dbname[NAMEDATALEN - 2 - i])) + break; + } + } - tuple = GetDatabaseTuple(in_dbname); if (!HeapTupleIsValid(tuple)) ereport(FATAL, (errcode(ERRCODE_UNDEFINED_DATABASE), errmsg("database \"%s\" does not exist", in_dbname))); + dbform = (Form_pg_database) GETSTRUCT(tuple); dboid = dbform->oid; + + if (MyProcPort && MyProcPort->database_name && + strcmp(MyProcPort->database_name, dbname) != 0) + { + pfree(MyProcPort->database_name); + MyProcPort->database_name = pstrdup(dbname); + + /* XXX: should we fix process title? */ + } } else if (!OidIsValid(dboid)) { @@ -1067,12 +1137,12 @@ InitPostgres(const char *in_dbname, Oid dboid, datform = (Form_pg_database) GETSTRUCT(tuple); if (!HeapTupleIsValid(tuple) || - (in_dbname && namestrcmp(&datform->datname, in_dbname))) + (in_dbname && namestrcmp(&datform->datname, dbname))) { if (in_dbname) ereport(FATAL, (errcode(ERRCODE_UNDEFINED_DATABASE), - errmsg("database \"%s\" does not exist", in_dbname), + errmsg("database \"%s\" does not exist", dbname), errdetail("It seems to have just been dropped or renamed."))); else ereport(FATAL, -- 2.34.1
--H+M0G8KNNopj95XV--

Message-ID: <no-message-id-25444@localhost>
Permalink:  ../no-message-id-25444@localhost/
Also on:    postgresql.org/message-id/no-message-id-25444@localhost

 ·  · 

reply

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 v2] attempt multibyte-aware truncation of database names
  In-Reply-To: <no-message-id-25444@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