Convert contrib/ltree's input functions to report errors softly

Reviewed by Tom Lane and Amul Sul

Discussion: https://postgr.es/m/49e598c2-cfe8-0928-b6fb-d0cc51aab626@dunslane.net
This commit is contained in:
Andrew Dunstan 2022-12-28 09:58:04 -05:00
parent 3b76622e04
commit 7a05425d96
4 changed files with 137 additions and 50 deletions

View File

@ -8084,3 +8084,28 @@ SELECT count(*) FROM _ltreetest WHERE t ? '{23.*.1,23.*.2}' ;
15 15
(1 row) (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)

View File

@ -24,8 +24,8 @@ typedef struct
#define LTPRS_WAITNAME 0 #define LTPRS_WAITNAME 0
#define LTPRS_WAITDELIM 1 #define LTPRS_WAITDELIM 1
static void finish_nodeitem(nodeitem *lptr, const char *ptr, static bool finish_nodeitem(nodeitem *lptr, const char *ptr,
bool is_lquery, int pos); bool is_lquery, int pos, struct Node *escontext);
/* /*
@ -33,7 +33,7 @@ static void finish_nodeitem(nodeitem *lptr, const char *ptr,
* returns an ltree * returns an ltree
*/ */
static ltree * static ltree *
parse_ltree(const char *buf) parse_ltree(const char *buf, struct Node *escontext)
{ {
const char *ptr; const char *ptr;
nodeitem *list, nodeitem *list,
@ -46,7 +46,7 @@ parse_ltree(const char *buf)
int charlen; int charlen;
int pos = 1; /* character position for error messages */ int pos = 1; /* character position for error messages */
#define UNCHAR ereport(ERROR, \ #define UNCHAR ereturn(escontext, NULL,\
errcode(ERRCODE_SYNTAX_ERROR), \ errcode(ERRCODE_SYNTAX_ERROR), \
errmsg("ltree syntax error at character %d", \ errmsg("ltree syntax error at character %d", \
pos)) pos))
@ -61,7 +61,7 @@ parse_ltree(const char *buf)
} }
if (num + 1 > LTREE_MAX_LEVELS) if (num + 1 > LTREE_MAX_LEVELS)
ereport(ERROR, ereturn(escontext, NULL,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("number of ltree labels (%d) exceeds the maximum allowed (%d)", errmsg("number of ltree labels (%d) exceeds the maximum allowed (%d)",
num + 1, LTREE_MAX_LEVELS))); num + 1, LTREE_MAX_LEVELS)));
@ -86,7 +86,8 @@ parse_ltree(const char *buf)
case LTPRS_WAITDELIM: case LTPRS_WAITDELIM:
if (t_iseq(ptr, '.')) if (t_iseq(ptr, '.'))
{ {
finish_nodeitem(lptr, ptr, false, pos); if (!finish_nodeitem(lptr, ptr, false, pos, escontext))
return NULL;
totallen += MAXALIGN(lptr->len + LEVEL_HDRSIZE); totallen += MAXALIGN(lptr->len + LEVEL_HDRSIZE);
lptr++; lptr++;
state = LTPRS_WAITNAME; state = LTPRS_WAITNAME;
@ -105,12 +106,13 @@ parse_ltree(const char *buf)
if (state == LTPRS_WAITDELIM) if (state == LTPRS_WAITDELIM)
{ {
finish_nodeitem(lptr, ptr, false, pos); if (!finish_nodeitem(lptr, ptr, false, pos, escontext))
return NULL;
totallen += MAXALIGN(lptr->len + LEVEL_HDRSIZE); totallen += MAXALIGN(lptr->len + LEVEL_HDRSIZE);
lptr++; lptr++;
} }
else if (!(state == LTPRS_WAITNAME && lptr == list)) else if (!(state == LTPRS_WAITNAME && lptr == list))
ereport(ERROR, ereturn(escontext, NULL,
(errcode(ERRCODE_SYNTAX_ERROR), (errcode(ERRCODE_SYNTAX_ERROR),
errmsg("ltree syntax error"), errmsg("ltree syntax error"),
errdetail("Unexpected end of input."))); errdetail("Unexpected end of input.")));
@ -172,8 +174,12 @@ Datum
ltree_in(PG_FUNCTION_ARGS) ltree_in(PG_FUNCTION_ARGS)
{ {
char *buf = (char *) PG_GETARG_POINTER(0); char *buf = (char *) PG_GETARG_POINTER(0);
ltree *res;
PG_RETURN_POINTER(parse_ltree(buf)); if ((res = parse_ltree(buf, fcinfo->context)) == NULL)
PG_RETURN_NULL();
PG_RETURN_POINTER(res);
} }
PG_FUNCTION_INFO_V1(ltree_out); PG_FUNCTION_INFO_V1(ltree_out);
@ -232,7 +238,7 @@ ltree_recv(PG_FUNCTION_ARGS)
elog(ERROR, "unsupported ltree version number %d", version); elog(ERROR, "unsupported ltree version number %d", version);
str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes); str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
res = parse_ltree(str); res = parse_ltree(str, NULL);
pfree(str); pfree(str);
PG_RETURN_POINTER(res); PG_RETURN_POINTER(res);
@ -259,7 +265,7 @@ ltree_recv(PG_FUNCTION_ARGS)
* returns an lquery * returns an lquery
*/ */
static lquery * static lquery *
parse_lquery(const char *buf) parse_lquery(const char *buf, struct Node *escontext)
{ {
const char *ptr; const char *ptr;
int num = 0, int num = 0,
@ -277,7 +283,7 @@ parse_lquery(const char *buf)
int charlen; int charlen;
int pos = 1; /* character position for error messages */ int pos = 1; /* character position for error messages */
#define UNCHAR ereport(ERROR, \ #define UNCHAR ereturn(escontext, NULL,\
errcode(ERRCODE_SYNTAX_ERROR), \ errcode(ERRCODE_SYNTAX_ERROR), \
errmsg("lquery syntax error at character %d", \ errmsg("lquery syntax error at character %d", \
pos)) pos))
@ -297,7 +303,7 @@ parse_lquery(const char *buf)
num++; num++;
if (num > LQUERY_MAX_LEVELS) if (num > LQUERY_MAX_LEVELS)
ereport(ERROR, ereturn(escontext, NULL,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("number of lquery items (%d) exceeds the maximum allowed (%d)", errmsg("number of lquery items (%d) exceeds the maximum allowed (%d)",
num, LQUERY_MAX_LEVELS))); num, LQUERY_MAX_LEVELS)));
@ -361,18 +367,21 @@ parse_lquery(const char *buf)
} }
else if (t_iseq(ptr, '|')) else if (t_iseq(ptr, '|'))
{ {
finish_nodeitem(lptr, ptr, true, pos); if (!finish_nodeitem(lptr, ptr, true, pos, escontext))
return NULL;
state = LQPRS_WAITVAR; state = LQPRS_WAITVAR;
} }
else if (t_iseq(ptr, '{')) else if (t_iseq(ptr, '{'))
{ {
finish_nodeitem(lptr, ptr, true, pos); if (!finish_nodeitem(lptr, ptr, true, pos, escontext))
return NULL;
curqlevel->flag |= LQL_COUNT; curqlevel->flag |= LQL_COUNT;
state = LQPRS_WAITFNUM; state = LQPRS_WAITFNUM;
} }
else if (t_iseq(ptr, '.')) else if (t_iseq(ptr, '.'))
{ {
finish_nodeitem(lptr, ptr, true, pos); if (!finish_nodeitem(lptr, ptr, true, pos, escontext))
return NULL;
state = LQPRS_WAITLEVEL; state = LQPRS_WAITLEVEL;
curqlevel = NEXTLEV(curqlevel); curqlevel = NEXTLEV(curqlevel);
} }
@ -407,7 +416,7 @@ parse_lquery(const char *buf)
int low = atoi(ptr); int low = atoi(ptr);
if (low < 0 || low > LTREE_MAX_LEVELS) if (low < 0 || low > LTREE_MAX_LEVELS)
ereport(ERROR, ereturn(escontext, NULL,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("lquery syntax error"), errmsg("lquery syntax error"),
errdetail("Low limit (%d) exceeds the maximum allowed (%d), at character %d.", errdetail("Low limit (%d) exceeds the maximum allowed (%d), at character %d.",
@ -425,13 +434,13 @@ parse_lquery(const char *buf)
int high = atoi(ptr); int high = atoi(ptr);
if (high < 0 || high > LTREE_MAX_LEVELS) if (high < 0 || high > LTREE_MAX_LEVELS)
ereport(ERROR, ereturn(escontext, NULL,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("lquery syntax error"), errmsg("lquery syntax error"),
errdetail("High limit (%d) exceeds the maximum allowed (%d), at character %d.", errdetail("High limit (%d) exceeds the maximum allowed (%d), at character %d.",
high, LTREE_MAX_LEVELS, pos))); high, LTREE_MAX_LEVELS, pos)));
else if (curqlevel->low > high) else if (curqlevel->low > high)
ereport(ERROR, ereturn(escontext, NULL,
(errcode(ERRCODE_SYNTAX_ERROR), (errcode(ERRCODE_SYNTAX_ERROR),
errmsg("lquery syntax error"), errmsg("lquery syntax error"),
errdetail("Low limit (%d) is greater than high limit (%d), at character %d.", errdetail("Low limit (%d) is greater than high limit (%d), at character %d.",
@ -485,11 +494,14 @@ parse_lquery(const char *buf)
} }
if (state == LQPRS_WAITDELIM) 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) else if (state == LQPRS_WAITOPEN)
curqlevel->high = LTREE_MAX_LEVELS; curqlevel->high = LTREE_MAX_LEVELS;
else if (state != LQPRS_WAITEND) else if (state != LQPRS_WAITEND)
ereport(ERROR, ereturn(escontext, NULL,
(errcode(ERRCODE_SYNTAX_ERROR), (errcode(ERRCODE_SYNTAX_ERROR),
errmsg("lquery syntax error"), errmsg("lquery syntax error"),
errdetail("Unexpected end of input."))); errdetail("Unexpected end of input.")));
@ -569,8 +581,9 @@ parse_lquery(const char *buf)
* Close out parsing an ltree or lquery nodeitem: * Close out parsing an ltree or lquery nodeitem:
* compute the correct length, and complain if it's not OK * compute the correct length, and complain if it's not OK
*/ */
static void static bool
finish_nodeitem(nodeitem *lptr, const char *ptr, bool is_lquery, int pos) finish_nodeitem(nodeitem *lptr, const char *ptr, bool is_lquery, int pos,
struct Node *escontext)
{ {
if (is_lquery) if (is_lquery)
{ {
@ -591,18 +604,19 @@ finish_nodeitem(nodeitem *lptr, const char *ptr, bool is_lquery, int pos)
/* Complain if it's empty or too long */ /* Complain if it's empty or too long */
if (lptr->len == 0) if (lptr->len == 0)
ereport(ERROR, ereturn(escontext, false,
(errcode(ERRCODE_SYNTAX_ERROR), (errcode(ERRCODE_SYNTAX_ERROR),
is_lquery ? is_lquery ?
errmsg("lquery syntax error at character %d", pos) : errmsg("lquery syntax error at character %d", pos) :
errmsg("ltree syntax error at character %d", pos), errmsg("ltree syntax error at character %d", pos),
errdetail("Empty labels are not allowed."))); errdetail("Empty labels are not allowed.")));
if (lptr->wlen > LTREE_LABEL_MAX_CHARS) if (lptr->wlen > LTREE_LABEL_MAX_CHARS)
ereport(ERROR, ereturn(escontext, false,
(errcode(ERRCODE_NAME_TOO_LONG), (errcode(ERRCODE_NAME_TOO_LONG),
errmsg("label string is too long"), errmsg("label string is too long"),
errdetail("Label length is %d, must be at most %d, at character %d.", errdetail("Label length is %d, must be at most %d, at character %d.",
lptr->wlen, LTREE_LABEL_MAX_CHARS, pos))); lptr->wlen, LTREE_LABEL_MAX_CHARS, pos)));
return true;
} }
/* /*
@ -730,8 +744,12 @@ Datum
lquery_in(PG_FUNCTION_ARGS) lquery_in(PG_FUNCTION_ARGS)
{ {
char *buf = (char *) PG_GETARG_POINTER(0); char *buf = (char *) PG_GETARG_POINTER(0);
lquery *res;
PG_RETURN_POINTER(parse_lquery(buf)); if ((res = parse_lquery(buf, fcinfo->context)) == NULL)
PG_RETURN_NULL();
PG_RETURN_POINTER(res);
} }
PG_FUNCTION_INFO_V1(lquery_out); PG_FUNCTION_INFO_V1(lquery_out);
@ -790,7 +808,7 @@ lquery_recv(PG_FUNCTION_ARGS)
elog(ERROR, "unsupported lquery version number %d", version); elog(ERROR, "unsupported lquery version number %d", version);
str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes); str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
res = parse_lquery(str); res = parse_lquery(str, NULL);
pfree(str); pfree(str);
PG_RETURN_POINTER(res); PG_RETURN_POINTER(res);

View File

@ -11,6 +11,7 @@
#include "libpq/pqformat.h" #include "libpq/pqformat.h"
#include "ltree.h" #include "ltree.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "nodes/miscnodes.h"
/* parser's states */ /* parser's states */
@ -37,6 +38,7 @@ typedef struct
char *buf; char *buf;
int32 state; int32 state;
int32 count; int32 count;
struct Node *escontext;
/* reverse polish notation in list (for temporary usage) */ /* reverse polish notation in list (for temporary usage) */
NODE *str; NODE *str;
/* number in str */ /* number in str */
@ -51,6 +53,8 @@ typedef struct
/* /*
* get token from query string * get token from query string
*
* caller needs to check if a soft-error was set if the result is ERR.
*/ */
static int32 static int32
gettoken_query(QPRS_STATE *state, int32 *val, int32 *lenval, char **strval, uint16 *flag) gettoken_query(QPRS_STATE *state, int32 *val, int32 *lenval, char **strval, uint16 *flag)
@ -84,7 +88,7 @@ gettoken_query(QPRS_STATE *state, int32 *val, int32 *lenval, char **strval, uint
*flag = 0; *flag = 0;
} }
else if (!t_isspace(state->buf)) else if (!t_isspace(state->buf))
ereport(ERROR, ereturn(state->escontext, ERR,
(errcode(ERRCODE_SYNTAX_ERROR), (errcode(ERRCODE_SYNTAX_ERROR),
errmsg("operand syntax error"))); errmsg("operand syntax error")));
break; break;
@ -92,7 +96,7 @@ gettoken_query(QPRS_STATE *state, int32 *val, int32 *lenval, char **strval, uint
if (ISALNUM(state->buf)) if (ISALNUM(state->buf))
{ {
if (*flag) if (*flag)
ereport(ERROR, ereturn(state->escontext, ERR,
(errcode(ERRCODE_SYNTAX_ERROR), (errcode(ERRCODE_SYNTAX_ERROR),
errmsg("modifiers syntax error"))); errmsg("modifiers syntax error")));
*lenval += charlen; *lenval += charlen;
@ -124,9 +128,13 @@ gettoken_query(QPRS_STATE *state, int32 *val, int32 *lenval, char **strval, uint
return (state->count < 0) ? ERR : CLOSE; return (state->count < 0) ? ERR : CLOSE;
} }
else if (*(state->buf) == '\0') else if (*(state->buf) == '\0')
{
return (state->count) ? ERR : END; return (state->count) ? ERR : END;
}
else if (!t_iseq(state->buf, ' ')) else if (!t_iseq(state->buf, ' '))
{
return ERR; return ERR;
}
break; break;
default: default:
return ERR; return ERR;
@ -135,12 +143,14 @@ gettoken_query(QPRS_STATE *state, int32 *val, int32 *lenval, char **strval, uint
state->buf += charlen; state->buf += charlen;
} }
/* should not get here */
} }
/* /*
* push new one in polish notation reverse view * push new one in polish notation reverse view
*/ */
static void static bool
pushquery(QPRS_STATE *state, int32 type, int32 val, int32 distance, int32 lenval, uint16 flag) pushquery(QPRS_STATE *state, int32 type, int32 val, int32 distance, int32 lenval, uint16 flag)
{ {
NODE *tmp = (NODE *) palloc(sizeof(NODE)); NODE *tmp = (NODE *) palloc(sizeof(NODE));
@ -149,11 +159,11 @@ pushquery(QPRS_STATE *state, int32 type, int32 val, int32 distance, int32 lenval
tmp->val = val; tmp->val = val;
tmp->flag = flag; tmp->flag = flag;
if (distance > 0xffff) if (distance > 0xffff)
ereport(ERROR, ereturn(state->escontext, false,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("value is too big"))); errmsg("value is too big")));
if (lenval > 0xff) if (lenval > 0xff)
ereport(ERROR, ereturn(state->escontext, false,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("operand is too long"))); errmsg("operand is too long")));
tmp->distance = distance; tmp->distance = distance;
@ -161,21 +171,23 @@ pushquery(QPRS_STATE *state, int32 type, int32 val, int32 distance, int32 lenval
tmp->next = state->str; tmp->next = state->str;
state->str = tmp; state->str = tmp;
state->num++; state->num++;
return true;
} }
/* /*
* This function is used for query text parsing * This function is used for query text parsing
*/ */
static void static bool
pushval_asis(QPRS_STATE *state, int type, char *strval, int lenval, uint16 flag) pushval_asis(QPRS_STATE *state, int type, char *strval, int lenval, uint16 flag)
{ {
if (lenval > 0xffff) if (lenval > 0xffff)
ereport(ERROR, ereturn(state->escontext, false,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("word is too long"))); errmsg("word is too long")));
pushquery(state, type, ltree_crc32_sz(strval, lenval), if (! pushquery(state, type, ltree_crc32_sz(strval, lenval),
state->curop - state->op, lenval, flag); state->curop - state->op, lenval, flag))
return false;
while (state->curop - state->op + lenval + 1 >= state->lenop) while (state->curop - state->op + lenval + 1 >= state->lenop)
{ {
@ -190,6 +202,7 @@ pushval_asis(QPRS_STATE *state, int type, char *strval, int lenval, uint16 flag)
*(state->curop) = '\0'; *(state->curop) = '\0';
state->curop++; state->curop++;
state->sumlen += lenval + 1; state->sumlen += lenval + 1;
return true;
} }
#define STACKDEPTH 32 #define STACKDEPTH 32
@ -215,17 +228,22 @@ makepol(QPRS_STATE *state)
switch (type) switch (type)
{ {
case VAL: case VAL:
pushval_asis(state, VAL, strval, lenval, flag); if (!pushval_asis(state, VAL, strval, lenval, flag))
return ERR;
while (lenstack && (stack[lenstack - 1] == (int32) '&' || while (lenstack && (stack[lenstack - 1] == (int32) '&' ||
stack[lenstack - 1] == (int32) '!')) stack[lenstack - 1] == (int32) '!'))
{ {
lenstack--; lenstack--;
pushquery(state, OPR, stack[lenstack], 0, 0, 0); if (!pushquery(state, OPR, stack[lenstack], 0, 0, 0))
return ERR;
} }
break; break;
case OPR: case OPR:
if (lenstack && val == (int32) '|') if (lenstack && val == (int32) '|')
pushquery(state, OPR, val, 0, 0, 0); {
if (!pushquery(state, OPR, val, 0, 0, 0))
return ERR;
}
else else
{ {
if (lenstack == STACKDEPTH) if (lenstack == STACKDEPTH)
@ -242,30 +260,35 @@ makepol(QPRS_STATE *state)
stack[lenstack - 1] == (int32) '!')) stack[lenstack - 1] == (int32) '!'))
{ {
lenstack--; lenstack--;
pushquery(state, OPR, stack[lenstack], 0, 0, 0); if (!pushquery(state, OPR, stack[lenstack], 0, 0, 0))
return ERR;
} }
break; break;
case CLOSE: case CLOSE:
while (lenstack) while (lenstack)
{ {
lenstack--; lenstack--;
pushquery(state, OPR, stack[lenstack], 0, 0, 0); if (!pushquery(state, OPR, stack[lenstack], 0, 0, 0))
return ERR;
}; };
return END; return END;
break; break;
case ERR: case ERR:
if (SOFT_ERROR_OCCURRED(state->escontext))
return ERR;
/* fall through */
default: default:
ereport(ERROR, ereturn(state->escontext, ERR,
(errcode(ERRCODE_SYNTAX_ERROR), (errcode(ERRCODE_SYNTAX_ERROR),
errmsg("syntax error"))); errmsg("syntax error")));
return ERR;
} }
} }
while (lenstack) while (lenstack)
{ {
lenstack--; lenstack--;
pushquery(state, OPR, stack[lenstack], 0, 0, 0); if (!pushquery(state, OPR, stack[lenstack], 0, 0, 0))
return ERR;
}; };
return END; return END;
} }
@ -304,7 +327,7 @@ findoprnd(ITEM *ptr, int32 *pos)
* input * input
*/ */
static ltxtquery * static ltxtquery *
queryin(char *buf) queryin(char *buf, struct Node *escontext)
{ {
QPRS_STATE state; QPRS_STATE state;
int32 i; int32 i;
@ -325,6 +348,7 @@ queryin(char *buf)
state.count = 0; state.count = 0;
state.num = 0; state.num = 0;
state.str = NULL; state.str = NULL;
state.escontext = escontext;
/* init list of operand */ /* init list of operand */
state.sumlen = 0; state.sumlen = 0;
@ -333,15 +357,16 @@ queryin(char *buf)
*(state.curop) = '\0'; *(state.curop) = '\0';
/* parse query & make polish notation (postfix, but in reverse order) */ /* parse query & make polish notation (postfix, but in reverse order) */
makepol(&state); if (makepol(&state) == ERR)
return NULL;
if (!state.num) if (!state.num)
ereport(ERROR, ereturn(escontext, NULL,
(errcode(ERRCODE_SYNTAX_ERROR), (errcode(ERRCODE_SYNTAX_ERROR),
errmsg("syntax error"), errmsg("syntax error"),
errdetail("Empty query."))); errdetail("Empty query.")));
if (LTXTQUERY_TOO_BIG(state.num, state.sumlen)) if (LTXTQUERY_TOO_BIG(state.num, state.sumlen))
ereport(ERROR, ereturn(escontext, NULL,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("ltxtquery is too large"))); errmsg("ltxtquery is too large")));
commonlen = COMPUTESIZE(state.num, state.sumlen); commonlen = COMPUTESIZE(state.num, state.sumlen);
@ -382,7 +407,11 @@ PG_FUNCTION_INFO_V1(ltxtq_in);
Datum Datum
ltxtq_in(PG_FUNCTION_ARGS) ltxtq_in(PG_FUNCTION_ARGS)
{ {
PG_RETURN_POINTER(queryin((char *) PG_GETARG_POINTER(0))); ltxtquery *res;
if ((res = queryin((char *) PG_GETARG_POINTER(0), fcinfo->context)) == NULL)
PG_RETURN_NULL();
PG_RETURN_POINTER(res);
} }
/* /*
@ -407,7 +436,7 @@ ltxtq_recv(PG_FUNCTION_ARGS)
elog(ERROR, "unsupported ltxtquery version number %d", version); elog(ERROR, "unsupported ltxtquery version number %d", version);
str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes); str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
res = queryin(str); res = queryin(str, NULL);
pfree(str); pfree(str);
PG_RETURN_POINTER(res); PG_RETURN_POINTER(res);

View File

@ -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.*.1' ;
SELECT count(*) FROM _ltreetest WHERE t ~ '23.*.2' ; SELECT count(*) FROM _ltreetest WHERE t ~ '23.*.2' ;
SELECT count(*) FROM _ltreetest WHERE t ? '{23.*.1,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);