diff --git a/contrib/ltree/expected/ltree.out b/contrib/ltree/expected/ltree.out index 5d9102cb6c9..c6d8f3ef75e 100644 --- a/contrib/ltree/expected/ltree.out +++ b/contrib/ltree/expected/ltree.out @@ -31,6 +31,29 @@ SELECT '1.2._3'::ltree; 1.2._3 (1 row) +-- empty labels not allowed +SELECT '.2.3'::ltree; +ERROR: ltree syntax error at character 1 +LINE 1: SELECT '.2.3'::ltree; + ^ +SELECT '1..3'::ltree; +ERROR: ltree syntax error at character 3 +LINE 1: SELECT '1..3'::ltree; + ^ +SELECT '1.2.'::ltree; +ERROR: ltree syntax error +LINE 1: SELECT '1.2.'::ltree; + ^ +DETAIL: Unexpected end of input. +SELECT repeat('x', 255)::ltree; + repeat +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +(1 row) + +SELECT repeat('x', 256)::ltree; +ERROR: label string is too long +DETAIL: Label length is 256, must be at most 255, at character 257. SELECT ltree2text('1.2.3.34.sdf'); ltree2text -------------- @@ -451,12 +474,81 @@ SELECT 'foo.bar{,}.!a*|b{1,}.c{,44}.d{3,4}'::lquery; foo.bar{,}.!a*|b{1,}.c{,44}.d{3,4} (1 row) +SELECT 'foo*@@*'::lquery; + lquery +-------- + foo@* +(1 row) + SELECT 'qwerty%@*.tu'::lquery; lquery -------------- qwerty%@*.tu (1 row) +-- empty labels not allowed +SELECT '.2.3'::lquery; +ERROR: lquery syntax error at character 1 +LINE 1: SELECT '.2.3'::lquery; + ^ +SELECT '1..3'::lquery; +ERROR: lquery syntax error at character 3 +LINE 1: SELECT '1..3'::lquery; + ^ +SELECT '1.2.'::lquery; +ERROR: lquery syntax error +LINE 1: SELECT '1.2.'::lquery; + ^ +DETAIL: Unexpected end of input. +SELECT '@.2.3'::lquery; +ERROR: lquery syntax error at character 1 +LINE 1: SELECT '@.2.3'::lquery; + ^ +SELECT '1.@.3'::lquery; +ERROR: lquery syntax error at character 3 +LINE 1: SELECT '1.@.3'::lquery; + ^ +SELECT '1.2.@'::lquery; +ERROR: lquery syntax error at character 5 +LINE 1: SELECT '1.2.@'::lquery; + ^ +SELECT '!.2.3'::lquery; +ERROR: lquery syntax error at character 2 +LINE 1: SELECT '!.2.3'::lquery; + ^ +DETAIL: Empty labels are not allowed. +SELECT '1.!.3'::lquery; +ERROR: lquery syntax error at character 4 +LINE 1: SELECT '1.!.3'::lquery; + ^ +DETAIL: Empty labels are not allowed. +SELECT '1.2.!'::lquery; +ERROR: lquery syntax error at character 6 +LINE 1: SELECT '1.2.!'::lquery; + ^ +DETAIL: Empty labels are not allowed. +SELECT '1.2.3|@.4'::lquery; +ERROR: lquery syntax error at character 7 +LINE 1: SELECT '1.2.3|@.4'::lquery; + ^ +SELECT (repeat('x', 255) || '*@@*')::lquery; + lquery +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@* +(1 row) + +SELECT (repeat('x', 256) || '*@@*')::lquery; +ERROR: label string is too long +DETAIL: Label length is 256, must be at most 255, at character 257. +SELECT ('!' || repeat('x', 255))::lquery; + lquery +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + !xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +(1 row) + +SELECT ('!' || repeat('x', 256))::lquery; +ERROR: label string is too long +DETAIL: Label length is 256, must be at most 255, at character 258. SELECT nlevel('1.2.3.4'); nlevel -------- @@ -1072,6 +1164,12 @@ SELECT 'QWER_TY'::ltree ~ 'q%@*'; t (1 row) +SELECT 'QWER_TY'::ltree ~ 'q%@*%@*'; + ?column? +---------- + t +(1 row) + SELECT 'QWER_TY'::ltree ~ 'Q_t%@*'; ?column? ---------- diff --git a/contrib/ltree/ltree_io.c b/contrib/ltree/ltree_io.c index b928b82d624..15115cb29f3 100644 --- a/contrib/ltree/ltree_io.c +++ b/contrib/ltree/ltree_io.c @@ -24,6 +24,10 @@ typedef struct #define LTPRS_WAITNAME 0 #define LTPRS_WAITDELIM 1 +static void finish_nodeitem(nodeitem *lptr, const char *ptr, + bool is_lquery, int pos); + + /* * expects a null terminated string * returns an ltree @@ -51,7 +55,7 @@ parse_ltree(const char *buf) while (*ptr) { charlen = pg_mblen(ptr); - if (charlen == 1 && t_iseq(ptr, '.')) + if (t_iseq(ptr, '.')) num++; ptr += charlen; } @@ -67,40 +71,32 @@ parse_ltree(const char *buf) { charlen = pg_mblen(ptr); - if (state == LTPRS_WAITNAME) + switch (state) { - if (ISALNUM(ptr)) - { - lptr->start = ptr; - lptr->wlen = 0; - state = LTPRS_WAITDELIM; - } - else - UNCHAR; + case LTPRS_WAITNAME: + if (ISALNUM(ptr)) + { + lptr->start = ptr; + lptr->wlen = 0; + state = LTPRS_WAITDELIM; + } + else + UNCHAR; + break; + case LTPRS_WAITDELIM: + if (t_iseq(ptr, '.')) + { + finish_nodeitem(lptr, ptr, false, pos); + totallen += MAXALIGN(lptr->len + LEVEL_HDRSIZE); + lptr++; + state = LTPRS_WAITNAME; + } + else if (!ISALNUM(ptr)) + UNCHAR; + break; + default: + elog(ERROR, "internal error in ltree parser"); } - else if (state == LTPRS_WAITDELIM) - { - if (charlen == 1 && t_iseq(ptr, '.')) - { - lptr->len = ptr - lptr->start; - if (lptr->wlen > LTREE_LABEL_MAX_CHARS) - ereport(ERROR, - (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))); - - totallen += MAXALIGN(lptr->len + LEVEL_HDRSIZE); - lptr++; - state = LTPRS_WAITNAME; - } - else if (!ISALNUM(ptr)) - UNCHAR; - } - else - /* internal error */ - elog(ERROR, "internal error in parser"); ptr += charlen; lptr->wlen++; @@ -109,14 +105,7 @@ parse_ltree(const char *buf) if (state == LTPRS_WAITDELIM) { - lptr->len = ptr - lptr->start; - if (lptr->wlen > LTREE_LABEL_MAX_CHARS) - ereport(ERROR, - (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))); - + finish_nodeitem(lptr, ptr, false, pos); totallen += MAXALIGN(lptr->len + LEVEL_HDRSIZE); lptr++; } @@ -298,13 +287,10 @@ parse_lquery(const char *buf) { charlen = pg_mblen(ptr); - if (charlen == 1) - { - if (t_iseq(ptr, '.')) - num++; - else if (t_iseq(ptr, '|')) - numOR++; - } + if (t_iseq(ptr, '.')) + num++; + else if (t_iseq(ptr, '|')) + numOR++; ptr += charlen; } @@ -321,220 +307,176 @@ parse_lquery(const char *buf) { charlen = pg_mblen(ptr); - if (state == LQPRS_WAITLEVEL) + switch (state) { - if (ISALNUM(ptr)) - { - GETVAR(curqlevel) = lptr = (nodeitem *) palloc0(sizeof(nodeitem) * (numOR + 1)); - lptr->start = ptr; - state = LQPRS_WAITDELIM; - curqlevel->numvar = 1; - } - else if (charlen == 1 && t_iseq(ptr, '!')) - { - GETVAR(curqlevel) = lptr = (nodeitem *) palloc0(sizeof(nodeitem) * (numOR + 1)); - lptr->start = ptr + 1; - state = LQPRS_WAITDELIM; - curqlevel->numvar = 1; - curqlevel->flag |= LQL_NOT; - hasnot = true; - } - else if (charlen == 1 && t_iseq(ptr, '*')) - state = LQPRS_WAITOPEN; - else - UNCHAR; - } - else if (state == LQPRS_WAITVAR) - { - if (ISALNUM(ptr)) - { - lptr++; - lptr->start = ptr; - state = LQPRS_WAITDELIM; - curqlevel->numvar++; - } - else - UNCHAR; - } - else if (state == LQPRS_WAITDELIM) - { - if (charlen == 1 && t_iseq(ptr, '@')) - { - if (lptr->start == ptr) + case LQPRS_WAITLEVEL: + if (ISALNUM(ptr)) + { + GETVAR(curqlevel) = lptr = (nodeitem *) palloc0(sizeof(nodeitem) * (numOR + 1)); + lptr->start = ptr; + state = LQPRS_WAITDELIM; + curqlevel->numvar = 1; + } + else if (t_iseq(ptr, '!')) + { + GETVAR(curqlevel) = lptr = (nodeitem *) palloc0(sizeof(nodeitem) * (numOR + 1)); + lptr->start = ptr + 1; + lptr->wlen = -1; /* compensate for counting ! below */ + state = LQPRS_WAITDELIM; + curqlevel->numvar = 1; + curqlevel->flag |= LQL_NOT; + hasnot = true; + } + else if (t_iseq(ptr, '*')) + state = LQPRS_WAITOPEN; + else UNCHAR; - lptr->flag |= LVAR_INCASE; - curqlevel->flag |= LVAR_INCASE; - } - else if (charlen == 1 && t_iseq(ptr, '*')) - { - if (lptr->start == ptr) + break; + case LQPRS_WAITVAR: + if (ISALNUM(ptr)) + { + lptr++; + lptr->start = ptr; + state = LQPRS_WAITDELIM; + curqlevel->numvar++; + } + else UNCHAR; - lptr->flag |= LVAR_ANYEND; - curqlevel->flag |= LVAR_ANYEND; - } - else if (charlen == 1 && t_iseq(ptr, '%')) - { - if (lptr->start == ptr) + break; + case LQPRS_WAITDELIM: + if (t_iseq(ptr, '@')) + { + lptr->flag |= LVAR_INCASE; + curqlevel->flag |= LVAR_INCASE; + } + else if (t_iseq(ptr, '*')) + { + lptr->flag |= LVAR_ANYEND; + curqlevel->flag |= LVAR_ANYEND; + } + else if (t_iseq(ptr, '%')) + { + lptr->flag |= LVAR_SUBLEXEME; + curqlevel->flag |= LVAR_SUBLEXEME; + } + else if (t_iseq(ptr, '|')) + { + finish_nodeitem(lptr, ptr, true, pos); + state = LQPRS_WAITVAR; + } + else if (t_iseq(ptr, '{')) + { + finish_nodeitem(lptr, ptr, true, pos); + curqlevel->flag |= LQL_COUNT; + state = LQPRS_WAITFNUM; + } + else if (t_iseq(ptr, '.')) + { + finish_nodeitem(lptr, ptr, true, pos); + state = LQPRS_WAITLEVEL; + curqlevel = NEXTLEV(curqlevel); + } + else if (ISALNUM(ptr)) + { + /* disallow more chars after a flag */ + if (lptr->flag) + UNCHAR; + } + else UNCHAR; - lptr->flag |= LVAR_SUBLEXEME; - curqlevel->flag |= LVAR_SUBLEXEME; - } - else if (charlen == 1 && t_iseq(ptr, '|')) - { - lptr->len = ptr - lptr->start - - ((lptr->flag & LVAR_SUBLEXEME) ? 1 : 0) - - ((lptr->flag & LVAR_INCASE) ? 1 : 0) - - ((lptr->flag & LVAR_ANYEND) ? 1 : 0); - if (lptr->wlen > LTREE_LABEL_MAX_CHARS) - ereport(ERROR, - (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))); - - state = LQPRS_WAITVAR; - } - else if (charlen == 1 && t_iseq(ptr, '{')) - { - lptr->len = ptr - lptr->start - - ((lptr->flag & LVAR_SUBLEXEME) ? 1 : 0) - - ((lptr->flag & LVAR_INCASE) ? 1 : 0) - - ((lptr->flag & LVAR_ANYEND) ? 1 : 0); - if (lptr->wlen > LTREE_LABEL_MAX_CHARS) - ereport(ERROR, - (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))); - - curqlevel->flag |= LQL_COUNT; - state = LQPRS_WAITFNUM; - } - else if (charlen == 1 && t_iseq(ptr, '.')) - { - lptr->len = ptr - lptr->start - - ((lptr->flag & LVAR_SUBLEXEME) ? 1 : 0) - - ((lptr->flag & LVAR_INCASE) ? 1 : 0) - - ((lptr->flag & LVAR_ANYEND) ? 1 : 0); - if (lptr->wlen > LTREE_LABEL_MAX_CHARS) - ereport(ERROR, - (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))); - - state = LQPRS_WAITLEVEL; - curqlevel = NEXTLEV(curqlevel); - } - else if (ISALNUM(ptr)) - { - if (lptr->flag) + break; + case LQPRS_WAITOPEN: + if (t_iseq(ptr, '{')) + state = LQPRS_WAITFNUM; + else if (t_iseq(ptr, '.')) + { + /* We only get here for '*', so these are correct defaults */ + curqlevel->low = 0; + curqlevel->high = LTREE_MAX_LEVELS; + curqlevel = NEXTLEV(curqlevel); + state = LQPRS_WAITLEVEL; + } + else UNCHAR; - } - else - UNCHAR; - } - else if (state == LQPRS_WAITOPEN) - { - if (charlen == 1 && t_iseq(ptr, '{')) - state = LQPRS_WAITFNUM; - else if (charlen == 1 && t_iseq(ptr, '.')) - { - /* We only get here for '*', so these are correct defaults */ - curqlevel->low = 0; - curqlevel->high = LTREE_MAX_LEVELS; - curqlevel = NEXTLEV(curqlevel); - state = LQPRS_WAITLEVEL; - } - else - UNCHAR; - } - else if (state == LQPRS_WAITFNUM) - { - if (charlen == 1 && t_iseq(ptr, ',')) - state = LQPRS_WAITSNUM; - else if (t_isdigit(ptr)) - { - int low = atoi(ptr); + break; + case LQPRS_WAITFNUM: + if (t_iseq(ptr, ',')) + state = LQPRS_WAITSNUM; + else if (t_isdigit(ptr)) + { + int low = atoi(ptr); - if (low < 0 || low > LTREE_MAX_LEVELS) - ereport(ERROR, - (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), - errmsg("lquery syntax error"), - errdetail("Low limit (%d) exceeds the maximum allowed (%d), at character %d.", - low, LTREE_MAX_LEVELS, pos))); + if (low < 0 || low > LTREE_MAX_LEVELS) + ereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("lquery syntax error"), + errdetail("Low limit (%d) exceeds the maximum allowed (%d), at character %d.", + low, LTREE_MAX_LEVELS, pos))); - curqlevel->low = (uint16) low; - state = LQPRS_WAITND; - } - else - UNCHAR; - } - else if (state == LQPRS_WAITSNUM) - { - if (t_isdigit(ptr)) - { - int high = atoi(ptr); + curqlevel->low = (uint16) low; + state = LQPRS_WAITND; + } + else + UNCHAR; + break; + case LQPRS_WAITSNUM: + if (t_isdigit(ptr)) + { + int high = atoi(ptr); - if (high < 0 || high > LTREE_MAX_LEVELS) - ereport(ERROR, - (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, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("lquery syntax error"), - errdetail("Low limit (%d) is greater than high limit (%d), at character %d.", - curqlevel->low, high, pos))); + if (high < 0 || high > LTREE_MAX_LEVELS) + ereport(ERROR, + (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, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("lquery syntax error"), + errdetail("Low limit (%d) is greater than high limit (%d), at character %d.", + curqlevel->low, high, pos))); - curqlevel->high = (uint16) high; - state = LQPRS_WAITCLOSE; - } - else if (charlen == 1 && t_iseq(ptr, '}')) - { - curqlevel->high = LTREE_MAX_LEVELS; - state = LQPRS_WAITEND; - } - else - UNCHAR; + curqlevel->high = (uint16) high; + state = LQPRS_WAITCLOSE; + } + else if (t_iseq(ptr, '}')) + { + curqlevel->high = LTREE_MAX_LEVELS; + state = LQPRS_WAITEND; + } + else + UNCHAR; + break; + case LQPRS_WAITCLOSE: + if (t_iseq(ptr, '}')) + state = LQPRS_WAITEND; + else if (!t_isdigit(ptr)) + UNCHAR; + break; + case LQPRS_WAITND: + if (t_iseq(ptr, '}')) + { + curqlevel->high = curqlevel->low; + state = LQPRS_WAITEND; + } + else if (t_iseq(ptr, ',')) + state = LQPRS_WAITSNUM; + else if (!t_isdigit(ptr)) + UNCHAR; + break; + case LQPRS_WAITEND: + if (t_iseq(ptr, '.')) + { + state = LQPRS_WAITLEVEL; + curqlevel = NEXTLEV(curqlevel); + } + else + UNCHAR; + break; + default: + elog(ERROR, "internal error in lquery parser"); } - else if (state == LQPRS_WAITCLOSE) - { - if (charlen == 1 && t_iseq(ptr, '}')) - state = LQPRS_WAITEND; - else if (!t_isdigit(ptr)) - UNCHAR; - } - else if (state == LQPRS_WAITND) - { - if (charlen == 1 && t_iseq(ptr, '}')) - { - curqlevel->high = curqlevel->low; - state = LQPRS_WAITEND; - } - else if (charlen == 1 && t_iseq(ptr, ',')) - state = LQPRS_WAITSNUM; - else if (!t_isdigit(ptr)) - UNCHAR; - } - else if (state == LQPRS_WAITEND) - { - if (charlen == 1 && t_iseq(ptr, '.')) - { - state = LQPRS_WAITLEVEL; - curqlevel = NEXTLEV(curqlevel); - } - else - UNCHAR; - } - else - /* internal error */ - elog(ERROR, "internal error in parser"); ptr += charlen; if (state == LQPRS_WAITDELIM) @@ -543,30 +485,7 @@ parse_lquery(const char *buf) } if (state == LQPRS_WAITDELIM) - { - if (lptr->start == ptr) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("lquery syntax error"), - errdetail("Unexpected end of input."))); - - lptr->len = ptr - lptr->start - - ((lptr->flag & LVAR_SUBLEXEME) ? 1 : 0) - - ((lptr->flag & LVAR_INCASE) ? 1 : 0) - - ((lptr->flag & LVAR_ANYEND) ? 1 : 0); - if (lptr->len == 0) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("lquery syntax error"), - errdetail("Unexpected end of input."))); - - if (lptr->wlen > LTREE_LABEL_MAX_CHARS) - ereport(ERROR, - (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))); - } + finish_nodeitem(lptr, ptr, true, pos); else if (state == LQPRS_WAITOPEN) curqlevel->high = LTREE_MAX_LEVELS; else if (state != LQPRS_WAITEND) @@ -646,6 +565,46 @@ parse_lquery(const char *buf) #undef UNCHAR } +/* + * 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) +{ + if (is_lquery) + { + /* + * Back up over any flag characters, and discount them from length and + * position. + */ + while (ptr > lptr->start && strchr("@*%", ptr[-1]) != NULL) + { + ptr--; + lptr->wlen--; + pos--; + } + } + + /* Now compute the byte length, which we weren't tracking before. */ + lptr->len = ptr - lptr->start; + + /* Complain if it's empty or too long */ + if (lptr->len == 0) + ereport(ERROR, + (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, + (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))); +} + /* * expects an lquery * returns a null terminated string diff --git a/contrib/ltree/sql/ltree.sql b/contrib/ltree/sql/ltree.sql index 0cf3dd61366..bf733ed17b9 100644 --- a/contrib/ltree/sql/ltree.sql +++ b/contrib/ltree/sql/ltree.sql @@ -10,6 +10,14 @@ SELECT '1'::ltree; SELECT '1.2'::ltree; SELECT '1.2._3'::ltree; +-- empty labels not allowed +SELECT '.2.3'::ltree; +SELECT '1..3'::ltree; +SELECT '1.2.'::ltree; + +SELECT repeat('x', 255)::ltree; +SELECT repeat('x', 256)::ltree; + SELECT ltree2text('1.2.3.34.sdf'); SELECT text2ltree('1.2.3.34.sdf'); @@ -88,8 +96,26 @@ SELECT '1.*.4|3|2.*{,4}'::lquery; SELECT '1.*.4|3|2.*{1,}'::lquery; SELECT '1.*.4|3|2.*{1}'::lquery; SELECT 'foo.bar{,}.!a*|b{1,}.c{,44}.d{3,4}'::lquery; +SELECT 'foo*@@*'::lquery; SELECT 'qwerty%@*.tu'::lquery; +-- empty labels not allowed +SELECT '.2.3'::lquery; +SELECT '1..3'::lquery; +SELECT '1.2.'::lquery; +SELECT '@.2.3'::lquery; +SELECT '1.@.3'::lquery; +SELECT '1.2.@'::lquery; +SELECT '!.2.3'::lquery; +SELECT '1.!.3'::lquery; +SELECT '1.2.!'::lquery; +SELECT '1.2.3|@.4'::lquery; + +SELECT (repeat('x', 255) || '*@@*')::lquery; +SELECT (repeat('x', 256) || '*@@*')::lquery; +SELECT ('!' || repeat('x', 255))::lquery; +SELECT ('!' || repeat('x', 256))::lquery; + SELECT nlevel('1.2.3.4'); SELECT nlevel(('1' || repeat('.1', 65534))::ltree); SELECT nlevel(('1' || repeat('.1', 65535))::ltree); @@ -200,6 +226,7 @@ SELECT 'a.b.c.d.e'::ltree ~ '!c{0,3}.!a{2,}'; SELECT 'a.b.c.d.e'::ltree ~ '!c{0,3}.!d{2,}.*'; SELECT 'QWER_TY'::ltree ~ 'q%@*'; +SELECT 'QWER_TY'::ltree ~ 'q%@*%@*'; SELECT 'QWER_TY'::ltree ~ 'Q_t%@*'; SELECT 'QWER_GY'::ltree ~ 'q_t%@*';