diff --git a/contrib/intarray/_int_bool.c b/contrib/intarray/_int_bool.c index 8fc6ad87fc7..6dfb2b1305f 100644 --- a/contrib/intarray/_int_bool.c +++ b/contrib/intarray/_int_bool.c @@ -435,37 +435,77 @@ boolop(PG_FUNCTION_ARGS) PG_RETURN_BOOL(result); } -static void -findoprnd(ITEM *ptr, int32 *pos) +/* + * Recursively fill the "left" fields of an ITEM array that represents + * a valid postfix tree. + * + * state: only needed for error reporting + * ptr: starting element of array + * pos: in/out argument, the array index this call is responsible to fill + * + * At exit, *pos has been decremented to point before the sub-tree whose + * top is the entry-time value of *pos. + * + * Returns true if okay, false if error (the only possible error is + * overflow of a "left" field). + */ +static bool +findoprnd(WORKSTATE *state, ITEM *ptr, int32 *pos) { + int32 mypos; + /* since this function recurses, it could be driven to stack overflow. */ check_stack_depth(); + /* get the position this call is supposed to update */ + mypos = *pos; + Assert(mypos >= 0); + + /* in all cases, we should decrement *pos to advance over this item */ + (*pos)--; + #ifdef BS_DEBUG - elog(DEBUG3, (ptr[*pos].type == OPR) ? - "%d %c" : "%d %d", *pos, ptr[*pos].val); + elog(DEBUG3, (ptr[mypos].type == OPR) ? + "%d %c" : "%d %d", mypos, ptr[mypos].val); #endif - if (ptr[*pos].type == VAL) + + if (ptr[mypos].type == VAL) { - ptr[*pos].left = 0; - (*pos)--; + /* base case: a VAL has no operand, so just set its left to zero */ + ptr[mypos].left = 0; } - else if (ptr[*pos].val == (int32) '!') + else if (ptr[mypos].val == (int32) '!') { - ptr[*pos].left = -1; - (*pos)--; - findoprnd(ptr, pos); + /* unary operator, likewise easy: operand is just before it */ + ptr[mypos].left = -1; + /* recurse to scan operand */ + if (!findoprnd(state, ptr, pos)) + return false; } else { - ITEM *curitem = &ptr[*pos]; - int32 tmp = *pos; + /* binary operator */ + int32 delta; - (*pos)--; - findoprnd(ptr, pos); - curitem->left = *pos - tmp; - findoprnd(ptr, pos); + /* recurse to scan right operand */ + if (!findoprnd(state, ptr, pos)) + return false; + /* we must fill left with offset to left operand's top */ + /* abs(delta) < QUERYTYPEMAXITEMS, so it can't overflow ... */ + delta = *pos - mypos; + /* ... but it might be too large to fit in the 16-bit left field */ + Assert(delta < 0); + if (unlikely(delta < PG_INT16_MIN)) + ereturn(state->escontext, false, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("query_int expression is too complex"))); + ptr[mypos].left = (int16) delta; + /* recurse to scan left operand */ + if (!findoprnd(state, ptr, pos)) + return false; } + + return true; } @@ -516,6 +556,7 @@ bqarr_in(PG_FUNCTION_ARGS) query->size = state.num; ptr = GETQUERY(query); + /* fill the query array from the data makepol constructed */ for (i = state.num - 1; i >= 0; i--) { ptr[i].type = state.str->type; @@ -525,8 +566,13 @@ bqarr_in(PG_FUNCTION_ARGS) state.str = tmp; } + /* now fill the "left" fields */ pos = query->size - 1; - findoprnd(ptr, &pos); + if (!findoprnd(&state, ptr, &pos)) + PG_RETURN_NULL(); + /* if successful, findoprnd should have scanned the whole array */ + Assert(pos == -1); + #ifdef BS_DEBUG initStringInfo(&pbuf); for (i = 0; i < query->size; i++) diff --git a/contrib/intarray/expected/_int.out b/contrib/intarray/expected/_int.out index d0e68d0447f..1700dc5a89d 100644 --- a/contrib/intarray/expected/_int.out +++ b/contrib/intarray/expected/_int.out @@ -398,6 +398,9 @@ SELECT '1&(2&(4&(5|!6)))'::query_int; 1 & 2 & 4 & ( 5 | !6 ) (1 row) +SELECT (SELECT '0 | ' || string_agg(i::text, ' & ') + FROM generate_series(1, 17000) AS i)::query_int; +ERROR: query_int expression is too complex -- test non-error-throwing input SELECT str as "query_int", pg_input_is_valid(str,'query_int') as ok, diff --git a/contrib/intarray/sql/_int.sql b/contrib/intarray/sql/_int.sql index 5668ab40704..47f751a1769 100644 --- a/contrib/intarray/sql/_int.sql +++ b/contrib/intarray/sql/_int.sql @@ -74,6 +74,8 @@ SELECT '1&(2&(4&(5&6)))'::query_int; SELECT '1&2&4&5&6'::query_int; SELECT '1&(2&(4&(5|6)))'::query_int; SELECT '1&(2&(4&(5|!6)))'::query_int; +SELECT (SELECT '0 | ' || string_agg(i::text, ' & ') + FROM generate_series(1, 17000) AS i)::query_int; -- test non-error-throwing input diff --git a/contrib/ltree/expected/ltree.out b/contrib/ltree/expected/ltree.out index 900bb6bbf31..5ec69ae4711 100644 --- a/contrib/ltree/expected/ltree.out +++ b/contrib/ltree/expected/ltree.out @@ -1281,6 +1281,9 @@ SELECT 'tree.awdfg_qwerty'::ltree @ 'tree & aw_rw%*'::ltxtquery; f (1 row) +SELECT (SELECT 'a | ' || string_agg('b', ' & ') + FROM generate_series(1, 17000) AS i)::ltxtquery; +ERROR: ltxtquery is too large --arrays SELECT '{1.2.3}'::ltree[] @> '1.2.3.4'; ?column? diff --git a/contrib/ltree/ltxtquery_io.c b/contrib/ltree/ltxtquery_io.c index 5e9738ac566..0f2954f31ba 100644 --- a/contrib/ltree/ltxtquery_io.c +++ b/contrib/ltree/ltxtquery_io.c @@ -294,33 +294,71 @@ makepol(QPRS_STATE *state) return END; } -static void -findoprnd(ITEM *ptr, int32 *pos) +/* + * Recursively fill the "left" fields of an ITEM array that represents + * a valid postfix tree. + * + * state: only needed for error reporting + * ptr: starting element of array + * pos: in/out argument, the array index this call is responsible to fill + * + * At exit, *pos has been incremented to point after the sub-tree whose + * top is the entry-time value of *pos. + * + * Returns true if okay, false if error (the only possible error is + * overflow of a "left" field). + */ +static bool +findoprnd(QPRS_STATE *state, ITEM *ptr, int32 *pos) { + int32 mypos; + /* since this function recurses, it could be driven to stack overflow. */ check_stack_depth(); - if (ptr[*pos].type == VAL || ptr[*pos].type == VALTRUE) + /* get the position this call is supposed to update */ + mypos = *pos; + + /* in all cases, we should increment *pos to advance over this item */ + (*pos)++; + + if (ptr[mypos].type == VAL || ptr[mypos].type == VALTRUE) { - ptr[*pos].left = 0; - (*pos)++; + /* base case: a VAL has no operand, so just set its left to zero */ + ptr[mypos].left = 0; } - else if (ptr[*pos].val == (int32) '!') + else if (ptr[mypos].val == (int32) '!') { - ptr[*pos].left = 1; - (*pos)++; - findoprnd(ptr, pos); + /* unary operator, likewise easy: operand is just after it */ + ptr[mypos].left = 1; + /* recurse to scan operand */ + if (!findoprnd(state, ptr, pos)) + return false; } else { - ITEM *curitem = &ptr[*pos]; - int32 tmp = *pos; + /* binary operator */ + int32 delta; - (*pos)++; - findoprnd(ptr, pos); - curitem->left = *pos - tmp; - findoprnd(ptr, pos); + /* recurse to scan right operand */ + if (!findoprnd(state, ptr, pos)) + return false; + /* we must fill left with offset to left operand's top */ + /* delta can't overflow, see LTXTQUERY_TOO_BIG ... */ + delta = *pos - mypos; + /* ... but it might be too large to fit in the 16-bit left field */ + Assert(delta > 0); + if (unlikely(delta > PG_INT16_MAX)) + ereturn(state->escontext, false, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("ltxtquery is too large"))); + ptr[mypos].left = (int16) delta; + /* recurse to scan left operand */ + if (!findoprnd(state, ptr, pos)) + return false; } + + return true; } @@ -396,7 +434,10 @@ queryin(char *buf, struct Node *escontext) /* set left operand's position for every operator */ pos = 0; - findoprnd(ptr, &pos); + if (!findoprnd(&state, ptr, &pos)) + return NULL; + /* if successful, findoprnd should have scanned the whole array */ + Assert(pos == state.num); return query; } diff --git a/contrib/ltree/sql/ltree.sql b/contrib/ltree/sql/ltree.sql index af8fffe4a3f..cf32619c8cd 100644 --- a/contrib/ltree/sql/ltree.sql +++ b/contrib/ltree/sql/ltree.sql @@ -252,6 +252,9 @@ SELECT 'tree.awdfg'::ltree @ 'tree & aWdfg@'::ltxtquery; SELECT 'tree.awdfg_qwerty'::ltree @ 'tree & aw_qw%*'::ltxtquery; SELECT 'tree.awdfg_qwerty'::ltree @ 'tree & aw_rw%*'::ltxtquery; +SELECT (SELECT 'a | ' || string_agg('b', ' & ') + FROM generate_series(1, 17000) AS i)::ltxtquery; + --arrays SELECT '{1.2.3}'::ltree[] @> '1.2.3.4';