Skip to content

Commit b1665bf

Browse files
committed
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
1 parent a46a701 commit b1665bf

File tree

6 files changed

+60
-38
lines changed

6 files changed

+60
-38
lines changed

contrib/ltree/expected/ltree.out

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
CREATE EXTENSION ltree;
2+
-- max length for a label
3+
\set maxlbl 1000
24
-- Check whether any of our opclasses fail amvalidate
35
SELECT amname, opcname
46
FROM pg_opclass opc LEFT JOIN pg_am am ON am.oid = opcmethod
@@ -25,6 +27,12 @@ SELECT '1.2'::ltree;
2527
1.2
2628
(1 row)
2729

30+
SELECT '1.2.-3'::ltree;
31+
ltree
32+
--------
33+
1.2.-3
34+
(1 row)
35+
2836
SELECT '1.2._3'::ltree;
2937
ltree
3038
--------
@@ -45,15 +53,15 @@ ERROR: ltree syntax error
4553
LINE 1: SELECT '1.2.'::ltree;
4654
^
4755
DETAIL: Unexpected end of input.
48-
SELECT repeat('x', 255)::ltree;
49-
repeat
50-
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
51-
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
56+
SELECT repeat('x', :maxlbl)::ltree;
57+
repeat
58+
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
59+
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
5260
(1 row)
5361

54-
SELECT repeat('x', 256)::ltree;
62+
SELECT repeat('x', :maxlbl + 1)::ltree;
5563
ERROR: label string is too long
56-
DETAIL: Label length is 256, must be at most 255, at character 257.
64+
DETAIL: Label length is 1001, must be at most 1000, at character 1002.
5765
SELECT ltree2text('1.2.3.34.sdf');
5866
ltree2text
5967
--------------
@@ -531,24 +539,24 @@ SELECT '1.2.3|@.4'::lquery;
531539
ERROR: lquery syntax error at character 7
532540
LINE 1: SELECT '1.2.3|@.4'::lquery;
533541
^
534-
SELECT (repeat('x', 255) || '*@@*')::lquery;
535-
lquery
536-
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
537-
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@*
542+
SELECT (repeat('x', :maxlbl) || '*@@*')::lquery;
543+
lquery
544+
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
545+
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@*
538546
(1 row)
539547

540-
SELECT (repeat('x', 256) || '*@@*')::lquery;
548+
SELECT (repeat('x', :maxlbl + 1) || '*@@*')::lquery;
541549
ERROR: label string is too long
542-
DETAIL: Label length is 256, must be at most 255, at character 257.
543-
SELECT ('!' || repeat('x', 255))::lquery;
544-
lquery
545-
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
546-
!xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
550+
DETAIL: Label length is 1001, must be at most 1000, at character 1002.
551+
SELECT ('!' || repeat('x', :maxlbl))::lquery;
552+
lquery
553+
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
554+
!xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
547555
(1 row)
548556

549-
SELECT ('!' || repeat('x', 256))::lquery;
557+
SELECT ('!' || repeat('x', :maxlbl + 1))::lquery;
550558
ERROR: label string is too long
551-
DETAIL: Label length is 256, must be at most 255, at character 258.
559+
DETAIL: Label length is 1001, must be at most 1000, at character 1003.
552560
SELECT nlevel('1.2.3.4');
553561
nlevel
554562
--------
@@ -1195,6 +1203,12 @@ SELECT 'tree & aw_qw%*'::ltxtquery;
11951203
tree & aw_qw%*
11961204
(1 row)
11971205

1206+
SELECT 'tree & aw-qw%*'::ltxtquery;
1207+
ltxtquery
1208+
----------------
1209+
tree & aw-qw%*
1210+
(1 row)
1211+
11981212
SELECT 'ltree.awdfg'::ltree @ '!tree & aWdf@*'::ltxtquery;
11991213
?column?
12001214
----------

contrib/ltree/ltree.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@
1212

1313
/*
1414
* We want the maximum length of a label to be encoding-independent, so
15-
* set it somewhat arbitrarily at 255 characters (not bytes), while using
15+
* set it somewhat arbitrarily at 1000 characters (not bytes), while using
1616
* uint16 fields to hold the byte length.
1717
*/
18-
#define LTREE_LABEL_MAX_CHARS 255
18+
#define LTREE_LABEL_MAX_CHARS 1000
1919

2020
/*
2121
* LOWER_NODE used to be defined in the Makefile via the compile flags.
@@ -126,7 +126,8 @@ typedef struct
126126

127127
#define LQUERY_HASNOT 0x01
128128

129-
#define ISALNUM(x) ( t_isalnum(x) || t_iseq(x, '_') )
129+
/* valid label chars are alphanumerics, underscores and hyphens */
130+
#define ISLABEL(x) ( t_isalnum(x) || t_iseq(x, '_') || t_iseq(x, '-') )
130131

131132
/* full text query */
132133

contrib/ltree/ltree_io.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ parse_ltree(const char *buf, struct Node *escontext)
7474
switch (state)
7575
{
7676
case LTPRS_WAITNAME:
77-
if (ISALNUM(ptr))
77+
if (ISLABEL(ptr))
7878
{
7979
lptr->start = ptr;
8080
lptr->wlen = 0;
@@ -92,7 +92,7 @@ parse_ltree(const char *buf, struct Node *escontext)
9292
lptr++;
9393
state = LTPRS_WAITNAME;
9494
}
95-
else if (!ISALNUM(ptr))
95+
else if (!ISLABEL(ptr))
9696
UNCHAR;
9797
break;
9898
default:
@@ -316,7 +316,7 @@ parse_lquery(const char *buf, struct Node *escontext)
316316
switch (state)
317317
{
318318
case LQPRS_WAITLEVEL:
319-
if (ISALNUM(ptr))
319+
if (ISLABEL(ptr))
320320
{
321321
GETVAR(curqlevel) = lptr = (nodeitem *) palloc0(sizeof(nodeitem) * (numOR + 1));
322322
lptr->start = ptr;
@@ -339,7 +339,7 @@ parse_lquery(const char *buf, struct Node *escontext)
339339
UNCHAR;
340340
break;
341341
case LQPRS_WAITVAR:
342-
if (ISALNUM(ptr))
342+
if (ISLABEL(ptr))
343343
{
344344
lptr++;
345345
lptr->start = ptr;
@@ -385,7 +385,7 @@ parse_lquery(const char *buf, struct Node *escontext)
385385
state = LQPRS_WAITLEVEL;
386386
curqlevel = NEXTLEV(curqlevel);
387387
}
388-
else if (ISALNUM(ptr))
388+
else if (ISLABEL(ptr))
389389
{
390390
/* disallow more chars after a flag */
391391
if (lptr->flag)

contrib/ltree/ltxtquery_io.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ gettoken_query(QPRS_STATE *state, int32 *val, int32 *lenval, char **strval, uint
8080
(state->buf)++;
8181
return OPEN;
8282
}
83-
else if (ISALNUM(state->buf))
83+
else if (ISLABEL(state->buf))
8484
{
8585
state->state = INOPERAND;
8686
*strval = state->buf;
@@ -93,7 +93,7 @@ gettoken_query(QPRS_STATE *state, int32 *val, int32 *lenval, char **strval, uint
9393
errmsg("operand syntax error")));
9494
break;
9595
case INOPERAND:
96-
if (ISALNUM(state->buf))
96+
if (ISLABEL(state->buf))
9797
{
9898
if (*flag)
9999
ereturn(state->escontext, ERR,

contrib/ltree/sql/ltree.sql

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
CREATE EXTENSION ltree;
22

3+
-- max length for a label
4+
\set maxlbl 1000
5+
36
-- Check whether any of our opclasses fail amvalidate
47
SELECT amname, opcname
58
FROM pg_opclass opc LEFT JOIN pg_am am ON am.oid = opcmethod
@@ -8,15 +11,16 @@ WHERE opc.oid >= 16384 AND NOT amvalidate(opc.oid);
811
SELECT ''::ltree;
912
SELECT '1'::ltree;
1013
SELECT '1.2'::ltree;
14+
SELECT '1.2.-3'::ltree;
1115
SELECT '1.2._3'::ltree;
1216

1317
-- empty labels not allowed
1418
SELECT '.2.3'::ltree;
1519
SELECT '1..3'::ltree;
1620
SELECT '1.2.'::ltree;
1721

18-
SELECT repeat('x', 255)::ltree;
19-
SELECT repeat('x', 256)::ltree;
22+
SELECT repeat('x', :maxlbl)::ltree;
23+
SELECT repeat('x', :maxlbl + 1)::ltree;
2024

2125
SELECT ltree2text('1.2.3.34.sdf');
2226
SELECT text2ltree('1.2.3.34.sdf');
@@ -111,10 +115,10 @@ SELECT '1.!.3'::lquery;
111115
SELECT '1.2.!'::lquery;
112116
SELECT '1.2.3|@.4'::lquery;
113117

114-
SELECT (repeat('x', 255) || '*@@*')::lquery;
115-
SELECT (repeat('x', 256) || '*@@*')::lquery;
116-
SELECT ('!' || repeat('x', 255))::lquery;
117-
SELECT ('!' || repeat('x', 256))::lquery;
118+
SELECT (repeat('x', :maxlbl) || '*@@*')::lquery;
119+
SELECT (repeat('x', :maxlbl + 1) || '*@@*')::lquery;
120+
SELECT ('!' || repeat('x', :maxlbl))::lquery;
121+
SELECT ('!' || repeat('x', :maxlbl + 1))::lquery;
118122

119123
SELECT nlevel('1.2.3.4');
120124
SELECT nlevel(('1' || repeat('.1', 65534))::ltree);
@@ -233,6 +237,8 @@ SELECT 'QWER_GY'::ltree ~ 'q_t%@*';
233237
--ltxtquery
234238
SELECT '!tree & aWdf@*'::ltxtquery;
235239
SELECT 'tree & aw_qw%*'::ltxtquery;
240+
SELECT 'tree & aw-qw%*'::ltxtquery;
241+
236242
SELECT 'ltree.awdfg'::ltree @ '!tree & aWdf@*'::ltxtquery;
237243
SELECT 'tree.awdfg'::ltree @ '!tree & aWdf@*'::ltxtquery;
238244
SELECT 'tree.awdfg'::ltree @ '!tree | aWdf@*'::ltxtquery;

doc/src/sgml/ltree.sgml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,11 @@
2323
<title>Definitions</title>
2424

2525
<para>
26-
A <firstterm>label</firstterm> is a sequence of alphanumeric characters
27-
and underscores (for example, in C locale the characters
28-
<literal>A-Za-z0-9_</literal> are allowed).
29-
Labels must be less than 256 characters long.
26+
A <firstterm>label</firstterm> is a sequence of alphanumeric characters,
27+
underscores, and hyphens. Valid alphanumeric character ranges are
28+
dependent on the database locale. For example, in C locale, the characters
29+
<literal>A-Za-z0-9_-</literal> are allowed.
30+
Labels must be no more than 1000 characters long.
3031
</para>
3132

3233
<para>

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy