mirror of https://github.com/postgres/postgres
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:
parent
3b76622e04
commit
7a05425d96
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue