diff --git a/src/backend/utils/adt/regexp.c b/src/backend/utils/adt/regexp.c
index caf45ef..dd46325 100644
--- a/src/backend/utils/adt/regexp.c
+++ b/src/backend/utils/adt/regexp.c
@@ -688,11 +688,16 @@ similar_escape(PG_FUNCTION_ARGS)
 		elen = VARSIZE_ANY_EXHDR(esc_text);
 		if (elen == 0)
 			e = NULL;			/* no escape character */
-		else if (elen != 1)
-			ereport(ERROR,
-					(errcode(ERRCODE_INVALID_ESCAPE_SEQUENCE),
-					 errmsg("invalid escape string"),
-				  errhint("Escape string must be empty or one character.")));
+		else
+		{
+			int			escape_mblen = pg_mbstrlen_with_len(e, elen);
+
+			if (escape_mblen > 1)
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_ESCAPE_SEQUENCE),
+						 errmsg("invalid escape string"),
+						 errhint("Escape string must be empty or one character.")));
+		}
 	}
 
 	/*----------
@@ -723,59 +728,94 @@ similar_escape(PG_FUNCTION_ARGS)
 	while (plen > 0)
 	{
 		char		pchar = *p;
+		int			mblen;
 
-		if (afterescape)
+		/*
+		 * If the escape string is single-byte character, we can process the
+		 * the pattern one byte at a time, ignoring multi-byte characters.
+		 * (This works because all server-encodings have the property that the
+		 * a non-first byte of a multi-byte characters always has the high-bit
+		 * set, and hence we cannot be fooled by a byte in the middle of a
+		 * multi-byte character.)
+		 */
+		if (elen == 1 || (mblen = pg_mblen(p)))
 		{
-			if (pchar == '"' && !incharclass)	/* for SUBSTRING patterns */
-				*r++ = ((nquotes++ % 2) == 0) ? '(' : ')';
-			else
+			if (afterescape)
+			{
+				if (pchar == '"' && !incharclass)	/* for SUBSTRING patterns */
+					*r++ = ((nquotes++ % 2) == 0) ? '(' : ')';
+				else
+				{
+					*r++ = '\\';
+					*r++ = pchar;
+				}
+				afterescape = false;
+			}
+			else if (e && pchar == *e)
+			{
+				/* SQL99 escape character; do not send to output */
+				afterescape = true;
+			}
+			else if (incharclass)
+			{
+				if (pchar == '\\')
+					*r++ = '\\';
+				*r++ = pchar;
+				if (pchar == ']')
+					incharclass = false;
+			}
+			else if (pchar == '[')
+			{
+				*r++ = pchar;
+				incharclass = true;
+			}
+			else if (pchar == '%')
+			{
+				*r++ = '.';
+				*r++ = '*';
+			}
+			else if (pchar == '_')
+				*r++ = '.';
+			else if (pchar == '(')
+			{
+				/* convert to non-capturing parenthesis */
+				*r++ = '(';
+				*r++ = '?';
+				*r++ = ':';
+			}
+			else if (pchar == '\\' || pchar == '.' ||
+					 pchar == '^' || pchar == '$')
 			{
 				*r++ = '\\';
 				*r++ = pchar;
 			}
-			afterescape = false;
-		}
-		else if (e && pchar == *e)
-		{
-			/* SQL99 escape character; do not send to output */
-			afterescape = true;
+			else
+				*r++ = pchar;
+			p++, plen--;
 		}
-		else if (incharclass)
+		else
 		{
-			if (pchar == '\\')
+			if (afterescape)
+			{
 				*r++ = '\\';
-			*r++ = pchar;
-			if (pchar == ']')
-				incharclass = false;
-		}
-		else if (pchar == '[')
-		{
-			*r++ = pchar;
-			incharclass = true;
-		}
-		else if (pchar == '%')
-		{
-			*r++ = '.';
-			*r++ = '*';
-		}
-		else if (pchar == '_')
-			*r++ = '.';
-		else if (pchar == '(')
-		{
-			/* convert to non-capturing parenthesis */
-			*r++ = '(';
-			*r++ = '?';
-			*r++ = ':';
-		}
-		else if (pchar == '\\' || pchar == '.' ||
-				 pchar == '^' || pchar == '$')
-		{
-			*r++ = '\\';
-			*r++ = pchar;
+				memcpy(r, p, mblen);
+				r += mblen;
+				afterescape = false;
+			}
+			else if (e && elen == mblen && memcmp(e, p, mblen) == 0)
+			{
+				/* SQL99 escape character; do not send to output */
+				afterescape = true;
+			}
+			else
+			{
+				memcpy(r, p, mblen);
+				r += mblen;
+			}
+
+			p += mblen;
+			plen -= mblen;
 		}
-		else
-			*r++ = pchar;
-		p++, plen--;
 	}
 
 	*r++ = ')';
