Skip to content

Commit 43e4377

Browse files
committed
Handle empty-string edge cases correctly in strpos().
Commit 9556aa0 rearranged the innards of text_position() in a way that would make it not work for empty search strings. Which is fine, because all callers of that code special-case an empty pattern in some way. However, the primary use-case (text_position itself) got special-cased incorrectly: historically it's returned 1 not 0 for an empty search string. Restore the historical behavior. Per complaint from Austin Drenski (via Shay Rojansky). Back-patch to v12 where it got broken. Discussion: https://postgr.es/m/CADT4RqAz7oN4vkPir86Kg1_mQBmBxCp-L_=9vRpgSNPJf0KRkw@mail.gmail.com
1 parent b1ad895 commit 43e4377

File tree

3 files changed

+33
-1
lines changed

3 files changed

+33
-1
lines changed

src/backend/utils/adt/varlena.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1118,7 +1118,12 @@ text_position(text *t1, text *t2, Oid collid)
11181118
TextPositionState state;
11191119
int result;
11201120

1121-
if (VARSIZE_ANY_EXHDR(t1) < 1 || VARSIZE_ANY_EXHDR(t2) < 1)
1121+
/* Empty needle always matches at position 1 */
1122+
if (VARSIZE_ANY_EXHDR(t2) < 1)
1123+
return 1;
1124+
1125+
/* Otherwise, can't match if haystack is shorter than needle */
1126+
if (VARSIZE_ANY_EXHDR(t1) < VARSIZE_ANY_EXHDR(t2))
11221127
return 0;
11231128

11241129
text_position_setup(t1, t2, collid, &state);
@@ -1272,6 +1277,9 @@ text_position_setup(text *t1, text *t2, Oid collid, TextPositionState *state)
12721277
* Advance to the next match, starting from the end of the previous match
12731278
* (or the beginning of the string, on first call). Returns true if a match
12741279
* is found.
1280+
*
1281+
* Note that this refuses to match an empty-string needle. Most callers
1282+
* will have handled that case specially and we'll never see it here.
12751283
*/
12761284
static bool
12771285
text_position_next(TextPositionState *state)

src/test/regress/expected/strings.out

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1371,6 +1371,24 @@ SELECT strpos('abcdef', 'xy') AS "pos_0";
13711371
0
13721372
(1 row)
13731373

1374+
SELECT strpos('abcdef', '') AS "pos_1";
1375+
pos_1
1376+
-------
1377+
1
1378+
(1 row)
1379+
1380+
SELECT strpos('', 'xy') AS "pos_0";
1381+
pos_0
1382+
-------
1383+
0
1384+
(1 row)
1385+
1386+
SELECT strpos('', '') AS "pos_1";
1387+
pos_1
1388+
-------
1389+
1
1390+
(1 row)
1391+
13741392
--
13751393
-- test replace
13761394
--

src/test/regress/sql/strings.sql

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,12 @@ SELECT strpos('abcdef', 'cd') AS "pos_3";
479479

480480
SELECT strpos('abcdef', 'xy') AS "pos_0";
481481

482+
SELECT strpos('abcdef', '') AS "pos_1";
483+
484+
SELECT strpos('', 'xy') AS "pos_0";
485+
486+
SELECT strpos('', '') AS "pos_1";
487+
482488
--
483489
-- test replace
484490
--

0 commit comments

Comments
 (0)