From fc55b9d81938c4121804a5f4d1dce19561e042cb Mon Sep 17 00:00:00 2001 From: Shihao Date: Fri, 25 Sep 2026 00:55:47 -0400 Subject: [PATCH v1 1/2] Reject LIKE patterns that end with an escape, whatever the input MatchText() raised "LIKE pattern must not end with escape character" only when matching got to the end of the pattern. If the text ran out first, or an earlier character did not match, the bad pattern quietly returned false. So '' LIKE '\' and 'x' LIKE 'y\' returned false, while 'xy' LIKE 'x\' raised the error. Check for this before matching. A pattern ends with an escape exactly when it ends with an odd number of backslashes, so we only need to look back from the last byte. Usually that byte is not a backslash and we stop there. This avoids the extra pass over the whole pattern that was the objection when this came up in bug #18765. This turns some queries that returned false into errors, so no back-patch, same as 3d8fd757326 and ece869b11ee. Reported-by: Qifan Liu Reported-by: Anmol Mohanty Discussion: https://postgr.es/m/19699-dbaa58bbf8db1859@postgresql.org Discussion: https://postgr.es/m/18765-6c26d2047e6f5143@postgresql.org --- src/backend/utils/adt/like_match.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/backend/utils/adt/like_match.c b/src/backend/utils/adt/like_match.c index defcaa96fb5..23bb6daeb9e 100644 --- a/src/backend/utils/adt/like_match.c +++ b/src/backend/utils/adt/like_match.c @@ -89,6 +89,28 @@ MatchText(const char *t, int tlen, const char *p, int plen, pg_locale_t locale) if (plen == 1 && *p == '%') return LIKE_TRUE; + /* + * Reject a pattern that ends with an escape character. The loop below + * only notices that if matching gets that far, so without this check the + * error would depend on the text. A pattern ends with an escape iff it + * ends with an odd number of backslashes, since each pair is an escaped + * backslash. That holds in multibyte encodings too, since a backslash + * byte can't be part of a multibyte character (see below). Usually the + * last byte is not a backslash, so this is cheap enough to repeat in + * recursive calls. + */ + if (plen > 0 && p[plen - 1] == '\\') + { + int nbackslashes = 1; + + while (nbackslashes < plen && p[plen - 1 - nbackslashes] == '\\') + nbackslashes++; + if (nbackslashes % 2 != 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_ESCAPE_SEQUENCE), + errmsg("LIKE pattern must not end with escape character"))); + } + /* Since this function recurses, it could be driven to stack overflow */ check_stack_depth(); -- 2.37.1 (Apple Git-137.1)