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 1rheyn-00Ci1G-IQ for pgsql-docs@arkaria.postgresql.org; Wed, 06 Mar 2024 00:17:54 +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 1rheyl-003to9-Ur for pgsql-docs@arkaria.postgresql.org; Wed, 06 Mar 2024 00:17:52 +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 1rheyl-003to1-NW for pgsql-docs@lists.postgresql.org; Wed, 06 Mar 2024 00:17:52 +0000 Received: from momjian.us ([72.94.173.45]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1rheyh-00339F-I5 for pgsql-docs@lists.postgresql.org; Wed, 06 Mar 2024 00:17:50 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=momjian.us; s=2024011501; h=In-Reply-To:Content-Type:MIME-Version:References:Message-ID: Subject:To:From:Date:Sender:Reply-To:Cc:Content-Transfer-Encoding:Content-ID: Content-Description; bh=h1C40DLLzTbgdPVtU9MkrNi318Qw5gsetx+IAdlhw7Q=; b=FfkA7 3KdjBf25gaufTi44OBThM/tyEWOxRNnXqVQDrPPwgBuiCZHTbEWi2PQBVmF9gJ8h5Ew52Wwf6giJy 0cATKaaHr++MUq6v4Dx42lNKnvIRTCQtMm46ufrGOvFe64tCOKcaP2tvtak8kMguCpwKOAqrFjYPb IhsbVv59T3JJiNPVuyBHmCl+nndlxBlqPq3+nj4MBd/4MpH4PQnOYYIXDQ3YswenaTYm9maaOyhM2 rV8XrMwUqulSmbK23BZzUnmAzyodLHkHlH65d6K+NY5U0cEPGm3doynxfHTIgE+9BNzrJYSDAsWfU xN5+dWCRPL/zic8hhb6ln+ZApdC+g==; Received: from bruce by momjian.us with local (Exim 4.96) (envelope-from ) id 1rheyh-001Kt4-2J; Tue, 05 Mar 2024 19:17:47 -0500 Date: Tue, 5 Mar 2024 19:17:47 -0500 From: Bruce Momjian To: dansonlinepresence@gmail.com, pgsql-docs@lists.postgresql.org Subject: Re: substring start position behavior Message-ID: References: <170905442373.643.11536838320909376197@wrigleys.postgresql.org> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="puTPz6xoKCo/h9cv" Content-Disposition: inline In-Reply-To: <170905442373.643.11536838320909376197@wrigleys.postgresql.org> List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --puTPz6xoKCo/h9cv Content-Type: text/plain; charset=us-ascii Content-Disposition: inline On Tue, Feb 27, 2024 at 05:20:23PM +0000, PG Doc comments form wrote: > The following documentation comment has been logged on the website: > > Page: https://www.postgresql.org/docs/16/functions-string.html > Description: > > Hey, > > I was confused by substring behavior today, when giving 0 as a start > position. I understand now that string indices are 1-based, have a certain > flexibility about where to start (allowing negative start positions), and > that this is defined in the standard SQL spec. > > I'm comfy with all this, but I think it'd be nice to have a hint in the pg > substring docs for nonpositive start positions, so that users don't have to > have paid for the standard SQL spec to get past this. To me, substring seems > like a relatively common function with relatively surprising behavior. I dug into this and quickly became as confused as you were. The best explanation I found of the current behavior is here (with diagram): https://www.mssqltips.com/sqlservertutorial/9374/sql-substring-function/ SELECT SUBSTRING('Hello world',-2,5) as msg The last Postgres community discussion of this behavior I could find was from 2007: https://www.postgresql.org/message-id/flat/12803.1168804636%40sss.pgh.pa.us#8316fb2298c9e49f77867a1ae2ead447 This web page explains the feature: https://stackoverflow.com/questions/33462061/sql-server-substring-position-negative-value but also asks: now the only question that remains is, "why would anyone need it to behave this way?" and the answer given is: @mao47 Well, it depends. I am not an author of implementation of SUBSTR but I guess with negative index it behaves like LEFT(string, LEN(string) - 1 - index). It works the same way in PostgreSQL so maybe it is SQL standard. Informix has substring() which matches the SQL standard, and substr() which uses negative start from the end of the string: https://www.ibm.com/docs/en/informix-servers/14.10?topic=smf-substr-function Oracle doesn't have substring(), just substr(), and matches Informix behavior, I think. I have developed the attached doc patch to document this. The only question is whether this substring behavior is so odd that we should not document it. -- Bruce Momjian https://momjian.us EDB https://enterprisedb.com Only you can decide what is important to you. --puTPz6xoKCo/h9cv Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="substring.diff" diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index e5fa82c161..45553ab824 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -2783,6 +2783,13 @@ SELECT NOT(ROW(table.*) IS NOT NULL) FROM TABLE; -- detect at least one null in specified. Provide at least one of start and count. + + Non-positive start values specify a + start position before the first character, and therefore the + count must be two greater than the absolute + value of start to begin returning characters + from the start of the string. + substring('Thomas' from 2 for 3) hom @@ -2794,6 +2801,10 @@ SELECT NOT(ROW(table.*) IS NOT NULL) FROM TABLE; -- detect at least one null in substring('Thomas' for 2) Th + + + substring('Thomas' from -4 for 8) + Tho --puTPz6xoKCo/h9cv--