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.96) (envelope-from ) id 1vrLOt-00G96p-1z for pgsql-bugs@arkaria.postgresql.org; Sat, 14 Feb 2026 19:33:55 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1vrLOr-0008Bg-0D for pgsql-bugs@arkaria.postgresql.org; Sat, 14 Feb 2026 19:33:53 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1vrLOq-0008BX-1c for pgsql-bugs@lists.postgresql.org; Sat, 14 Feb 2026 19:33:52 +0000 Received: from mail-dy1-x1332.google.com ([2607:f8b0:4864:20::1332]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1vrLOn-00000000gVD-3dxV for pgsql-bugs@lists.postgresql.org; Sat, 14 Feb 2026 19:33:51 +0000 Received: by mail-dy1-x1332.google.com with SMTP id 5a478bee46e88-2baab3137bcso1756094eec.0 for ; Sat, 14 Feb 2026 11:33:49 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=leadboat.com; s=google; t=1771097627; x=1771702427; darn=lists.postgresql.org; h=user-agent:in-reply-to:content-transfer-encoding :content-disposition:mime-version:references:message-id:subject:cc :to:from:date:from:to:cc:subject:date:message-id:reply-to; bh=+J1Lx2InI2awsYoXNPXUUbL6lDT2zTM6JacrTH1ZtQQ=; b=BXCEh9pUPy24isfs9H+p9vfCAMYIH2Cqpd03CgVw2HJczfGlM/aJ1BRjJ0IejqReLd mWbGW+2MZP/723vmB+xVUToajkGyKirHrxDbndx7yowDu0DM8SIo6KErLbTNuCtbvc2u UQ9r9dnRRmBg5dTV9mrfOeo/FeRO+Fpw2DcNM= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1771097627; x=1771702427; h=user-agent:in-reply-to:content-transfer-encoding :content-disposition:mime-version:references:message-id:subject:cc :to:from:date:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=+J1Lx2InI2awsYoXNPXUUbL6lDT2zTM6JacrTH1ZtQQ=; b=ap3zo4Z5E6Mj0Oa7AvPzyKVL7POnFyyTLtpDkJ28tHwu7IBYMDbkmh87I8wOg9y64I kKn2b2mN6OKYAFa9xHBb959W5mGqrB6AV1Ro/HGnAVlbaU2q4+5e1ztJJIsccwe/ToGv //3NJlIn0iEkU3wRD+d+nsPBZ8h88h1C5pwMkqXvR0nCco83EE8rmAQ65ONBsM1OV4oB nGKWpcY05Mh7tacnTM/ELbbECMkEjRmrS+Q7muUXZZQ/NicRRMpcLI9a6ObVwTt0J8T/ bc8/33UwJ2wiD5nuIIp8/i3BEFlwwIzmVrIkuaT2mhlywxUviRy1j2QT1g7pDON54MJJ kV4Q== X-Forwarded-Encrypted: i=1; AJvYcCVnIrSApq8s3oRWMmwrK2gvVEBLS93l8xIQ/lqbKR2BYfhKe7FfgNUQ4Q/ybzemG7UCrvv5at5Xxet3@lists.postgresql.org X-Gm-Message-State: AOJu0YzH3LInp3hEp47sAuPwREx4IRhAZa/L4fB+BCoW/CVdu0xdd4nw v6NX+2cj+D4juJ/GVLHJzuAWTey/oTVVg9zyFOX0ul9sdKfGoZqSI8S1SpRa4LOMeQ== X-Gm-Gg: AZuq6aKNJzF1sCo5FyGGanGrcq6YHuehR+uqvIR2UyMOpfUXGeXuQDQML8gl2CRcb/g oX3HDEO+jySFxWAP2pvX/BwQffuOENE8RnKMwoLlmiIsyYQi6n1y4aep6z9WtklA/ImapryYF9q OzYmPUCdITo/22vVtk2O+looj1nTqk46Mb7ZrnFyhrydFBZyPuC2D4pC4ijPzL4TAqTbus5+okQ XWUco1ZT0CNRppGri+WoZTD/lsgZXaQknYDf31fCDY73+IxbhxJ5od2MzhXFRs0AFkrkWSsvXXY BfnDNcpahmStIrQTX6n7/hKmSO1hKq0fUj3EHcmvQUol7MKusaEhYHqfu1fzDdxW+x4+HfL+U7y Imx4SI2wCvZJV368/avpsJr0vhyGwNEu4ABFBKeRNkNfgfNoomhxZZPDcxPw1GLQSqTVfiUFL/b W5ZpB6yMJZ6GdGEvcNiKT82k+D/DbO3xYZy9LMpH3+g0bsm3fW22JOXZG22ETb5n1jiS9sWA== X-Received: by 2002:a05:693c:300c:b0:2ae:55f0:f2be with SMTP id 5a478bee46e88-2bac97d281fmr1203889eec.31.1771097627320; Sat, 14 Feb 2026 11:33:47 -0800 (PST) Received: from rfd.leadboat.com (c-73-15-160-255.hsd1.ca.comcast.net. [73.15.160.255]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-2bacb5653aesm3079590eec.11.2026.02.14.11.33.46 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Sat, 14 Feb 2026 11:33:46 -0800 (PST) Date: Sat, 14 Feb 2026 11:33:44 -0800 From: Noah Misch To: Thomas Munro Cc: ranvis@gmail.com, pgsql-bugs@lists.postgresql.org Subject: Re: BUG #19406: substring(text) fails on valid UTF-8 toasted value in PostgreSQL 15.16 Message-ID: <20260214193344.48@rfd.leadboat.com> References: <19406-9867fddddd724fca@postgresql.org> <20260213172702.71@rfd.leadboat.com> <20260213224804.2c@rfd.leadboat.com> <20260214002113.1f.noahmisch@microsoft.com> <20260214053821.fa.noahmisch@microsoft.com> MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Disposition: inline Content-Transfer-Encoding: 8bit In-Reply-To: User-Agent: Mutt/2.2.12 (2023-09-09) List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk On Sun, Feb 15, 2026 at 08:07:22AM +1300, Thomas Munro wrote: > On Sat, Feb 14, 2026 at 9:15 PM Thomas Munro wrote: > > On Sat, Feb 14, 2026 at 6:38 PM Noah Misch wrote: > > > [mblen-valgrind-after-report-v1.patch] > > > > LGTM. The new valgrind check should clearly be after the new non-local exit. > > > > Studying the other patch... Thank you. > /* > - * Total slice size in bytes can't be any > longer than the start > - * position plus substring length times the > encoding max length. > - * If that overflows, we can just use -1. > + * Total slice size in bytes can't be any > longer than the > + * inclusive end position times the encoding > max length. If that > + * overflows, we can just use -1. > */ > - if (pg_mul_s32_overflow(E, eml, &slice_size)) > + if (pg_mul_s32_overflow(E - 1, eml, &slice_size)) > slice_size = -1; > > Isn't it still conceptually "exclusive", but adjusted to be zero-indexed? Since it's discrete (being an integer), "up to E, exclusive" and "up to E - 1, inclusive" are the same thing. My comment may not be the optimal way to express that. > /* Now we can get the actual length of the slice in MB > characters */ > - slice_strlen = pg_mbstrlen_with_len(VARDATA_ANY(slice), > - > slice_len); > + slice_strlen = > + (slice_size == -1 ? > + pg_mbstrlen_with_len(VARDATA_ANY(slice), slice_len) : > + pg_mbcharcliplen_chars(VARDATA_ANY(slice), > slice_len, E - 1)); > > Comment presumably needs adjustment to say that we only count as far > as we need to, and why. Changed to: /* * Now we can get the actual length of the slice in MB characters, * stopping at the end of the substring. Continuing beyond the * substring end could find an incomplete character attributable * solely to DatumGetTextPSlice() chopping in the middle of a * character, and it would be superfluous work at best. */ > There is something a bit strange about all this, though. > pg_mbstrlen_with_len(..., -1) returns 0, so if you ask for characters > that really exist past 2^29 (~500 million), you must get an empty > string, right? That's hard to reach, pre-existing and out of scope > for the immediate problem report, except ... now we're contorting the > code even further to keep it. - slice_size is the amount we *requested* from the toaster. It can be -1, which retrieves the max available. - slice_len is the amount *returned* from the toaster. It's nonnegative. Does a behavior specific to strings >2^29 still exist? > The outline I had come up with before seeing your patch was: let's > just delete it. The position search can check bounds incrementally, > following our general approach. This avoids the reported problem by > ditching the pre-flight scan through the slice (up to 4x more > pg_mblen_XXX calls and memory access than we strictly need), and also > the special cases for empty strings since they already fall out of the > general behaviour (am I missing something?), not leaving much code > behind. Like you, I made a note that it's wasteful to make two mblen passes over the string. I'm only seeing a 50% reduction in mblen calls, not an 80% reduction, but I didn't look too closely. I guessed such a change would be less clearly correct, so I figured it would be less suitable for back branches. Hence, I didn't draft it. > As far as I can see so far, the only user-visible side-effect > requires corruption: substring() moves from the > internal-NUL-is-terminator category to internal-NUL-is-character > category, but that's an implementation detail. That does carry some risk, not necessarily too much to accept. > When I saw your patch yesterday, I initially abandoned the thought, > thinking that your idea looked more conservative, but after sleeping > on it and reflecting again on these oddities, I have merged my draft > implementation with your tests, ancient detoasting fence post > observation and commit message, just to see if you think this approach > might be worth considering further. My first impression, hurried due to the commit ETA in 30 minutes, is that this is less conservative and should hold for master-only.