From 71e1f531d36b1fb54d0fa2568c26c4f1f44cab3f Mon Sep 17 00:00:00 2001 From: Bruce Momjian Date: Wed, 19 Feb 2003 03:50:09 +0000 Subject: [PATCH] Please apply patches for contrib/ltree. ltree_73.patch.gz - for 7.3 : Fix ~ operation bug: eg '1.1.1' ~ '*.1' ltree_74.patch.gz - for current CVS Fix ~ operation bug: eg '1.1.1' ~ '*.1' Add ? operation Optimize index storage Last change needs drop/create all ltree indexes, so only for 7.4 Teodor Sigaev --- contrib/ltree/README.ltree | 14 +- contrib/ltree/_ltree_gist.c | 29 ++- contrib/ltree/_ltree_op.c | 38 ++++ contrib/ltree/expected/ltree.out | 324 ++++++++++++++++++++----------- contrib/ltree/lquery_op.c | 69 ++++++- contrib/ltree/ltree.h | 6 +- contrib/ltree/ltree.sql.in | 97 +++++++++ contrib/ltree/ltree_gist.c | 30 ++- contrib/ltree/sql/ltree.sql | 30 ++- 9 files changed, 509 insertions(+), 128 deletions(-) diff --git a/contrib/ltree/README.ltree b/contrib/ltree/README.ltree index a23f7b0945..e9471b4e35 100644 --- a/contrib/ltree/README.ltree +++ b/contrib/ltree/README.ltree @@ -110,6 +110,9 @@ ltree <@ ltree equal). ltree ~ lquery, lquery ~ ltree - return TRUE if node represented by ltree satisfies lquery. +ltree ? lquery[], lquery ? ltree[] + - return TRUE if node represented by ltree satisfies at least one lquery + from array. ltree @ ltxtquery, ltxtquery @ ltree - return TRUE if node represented by ltree satisfies ltxtquery. ltree || ltree, ltree || text, text || ltree @@ -123,6 +126,9 @@ ltree @> ltree[], ltree[] <@ ltree - returns TRUE if array ltree[] contains a descendant of ltree. ltree[] ~ lquery, lquery ~ ltree[] - returns TRUE if array ltree[] contains label paths matched lquery. +ltree[] ? lquery[], lquery[] ? ltree[] + - returns TRUE if array ltree[] contains label paths matched atleaset one + lquery from array. ltree[] @ ltxtquery, ltxtquery @ ltree[] - returns TRUE if array ltree[] contains label paths matched ltxtquery (full text search). @@ -142,11 +148,11 @@ Various indices could be created to speed up execution of operations: * B-tree index over ltree: <, <=, =, =>, > * GiST index over ltree: - <, <=, =, =>, >, @>, <@, @, ~ + <, <=, =, =>, >, @>, <@, @, ~, ? Example: create index path_gist_idx on test using gist (path); * GiST index over ltree[]: - ltree[]<@ ltree, ltree @> ltree[], @, ~. + ltree[]<@ ltree, ltree @> ltree[], @, ~, ?. Example: create index path_gist_idx on test using gist (array_path); Notices: This index is lossy. @@ -426,6 +432,10 @@ appreciate your input. So far, below some (rather obvious) results: CHANGES +Feb 7, 2003 + Add ? operation + Fix ~ operation bug: eg '1.1.1' ~ '*.1' + Optimize index storage Aug 9, 2002 Fixed very stupid but important bug :-) July 31, 2002 diff --git a/contrib/ltree/_ltree_gist.c b/contrib/ltree/_ltree_gist.c index d8efd8096d..1791f5bd8d 100644 --- a/contrib/ltree/_ltree_gist.c +++ b/contrib/ltree/_ltree_gist.c @@ -96,7 +96,7 @@ _ltree_compress(PG_FUNCTION_ARGS) entry->rel, entry->page, entry->offset, key->len, FALSE); } - else + else if ( !LTG_ISALLTRUE(entry->key) ) { int4 i, len; @@ -105,10 +105,9 @@ _ltree_compress(PG_FUNCTION_ARGS) BITVECP sign = LTG_SIGN(DatumGetPointer(entry->key)); ALOOPBYTE( - if (sign[i] != 0xff) + if ((sign[i]&0xff) != 0xff) PG_RETURN_POINTER(retval); ); - len = LTG_HDRSIZE; key = (ltree_gist *) palloc(len); key->len = len; @@ -222,7 +221,7 @@ _ltree_penalty(PG_FUNCTION_ARGS) if (LTG_ISALLTRUE(origval)) { - *penalty = 0.0; + *penalty = 0.1; PG_RETURN_POINTER(penalty); } @@ -489,7 +488,7 @@ _ltree_picksplit(PG_FUNCTION_ARGS) ); } - if (size_alpha - size_l < size_beta - size_r + WISH_F(v->spl_nleft, v->spl_nright, 0.1)) + if (size_alpha - size_l < size_beta - size_r + WISH_F(v->spl_nleft, v->spl_nright, 0.00001)) { if (!LTG_ISALLTRUE(datum_l)) { @@ -613,6 +612,22 @@ gist_qe(ltree_gist * key, lquery * query) return true; } +static bool +_arrq_cons(ltree_gist *key, ArrayType *_query) { + lquery *query = (lquery *) ARR_DATA_PTR(_query); + int num = ArrayGetNItems(ARR_NDIM(_query), ARR_DIMS(_query)); + + if (ARR_NDIM(_query) != 1) + elog(ERROR, "Dimension of array != 1"); + + while (num > 0) { + if ( gist_qe(key, query) ) + return true; + num--; + query = (lquery*)NEXTVAL(query); + } + return false; +} Datum _ltree_consistent(PG_FUNCTION_ARGS) @@ -641,6 +656,10 @@ _ltree_consistent(PG_FUNCTION_ARGS) case 15: res = gist_qtxt(key, (ltxtquery *) query); break; + case 16: + case 17: + res = _arrq_cons(key, (ArrayType *) query); + break; default: elog(ERROR, "Unknown StrategyNumber: %d", strategy); } diff --git a/contrib/ltree/_ltree_op.c b/contrib/ltree/_ltree_op.c index 59de0a51e4..f7cd8f2ab6 100644 --- a/contrib/ltree/_ltree_op.c +++ b/contrib/ltree/_ltree_op.c @@ -13,6 +13,8 @@ PG_FUNCTION_INFO_V1(_ltree_risparent); PG_FUNCTION_INFO_V1(_ltree_r_risparent); PG_FUNCTION_INFO_V1(_ltq_regex); PG_FUNCTION_INFO_V1(_ltq_rregex); +PG_FUNCTION_INFO_V1(_lt_q_regex); +PG_FUNCTION_INFO_V1(_lt_q_rregex); PG_FUNCTION_INFO_V1(_ltxtq_exec); PG_FUNCTION_INFO_V1(_ltxtq_rexec); @@ -126,6 +128,42 @@ _ltq_rregex(PG_FUNCTION_ARGS) )); } +Datum +_lt_q_regex(PG_FUNCTION_ARGS) +{ + ArrayType *_tree = PG_GETARG_ARRAYTYPE_P(0); + ArrayType *_query = PG_GETARG_ARRAYTYPE_P(1); + lquery *query = (lquery *) ARR_DATA_PTR(_query); + bool res = false; + int num = ArrayGetNItems(ARR_NDIM(_query), ARR_DIMS(_query)); + + if (ARR_NDIM(_query) != 1) + elog(ERROR, "Dimension of array != 1"); + + while (num > 0) { + if ( array_iterator(_tree, ltq_regex, (void*)query, NULL) ) { + res = true; + break; + } + num--; + query = (lquery*)NEXTVAL(query); + } + + PG_FREE_IF_COPY(_tree, 0); + PG_FREE_IF_COPY(_query, 1); + PG_RETURN_BOOL(res); +} + +Datum +_lt_q_rregex(PG_FUNCTION_ARGS) +{ + PG_RETURN_DATUM(DirectFunctionCall2(_lt_q_regex, + PG_GETARG_DATUM(1), + PG_GETARG_DATUM(0) + )); +} + + Datum _ltxtq_exec(PG_FUNCTION_ARGS) { diff --git a/contrib/ltree/expected/ltree.out b/contrib/ltree/expected/ltree.out index 1b6526c5e9..f4ab1f6378 100644 --- a/contrib/ltree/expected/ltree.out +++ b/contrib/ltree/expected/ltree.out @@ -3,10 +3,10 @@ psql:ltree.sql:9: NOTICE: ProcedureCreate: type ltree is not yet defined psql:ltree.sql:14: NOTICE: Argument type "ltree" is only a shell psql:ltree.sql:281: NOTICE: ProcedureCreate: type lquery is not yet defined psql:ltree.sql:286: NOTICE: Argument type "lquery" is only a shell -psql:ltree.sql:345: NOTICE: ProcedureCreate: type ltxtquery is not yet defined -psql:ltree.sql:350: NOTICE: Argument type "ltxtquery" is only a shell -psql:ltree.sql:412: NOTICE: ProcedureCreate: type ltree_gist is not yet defined -psql:ltree.sql:417: NOTICE: Argument type "ltree_gist" is only a shell +psql:ltree.sql:392: NOTICE: ProcedureCreate: type ltxtquery is not yet defined +psql:ltree.sql:397: NOTICE: Argument type "ltxtquery" is only a shell +psql:ltree.sql:459: NOTICE: ProcedureCreate: type ltree_gist is not yet defined +psql:ltree.sql:464: NOTICE: Argument type "ltree_gist" is only a shell SELECT ''::ltree; ltree ------- @@ -963,6 +963,36 @@ SELECT '{j.k.l.m, g.b.c.d.e}'::ltree[] ~ 'A*@|g.b.c.d.e'; t (1 row) +SELECT 'a.b.c.d.e'::ltree ? '{A.b.c.d.e}'; + ?column? +---------- + f +(1 row) + +SELECT 'a.b.c.d.e'::ltree ? '{a.b.c.d.e}'; + ?column? +---------- + t +(1 row) + +SELECT 'a.b.c.d.e'::ltree ? '{A.b.c.d.e, a.*}'; + ?column? +---------- + t +(1 row) + +SELECT '{a.b.c.d.e,B.df}'::ltree[] ? '{A.b.c.d.e}'; + ?column? +---------- + f +(1 row) + +SELECT '{a.b.c.d.e,B.df}'::ltree[] ? '{A.b.c.d.e,*.df}'; + ?column? +---------- + t +(1 row) + --exractors SELECT ('{3456,1.2.3.34}'::ltree[] ?@> '1.2.3.4') is null; ?column? @@ -3058,6 +3088,50 @@ SELECT * FROM ltreetest WHERE t <@ '1.1.1' order by t asc; 1.1.1.2.1 (4 rows) +SELECT * FROM ltreetest WHERE t @ '23 & 1' order by t asc; + t +-------------------------------- + 1.10.23.25.5.11 + 1.10.4.18.22.23.24 + 1.12.25.26.22.8.15.23 + 1.19.22.11.14.7.32.23.19.14 + 1.21.28.4.23 + 1.26.15.23.5.31.29.11.19.28.1 + 1.27.22.23.2.26.32.17.7.9 + 10.12.9.6.6.26.14.8.23.1.25 + 12.27.23.32.1.1.9.29.13 + 14.1.15.25.27.23.25.26.28.10 + 14.27.29.23.4.1.17.32.6.25.22 + 15.11.26.1.30.6.23.5 + 19.22.29.32.1.21.26.24.23.17 + 19.9.32.23.13.24.1 + 21.23.17.8.23.11.8.1 + 22.30.31.24.23.22.5.20.28.1 + 23.1.23.18.12.29 + 23.12.1.5.32.25.8.24.1.25 + 23.12.32.22.19.1.22.4 + 23.17.22.1.23.4.29.32.4.1 + 23.17.25.4.1.16.29.10 + 23.19.17.31.29.13.1.12.5.25 + 23.22.10.1.14.24 + 23.28.1 + 23.3.32.21.5.14.10.17.1 + 27.29.1.5.30.6.22.16.23.2.28 + 28.23.2.30.3.8.1.15.15.14.13 + 29.23.1.21.31.8 + 29.23.15.25.1.6.6.10 + 30.23.10.1.10.7.22.28.18.11.17 + 31.30.23.7.7.24.32.10.11.1.31 + 32.1.23.20.14.12.23.5.32.15 + 32.15.20.28.5.1.23.4 + 5.13.23.19.28.26.27.6.1.22 + 6.18.1.4.18.23 + 7.23.1.24.29.13.31.19.23.17.7 + 8.16.1.16.28.6.3.22.6.23 + 8.2.18.23.5.16.17.1 + 9.5.9.3.23.9.25.14.1.29.28 +(39 rows) + SELECT * FROM ltreetest WHERE t ~ '1.1.1.*' order by t asc; t ----------- @@ -3071,9 +3145,17 @@ SELECT * FROM ltreetest WHERE t ~ '*.1' order by t asc; t -------------------------------- 1 + 1.1 + 1.1.1 + 1.1.1.1 + 1.1.1.2.1 + 1.1.2.1 + 1.26.15.23.5.31.29.11.19.28.1 + 10.13.22.1.8.30.9.24.1.2.1 10.22.1 10.26.30.15.1 11.1 + 12.1.1 17.25.2.13.10.27.13.1 18.13.6.12.26.26.26.29.18.20.1 19.20.25.7.27.28.27.17.9.3.1 @@ -3083,6 +3165,7 @@ SELECT * FROM ltreetest WHERE t ~ '*.1' order by t asc; 21.18.2.1 21.23.17.8.23.11.8.1 22.30.31.24.23.22.5.20.28.1 + 23.17.22.1.23.4.29.32.4.1 23.28.1 23.3.32.21.5.14.10.17.1 25.6.12.16.1 @@ -3095,14 +3178,7 @@ SELECT * FROM ltreetest WHERE t ~ '*.1' order by t asc; 8.2.18.23.5.16.17.1 8.32.30.1 9.21.20.29.1 -(25 rows) - -SELECT * FROM ltreetest WHERE t ~ '23.*.1' order by t asc; - t -------------------------- - 23.28.1 - 23.3.32.21.5.14.10.17.1 -(2 rows) +(34 rows) SELECT * FROM ltreetest WHERE t ~ '23.*{1}.1' order by t asc; t @@ -3110,49 +3186,28 @@ SELECT * FROM ltreetest WHERE t ~ '23.*{1}.1' order by t asc; 23.28.1 (1 row) -SELECT * FROM ltreetest WHERE t @ '23 & 1' order by t asc; - t --------------------------------- - 1.10.23.25.5.11 - 1.10.4.18.22.23.24 - 1.12.25.26.22.8.15.23 - 1.19.22.11.14.7.32.23.19.14 - 1.21.28.4.23 - 1.26.15.23.5.31.29.11.19.28.1 - 1.27.22.23.2.26.32.17.7.9 - 10.12.9.6.6.26.14.8.23.1.25 - 12.27.23.32.1.1.9.29.13 - 14.1.15.25.27.23.25.26.28.10 - 14.27.29.23.4.1.17.32.6.25.22 - 15.11.26.1.30.6.23.5 - 19.22.29.32.1.21.26.24.23.17 - 19.9.32.23.13.24.1 - 21.23.17.8.23.11.8.1 - 22.30.31.24.23.22.5.20.28.1 - 23.1.23.18.12.29 - 23.12.1.5.32.25.8.24.1.25 - 23.12.32.22.19.1.22.4 +SELECT * FROM ltreetest WHERE t ~ '23.*.1' order by t asc; + t +--------------------------- 23.17.22.1.23.4.29.32.4.1 - 23.17.25.4.1.16.29.10 - 23.19.17.31.29.13.1.12.5.25 - 23.22.10.1.14.24 23.28.1 23.3.32.21.5.14.10.17.1 - 27.29.1.5.30.6.22.16.23.2.28 - 28.23.2.30.3.8.1.15.15.14.13 - 29.23.1.21.31.8 - 29.23.15.25.1.6.6.10 - 30.23.10.1.10.7.22.28.18.11.17 - 31.30.23.7.7.24.32.10.11.1.31 - 32.1.23.20.14.12.23.5.32.15 - 32.15.20.28.5.1.23.4 - 5.13.23.19.28.26.27.6.1.22 - 6.18.1.4.18.23 - 7.23.1.24.29.13.31.19.23.17.7 - 8.16.1.16.28.6.3.22.6.23 - 8.2.18.23.5.16.17.1 - 9.5.9.3.23.9.25.14.1.29.28 -(39 rows) +(3 rows) + +SELECT * FROM ltreetest WHERE t ~ '23.*.2' order by t asc; + t +------------------ + 23.20.12.16.15.2 +(1 row) + +SELECT * FROM ltreetest WHERE t ? '{23.*.1,23.*.2}' order by t asc; + t +--------------------------- + 23.17.22.1.23.4.29.32.4.1 + 23.20.12.16.15.2 + 23.28.1 + 23.3.32.21.5.14.10.17.1 +(4 rows) create unique index tstidx on ltreetest (t); set enable_seqscan=off; @@ -7253,6 +7308,50 @@ SELECT * FROM ltreetest WHERE t <@ '1.1.1' order by t asc; 1.1.1.2.1 (4 rows) +SELECT * FROM ltreetest WHERE t @ '23 & 1' order by t asc; + t +-------------------------------- + 1.10.23.25.5.11 + 1.10.4.18.22.23.24 + 1.12.25.26.22.8.15.23 + 1.19.22.11.14.7.32.23.19.14 + 1.21.28.4.23 + 1.26.15.23.5.31.29.11.19.28.1 + 1.27.22.23.2.26.32.17.7.9 + 10.12.9.6.6.26.14.8.23.1.25 + 12.27.23.32.1.1.9.29.13 + 14.1.15.25.27.23.25.26.28.10 + 14.27.29.23.4.1.17.32.6.25.22 + 15.11.26.1.30.6.23.5 + 19.22.29.32.1.21.26.24.23.17 + 19.9.32.23.13.24.1 + 21.23.17.8.23.11.8.1 + 22.30.31.24.23.22.5.20.28.1 + 23.1.23.18.12.29 + 23.12.1.5.32.25.8.24.1.25 + 23.12.32.22.19.1.22.4 + 23.17.22.1.23.4.29.32.4.1 + 23.17.25.4.1.16.29.10 + 23.19.17.31.29.13.1.12.5.25 + 23.22.10.1.14.24 + 23.28.1 + 23.3.32.21.5.14.10.17.1 + 27.29.1.5.30.6.22.16.23.2.28 + 28.23.2.30.3.8.1.15.15.14.13 + 29.23.1.21.31.8 + 29.23.15.25.1.6.6.10 + 30.23.10.1.10.7.22.28.18.11.17 + 31.30.23.7.7.24.32.10.11.1.31 + 32.1.23.20.14.12.23.5.32.15 + 32.15.20.28.5.1.23.4 + 5.13.23.19.28.26.27.6.1.22 + 6.18.1.4.18.23 + 7.23.1.24.29.13.31.19.23.17.7 + 8.16.1.16.28.6.3.22.6.23 + 8.2.18.23.5.16.17.1 + 9.5.9.3.23.9.25.14.1.29.28 +(39 rows) + SELECT * FROM ltreetest WHERE t ~ '1.1.1.*' order by t asc; t ----------- @@ -7266,9 +7365,17 @@ SELECT * FROM ltreetest WHERE t ~ '*.1' order by t asc; t -------------------------------- 1 + 1.1 + 1.1.1 + 1.1.1.1 + 1.1.1.2.1 + 1.1.2.1 + 1.26.15.23.5.31.29.11.19.28.1 + 10.13.22.1.8.30.9.24.1.2.1 10.22.1 10.26.30.15.1 11.1 + 12.1.1 17.25.2.13.10.27.13.1 18.13.6.12.26.26.26.29.18.20.1 19.20.25.7.27.28.27.17.9.3.1 @@ -7278,6 +7385,7 @@ SELECT * FROM ltreetest WHERE t ~ '*.1' order by t asc; 21.18.2.1 21.23.17.8.23.11.8.1 22.30.31.24.23.22.5.20.28.1 + 23.17.22.1.23.4.29.32.4.1 23.28.1 23.3.32.21.5.14.10.17.1 25.6.12.16.1 @@ -7290,14 +7398,7 @@ SELECT * FROM ltreetest WHERE t ~ '*.1' order by t asc; 8.2.18.23.5.16.17.1 8.32.30.1 9.21.20.29.1 -(25 rows) - -SELECT * FROM ltreetest WHERE t ~ '23.*.1' order by t asc; - t -------------------------- - 23.28.1 - 23.3.32.21.5.14.10.17.1 -(2 rows) +(34 rows) SELECT * FROM ltreetest WHERE t ~ '23.*{1}.1' order by t asc; t @@ -7305,49 +7406,28 @@ SELECT * FROM ltreetest WHERE t ~ '23.*{1}.1' order by t asc; 23.28.1 (1 row) -SELECT * FROM ltreetest WHERE t @ '23 & 1' order by t asc; - t --------------------------------- - 1.10.23.25.5.11 - 1.10.4.18.22.23.24 - 1.12.25.26.22.8.15.23 - 1.19.22.11.14.7.32.23.19.14 - 1.21.28.4.23 - 1.26.15.23.5.31.29.11.19.28.1 - 1.27.22.23.2.26.32.17.7.9 - 10.12.9.6.6.26.14.8.23.1.25 - 12.27.23.32.1.1.9.29.13 - 14.1.15.25.27.23.25.26.28.10 - 14.27.29.23.4.1.17.32.6.25.22 - 15.11.26.1.30.6.23.5 - 19.22.29.32.1.21.26.24.23.17 - 19.9.32.23.13.24.1 - 21.23.17.8.23.11.8.1 - 22.30.31.24.23.22.5.20.28.1 - 23.1.23.18.12.29 - 23.12.1.5.32.25.8.24.1.25 - 23.12.32.22.19.1.22.4 +SELECT * FROM ltreetest WHERE t ~ '23.*.1' order by t asc; + t +--------------------------- 23.17.22.1.23.4.29.32.4.1 - 23.17.25.4.1.16.29.10 - 23.19.17.31.29.13.1.12.5.25 - 23.22.10.1.14.24 23.28.1 23.3.32.21.5.14.10.17.1 - 27.29.1.5.30.6.22.16.23.2.28 - 28.23.2.30.3.8.1.15.15.14.13 - 29.23.1.21.31.8 - 29.23.15.25.1.6.6.10 - 30.23.10.1.10.7.22.28.18.11.17 - 31.30.23.7.7.24.32.10.11.1.31 - 32.1.23.20.14.12.23.5.32.15 - 32.15.20.28.5.1.23.4 - 5.13.23.19.28.26.27.6.1.22 - 6.18.1.4.18.23 - 7.23.1.24.29.13.31.19.23.17.7 - 8.16.1.16.28.6.3.22.6.23 - 8.2.18.23.5.16.17.1 - 9.5.9.3.23.9.25.14.1.29.28 -(39 rows) +(3 rows) + +SELECT * FROM ltreetest WHERE t ~ '23.*.2' order by t asc; + t +------------------ + 23.20.12.16.15.2 +(1 row) + +SELECT * FROM ltreetest WHERE t ? '{23.*.1,23.*.2}' order by t asc; + t +--------------------------- + 23.17.22.1.23.4.29.32.4.1 + 23.20.12.16.15.2 + 23.28.1 + 23.3.32.21.5.14.10.17.1 +(4 rows) create table _ltreetest (t ltree[]); \copy _ltreetest FROM 'data/_ltree.data' @@ -7363,6 +7443,12 @@ SELECT count(*) FROM _ltreetest WHERE t <@ '1.1.1' ; 19 (1 row) +SELECT count(*) FROM _ltreetest WHERE t @ '23 & 1' ; + count +------- + 147 +(1 row) + SELECT count(*) FROM _ltreetest WHERE t ~ '1.1.1.*' ; count ------- @@ -7372,25 +7458,31 @@ SELECT count(*) FROM _ltreetest WHERE t ~ '1.1.1.*' ; SELECT count(*) FROM _ltreetest WHERE t ~ '*.1' ; count ------- - 83 + 109 +(1 row) + +SELECT count(*) FROM _ltreetest WHERE t ~ '23.*{1}.1' ; + count +------- + 5 (1 row) SELECT count(*) FROM _ltreetest WHERE t ~ '23.*.1' ; count ------- - 10 + 11 (1 row) -SELECT count(*) FROM _ltreetest WHERE t ~ '23.*{1}.1' ; +SELECT count(*) FROM _ltreetest WHERE t ~ '23.*.2' ; count ------- 5 (1 row) -SELECT count(*) FROM _ltreetest WHERE t @ '23 & 1' ; +SELECT count(*) FROM _ltreetest WHERE t ? '{23.*.1,23.*.2}' ; count ------- - 147 + 15 (1 row) create index _tstidx on _ltreetest using gist (t); @@ -7407,6 +7499,12 @@ SELECT count(*) FROM _ltreetest WHERE t <@ '1.1.1' ; 19 (1 row) +SELECT count(*) FROM _ltreetest WHERE t @ '23 & 1' ; + count +------- + 147 +(1 row) + SELECT count(*) FROM _ltreetest WHERE t ~ '1.1.1.*' ; count ------- @@ -7416,24 +7514,30 @@ SELECT count(*) FROM _ltreetest WHERE t ~ '1.1.1.*' ; SELECT count(*) FROM _ltreetest WHERE t ~ '*.1' ; count ------- - 83 + 109 +(1 row) + +SELECT count(*) FROM _ltreetest WHERE t ~ '23.*{1}.1' ; + count +------- + 5 (1 row) SELECT count(*) FROM _ltreetest WHERE t ~ '23.*.1' ; count ------- - 10 + 11 (1 row) -SELECT count(*) FROM _ltreetest WHERE t ~ '23.*{1}.1' ; +SELECT count(*) FROM _ltreetest WHERE t ~ '23.*.2' ; count ------- 5 (1 row) -SELECT count(*) FROM _ltreetest WHERE t @ '23 & 1' ; +SELECT count(*) FROM _ltreetest WHERE t ? '{23.*.1,23.*.2}' ; count ------- - 147 + 15 (1 row) diff --git a/contrib/ltree/lquery_op.c b/contrib/ltree/lquery_op.c index e24cc8559f..1156723d58 100644 --- a/contrib/ltree/lquery_op.c +++ b/contrib/ltree/lquery_op.c @@ -5,10 +5,16 @@ #include "ltree.h" #include +#include "utils/array.h" PG_FUNCTION_INFO_V1(ltq_regex); PG_FUNCTION_INFO_V1(ltq_rregex); +PG_FUNCTION_INFO_V1(lt_q_regex); +PG_FUNCTION_INFO_V1(lt_q_rregex); + +#define NEXTVAL(x) ( (lquery*)( (char*)(x) + INTALIGN( VARSIZE(x) ) ) ) + typedef struct { lquery_level *q; @@ -39,7 +45,7 @@ getlexem(char *start, char *end, int *len) } bool - compare_subnode(ltree_level * t, char *qn, int len, int (*cmpptr) (const char *, const char *, size_t), bool anyend) +compare_subnode(ltree_level * t, char *qn, int len, int (*cmpptr) (const char *, const char *, size_t), bool anyend) { char *endt = t->name + t->len; char *endq = qn + len; @@ -117,6 +123,11 @@ printFieldNot(FieldNot *fn ) { } */ +static struct { + bool muse; + uint32 high_pos; +} SomeStack = {false,0,}; + static bool checkCond(lquery_level * curq, int query_numlevel, ltree_level * curt, int tree_numlevel, FieldNot * ptr) { @@ -129,6 +140,14 @@ checkCond(lquery_level * curq, int query_numlevel, ltree_level * curt, int tree_ lquery_level *prevq = NULL; ltree_level *prevt = NULL; + if ( SomeStack.muse ) { + high_pos = SomeStack.high_pos; + qlen--; + prevq = curq; + curq = LQL_NEXT(curq); + SomeStack.muse = false; + } + while (tlen > 0 && qlen > 0) { if (curq->numvar) @@ -181,6 +200,15 @@ checkCond(lquery_level * curq, int query_numlevel, ltree_level * curt, int tree_ curt = LEVEL_NEXT(curt); tlen--; cur_tpos++; + if ( isok && prevq && prevq->numvar==0 && tlen>0 && cur_tpos <= high_pos ) { + FieldNot tmpptr; + if ( ptr ) + memcpy(&tmpptr,ptr,sizeof(FieldNot)); + SomeStack.high_pos = high_pos-cur_tpos; + SomeStack.muse = true; + if ( checkCond(prevq, qlen+1, curt, tlen, (ptr) ? &tmpptr : NULL) ) + return true; + } if (!isok && ptr) ptr->nt++; } @@ -278,3 +306,42 @@ ltq_rregex(PG_FUNCTION_ARGS) PG_GETARG_DATUM(0) )); } + +Datum +lt_q_regex(PG_FUNCTION_ARGS) +{ + ltree *tree = PG_GETARG_LTREE(0); + ArrayType *_query = PG_GETARG_ARRAYTYPE_P(1); + lquery *query = (lquery *) ARR_DATA_PTR(_query); + bool res = false; + int num = ArrayGetNItems(ARR_NDIM(_query), ARR_DIMS(_query)); + + if (ARR_NDIM(_query) != 1) + elog(ERROR, "Dimension of array != 1"); + + while (num > 0) { + if (DatumGetBool(DirectFunctionCall2(ltq_regex, + PointerGetDatum(tree), PointerGetDatum(query)))) { + + res = true; + break; + } + num--; + query = NEXTVAL(query); + } + + PG_FREE_IF_COPY(tree, 0); + PG_FREE_IF_COPY(_query, 1); + PG_RETURN_BOOL(res); +} + +Datum +lt_q_rregex(PG_FUNCTION_ARGS) +{ + PG_RETURN_DATUM(DirectFunctionCall2(lt_q_regex, + PG_GETARG_DATUM(1), + PG_GETARG_DATUM(0) + )); +} + + diff --git a/contrib/ltree/ltree.h b/contrib/ltree/ltree.h index 606b3e3a74..1bc36e722d 100644 --- a/contrib/ltree/ltree.h +++ b/contrib/ltree/ltree.h @@ -140,10 +140,14 @@ Datum ltree_isparent(PG_FUNCTION_ARGS); Datum ltree_risparent(PG_FUNCTION_ARGS); Datum ltq_regex(PG_FUNCTION_ARGS); Datum ltq_rregex(PG_FUNCTION_ARGS); +Datum lt_q_regex(PG_FUNCTION_ARGS); +Datum lt_q_rregex(PG_FUNCTION_ARGS); Datum ltxtq_exec(PG_FUNCTION_ARGS); Datum ltxtq_rexec(PG_FUNCTION_ARGS); Datum _ltq_regex(PG_FUNCTION_ARGS); Datum _ltq_rregex(PG_FUNCTION_ARGS); +Datum _lt_q_regex(PG_FUNCTION_ARGS); +Datum _lt_q_rregex(PG_FUNCTION_ARGS); Datum _ltxtq_exec(PG_FUNCTION_ARGS); Datum _ltxtq_rexec(PG_FUNCTION_ARGS); Datum _ltree_isparent(PG_FUNCTION_ARGS); @@ -173,7 +177,7 @@ ltree *lca_inner(ltree ** a, int len); /* GiST support for ltree */ #define BITBYTE 8 -#define SIGLENINT 8 +#define SIGLENINT 2 #define SIGLEN ( sizeof(int4)*SIGLENINT ) #define SIGLENBIT (SIGLEN*BITBYTE) typedef unsigned char BITVEC[SIGLEN]; diff --git a/contrib/ltree/ltree.sql.in b/contrib/ltree/ltree.sql.in index eae1454a9a..3bf7617fff 100644 --- a/contrib/ltree/ltree.sql.in +++ b/contrib/ltree/ltree.sql.in @@ -339,6 +339,53 @@ CREATE OPERATOR ^~ ( JOIN = contjoinsel ); +CREATE FUNCTION lt_q_regex(ltree,_lquery) +RETURNS bool +AS 'MODULE_PATHNAME' +LANGUAGE 'C' WITH (isstrict,iscachable); + +CREATE FUNCTION lt_q_rregex(_lquery,ltree) +RETURNS bool +AS 'MODULE_PATHNAME' +LANGUAGE 'C' WITH (isstrict,iscachable); + +CREATE OPERATOR ? ( + LEFTARG = ltree, + RIGHTARG = _lquery, + PROCEDURE = lt_q_regex, + COMMUTATOR = '?', + RESTRICT = contsel, + JOIN = contjoinsel +); + +CREATE OPERATOR ? ( + LEFTARG = _lquery, + RIGHTARG = ltree, + PROCEDURE = lt_q_rregex, + COMMUTATOR = '?', + RESTRICT = contsel, + JOIN = contjoinsel +); + +--not-indexed +CREATE OPERATOR ^? ( + LEFTARG = ltree, + RIGHTARG = _lquery, + PROCEDURE = lt_q_regex, + COMMUTATOR = '^?', + RESTRICT = contsel, + JOIN = contjoinsel +); + +CREATE OPERATOR ^? ( + LEFTARG = _lquery, + RIGHTARG = ltree, + PROCEDURE = lt_q_rregex, + COMMUTATOR = '^?', + RESTRICT = contsel, + JOIN = contjoinsel +); + CREATE FUNCTION ltxtq_in(cstring) RETURNS ltxtquery AS 'MODULE_PATHNAME' @@ -452,6 +499,8 @@ CREATE OPERATOR CLASS gist_ltree_ops OPERATOR 13 ~ (lquery, ltree) , OPERATOR 14 @ (ltree, ltxtquery) , OPERATOR 15 @ (ltxtquery, ltree) , + OPERATOR 16 ? (ltree, _lquery) , + OPERATOR 17 ? (_lquery, ltree) , FUNCTION 1 ltree_consistent (internal, internal, int2), FUNCTION 2 ltree_union (bytea, internal), FUNCTION 3 ltree_compress (internal), @@ -494,6 +543,16 @@ RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE 'C' WITH (isstrict,iscachable); +CREATE FUNCTION _lt_q_regex(_ltree,_lquery) +RETURNS bool +AS 'MODULE_PATHNAME' +LANGUAGE 'C' WITH (isstrict,iscachable); + +CREATE FUNCTION _lt_q_rregex(_lquery,_ltree) +RETURNS bool +AS 'MODULE_PATHNAME' +LANGUAGE 'C' WITH (isstrict,iscachable); + CREATE FUNCTION _ltxtq_exec(_ltree, ltxtquery) RETURNS bool AS 'MODULE_PATHNAME' @@ -558,6 +617,24 @@ CREATE OPERATOR ~ ( JOIN = contjoinsel ); +CREATE OPERATOR ? ( + LEFTARG = _ltree, + RIGHTARG = _lquery, + PROCEDURE = _lt_q_regex, + COMMUTATOR = '?', + RESTRICT = contsel, + JOIN = contjoinsel +); + +CREATE OPERATOR ? ( + LEFTARG = _lquery, + RIGHTARG = _ltree, + PROCEDURE = _lt_q_rregex, + COMMUTATOR = '?', + RESTRICT = contsel, + JOIN = contjoinsel +); + CREATE OPERATOR @ ( LEFTARG = _ltree, RIGHTARG = ltxtquery, @@ -632,6 +709,24 @@ CREATE OPERATOR ^~ ( JOIN = contjoinsel ); +CREATE OPERATOR ^? ( + LEFTARG = _ltree, + RIGHTARG = _lquery, + PROCEDURE = _lt_q_regex, + COMMUTATOR = '^?', + RESTRICT = contsel, + JOIN = contjoinsel +); + +CREATE OPERATOR ^? ( + LEFTARG = _lquery, + RIGHTARG = _ltree, + PROCEDURE = _lt_q_rregex, + COMMUTATOR = '^?', + RESTRICT = contsel, + JOIN = contjoinsel +); + CREATE OPERATOR ^@ ( LEFTARG = _ltree, RIGHTARG = ltxtquery, @@ -729,6 +824,8 @@ CREATE OPERATOR CLASS gist__ltree_ops OPERATOR 13 ~ (lquery, _ltree) RECHECK , OPERATOR 14 @ (_ltree, ltxtquery) RECHECK , OPERATOR 15 @ (ltxtquery, _ltree) RECHECK , + OPERATOR 16 ? (_ltree, _lquery) RECHECK , + OPERATOR 17 ? (_lquery, _ltree) RECHECK , FUNCTION 1 _ltree_consistent (internal, internal, int2), FUNCTION 2 _ltree_union (bytea, internal), FUNCTION 3 _ltree_compress (internal), diff --git a/contrib/ltree/ltree_gist.c b/contrib/ltree/ltree_gist.c index 5c5f4c7d6d..41c92ac57b 100644 --- a/contrib/ltree/ltree_gist.c +++ b/contrib/ltree/ltree_gist.c @@ -7,9 +7,11 @@ #include "access/gist.h" #include "access/rtree.h" #include "access/nbtree.h" - +#include "utils/array.h" #include "crc32.h" +#define NEXTVAL(x) ( (lquery*)( (char*)(x) + INTALIGN( VARSIZE(x) ) ) ) + PG_FUNCTION_INFO_V1(ltree_gist_in); Datum ltree_gist_in(PG_FUNCTION_ARGS); @@ -596,6 +598,22 @@ gist_qtxt(ltree_gist * key, ltxtquery * query) ); } +static bool +arrq_cons(ltree_gist *key, ArrayType *_query) { + lquery *query = (lquery *) ARR_DATA_PTR(_query); + int num = ArrayGetNItems(ARR_NDIM(_query), ARR_DIMS(_query)); + + if (ARR_NDIM(_query) != 1) + elog(ERROR, "Dimension of array != 1"); + + while (num > 0) { + if ( gist_qe(key, query) && gist_between(key, query) ) + return true; + num--; + query = NEXTVAL(query); + } + return false; +} Datum ltree_consistent(PG_FUNCTION_ARGS) @@ -672,6 +690,16 @@ ltree_consistent(PG_FUNCTION_ARGS) else res = gist_qtxt(key, (ltxtquery *) query); break; + case 16: + case 17: + if (GIST_LEAF(entry)) + res = DatumGetBool(DirectFunctionCall2(lt_q_regex, + PointerGetDatum(LTG_NODE(key)), + PointerGetDatum((ArrayType *) query) + )); + else + res = arrq_cons(key, (ArrayType *) query); + break; default: elog(ERROR, "Unknown StrategyNumber: %d", strategy); } diff --git a/contrib/ltree/sql/ltree.sql b/contrib/ltree/sql/ltree.sql index 141bd43436..9e8f485f1e 100644 --- a/contrib/ltree/sql/ltree.sql +++ b/contrib/ltree/sql/ltree.sql @@ -139,6 +139,7 @@ SELECT 'a.b.c.d.e'::ltree ~ '!b.*{1}.!c.*'; SELECT 'a.b.c.d.e'::ltree ~ '*.!b.*{1}.!c.*'; SELECT 'a.b.c.d.e'::ltree ~ '*.!b.*.!c.*'; + SELECT 'QWER_TY'::ltree ~ 'q%@*'; SELECT 'QWER_TY'::ltree ~ 'Q_t%@*'; SELECT 'QWER_GY'::ltree ~ 'q_t%@*'; @@ -175,6 +176,11 @@ SELECT '{1.2.3.4.5, 7.12.asd}'::ltree[] @> '1.2.3.4'; SELECT '{1.3.3, 7.12.asd}'::ltree[] @> '1.2.3.4'; SELECT '{ltree.asd, tree.awdfg}'::ltree[] @ 'tree & aWdfg@'::ltxtquery; SELECT '{j.k.l.m, g.b.c.d.e}'::ltree[] ~ 'A*@|g.b.c.d.e'; +SELECT 'a.b.c.d.e'::ltree ? '{A.b.c.d.e}'; +SELECT 'a.b.c.d.e'::ltree ? '{a.b.c.d.e}'; +SELECT 'a.b.c.d.e'::ltree ? '{A.b.c.d.e, a.*}'; +SELECT '{a.b.c.d.e,B.df}'::ltree[] ? '{A.b.c.d.e}'; +SELECT '{a.b.c.d.e,B.df}'::ltree[] ? '{A.b.c.d.e,*.df}'; --exractors SELECT ('{3456,1.2.3.34}'::ltree[] ?@> '1.2.3.4') is null; @@ -194,11 +200,13 @@ SELECT * FROM ltreetest WHERE t >= '12.3' order by t asc; SELECT * FROM ltreetest WHERE t > '12.3' order by t asc; SELECT * FROM ltreetest WHERE t @> '1.1.1' order by t asc; SELECT * FROM ltreetest WHERE t <@ '1.1.1' order by t asc; +SELECT * FROM ltreetest WHERE t @ '23 & 1' order by t asc; SELECT * FROM ltreetest WHERE t ~ '1.1.1.*' order by t asc; SELECT * FROM ltreetest WHERE t ~ '*.1' order by t asc; -SELECT * FROM ltreetest WHERE t ~ '23.*.1' order by t asc; SELECT * FROM ltreetest WHERE t ~ '23.*{1}.1' order by t asc; -SELECT * FROM ltreetest WHERE t @ '23 & 1' order by t asc; +SELECT * FROM ltreetest WHERE t ~ '23.*.1' order by t asc; +SELECT * FROM ltreetest WHERE t ~ '23.*.2' order by t asc; +SELECT * FROM ltreetest WHERE t ? '{23.*.1,23.*.2}' order by t asc; create unique index tstidx on ltreetest (t); set enable_seqscan=off; @@ -220,31 +228,37 @@ SELECT * FROM ltreetest WHERE t >= '12.3' order by t asc; SELECT * FROM ltreetest WHERE t > '12.3' order by t asc; SELECT * FROM ltreetest WHERE t @> '1.1.1' order by t asc; SELECT * FROM ltreetest WHERE t <@ '1.1.1' order by t asc; +SELECT * FROM ltreetest WHERE t @ '23 & 1' order by t asc; SELECT * FROM ltreetest WHERE t ~ '1.1.1.*' order by t asc; SELECT * FROM ltreetest WHERE t ~ '*.1' order by t asc; -SELECT * FROM ltreetest WHERE t ~ '23.*.1' order by t asc; SELECT * FROM ltreetest WHERE t ~ '23.*{1}.1' order by t asc; -SELECT * FROM ltreetest WHERE t @ '23 & 1' order by t asc; +SELECT * FROM ltreetest WHERE t ~ '23.*.1' order by t asc; +SELECT * FROM ltreetest WHERE t ~ '23.*.2' order by t asc; +SELECT * FROM ltreetest WHERE t ? '{23.*.1,23.*.2}' order by t asc; create table _ltreetest (t ltree[]); \copy _ltreetest FROM 'data/_ltree.data' SELECT count(*) FROM _ltreetest WHERE t @> '1.1.1' ; SELECT count(*) FROM _ltreetest WHERE t <@ '1.1.1' ; +SELECT count(*) FROM _ltreetest WHERE t @ '23 & 1' ; SELECT count(*) FROM _ltreetest WHERE t ~ '1.1.1.*' ; SELECT count(*) FROM _ltreetest WHERE t ~ '*.1' ; -SELECT count(*) FROM _ltreetest WHERE t ~ '23.*.1' ; SELECT count(*) FROM _ltreetest WHERE t ~ '23.*{1}.1' ; -SELECT count(*) FROM _ltreetest WHERE t @ '23 & 1' ; +SELECT count(*) FROM _ltreetest WHERE t ~ '23.*.1' ; +SELECT count(*) FROM _ltreetest WHERE t ~ '23.*.2' ; +SELECT count(*) FROM _ltreetest WHERE t ? '{23.*.1,23.*.2}' ; create index _tstidx on _ltreetest using gist (t); set enable_seqscan=off; SELECT count(*) FROM _ltreetest WHERE t @> '1.1.1' ; SELECT count(*) FROM _ltreetest WHERE t <@ '1.1.1' ; +SELECT count(*) FROM _ltreetest WHERE t @ '23 & 1' ; SELECT count(*) FROM _ltreetest WHERE t ~ '1.1.1.*' ; SELECT count(*) FROM _ltreetest WHERE t ~ '*.1' ; -SELECT count(*) FROM _ltreetest WHERE t ~ '23.*.1' ; SELECT count(*) FROM _ltreetest WHERE t ~ '23.*{1}.1' ; -SELECT count(*) FROM _ltreetest WHERE t @ '23 & 1' ; +SELECT count(*) FROM _ltreetest WHERE t ~ '23.*.1' ; +SELECT count(*) FROM _ltreetest WHERE t ~ '23.*.2' ; +SELECT count(*) FROM _ltreetest WHERE t ? '{23.*.1,23.*.2}' ; -- GitLab