On 2022-12-26 Mo 14:12, Andrew Dunstan wrote:
> On 2022-12-26 Mo 12:47, Tom Lane wrote:
>> Here's a proposed patch for making tsvectorin and tsqueryin
>> report errors softly.  We have to take the changes down a
>> couple of levels of subroutines, but it's not hugely difficult.
>
> Great!
>
>
>> With the other patches I've posted recently, this covers all
>> of the core datatype input functions.  There are still half
>> a dozen to tackle in contrib.
>>
>>                      
>
> Yeah, I'm currently looking at those in ltree.
>
>

Here's a patch that covers the ltree and intarray contrib modules. I
think that would leave just hstore to be done.


cheers


andrew

--
Andrew Dunstan
EDB: https://www.enterprisedb.com
diff --git a/contrib/intarray/_int_bool.c b/contrib/intarray/_int_bool.c
index 3ed88af76d..77bbf77c43 100644
--- a/contrib/intarray/_int_bool.c
+++ b/contrib/intarray/_int_bool.c
@@ -149,8 +149,8 @@ pushquery(WORKSTATE *state, int32 type, int32 val)
 /*
  * make polish notation of query
  */
-static int32
-makepol(WORKSTATE *state)
+static bool
+makepol(WORKSTATE *state, int32 *res, struct Node *escontext)
 {
 	int32		val,
 				type;
@@ -179,7 +179,7 @@ makepol(WORKSTATE *state)
 				else
 				{
 					if (lenstack == STACKDEPTH)
-						ereport(ERROR,
+						ereturn(escontext, false,
 								(errcode(ERRCODE_STATEMENT_TOO_COMPLEX),
 								 errmsg("statement too complex")));
 					stack[lenstack] = val;
@@ -187,8 +187,10 @@ makepol(WORKSTATE *state)
 				}
 				break;
 			case OPEN:
-				if (makepol(state) == ERR)
-					return ERR;
+				if (!makepol(state, res, escontext))
+					return false;
+				if (*res == ERR)
+					return true;
 				while (lenstack && (stack[lenstack - 1] == (int32) '&' ||
 									stack[lenstack - 1] == (int32) '!'))
 				{
@@ -202,14 +204,14 @@ makepol(WORKSTATE *state)
 					lenstack--;
 					pushquery(state, OPR, stack[lenstack]);
 				};
-				return END;
+				*res = END;
+				return true;
 				break;
 			case ERR:
 			default:
-				ereport(ERROR,
+				ereturn(escontext, false,
 						(errcode(ERRCODE_SYNTAX_ERROR),
 						 errmsg("syntax error")));
-				return ERR;
 		}
 	}
 
@@ -218,7 +220,8 @@ makepol(WORKSTATE *state)
 		lenstack--;
 		pushquery(state, OPR, stack[lenstack]);
 	};
-	return END;
+	*res = END;
+	return true;
 }
 
 typedef struct
@@ -483,6 +486,8 @@ bqarr_in(PG_FUNCTION_ARGS)
 	ITEM	   *ptr;
 	NODE	   *tmp;
 	int32		pos = 0;
+	int32       polres;
+	struct Node *escontext = fcinfo->context;
 
 #ifdef BS_DEBUG
 	StringInfoData pbuf;
@@ -495,14 +500,15 @@ bqarr_in(PG_FUNCTION_ARGS)
 	state.str = NULL;
 
 	/* make polish notation (postfix, but in reverse order) */
-	makepol(&state);
+	if (!makepol(&state, &polres, escontext))
+		PG_RETURN_NULL();
 	if (!state.num)
-		ereport(ERROR,
+		ereturn(escontext, (Datum) 0,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 				 errmsg("empty query")));
 
 	if (state.num > QUERYTYPEMAXITEMS)
-		ereport(ERROR,
+		ereturn(escontext, (Datum) 0,
 				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
 				 errmsg("number of query items (%d) exceeds the maximum allowed (%d)",
 						state.num, (int) QUERYTYPEMAXITEMS)));
diff --git a/contrib/intarray/expected/_int.out b/contrib/intarray/expected/_int.out
index a09d40efa1..c953065a5c 100644
--- a/contrib/intarray/expected/_int.out
+++ b/contrib/intarray/expected/_int.out
@@ -398,6 +398,21 @@ SELECT '1&(2&(4&(5|!6)))'::query_int;
  1 & 2 & 4 & ( 5 | !6 )
 (1 row)
 
+-- test non-error-throwing input
+SELECT str as "query_int",
+       pg_input_is_valid(str,'query_int') as ok,
+       pg_input_error_message(str,'query_int') as errmsg
+FROM (VALUES ('1&(2&(4&(5|6)))'),
+             ('1#(2&(4&(5&6)))'),
+             ('foo'))
+      AS a(str);
+    query_int    | ok |    errmsg    
+-----------------+----+--------------
+ 1&(2&(4&(5|6))) | t  | 
+ 1#(2&(4&(5&6))) | f  | syntax error
+ foo             | f  | syntax error
+(3 rows)
+
 CREATE TABLE test__int( a int[] );
 \copy test__int from 'data/test__int.data'
 ANALYZE test__int;
diff --git a/contrib/intarray/sql/_int.sql b/contrib/intarray/sql/_int.sql
index b26fc57e4d..4c9ba4c1fb 100644
--- a/contrib/intarray/sql/_int.sql
+++ b/contrib/intarray/sql/_int.sql
@@ -75,6 +75,17 @@ SELECT '1&2&4&5&6'::query_int;
 SELECT '1&(2&(4&(5|6)))'::query_int;
 SELECT '1&(2&(4&(5|!6)))'::query_int;
 
+-- test non-error-throwing input
+
+SELECT str as "query_int",
+       pg_input_is_valid(str,'query_int') as ok,
+       pg_input_error_message(str,'query_int') as errmsg
+FROM (VALUES ('1&(2&(4&(5|6)))'),
+             ('1#(2&(4&(5&6)))'),
+             ('foo'))
+      AS a(str);
+
+
 
 CREATE TABLE test__int( a int[] );
 \copy test__int from 'data/test__int.data'
diff --git a/contrib/ltree/expected/ltree.out b/contrib/ltree/expected/ltree.out
index c6d8f3ef75..b95be71c78 100644
--- a/contrib/ltree/expected/ltree.out
+++ b/contrib/ltree/expected/ltree.out
@@ -8084,3 +8084,28 @@ SELECT count(*) FROM _ltreetest WHERE t ? '{23.*.1,23.*.2}' ;
     15
 (1 row)
 
+-- test non-error-throwing input
+SELECT str as "value", typ as "type",
+       pg_input_is_valid(str,typ) as ok,
+       pg_input_error_message(str,typ) as errmsg
+FROM (VALUES ('.2.3', 'ltree'),
+             ('1.2.', 'ltree'),
+             ('1.2.3','ltree'),
+             ('@.2.3','lquery'),
+             (' 2.3', 'lquery'),
+             ('1.2.3','lquery'),
+             ('$tree & aWdf@*','ltxtquery'),
+             ('!tree & aWdf@*','ltxtquery'))
+      AS a(str,typ);
+     value      |   type    | ok |               errmsg               
+----------------+-----------+----+------------------------------------
+ .2.3           | ltree     | f  | ltree syntax error at character 1
+ 1.2.           | ltree     | f  | ltree syntax error
+ 1.2.3          | ltree     | t  | 
+ @.2.3          | lquery    | f  | lquery syntax error at character 1
+  2.3           | lquery    | f  | lquery syntax error at character 1
+ 1.2.3          | lquery    | t  | 
+ $tree & aWdf@* | ltxtquery | f  | operand syntax error
+ !tree & aWdf@* | ltxtquery | t  | 
+(8 rows)
+
diff --git a/contrib/ltree/ltree_io.c b/contrib/ltree/ltree_io.c
index 15115cb29f..5d70755b75 100644
--- a/contrib/ltree/ltree_io.c
+++ b/contrib/ltree/ltree_io.c
@@ -24,16 +24,16 @@ typedef struct
 #define LTPRS_WAITNAME	0
 #define LTPRS_WAITDELIM 1
 
-static void finish_nodeitem(nodeitem *lptr, const char *ptr,
-							bool is_lquery, int pos);
+static bool finish_nodeitem(nodeitem *lptr, const char *ptr,
+							bool is_lquery, int pos, struct Node *escontext);
 
 
 /*
  * expects a null terminated string
  * returns an ltree
  */
-static ltree *
-parse_ltree(const char *buf)
+static bool
+parse_ltree(const char *buf, ltree **res, struct Node *escontext)
 {
 	const char *ptr;
 	nodeitem   *list,
@@ -46,7 +46,7 @@ parse_ltree(const char *buf)
 	int			charlen;
 	int			pos = 1;		/* character position for error messages */
 
-#define UNCHAR ereport(ERROR, \
+#define UNCHAR ereturn(escontext, false,\
 					   errcode(ERRCODE_SYNTAX_ERROR), \
 					   errmsg("ltree syntax error at character %d", \
 							  pos))
@@ -61,7 +61,7 @@ parse_ltree(const char *buf)
 	}
 
 	if (num + 1 > LTREE_MAX_LEVELS)
-		ereport(ERROR,
+		ereturn(escontext, false,
 				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
 				 errmsg("number of ltree labels (%d) exceeds the maximum allowed (%d)",
 						num + 1, LTREE_MAX_LEVELS)));
@@ -86,7 +86,8 @@ parse_ltree(const char *buf)
 			case LTPRS_WAITDELIM:
 				if (t_iseq(ptr, '.'))
 				{
-					finish_nodeitem(lptr, ptr, false, pos);
+					if (!finish_nodeitem(lptr, ptr, false, pos, escontext))
+						return false;
 					totallen += MAXALIGN(lptr->len + LEVEL_HDRSIZE);
 					lptr++;
 					state = LTPRS_WAITNAME;
@@ -105,12 +106,13 @@ parse_ltree(const char *buf)
 
 	if (state == LTPRS_WAITDELIM)
 	{
-		finish_nodeitem(lptr, ptr, false, pos);
+		if (!finish_nodeitem(lptr, ptr, false, pos, escontext))
+			return false;
 		totallen += MAXALIGN(lptr->len + LEVEL_HDRSIZE);
 		lptr++;
 	}
 	else if (!(state == LTPRS_WAITNAME && lptr == list))
-		ereport(ERROR,
+		ereturn(escontext, false,
 				(errcode(ERRCODE_SYNTAX_ERROR),
 				 errmsg("ltree syntax error"),
 				 errdetail("Unexpected end of input.")));
@@ -129,7 +131,8 @@ parse_ltree(const char *buf)
 	}
 
 	pfree(list);
-	return result;
+	*res = result;
+	return true;
 
 #undef UNCHAR
 }
@@ -172,8 +175,12 @@ Datum
 ltree_in(PG_FUNCTION_ARGS)
 {
 	char	   *buf = (char *) PG_GETARG_POINTER(0);
+	ltree      *res;
 
-	PG_RETURN_POINTER(parse_ltree(buf));
+	if (!parse_ltree(buf, &res, fcinfo->context))
+		PG_RETURN_NULL();
+
+	PG_RETURN_POINTER(res);
 }
 
 PG_FUNCTION_INFO_V1(ltree_out);
@@ -232,7 +239,7 @@ ltree_recv(PG_FUNCTION_ARGS)
 		elog(ERROR, "unsupported ltree version number %d", version);
 
 	str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
-	res = parse_ltree(str);
+	(void) parse_ltree(str, &res, NULL);
 	pfree(str);
 
 	PG_RETURN_POINTER(res);
@@ -258,8 +265,8 @@ ltree_recv(PG_FUNCTION_ARGS)
  * expects a null terminated string
  * returns an lquery
  */
-static lquery *
-parse_lquery(const char *buf)
+static bool
+parse_lquery(const char *buf, lquery **res, struct Node *escontext)
 {
 	const char *ptr;
 	int			num = 0,
@@ -277,7 +284,7 @@ parse_lquery(const char *buf)
 	int			charlen;
 	int			pos = 1;		/* character position for error messages */
 
-#define UNCHAR ereport(ERROR, \
+#define UNCHAR ereturn(escontext, false,\
 					   errcode(ERRCODE_SYNTAX_ERROR), \
 					   errmsg("lquery syntax error at character %d", \
 							  pos))
@@ -297,7 +304,7 @@ parse_lquery(const char *buf)
 
 	num++;
 	if (num > LQUERY_MAX_LEVELS)
-		ereport(ERROR,
+		ereturn(escontext, false,
 				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
 				 errmsg("number of lquery items (%d) exceeds the maximum allowed (%d)",
 						num, LQUERY_MAX_LEVELS)));
@@ -361,18 +368,21 @@ parse_lquery(const char *buf)
 				}
 				else if (t_iseq(ptr, '|'))
 				{
-					finish_nodeitem(lptr, ptr, true, pos);
+					if (!finish_nodeitem(lptr, ptr, true, pos, escontext))
+						return false;
 					state = LQPRS_WAITVAR;
 				}
 				else if (t_iseq(ptr, '{'))
 				{
-					finish_nodeitem(lptr, ptr, true, pos);
+					if (!finish_nodeitem(lptr, ptr, true, pos, escontext))
+						return false;
 					curqlevel->flag |= LQL_COUNT;
 					state = LQPRS_WAITFNUM;
 				}
 				else if (t_iseq(ptr, '.'))
 				{
-					finish_nodeitem(lptr, ptr, true, pos);
+					if (!finish_nodeitem(lptr, ptr, true, pos, escontext))
+						return false;
 					state = LQPRS_WAITLEVEL;
 					curqlevel = NEXTLEV(curqlevel);
 				}
@@ -407,7 +417,7 @@ parse_lquery(const char *buf)
 					int			low = atoi(ptr);
 
 					if (low < 0 || low > LTREE_MAX_LEVELS)
-						ereport(ERROR,
+						ereturn(escontext, false,
 								(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
 								 errmsg("lquery syntax error"),
 								 errdetail("Low limit (%d) exceeds the maximum allowed (%d), at character %d.",
@@ -425,13 +435,13 @@ parse_lquery(const char *buf)
 					int			high = atoi(ptr);
 
 					if (high < 0 || high > LTREE_MAX_LEVELS)
-						ereport(ERROR,
+						ereturn(escontext, false,
 								(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
 								 errmsg("lquery syntax error"),
 								 errdetail("High limit (%d) exceeds the maximum allowed (%d), at character %d.",
 										   high, LTREE_MAX_LEVELS, pos)));
 					else if (curqlevel->low > high)
-						ereport(ERROR,
+						ereturn(escontext, false,
 								(errcode(ERRCODE_SYNTAX_ERROR),
 								 errmsg("lquery syntax error"),
 								 errdetail("Low limit (%d) is greater than high limit (%d), at character %d.",
@@ -485,11 +495,14 @@ parse_lquery(const char *buf)
 	}
 
 	if (state == LQPRS_WAITDELIM)
-		finish_nodeitem(lptr, ptr, true, pos);
+	{
+		if (!finish_nodeitem(lptr, ptr, true, pos, escontext))
+			return false;
+	}
 	else if (state == LQPRS_WAITOPEN)
 		curqlevel->high = LTREE_MAX_LEVELS;
 	else if (state != LQPRS_WAITEND)
-		ereport(ERROR,
+		ereturn(escontext, false,
 				(errcode(ERRCODE_SYNTAX_ERROR),
 				 errmsg("lquery syntax error"),
 				 errdetail("Unexpected end of input.")));
@@ -560,7 +573,8 @@ parse_lquery(const char *buf)
 	}
 
 	pfree(tmpql);
-	return result;
+	*res = result;
+	return true;
 
 #undef UNCHAR
 }
@@ -569,8 +583,9 @@ parse_lquery(const char *buf)
  * Close out parsing an ltree or lquery nodeitem:
  * compute the correct length, and complain if it's not OK
  */
-static void
-finish_nodeitem(nodeitem *lptr, const char *ptr, bool is_lquery, int pos)
+static bool
+finish_nodeitem(nodeitem *lptr, const char *ptr, bool is_lquery, int pos,
+	struct Node *escontext)
 {
 	if (is_lquery)
 	{
@@ -591,18 +606,19 @@ finish_nodeitem(nodeitem *lptr, const char *ptr, bool is_lquery, int pos)
 
 	/* Complain if it's empty or too long */
 	if (lptr->len == 0)
-		ereport(ERROR,
+		ereturn(escontext, false,
 				(errcode(ERRCODE_SYNTAX_ERROR),
 				 is_lquery ?
 				 errmsg("lquery syntax error at character %d", pos) :
 				 errmsg("ltree syntax error at character %d", pos),
 				 errdetail("Empty labels are not allowed.")));
 	if (lptr->wlen > LTREE_LABEL_MAX_CHARS)
-		ereport(ERROR,
+		ereturn(escontext, false,
 				(errcode(ERRCODE_NAME_TOO_LONG),
 				 errmsg("label string is too long"),
 				 errdetail("Label length is %d, must be at most %d, at character %d.",
 						   lptr->wlen, LTREE_LABEL_MAX_CHARS, pos)));
+	return true;
 }
 
 /*
@@ -730,8 +746,12 @@ Datum
 lquery_in(PG_FUNCTION_ARGS)
 {
 	char	   *buf = (char *) PG_GETARG_POINTER(0);
+	lquery     *res;
 
-	PG_RETURN_POINTER(parse_lquery(buf));
+	if (!parse_lquery(buf, &res, fcinfo->context))
+		PG_RETURN_NULL();
+
+	PG_RETURN_POINTER(res);
 }
 
 PG_FUNCTION_INFO_V1(lquery_out);
@@ -790,7 +810,7 @@ lquery_recv(PG_FUNCTION_ARGS)
 		elog(ERROR, "unsupported lquery version number %d", version);
 
 	str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
-	res = parse_lquery(str);
+	(void) parse_lquery(str, &res, NULL);
 	pfree(str);
 
 	PG_RETURN_POINTER(res);
diff --git a/contrib/ltree/ltxtquery_io.c b/contrib/ltree/ltxtquery_io.c
index 8ab0ce8e52..93caad5fa2 100644
--- a/contrib/ltree/ltxtquery_io.c
+++ b/contrib/ltree/ltxtquery_io.c
@@ -52,8 +52,8 @@ typedef struct
 /*
  * get token from query string
  */
-static int32
-gettoken_query(QPRS_STATE *state, int32 *val, int32 *lenval, char **strval, uint16 *flag)
+static bool
+gettoken_query(QPRS_STATE *state, int32 *res, int32 *val, int32 *lenval, char **strval, uint16 *flag, struct Node *escontext)
 {
 	int			charlen;
 
@@ -68,13 +68,15 @@ gettoken_query(QPRS_STATE *state, int32 *val, int32 *lenval, char **strval, uint
 				{
 					(state->buf)++;
 					*val = (int32) '!';
-					return OPR;
+					*res = OPR;
+					return true;
 				}
 				else if (t_iseq(state->buf, '('))
 				{
 					state->count++;
 					(state->buf)++;
-					return OPEN;
+					*res = OPEN;
+					return true;
 				}
 				else if (ISALNUM(state->buf))
 				{
@@ -84,7 +86,7 @@ gettoken_query(QPRS_STATE *state, int32 *val, int32 *lenval, char **strval, uint
 					*flag = 0;
 				}
 				else if (!t_isspace(state->buf))
-					ereport(ERROR,
+					ereturn(escontext, false,
 							(errcode(ERRCODE_SYNTAX_ERROR),
 							 errmsg("operand syntax error")));
 				break;
@@ -92,7 +94,7 @@ gettoken_query(QPRS_STATE *state, int32 *val, int32 *lenval, char **strval, uint
 				if (ISALNUM(state->buf))
 				{
 					if (*flag)
-						ereport(ERROR,
+						ereturn(escontext, false,
 								(errcode(ERRCODE_SYNTAX_ERROR),
 								 errmsg("modifiers syntax error")));
 					*lenval += charlen;
@@ -106,7 +108,8 @@ gettoken_query(QPRS_STATE *state, int32 *val, int32 *lenval, char **strval, uint
 				else
 				{
 					state->state = WAITOPERATOR;
-					return VAL;
+					*res = VAL;
+					return true;
 				}
 				break;
 			case WAITOPERATOR:
@@ -115,33 +118,44 @@ gettoken_query(QPRS_STATE *state, int32 *val, int32 *lenval, char **strval, uint
 					state->state = WAITOPERAND;
 					*val = (int32) *(state->buf);
 					(state->buf)++;
-					return OPR;
+					*res = OPR;
+					return true;
 				}
 				else if (t_iseq(state->buf, ')'))
 				{
 					(state->buf)++;
 					state->count--;
-					return (state->count < 0) ? ERR : CLOSE;
+					*res = (state->count < 0) ? ERR : CLOSE;
+					return true;
 				}
 				else if (*(state->buf) == '\0')
-					return (state->count) ? ERR : END;
+				{
+					*res = (state->count) ? ERR : END;
+					return true;
+				}
 				else if (!t_iseq(state->buf, ' '))
-					return ERR;
+				{
+					*res = ERR;
+					return true;
+				}
 				break;
 			default:
-				return ERR;
+				*res = ERR;
+				return true;
 				break;
 		}
 
 		state->buf += charlen;
 	}
+
+	/* should not get here */
 }
 
 /*
  * push new one in polish notation reverse view
  */
-static void
-pushquery(QPRS_STATE *state, int32 type, int32 val, int32 distance, int32 lenval, uint16 flag)
+static bool
+pushquery(QPRS_STATE *state, int32 type, int32 val, int32 distance, int32 lenval, uint16 flag, struct Node *escontext)
 {
 	NODE	   *tmp = (NODE *) palloc(sizeof(NODE));
 
@@ -149,11 +163,11 @@ pushquery(QPRS_STATE *state, int32 type, int32 val, int32 distance, int32 lenval
 	tmp->val = val;
 	tmp->flag = flag;
 	if (distance > 0xffff)
-		ereport(ERROR,
+		ereturn(escontext, false,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 				 errmsg("value is too big")));
 	if (lenval > 0xff)
-		ereport(ERROR,
+		ereturn(escontext, false,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 				 errmsg("operand is too long")));
 	tmp->distance = distance;
@@ -161,21 +175,24 @@ pushquery(QPRS_STATE *state, int32 type, int32 val, int32 distance, int32 lenval
 	tmp->next = state->str;
 	state->str = tmp;
 	state->num++;
+	return true;
 }
 
 /*
  * This function is used for query text parsing
  */
-static void
-pushval_asis(QPRS_STATE *state, int type, char *strval, int lenval, uint16 flag)
+static bool
+pushval_asis(QPRS_STATE *state, int type, char *strval, int lenval, uint16 flag,
+	struct Node *escontext)
 {
 	if (lenval > 0xffff)
-		ereport(ERROR,
+		ereturn(escontext, false,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 				 errmsg("word is too long")));
 
-	pushquery(state, type, ltree_crc32_sz(strval, lenval),
-			  state->curop - state->op, lenval, flag);
+	if (! pushquery(state, type, ltree_crc32_sz(strval, lenval),
+					state->curop - state->op, lenval, flag, escontext))
+		return false;
 
 	while (state->curop - state->op + lenval + 1 >= state->lenop)
 	{
@@ -190,14 +207,15 @@ pushval_asis(QPRS_STATE *state, int type, char *strval, int lenval, uint16 flag)
 	*(state->curop) = '\0';
 	state->curop++;
 	state->sumlen += lenval + 1;
+	return true;
 }
 
 #define STACKDEPTH		32
 /*
  * make polish notation of query
  */
-static int32
-makepol(QPRS_STATE *state)
+static bool
+makepol(QPRS_STATE *state, int32 *res, struct Node *escontext)
 {
 	int32		val = 0,
 				type;
@@ -210,22 +228,33 @@ makepol(QPRS_STATE *state)
 	/* since this function recurses, it could be driven to stack overflow */
 	check_stack_depth();
 
-	while ((type = gettoken_query(state, &val, &lenval, &strval, &flag)) != END)
+	for (;;)
 	{
+		if (!gettoken_query(state, &type, &val, &lenval, &strval, &flag,
+				escontext))
+			return false;
+		if (type == END)
+			break;
 		switch (type)
 		{
 			case VAL:
-				pushval_asis(state, VAL, strval, lenval, flag);
+				if (!pushval_asis(state, VAL, strval, lenval, flag, escontext))
+					return false;
 				while (lenstack && (stack[lenstack - 1] == (int32) '&' ||
 									stack[lenstack - 1] == (int32) '!'))
 				{
 					lenstack--;
-					pushquery(state, OPR, stack[lenstack], 0, 0, 0);
+					if (!pushquery(state, OPR, stack[lenstack], 0, 0, 0,
+								   escontext))
+						return false;;
 				}
 				break;
 			case OPR:
 				if (lenstack && val == (int32) '|')
-					pushquery(state, OPR, val, 0, 0, 0);
+				{
+					if (!pushquery(state, OPR, val, 0, 0, 0, escontext))
+						return false;
+				}
 				else
 				{
 					if (lenstack == STACKDEPTH)
@@ -236,38 +265,46 @@ makepol(QPRS_STATE *state)
 				}
 				break;
 			case OPEN:
-				if (makepol(state) == ERR)
-					return ERR;
+				if (!makepol(state, res, escontext))
+					return false;
+				if (*res == ERR)
+					return true;
 				while (lenstack && (stack[lenstack - 1] == (int32) '&' ||
 									stack[lenstack - 1] == (int32) '!'))
 				{
 					lenstack--;
-					pushquery(state, OPR, stack[lenstack], 0, 0, 0);
+					if (!pushquery(state, OPR, stack[lenstack], 0, 0, 0,
+								   escontext))
+						return false;
 				}
 				break;
 			case CLOSE:
 				while (lenstack)
 				{
 					lenstack--;
-					pushquery(state, OPR, stack[lenstack], 0, 0, 0);
+					if (!pushquery(state, OPR, stack[lenstack], 0, 0, 0,
+								   escontext))
+						return false;
 				};
-				return END;
+				*res = END;
+				return true;
 				break;
 			case ERR:
 			default:
-				ereport(ERROR,
+				ereturn(escontext, false,
 						(errcode(ERRCODE_SYNTAX_ERROR),
 						 errmsg("syntax error")));
 
-				return ERR;
 		}
 	}
 	while (lenstack)
 	{
 		lenstack--;
-		pushquery(state, OPR, stack[lenstack], 0, 0, 0);
+		if (!pushquery(state, OPR, stack[lenstack], 0, 0, 0, escontext))
+			return false;
 	};
-	return END;
+	*res = END;
+	return true;
 }
 
 static void
@@ -303,8 +340,8 @@ findoprnd(ITEM *ptr, int32 *pos)
 /*
  * input
  */
-static ltxtquery *
-queryin(char *buf)
+static bool
+queryin(char *buf, ltxtquery **res, struct Node *escontext)
 {
 	QPRS_STATE	state;
 	int32		i;
@@ -313,6 +350,7 @@ queryin(char *buf)
 	ITEM	   *ptr;
 	NODE	   *tmp;
 	int32		pos = 0;
+	int32       polres;
 
 #ifdef BS_DEBUG
 	char		pbuf[16384],
@@ -333,15 +371,16 @@ queryin(char *buf)
 	*(state.curop) = '\0';
 
 	/* parse query & make polish notation (postfix, but in reverse order) */
-	makepol(&state);
+	if (!makepol(&state, &polres, escontext))
+		return false;
 	if (!state.num)
-		ereport(ERROR,
+		ereturn(escontext, false,
 				(errcode(ERRCODE_SYNTAX_ERROR),
 				 errmsg("syntax error"),
 				 errdetail("Empty query.")));
 
 	if (LTXTQUERY_TOO_BIG(state.num, state.sumlen))
-		ereport(ERROR,
+		ereturn(escontext, false,
 				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
 				 errmsg("ltxtquery is too large")));
 	commonlen = COMPUTESIZE(state.num, state.sumlen);
@@ -372,7 +411,8 @@ queryin(char *buf)
 	pos = 0;
 	findoprnd(ptr, &pos);
 
-	return query;
+	*res = query;
+	return true;
 }
 
 /*
@@ -382,7 +422,11 @@ PG_FUNCTION_INFO_V1(ltxtq_in);
 Datum
 ltxtq_in(PG_FUNCTION_ARGS)
 {
-	PG_RETURN_POINTER(queryin((char *) PG_GETARG_POINTER(0)));
+	ltxtquery *res;
+
+	if (! queryin((char *) PG_GETARG_POINTER(0), &res, fcinfo->context))
+		PG_RETURN_NULL();
+	PG_RETURN_POINTER(res);
 }
 
 /*
@@ -407,7 +451,7 @@ ltxtq_recv(PG_FUNCTION_ARGS)
 		elog(ERROR, "unsupported ltxtquery version number %d", version);
 
 	str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
-	res = queryin(str);
+	(void) queryin(str, &res, NULL);
 	pfree(str);
 
 	PG_RETURN_POINTER(res);
diff --git a/contrib/ltree/sql/ltree.sql b/contrib/ltree/sql/ltree.sql
index bf733ed17b..eabef4f851 100644
--- a/contrib/ltree/sql/ltree.sql
+++ b/contrib/ltree/sql/ltree.sql
@@ -382,3 +382,18 @@ SELECT count(*) FROM _ltreetest WHERE t ~ '23.*{1}.1' ;
 SELECT count(*) FROM _ltreetest WHERE t ~ '23.*.1' ;
 SELECT count(*) FROM _ltreetest WHERE t ~ '23.*.2' ;
 SELECT count(*) FROM _ltreetest WHERE t ? '{23.*.1,23.*.2}' ;
+
+-- test non-error-throwing input
+
+SELECT str as "value", typ as "type",
+       pg_input_is_valid(str,typ) as ok,
+       pg_input_error_message(str,typ) as errmsg
+FROM (VALUES ('.2.3', 'ltree'),
+             ('1.2.', 'ltree'),
+             ('1.2.3','ltree'),
+             ('@.2.3','lquery'),
+             (' 2.3', 'lquery'),
+             ('1.2.3','lquery'),
+             ('$tree & aWdf@*','ltxtquery'),
+             ('!tree & aWdf@*','ltxtquery'))
+      AS a(str,typ);

Reply via email to