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);