1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-05 23:56:58 +03:00

Allow hyphens in ltree labels

Also increase the allowed length of labels to 1000 characters

Garen Torikian

Discussion: https://postgr.es/m/CAGXsc+-mNg9Gc0rp-ER0sv+zkZSZp2wE9-LX6XcoWSLVz22tZA@mail.gmail.com
This commit is contained in:
Andrew Dunstan 2023-01-06 16:03:19 -05:00
parent a46a7011b2
commit b1665bf01e
6 changed files with 60 additions and 38 deletions

View File

@ -1,4 +1,6 @@
CREATE EXTENSION ltree; CREATE EXTENSION ltree;
-- max length for a label
\set maxlbl 1000
-- Check whether any of our opclasses fail amvalidate -- Check whether any of our opclasses fail amvalidate
SELECT amname, opcname SELECT amname, opcname
FROM pg_opclass opc LEFT JOIN pg_am am ON am.oid = opcmethod FROM pg_opclass opc LEFT JOIN pg_am am ON am.oid = opcmethod
@ -25,6 +27,12 @@ SELECT '1.2'::ltree;
1.2 1.2
(1 row) (1 row)
SELECT '1.2.-3'::ltree;
ltree
--------
1.2.-3
(1 row)
SELECT '1.2._3'::ltree; SELECT '1.2._3'::ltree;
ltree ltree
-------- --------
@ -45,15 +53,15 @@ ERROR: ltree syntax error
LINE 1: SELECT '1.2.'::ltree; LINE 1: SELECT '1.2.'::ltree;
^ ^
DETAIL: Unexpected end of input. DETAIL: Unexpected end of input.
SELECT repeat('x', 255)::ltree; SELECT repeat('x', :maxlbl)::ltree;
repeat repeat
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
(1 row) (1 row)
SELECT repeat('x', 256)::ltree; SELECT repeat('x', :maxlbl + 1)::ltree;
ERROR: label string is too long ERROR: label string is too long
DETAIL: Label length is 256, must be at most 255, at character 257. DETAIL: Label length is 1001, must be at most 1000, at character 1002.
SELECT ltree2text('1.2.3.34.sdf'); SELECT ltree2text('1.2.3.34.sdf');
ltree2text ltree2text
-------------- --------------
@ -531,24 +539,24 @@ SELECT '1.2.3|@.4'::lquery;
ERROR: lquery syntax error at character 7 ERROR: lquery syntax error at character 7
LINE 1: SELECT '1.2.3|@.4'::lquery; LINE 1: SELECT '1.2.3|@.4'::lquery;
^ ^
SELECT (repeat('x', 255) || '*@@*')::lquery; SELECT (repeat('x', :maxlbl) || '*@@*')::lquery;
lquery lquery
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@*
(1 row) (1 row)
SELECT (repeat('x', 256) || '*@@*')::lquery; SELECT (repeat('x', :maxlbl + 1) || '*@@*')::lquery;
ERROR: label string is too long ERROR: label string is too long
DETAIL: Label length is 256, must be at most 255, at character 257. DETAIL: Label length is 1001, must be at most 1000, at character 1002.
SELECT ('!' || repeat('x', 255))::lquery; SELECT ('!' || repeat('x', :maxlbl))::lquery;
lquery lquery
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
!xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx !xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
(1 row) (1 row)
SELECT ('!' || repeat('x', 256))::lquery; SELECT ('!' || repeat('x', :maxlbl + 1))::lquery;
ERROR: label string is too long ERROR: label string is too long
DETAIL: Label length is 256, must be at most 255, at character 258. DETAIL: Label length is 1001, must be at most 1000, at character 1003.
SELECT nlevel('1.2.3.4'); SELECT nlevel('1.2.3.4');
nlevel nlevel
-------- --------
@ -1195,6 +1203,12 @@ SELECT 'tree & aw_qw%*'::ltxtquery;
tree & aw_qw%* tree & aw_qw%*
(1 row) (1 row)
SELECT 'tree & aw-qw%*'::ltxtquery;
ltxtquery
----------------
tree & aw-qw%*
(1 row)
SELECT 'ltree.awdfg'::ltree @ '!tree & aWdf@*'::ltxtquery; SELECT 'ltree.awdfg'::ltree @ '!tree & aWdf@*'::ltxtquery;
?column? ?column?
---------- ----------

View File

@ -12,10 +12,10 @@
/* /*
* We want the maximum length of a label to be encoding-independent, so * We want the maximum length of a label to be encoding-independent, so
* set it somewhat arbitrarily at 255 characters (not bytes), while using * set it somewhat arbitrarily at 1000 characters (not bytes), while using
* uint16 fields to hold the byte length. * uint16 fields to hold the byte length.
*/ */
#define LTREE_LABEL_MAX_CHARS 255 #define LTREE_LABEL_MAX_CHARS 1000
/* /*
* LOWER_NODE used to be defined in the Makefile via the compile flags. * LOWER_NODE used to be defined in the Makefile via the compile flags.
@ -126,7 +126,8 @@ typedef struct
#define LQUERY_HASNOT 0x01 #define LQUERY_HASNOT 0x01
#define ISALNUM(x) ( t_isalnum(x) || t_iseq(x, '_') ) /* valid label chars are alphanumerics, underscores and hyphens */
#define ISLABEL(x) ( t_isalnum(x) || t_iseq(x, '_') || t_iseq(x, '-') )
/* full text query */ /* full text query */

View File

@ -74,7 +74,7 @@ parse_ltree(const char *buf, struct Node *escontext)
switch (state) switch (state)
{ {
case LTPRS_WAITNAME: case LTPRS_WAITNAME:
if (ISALNUM(ptr)) if (ISLABEL(ptr))
{ {
lptr->start = ptr; lptr->start = ptr;
lptr->wlen = 0; lptr->wlen = 0;
@ -92,7 +92,7 @@ parse_ltree(const char *buf, struct Node *escontext)
lptr++; lptr++;
state = LTPRS_WAITNAME; state = LTPRS_WAITNAME;
} }
else if (!ISALNUM(ptr)) else if (!ISLABEL(ptr))
UNCHAR; UNCHAR;
break; break;
default: default:
@ -316,7 +316,7 @@ parse_lquery(const char *buf, struct Node *escontext)
switch (state) switch (state)
{ {
case LQPRS_WAITLEVEL: case LQPRS_WAITLEVEL:
if (ISALNUM(ptr)) if (ISLABEL(ptr))
{ {
GETVAR(curqlevel) = lptr = (nodeitem *) palloc0(sizeof(nodeitem) * (numOR + 1)); GETVAR(curqlevel) = lptr = (nodeitem *) palloc0(sizeof(nodeitem) * (numOR + 1));
lptr->start = ptr; lptr->start = ptr;
@ -339,7 +339,7 @@ parse_lquery(const char *buf, struct Node *escontext)
UNCHAR; UNCHAR;
break; break;
case LQPRS_WAITVAR: case LQPRS_WAITVAR:
if (ISALNUM(ptr)) if (ISLABEL(ptr))
{ {
lptr++; lptr++;
lptr->start = ptr; lptr->start = ptr;
@ -385,7 +385,7 @@ parse_lquery(const char *buf, struct Node *escontext)
state = LQPRS_WAITLEVEL; state = LQPRS_WAITLEVEL;
curqlevel = NEXTLEV(curqlevel); curqlevel = NEXTLEV(curqlevel);
} }
else if (ISALNUM(ptr)) else if (ISLABEL(ptr))
{ {
/* disallow more chars after a flag */ /* disallow more chars after a flag */
if (lptr->flag) if (lptr->flag)

View File

@ -80,7 +80,7 @@ gettoken_query(QPRS_STATE *state, int32 *val, int32 *lenval, char **strval, uint
(state->buf)++; (state->buf)++;
return OPEN; return OPEN;
} }
else if (ISALNUM(state->buf)) else if (ISLABEL(state->buf))
{ {
state->state = INOPERAND; state->state = INOPERAND;
*strval = state->buf; *strval = state->buf;
@ -93,7 +93,7 @@ gettoken_query(QPRS_STATE *state, int32 *val, int32 *lenval, char **strval, uint
errmsg("operand syntax error"))); errmsg("operand syntax error")));
break; break;
case INOPERAND: case INOPERAND:
if (ISALNUM(state->buf)) if (ISLABEL(state->buf))
{ {
if (*flag) if (*flag)
ereturn(state->escontext, ERR, ereturn(state->escontext, ERR,

View File

@ -1,5 +1,8 @@
CREATE EXTENSION ltree; CREATE EXTENSION ltree;
-- max length for a label
\set maxlbl 1000
-- Check whether any of our opclasses fail amvalidate -- Check whether any of our opclasses fail amvalidate
SELECT amname, opcname SELECT amname, opcname
FROM pg_opclass opc LEFT JOIN pg_am am ON am.oid = opcmethod FROM pg_opclass opc LEFT JOIN pg_am am ON am.oid = opcmethod
@ -8,6 +11,7 @@ WHERE opc.oid >= 16384 AND NOT amvalidate(opc.oid);
SELECT ''::ltree; SELECT ''::ltree;
SELECT '1'::ltree; SELECT '1'::ltree;
SELECT '1.2'::ltree; SELECT '1.2'::ltree;
SELECT '1.2.-3'::ltree;
SELECT '1.2._3'::ltree; SELECT '1.2._3'::ltree;
-- empty labels not allowed -- empty labels not allowed
@ -15,8 +19,8 @@ SELECT '.2.3'::ltree;
SELECT '1..3'::ltree; SELECT '1..3'::ltree;
SELECT '1.2.'::ltree; SELECT '1.2.'::ltree;
SELECT repeat('x', 255)::ltree; SELECT repeat('x', :maxlbl)::ltree;
SELECT repeat('x', 256)::ltree; SELECT repeat('x', :maxlbl + 1)::ltree;
SELECT ltree2text('1.2.3.34.sdf'); SELECT ltree2text('1.2.3.34.sdf');
SELECT text2ltree('1.2.3.34.sdf'); SELECT text2ltree('1.2.3.34.sdf');
@ -111,10 +115,10 @@ SELECT '1.!.3'::lquery;
SELECT '1.2.!'::lquery; SELECT '1.2.!'::lquery;
SELECT '1.2.3|@.4'::lquery; SELECT '1.2.3|@.4'::lquery;
SELECT (repeat('x', 255) || '*@@*')::lquery; SELECT (repeat('x', :maxlbl) || '*@@*')::lquery;
SELECT (repeat('x', 256) || '*@@*')::lquery; SELECT (repeat('x', :maxlbl + 1) || '*@@*')::lquery;
SELECT ('!' || repeat('x', 255))::lquery; SELECT ('!' || repeat('x', :maxlbl))::lquery;
SELECT ('!' || repeat('x', 256))::lquery; SELECT ('!' || repeat('x', :maxlbl + 1))::lquery;
SELECT nlevel('1.2.3.4'); SELECT nlevel('1.2.3.4');
SELECT nlevel(('1' || repeat('.1', 65534))::ltree); SELECT nlevel(('1' || repeat('.1', 65534))::ltree);
@ -233,6 +237,8 @@ SELECT 'QWER_GY'::ltree ~ 'q_t%@*';
--ltxtquery --ltxtquery
SELECT '!tree & aWdf@*'::ltxtquery; SELECT '!tree & aWdf@*'::ltxtquery;
SELECT 'tree & aw_qw%*'::ltxtquery; SELECT 'tree & aw_qw%*'::ltxtquery;
SELECT 'tree & aw-qw%*'::ltxtquery;
SELECT 'ltree.awdfg'::ltree @ '!tree & aWdf@*'::ltxtquery; SELECT 'ltree.awdfg'::ltree @ '!tree & aWdf@*'::ltxtquery;
SELECT 'tree.awdfg'::ltree @ '!tree & aWdf@*'::ltxtquery; SELECT 'tree.awdfg'::ltree @ '!tree & aWdf@*'::ltxtquery;
SELECT 'tree.awdfg'::ltree @ '!tree | aWdf@*'::ltxtquery; SELECT 'tree.awdfg'::ltree @ '!tree | aWdf@*'::ltxtquery;

View File

@ -23,10 +23,11 @@
<title>Definitions</title> <title>Definitions</title>
<para> <para>
A <firstterm>label</firstterm> is a sequence of alphanumeric characters A <firstterm>label</firstterm> is a sequence of alphanumeric characters,
and underscores (for example, in C locale the characters underscores, and hyphens. Valid alphanumeric character ranges are
<literal>A-Za-z0-9_</literal> are allowed). dependent on the database locale. For example, in C locale, the characters
Labels must be less than 256 characters long. <literal>A-Za-z0-9_-</literal> are allowed.
Labels must be no more than 1000 characters long.
</para> </para>
<para> <para>