diff --git a/src/backend/utils/adt/numutils.c b/src/backend/utils/adt/numutils.c
new file mode 100644
index 471fbb7..e2320e6
--- a/src/backend/utils/adt/numutils.c
+++ b/src/backend/utils/adt/numutils.c
@@ -314,113 +314,124 @@ pg_strtoint32_safe(const char *s, Node *
 	else if (*ptr == '+')
 		ptr++;
 
-	/* process digits */
-	if (ptr[0] == '0' && (ptr[1] == 'x' || ptr[1] == 'X'))
+	/*
+	 * Process the number's digits. We optimize for decimal input (expected to
+	 * be the most common case) first. Anything that doesn't start with a base
+	 * prefix indicator must be decimal.
+	 */
+
+	/* process decimal digits */
+	if (likely(ptr[0] != '0' || !isalpha((unsigned char) ptr[1])))
 	{
-		firstdigit = ptr += 2;
+		firstdigit = ptr;
 
 		while (*ptr)
 		{
-			if (isxdigit((unsigned char) *ptr))
+			if (isdigit((unsigned char) *ptr))
 			{
-				if (unlikely(tmp > -(PG_INT32_MIN / 16)))
+				if (unlikely(tmp > -(PG_INT32_MIN / 10)))
 					goto out_of_range;
 
-				tmp = tmp * 16 + hexlookup[(unsigned char) *ptr++];
+				tmp = tmp * 10 + (*ptr++ - '0');
 			}
 			else if (*ptr == '_')
 			{
-				/* underscore must be followed by more digits */
+				/* underscore may not be first */
+				if (unlikely(ptr == firstdigit))
+					goto invalid_syntax;
+				/* and it must be followed by more digits */
 				ptr++;
-				if (*ptr == '\0' || !isxdigit((unsigned char) *ptr))
+				if (*ptr == '\0' || !isdigit((unsigned char) *ptr))
 					goto invalid_syntax;
 			}
 			else
 				break;
 		}
 	}
-	else if (ptr[0] == '0' && (ptr[1] == 'o' || ptr[1] == 'O'))
+	/* process hex digits */
+	else if (ptr[1] == 'x' || ptr[1] == 'X')
 	{
 		firstdigit = ptr += 2;
 
 		while (*ptr)
 		{
-			if (*ptr >= '0' && *ptr <= '7')
+			if (isxdigit((unsigned char) *ptr))
 			{
-				if (unlikely(tmp > -(PG_INT32_MIN / 8)))
+				if (unlikely(tmp > -(PG_INT32_MIN / 16)))
 					goto out_of_range;
 
-				tmp = tmp * 8 + (*ptr++ - '0');
+				tmp = tmp * 16 + hexlookup[(unsigned char) *ptr++];
 			}
 			else if (*ptr == '_')
 			{
 				/* underscore must be followed by more digits */
 				ptr++;
-				if (*ptr == '\0' || *ptr < '0' || *ptr > '7')
+				if (*ptr == '\0' || !isxdigit((unsigned char) *ptr))
 					goto invalid_syntax;
 			}
 			else
 				break;
 		}
 	}
-	else if (ptr[0] == '0' && (ptr[1] == 'b' || ptr[1] == 'B'))
+	/* process octal digits */
+	else if (ptr[1] == 'o' || ptr[1] == 'O')
 	{
 		firstdigit = ptr += 2;
 
 		while (*ptr)
 		{
-			if (*ptr >= '0' && *ptr <= '1')
+			if (*ptr >= '0' && *ptr <= '7')
 			{
-				if (unlikely(tmp > -(PG_INT32_MIN / 2)))
+				if (unlikely(tmp > -(PG_INT32_MIN / 8)))
 					goto out_of_range;
 
-				tmp = tmp * 2 + (*ptr++ - '0');
+				tmp = tmp * 8 + (*ptr++ - '0');
 			}
 			else if (*ptr == '_')
 			{
 				/* underscore must be followed by more digits */
 				ptr++;
-				if (*ptr == '\0' || *ptr < '0' || *ptr > '1')
+				if (*ptr == '\0' || *ptr < '0' || *ptr > '7')
 					goto invalid_syntax;
 			}
 			else
 				break;
 		}
 	}
-	else
+	/* process binary digits */
+	else if (ptr[1] == 'b' || ptr[1] == 'B')
 	{
-		firstdigit = ptr;
+		firstdigit = ptr += 2;
 
 		while (*ptr)
 		{
-			if (isdigit((unsigned char) *ptr))
+			if (*ptr >= '0' && *ptr <= '1')
 			{
-				if (unlikely(tmp > -(PG_INT32_MIN / 10)))
+				if (unlikely(tmp > -(PG_INT32_MIN / 2)))
 					goto out_of_range;
 
-				tmp = tmp * 10 + (*ptr++ - '0');
+				tmp = tmp * 2 + (*ptr++ - '0');
 			}
 			else if (*ptr == '_')
 			{
-				/* underscore may not be first */
-				if (unlikely(ptr == firstdigit))
-					goto invalid_syntax;
-				/* and it must be followed by more digits */
+				/* underscore must be followed by more digits */
 				ptr++;
-				if (*ptr == '\0' || !isdigit((unsigned char) *ptr))
+				if (*ptr == '\0' || *ptr < '0' || *ptr > '1')
 					goto invalid_syntax;
 			}
 			else
 				break;
 		}
 	}
+	else
+		goto invalid_syntax;
 
 	/* require at least one digit */
 	if (unlikely(ptr == firstdigit))
 		goto invalid_syntax;
 
 	/* allow trailing whitespace, but not other trailing chars */
-	while (*ptr != '\0' && isspace((unsigned char) *ptr))
+	while (isspace((unsigned char) *ptr))
 		ptr++;
 
 	if (unlikely(*ptr != '\0'))
