diff --git a/src/backend/utils/adt/numutils.c b/src/backend/utils/adt/numutils.c
index 471fbb7ee6..54c2b31c66 100644
--- a/src/backend/utils/adt/numutils.c
+++ b/src/backend/utils/adt/numutils.c
@@ -141,6 +141,10 @@ pg_strtoint16_safe(const char *s, Node *escontext)
 	{
 		firstdigit = ptr += 2;
 
+		/* require at least one digit or underscore */
+		if (unlikely(!isxdigit((unsigned char) *ptr) && *ptr != '_'))
+			goto invalid_syntax;
+
 		while (*ptr)
 		{
 			if (isxdigit((unsigned char) *ptr))
@@ -165,6 +169,10 @@ pg_strtoint16_safe(const char *s, Node *escontext)
 	{
 		firstdigit = ptr += 2;
 
+		/* require at least one digit or underscore */
+		if (unlikely((*ptr < '0' || *ptr > '7') && *ptr != '_'))
+			goto invalid_syntax;
+
 		while (*ptr)
 		{
 			if (*ptr >= '0' && *ptr <= '7')
@@ -189,6 +197,10 @@ pg_strtoint16_safe(const char *s, Node *escontext)
 	{
 		firstdigit = ptr += 2;
 
+		/* require at least one digit or underscore */
+		if (unlikely((*ptr < '0' || *ptr > '1') && *ptr != '_'))
+			goto invalid_syntax;
+
 		while (*ptr)
 		{
 			if (*ptr >= '0' && *ptr <= '1')
@@ -213,6 +225,10 @@ pg_strtoint16_safe(const char *s, Node *escontext)
 	{
 		firstdigit = ptr;
 
+		/* require at least one digit */
+		if (unlikely(!isdigit((unsigned char) *ptr)))
+			goto invalid_syntax;
+
 		while (*ptr)
 		{
 			if (isdigit((unsigned char) *ptr))
@@ -319,6 +335,10 @@ pg_strtoint32_safe(const char *s, Node *escontext)
 	{
 		firstdigit = ptr += 2;
 
+		/* require at least one digit or underscore */
+		if (unlikely(!isxdigit((unsigned char) *ptr) && *ptr != '_'))
+			goto invalid_syntax;
+
 		while (*ptr)
 		{
 			if (isxdigit((unsigned char) *ptr))
@@ -343,6 +363,10 @@ pg_strtoint32_safe(const char *s, Node *escontext)
 	{
 		firstdigit = ptr += 2;
 
+		/* require at least one digit or underscore */
+		if (unlikely((*ptr < '0' || *ptr > '7') && *ptr != '_'))
+			goto invalid_syntax;
+
 		while (*ptr)
 		{
 			if (*ptr >= '0' && *ptr <= '7')
@@ -367,6 +391,10 @@ pg_strtoint32_safe(const char *s, Node *escontext)
 	{
 		firstdigit = ptr += 2;
 
+		/* require at least one digit or underscore */
+		if (unlikely((*ptr < '0' || *ptr > '1') && *ptr != '_'))
+			goto invalid_syntax;
+
 		while (*ptr)
 		{
 			if (*ptr >= '0' && *ptr <= '1')
@@ -391,6 +419,10 @@ pg_strtoint32_safe(const char *s, Node *escontext)
 	{
 		firstdigit = ptr;
 
+		/* require at least one digit */
+		if (unlikely(!isdigit((unsigned char) *ptr)))
+			goto invalid_syntax;
+
 		while (*ptr)
 		{
 			if (isdigit((unsigned char) *ptr))
@@ -497,6 +529,10 @@ pg_strtoint64_safe(const char *s, Node *escontext)
 	{
 		firstdigit = ptr += 2;
 
+		/* require at least one digit or underscore */
+		if (unlikely(!isxdigit((unsigned char) *ptr) && *ptr != '_'))
+			goto invalid_syntax;
+
 		while (*ptr)
 		{
 			if (isxdigit((unsigned char) *ptr))
@@ -521,6 +557,10 @@ pg_strtoint64_safe(const char *s, Node *escontext)
 	{
 		firstdigit = ptr += 2;
 
+		/* require at least one digit or underscore */
+		if (unlikely((*ptr < '0' || *ptr > '7') && *ptr != '_'))
+			goto invalid_syntax;
+
 		while (*ptr)
 		{
 			if (*ptr >= '0' && *ptr <= '7')
@@ -545,6 +585,10 @@ pg_strtoint64_safe(const char *s, Node *escontext)
 	{
 		firstdigit = ptr += 2;
 
+		/* require at least one digit or underscore */
+		if (unlikely((*ptr < '0' || *ptr > '1') && *ptr != '_'))
+			goto invalid_syntax;
+
 		while (*ptr)
 		{
 			if (*ptr >= '0' && *ptr <= '1')
@@ -569,6 +613,10 @@ pg_strtoint64_safe(const char *s, Node *escontext)
 	{
 		firstdigit = ptr;
 
+		/* require at least one digit */
+		if (unlikely(!isdigit((unsigned char) *ptr)))
+			goto invalid_syntax;
+
 		while (*ptr)
 		{
 			if (isdigit((unsigned char) *ptr))
