diff --git a/contrib/ltree/expected/ltree.out b/contrib/ltree/expected/ltree.out index 8226930905..41e7f94787 100644 --- a/contrib/ltree/expected/ltree.out +++ b/contrib/ltree/expected/ltree.out @@ -457,6 +457,52 @@ SELECT nlevel('1.2.3.4'); 4 (1 row) +SELECT nlevel(('1' || repeat('.1', 65534))::ltree); + nlevel +-------- + 65535 +(1 row) + +SELECT nlevel(('1' || repeat('.1', 65535))::ltree); +ERROR: number of ltree levels (65536) exceeds the maximum allowed (65535) +SELECT nlevel(('1' || repeat('.1', 65534))::ltree || '1'); +ERROR: number of ltree levels (65536) exceeds the maximum allowed (65535) +SELECT ('1' || repeat('.1', 65534))::lquery IS NULL; + ?column? +---------- + f +(1 row) + +SELECT ('1' || repeat('.1', 65535))::lquery IS NULL; +ERROR: number of lquery levels (65536) exceeds the maximum allowed (65535) +SELECT '*{65535}'::lquery; + lquery +---------- + *{65535} +(1 row) + +SELECT '*{65536}'::lquery; +ERROR: lquery syntax error +LINE 1: SELECT '*{65536}'::lquery; + ^ +DETAIL: Low limit (65536) exceeds the maximum allowed (65535). +SELECT '*{,65534}'::lquery; + lquery +----------- + *{,65534} +(1 row) + +SELECT '*{,65535}'::lquery; + lquery +-------- + * +(1 row) + +SELECT '*{,65536}'::lquery; +ERROR: lquery syntax error +LINE 1: SELECT '*{,65536}'::lquery; + ^ +DETAIL: High limit (65536) exceeds the maximum allowed (65535). SELECT '1.2'::ltree < '2.2'::ltree; ?column? ---------- diff --git a/contrib/ltree/ltree.h b/contrib/ltree/ltree.h index 366e58004c..a5608ca1f6 100644 --- a/contrib/ltree/ltree.h +++ b/contrib/ltree/ltree.h @@ -25,6 +25,7 @@ typedef struct #define LTREE_HDRSIZE MAXALIGN( offsetof(ltree, data) ) #define LTREE_FIRST(x) ( (ltree_level*)( ((char*)(x))+LTREE_HDRSIZE ) ) +#define LTREE_MAX_LEVELS PG_UINT16_MAX /* ltree.numlevel is uint16 */ /* lquery */ @@ -77,6 +78,7 @@ typedef struct #define LQUERY_HDRSIZE MAXALIGN( offsetof(lquery, data) ) #define LQUERY_FIRST(x) ( (lquery_level*)( ((char*)(x))+LQUERY_HDRSIZE ) ) +#define LQUERY_MAX_LEVELS PG_UINT16_MAX /* lquery.numlevel is uint16 */ #define LQUERY_HASNOT 0x01 diff --git a/contrib/ltree/ltree_io.c b/contrib/ltree/ltree_io.c index 900a46a9e7..3ae7dc0d06 100644 --- a/contrib/ltree/ltree_io.c +++ b/contrib/ltree/ltree_io.c @@ -58,11 +58,11 @@ ltree_in(PG_FUNCTION_ARGS) ptr += charlen; } - if (num + 1 > MaxAllocSize / sizeof(nodeitem)) + if (num + 1 > LTREE_MAX_LEVELS) ereport(ERROR, (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), - errmsg("number of levels (%d) exceeds the maximum allowed (%d)", - num + 1, (int) (MaxAllocSize / sizeof(nodeitem))))); + errmsg("number of ltree levels (%d) exceeds the maximum allowed (%d)", + num + 1, LTREE_MAX_LEVELS))); list = lptr = (nodeitem *) palloc(sizeof(nodeitem) * (num + 1)); ptr = buf; while (*ptr) @@ -227,11 +227,11 @@ lquery_in(PG_FUNCTION_ARGS) } num++; - if (num > MaxAllocSize / ITEMSIZE) + if (num > LQUERY_MAX_LEVELS) ereport(ERROR, (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), - errmsg("number of levels (%d) exceeds the maximum allowed (%d)", - num, (int) (MaxAllocSize / ITEMSIZE)))); + errmsg("number of lquery levels (%d) exceeds the maximum allowed (%d)", + num, LQUERY_MAX_LEVELS))); curqlevel = tmpql = (lquery_level *) palloc0(ITEMSIZE * num); ptr = buf; while (*ptr) @@ -344,7 +344,7 @@ lquery_in(PG_FUNCTION_ARGS) else if (charlen == 1 && t_iseq(ptr, '.')) { curqlevel->low = 0; - curqlevel->high = 0xffff; + curqlevel->high = LTREE_MAX_LEVELS; curqlevel = NEXTLEV(curqlevel); state = LQPRS_WAITLEVEL; } @@ -357,7 +357,16 @@ lquery_in(PG_FUNCTION_ARGS) state = LQPRS_WAITSNUM; else if (t_isdigit(ptr)) { - curqlevel->low = atoi(ptr); + int low = atoi(ptr); + + if (low < 0 || low > LTREE_MAX_LEVELS) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("lquery syntax error"), + errdetail("Low limit (%d) exceeds the maximum allowed (%d).", + low, LTREE_MAX_LEVELS))); + + curqlevel->low = (uint16) low; state = LQPRS_WAITND; } else @@ -367,12 +376,21 @@ lquery_in(PG_FUNCTION_ARGS) { if (t_isdigit(ptr)) { - curqlevel->high = atoi(ptr); + int high = atoi(ptr); + + if (high < 0 || high > LTREE_MAX_LEVELS) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("lquery syntax error"), + errdetail("High limit (%d) exceeds the maximum allowed (%d).", + high, LTREE_MAX_LEVELS))); + + curqlevel->high = (uint16) high; state = LQPRS_WAITCLOSE; } else if (charlen == 1 && t_iseq(ptr, '}')) { - curqlevel->high = 0xffff; + curqlevel->high = LTREE_MAX_LEVELS; state = LQPRS_WAITEND; } else @@ -422,7 +440,7 @@ lquery_in(PG_FUNCTION_ARGS) if (lptr->start == ptr) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("syntax error"), + errmsg("lquery syntax error"), errdetail("Unexpected end of line."))); lptr->len = ptr - lptr->start - @@ -432,7 +450,7 @@ lquery_in(PG_FUNCTION_ARGS) if (lptr->len == 0) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("syntax error"), + errmsg("lquery syntax error"), errdetail("Unexpected end of line."))); if (lptr->wlen > 255) @@ -444,11 +462,11 @@ lquery_in(PG_FUNCTION_ARGS) lptr->wlen, pos))); } else if (state == LQPRS_WAITOPEN) - curqlevel->high = 0xffff; + curqlevel->high = LTREE_MAX_LEVELS; else if (state != LQPRS_WAITEND) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("syntax error"), + errmsg("lquery syntax error"), errdetail("Unexpected end of line."))); curqlevel = tmpql; @@ -468,8 +486,8 @@ lquery_in(PG_FUNCTION_ARGS) else if (curqlevel->low > curqlevel->high) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("syntax error"), - errdetail("Low limit(%d) is greater than upper(%d).", + errmsg("lquery syntax error"), + errdetail("Low limit (%d) is greater than upper (%d).", curqlevel->low, curqlevel->high))); curqlevel = NEXTLEV(curqlevel); @@ -593,7 +611,7 @@ lquery_out(PG_FUNCTION_ARGS) } else if (curqlevel->low == 0) { - if (curqlevel->high == 0xffff) + if (curqlevel->high == LTREE_MAX_LEVELS) { *ptr = '*'; *(ptr + 1) = '\0'; @@ -601,7 +619,7 @@ lquery_out(PG_FUNCTION_ARGS) else sprintf(ptr, "*{,%d}", curqlevel->high); } - else if (curqlevel->high == 0xffff) + else if (curqlevel->high == LTREE_MAX_LEVELS) { sprintf(ptr, "*{%d,}", curqlevel->low); } diff --git a/contrib/ltree/ltree_op.c b/contrib/ltree/ltree_op.c index 070868f25a..34e6e4b2aa 100644 --- a/contrib/ltree/ltree_op.c +++ b/contrib/ltree/ltree_op.c @@ -274,10 +274,17 @@ static ltree * ltree_concat(ltree *a, ltree *b) { ltree *r; + int numlevel = (int) a->numlevel + b->numlevel; + + if (numlevel > LTREE_MAX_LEVELS) + ereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("number of ltree levels (%d) exceeds the maximum allowed (%d)", + numlevel, LTREE_MAX_LEVELS))); r = (ltree *) palloc0(VARSIZE(a) + VARSIZE(b) - LTREE_HDRSIZE); SET_VARSIZE(r, VARSIZE(a) + VARSIZE(b) - LTREE_HDRSIZE); - r->numlevel = a->numlevel + b->numlevel; + r->numlevel = (uint16) numlevel; memcpy(LTREE_FIRST(r), LTREE_FIRST(a), VARSIZE(a) - LTREE_HDRSIZE); memcpy(((char *) LTREE_FIRST(r)) + VARSIZE(a) - LTREE_HDRSIZE, diff --git a/contrib/ltree/sql/ltree.sql b/contrib/ltree/sql/ltree.sql index 846b04e48e..fca3ae645d 100644 --- a/contrib/ltree/sql/ltree.sql +++ b/contrib/ltree/sql/ltree.sql @@ -90,6 +90,17 @@ SELECT '1.*.4|3|2.*{1}'::lquery; SELECT 'qwerty%@*.tu'::lquery; SELECT nlevel('1.2.3.4'); +SELECT nlevel(('1' || repeat('.1', 65534))::ltree); +SELECT nlevel(('1' || repeat('.1', 65535))::ltree); +SELECT nlevel(('1' || repeat('.1', 65534))::ltree || '1'); +SELECT ('1' || repeat('.1', 65534))::lquery IS NULL; +SELECT ('1' || repeat('.1', 65535))::lquery IS NULL; +SELECT '*{65535}'::lquery; +SELECT '*{65536}'::lquery; +SELECT '*{,65534}'::lquery; +SELECT '*{,65535}'::lquery; +SELECT '*{,65536}'::lquery; + SELECT '1.2'::ltree < '2.2'::ltree; SELECT '1.2'::ltree <= '2.2'::ltree; SELECT '2.2'::ltree = '2.2'::ltree; diff --git a/doc/src/sgml/ltree.sgml b/doc/src/sgml/ltree.sgml index b4e07f6510..0c74aa705e 100644 --- a/doc/src/sgml/ltree.sgml +++ b/doc/src/sgml/ltree.sgml @@ -37,8 +37,7 @@ A label path is a sequence of zero or more labels separated by dots, for example L1.L2.L3, representing a path from the root of a hierarchical tree to a particular node. The - length of a label path must be less than 65kB, but keeping it under 2kB is - preferable. + length of a label path cannot exceed 65535 labels.