Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1tEZre-008LVT-Ce for pgsql-bugs@arkaria.postgresql.org; Fri, 22 Nov 2024 20:02:50 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.94.2) (envelope-from ) id 1tEZqe-00BnQR-6k for pgsql-bugs@arkaria.postgresql.org; Fri, 22 Nov 2024 20:01:48 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1tEZqd-00BnQI-P4 for pgsql-bugs@lists.postgresql.org; Fri, 22 Nov 2024 20:01:47 +0000 Received: from mail-qt1-x82d.google.com ([2607:f8b0:4864:20::82d]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.94.2) (envelope-from ) id 1tEZqa-003GgU-Vy for pgsql-bugs@lists.postgresql.org; Fri, 22 Nov 2024 20:01:46 +0000 Received: by mail-qt1-x82d.google.com with SMTP id d75a77b69052e-460dce6fff9so16769951cf.1 for ; Fri, 22 Nov 2024 12:01:44 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1732305704; x=1732910504; darn=lists.postgresql.org; h=in-reply-to:content-disposition:mime-version:references:message-id :subject:cc:to:from:date:from:to:cc:subject:date:message-id:reply-to; bh=v7pPOMmijFD2fPzkmT7cgHoZLAPzWAAm7GYXhgoTnRE=; b=KblDJKq0kGAp/luCPJFweE7kth08/Zq/3mwVEqdUBwP1EFNzJFwMnKevguSHEhI9jF op7leNfxnnM8gYWTQ8832zVHvVMoioglkfa++t6DAsjpvg5Nxixe8EqISLaeBx2Lscwz XM+I74eLuXnYTh25HHbllrLUuXCso+s+tZU/h1hgaLgJCYFmgqmWLqVAy0u1yYT+3Ym4 kEsIfXhlAMrbPOwvQJpAF19QhFUfrgweR5Qrmr7J3t7HYXiXl+kT5yywLkEudNV5NPvF JvjLrgBHv16QWYg18YTaYa08ndPKw19R2hOfO54mFlHD1Y1OFzw51RATVeKsrSDR8x/s 63CQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1732305704; x=1732910504; h=in-reply-to:content-disposition:mime-version:references:message-id :subject:cc:to:from:date:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=v7pPOMmijFD2fPzkmT7cgHoZLAPzWAAm7GYXhgoTnRE=; b=OfrCEBqNFa8ePkyZYNg6G/S59bkA04a4zZHBsFOzC9PNuPtGRulEBkmDvn4xnXq07k AOoPC4n4KX1wP7qbvgfRHEa01KoD8nFOCRXLlMqu5sWFhSkT5yIIgksk0n+i90IdnWjp xni/ujupY9FsDDR7+qEKJQUhTC7UevboDIXWjq36hUZF4xO9SQwg8c26H3lcEFwwVzdI 8TLX2B4nNJKhUuhL+gr9TDdVbDbkmbBgcHJHNbV+YD+1dseZupxLbBmHsw6xNv1hDCeR /lzzOOkJehu6d3vFoBYprFRPXyC3ns/lEfcDCZyfL+CqjJ+cZpK8rBSHplVHEVb+v5UZ OhMg== X-Forwarded-Encrypted: i=1; AJvYcCWO/gMCtoky28zWOVJlHxQCWLzvmduQrTmOFBOOW+RFCjeL55U25F7hEAa2kLYsbtWTeuplKi60CD/M@lists.postgresql.org X-Gm-Message-State: AOJu0YxmezWS5brkfvQpDsikcPLTDJcU+GG8ksB9d3Y/XHduGp2DOUb2 eXKr+njjyvQy8dRpSzCMOW1tYxIKDAK3HQ0O+AMvMyq5xbxy38wl X-Gm-Gg: ASbGncuVJ/wVhgal3yoeJDgrXwiu+otcieQ3DpfkWt5bIM1Lzfi0Ol3j32uscq8K+T/ oXsx+FDSAPifsUToHm0tBsqEel4b/1ZRuf+rMm2VvhY83NgrazIhR0Ulb+Y8iIpG9fF+K7iVUfm Evc/Nlqdsdv/P3mrzJBGoUteR5ObF0pNQCHOYgenj8FnVAElQI70SpsGflMDAHvQETztYtaXeC/ 9UKKu6gLUue6PFoF+lb+GSChNuXqGDyFa2/ouMQDeMPQ8BMXjn0bc35CawqkJNwEDHpSYA+GBLR uyL8IFZo8N6Cq5TGo8Yk2MZGNlG4ZF3fbQjUM+mD X-Google-Smtp-Source: AGHT+IFjfX0HwGm6d3Qmwgw+RhZN8xWARlz+C9PcxMjURH1IeoMMlmQQ+vTGtcCXh/mLzGhTxyHzbg== X-Received: by 2002:a05:622a:5a0f:b0:461:148b:188b with SMTP id d75a77b69052e-4653d567d63mr59824821cf.12.1732305703944; Fri, 22 Nov 2024 12:01:43 -0800 (PST) Received: from nathan (162-195-168-172.lightspeed.stlsmo.sbcglobal.net. [162.195.168.172]) by smtp.gmail.com with ESMTPSA id d75a77b69052e-4653c47f124sm15518851cf.62.2024.11.22.12.01.42 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 22 Nov 2024 12:01:43 -0800 (PST) Date: Fri, 22 Nov 2024 14:01:41 -0600 From: Nathan Bossart To: Tom Lane Cc: Bertrand Drouvot , Bruce Momjian , adam@labkey.com, pgsql-bugs@lists.postgresql.org Subject: Re: BUG #18711: Attempting a connection with a database name longer than 63 characters now fails Message-ID: References: <659706.1732303427@sss.pgh.pa.us> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="C/iOis7Qr7jPeu5S" Content-Disposition: inline In-Reply-To: <659706.1732303427@sss.pgh.pa.us> List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --C/iOis7Qr7jPeu5S Content-Type: text/plain; charset=us-ascii Content-Disposition: inline On Fri, Nov 22, 2024 at 02:23:47PM -0500, Tom Lane wrote: > After more thought I don't really like the idea of failing if there > are multiple matches. It means that we might fail in cases where > pre-v17 worked fine (because NAMEDATALEN-1 was accidentally the > right truncation point). ISTM the entire point of this patch is to > restore the pre-v17 behavior as much as possible, so that seems like > the wrong outcome. That's fine with me. > So that means we could do something like the attached. (There's > room for argument about which error messages in InitPostgres > should use in_dbname versus the truncated name, but I chose to > use in_dbname for the two "does not exist" reports.) Looks reasonable to me. I had been thinking of ways to simplify this code based on Bruce's feedback, and I came up with the attached. If nothing else, this at least helps minimize the amount of code that is only reached in extremely rare cases, but it's probably not quite as performant as v4 for the usual path, so v4 may still be the way to go. I think we'll also want to do something about MyProcPort->database_name (and the user name, of course). In a previous patch, I tried to update it when a match was found, but maybe it'd be better to postpone setting those values at all until after we've found the database tuple. AFAICT they are only used for the process title before this code is reached. -- nathan --C/iOis7Qr7jPeu5S Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v5-attempt-multibyte-aware-truncation.patch" diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c index 5b657a3f13..bfbc41aceb 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; 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, --C/iOis7Qr7jPeu5S--