diff --git a/contrib/ltree/README.ltree b/contrib/ltree/README.ltree index e9471b4e35..249678147a 100644 --- a/contrib/ltree/README.ltree +++ b/contrib/ltree/README.ltree @@ -192,7 +192,24 @@ int4 nlevel 3 Note, that arguments start, end, OFFSET, LEN have meaning of level of the node ! - + +int4 index(ltree,ltree), int4 index(ltree,ltree,OFFSET) + returns number of level of the first occurence of second argument in first + one beginning from OFFSET. if OFFSET is negative, than search begins from | + OFFSET| levels from the end of the path. + SELECT index('0.1.2.3.5.4.5.6.8.5.6.8','5.6',3); + index + ------- + 6 + SELECT index('0.1.2.3.5.4.5.6.8.5.6.8','5.6',-4); + index + ------- + 9 + +ltree text2ltree(text), text ltree2text(text) + cast functions for ltree and text. + + ltree lca(ltree,ltree,...) (up to 8 arguments) ltree lca(ltree[]) Returns Lowest Common Ancestor (lca) @@ -432,6 +449,9 @@ appreciate your input. So far, below some (rather obvious) results: CHANGES +Mar 28, 2003 + Added functions index(ltree,ltree,offset), text2ltree(text), + ltree2text(text) Feb 7, 2003 Add ? operation Fix ~ operation bug: eg '1.1.1' ~ '*.1' diff --git a/contrib/ltree/expected/ltree.out b/contrib/ltree/expected/ltree.out index f4ab1f6378..01246444f8 100644 --- a/contrib/ltree/expected/ltree.out +++ b/contrib/ltree/expected/ltree.out @@ -1,12 +1,12 @@ \set ECHO none 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: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 +psql:ltree.sql:301: NOTICE: ProcedureCreate: type lquery is not yet defined +psql:ltree.sql:306: NOTICE: Argument type "lquery" is only a shell +psql:ltree.sql:412: NOTICE: ProcedureCreate: type ltxtquery is not yet defined +psql:ltree.sql:417: NOTICE: Argument type "ltxtquery" is only a shell +psql:ltree.sql:479: NOTICE: ProcedureCreate: type ltree_gist is not yet defined +psql:ltree.sql:484: NOTICE: Argument type "ltree_gist" is only a shell SELECT ''::ltree; ltree ------- @@ -31,6 +31,18 @@ SELECT '1.2._3'::ltree; 1.2._3 (1 row) +SELECT ltree2text('1.2.3.34.sdf'); + ltree2text +-------------- + 1.2.3.34.sdf +(1 row) + +SELECT text2ltree('1.2.3.34.sdf'); + text2ltree +-------------- + 1.2.3.34.sdf +(1 row) + SELECT subltree('Top.Child1.Child2',1,2); subltree ---------- @@ -85,6 +97,114 @@ SELECT subpath('Top.Child1.Child2',1); Child1.Child2 (1 row) +SELECT index('1.2.3.4.5.6','1.2'); + index +------- + 0 +(1 row) + +SELECT index('a.1.2.3.4.5.6','1.2'); + index +------- + 1 +(1 row) + +SELECT index('a.1.2.3.4.5.6','1.2.3'); + index +------- + 1 +(1 row) + +SELECT index('a.1.2.3.4.5.6','1.2.3.j'); + index +------- + -1 +(1 row) + +SELECT index('a.1.2.3.4.5.6','1.2.3.j.4.5.5.5.5.5.5'); + index +------- + -1 +(1 row) + +SELECT index('a.1.2.3.4.5.6','1.2.3'); + index +------- + 1 +(1 row) + +SELECT index('a.1.2.3.4.5.6','6'); + index +------- + 6 +(1 row) + +SELECT index('a.1.2.3.4.5.6','6.1'); + index +------- + -1 +(1 row) + +SELECT index('a.1.2.3.4.5.6','5.6'); + index +------- + 5 +(1 row) + +SELECT index('0.1.2.3.5.4.5.6','5.6'); + index +------- + 6 +(1 row) + +SELECT index('0.1.2.3.5.4.5.6.8.5.6.8','5.6',3); + index +------- + 6 +(1 row) + +SELECT index('0.1.2.3.5.4.5.6.8.5.6.8','5.6',6); + index +------- + 6 +(1 row) + +SELECT index('0.1.2.3.5.4.5.6.8.5.6.8','5.6',7); + index +------- + 9 +(1 row) + +SELECT index('0.1.2.3.5.4.5.6.8.5.6.8','5.6',-7); + index +------- + 6 +(1 row) + +SELECT index('0.1.2.3.5.4.5.6.8.5.6.8','5.6',-4); + index +------- + 9 +(1 row) + +SELECT index('0.1.2.3.5.4.5.6.8.5.6.8','5.6',-3); + index +------- + 9 +(1 row) + +SELECT index('0.1.2.3.5.4.5.6.8.5.6.8','5.6',-2); + index +------- + -1 +(1 row) + +SELECT index('0.1.2.3.5.4.5.6.8.5.6.8','5.6',-20000); + index +------- + 6 +(1 row) + SELECT 'Top.Child1.Child2'::ltree || 'Child3'::text; ?column? -------------------------- diff --git a/contrib/ltree/ltree.sql.in b/contrib/ltree/ltree.sql.in index 3bf7617fff..2f267b9828 100644 --- a/contrib/ltree/ltree.sql.in +++ b/contrib/ltree/ltree.sql.in @@ -137,11 +137,31 @@ RETURNS ltree AS 'MODULE_PATHNAME' LANGUAGE 'C' WITH (isstrict,iscachable); +CREATE FUNCTION index(ltree,ltree) +RETURNS int4 +AS 'MODULE_PATHNAME', 'ltree_index' +LANGUAGE 'C' WITH (isstrict,iscachable); + +CREATE FUNCTION index(ltree,ltree,int4) +RETURNS int4 +AS 'MODULE_PATHNAME', 'ltree_index' +LANGUAGE 'C' WITH (isstrict,iscachable); + CREATE FUNCTION nlevel(ltree) RETURNS int4 AS 'MODULE_PATHNAME' LANGUAGE 'C' WITH (isstrict,iscachable); +CREATE FUNCTION ltree2text(ltree) +RETURNS text +AS 'MODULE_PATHNAME' +LANGUAGE 'C' WITH (isstrict,iscachable); + +CREATE FUNCTION text2ltree(text) +RETURNS ltree +AS 'MODULE_PATHNAME' +LANGUAGE 'C' WITH (isstrict,iscachable); + CREATE FUNCTION lca(_ltree) RETURNS ltree AS 'MODULE_PATHNAME','_lca' diff --git a/contrib/ltree/ltree_op.c b/contrib/ltree/ltree_op.c index 4dcf6f7363..28fcfb7f7e 100644 --- a/contrib/ltree/ltree_op.c +++ b/contrib/ltree/ltree_op.c @@ -19,10 +19,13 @@ PG_FUNCTION_INFO_V1(ltree_isparent); PG_FUNCTION_INFO_V1(ltree_risparent); PG_FUNCTION_INFO_V1(subltree); PG_FUNCTION_INFO_V1(subpath); +PG_FUNCTION_INFO_V1(ltree_index); PG_FUNCTION_INFO_V1(ltree_addltree); PG_FUNCTION_INFO_V1(ltree_addtext); PG_FUNCTION_INFO_V1(ltree_textadd); PG_FUNCTION_INFO_V1(lca); +PG_FUNCTION_INFO_V1(ltree2text); +PG_FUNCTION_INFO_V1(text2ltree); Datum ltree_cmp(PG_FUNCTION_ARGS); Datum ltree_lt(PG_FUNCTION_ARGS); Datum ltree_le(PG_FUNCTION_ARGS); @@ -33,10 +36,13 @@ Datum ltree_gt(PG_FUNCTION_ARGS); Datum nlevel(PG_FUNCTION_ARGS); Datum subltree(PG_FUNCTION_ARGS); Datum subpath(PG_FUNCTION_ARGS); +Datum ltree_index(PG_FUNCTION_ARGS); Datum ltree_addltree(PG_FUNCTION_ARGS); Datum ltree_addtext(PG_FUNCTION_ARGS); Datum ltree_textadd(PG_FUNCTION_ARGS); Datum lca(PG_FUNCTION_ARGS); +Datum ltree2text(PG_FUNCTION_ARGS); +Datum text2ltree(PG_FUNCTION_ARGS); int ltree_compare(const ltree * a, const ltree * b) @@ -317,6 +323,57 @@ ltree_addtext(PG_FUNCTION_ARGS) PG_RETURN_POINTER(r); } +Datum +ltree_index(PG_FUNCTION_ARGS) +{ + ltree *a = PG_GETARG_LTREE(0); + ltree *b = PG_GETARG_LTREE(1); + int start=(fcinfo->nargs == 3) ? PG_GETARG_INT32(2) : 0; + int i,j; + ltree_level *startptr, *aptr, *bptr; + bool found=false; + + if ( start < 0 ) { + if ( -start >= a->numlevel ) + start=0; + else + start = (int)(a->numlevel)+start; + } + + if ( a->numlevel - start < b->numlevel || a->numlevel==0 || b->numlevel==0 ) { + PG_FREE_IF_COPY(a, 0); + PG_FREE_IF_COPY(b, 1); + PG_RETURN_INT32(-1); + } + + startptr=LTREE_FIRST(a); + for(i=0; i<=a->numlevel-b->numlevel; i++) { + if ( i>=start ) { + aptr=startptr; + bptr=LTREE_FIRST(b); + for(j=0;jnumlevel;j++) { + if ( !(aptr->len==bptr->len && strncmp(aptr->name,bptr->name, aptr->len)==0) ) + break; + aptr=LEVEL_NEXT(aptr); + bptr=LEVEL_NEXT(bptr); + } + + if ( j==b->numlevel ) { + found=true; + break; + } + } + startptr=LEVEL_NEXT(startptr); + } + + if ( !found ) + i=-1; + + PG_FREE_IF_COPY(a, 0); + PG_FREE_IF_COPY(b, 1); + PG_RETURN_INT32(i); +} + Datum ltree_textadd(PG_FUNCTION_ARGS) { @@ -431,3 +488,55 @@ lca(PG_FUNCTION_ARGS) else PG_RETURN_NULL(); } + +Datum +text2ltree(PG_FUNCTION_ARGS) +{ + text *in = PG_GETARG_TEXT_P(0); + char *s = (char *) palloc(VARSIZE(in) - VARHDRSZ + 1); + ltree *out; + + memcpy(s, VARDATA(in), VARSIZE(in) - VARHDRSZ); + s[VARSIZE(in) - VARHDRSZ] = '\0'; + + out = (ltree *) DatumGetPointer(DirectFunctionCall1( + ltree_in, + PointerGetDatum(s) + )); + pfree(s); + PG_FREE_IF_COPY(in,0); + PG_RETURN_POINTER(out); +} + + +Datum +ltree2text(PG_FUNCTION_ARGS) +{ + ltree *in = PG_GETARG_LTREE(0); + char *ptr; + int i; + ltree_level *curlevel; + text *out; + + out=(text*)palloc(in->len+VARHDRSZ); + ptr = VARDATA(out); + curlevel = LTREE_FIRST(in); + for (i = 0; i < in->numlevel; i++) { + if (i != 0) { + *ptr = '.'; + ptr++; + } + memcpy(ptr, curlevel->name, curlevel->len); + ptr += curlevel->len; + curlevel = LEVEL_NEXT(curlevel); + } + + VARATT_SIZEP(out) = VARHDRSZ + (ptr-VARDATA(out)); + PG_FREE_IF_COPY(in, 0); + + PG_RETURN_POINTER(out); +} + + + + diff --git a/contrib/ltree/sql/ltree.sql b/contrib/ltree/sql/ltree.sql index 9e8f485f1e..8d28c9e53e 100644 --- a/contrib/ltree/sql/ltree.sql +++ b/contrib/ltree/sql/ltree.sql @@ -7,6 +7,9 @@ SELECT '1'::ltree; SELECT '1.2'::ltree; SELECT '1.2._3'::ltree; +SELECT ltree2text('1.2.3.34.sdf'); +SELECT text2ltree('1.2.3.34.sdf'); + SELECT subltree('Top.Child1.Child2',1,2); SELECT subpath('Top.Child1.Child2',1,2); SELECT subpath('Top.Child1.Child2',-1,1); @@ -17,6 +20,27 @@ SELECT subpath('Top.Child1.Child2',1,0); SELECT subpath('Top.Child1.Child2',0); SELECT subpath('Top.Child1.Child2',1); + +SELECT index('1.2.3.4.5.6','1.2'); +SELECT index('a.1.2.3.4.5.6','1.2'); +SELECT index('a.1.2.3.4.5.6','1.2.3'); +SELECT index('a.1.2.3.4.5.6','1.2.3.j'); +SELECT index('a.1.2.3.4.5.6','1.2.3.j.4.5.5.5.5.5.5'); +SELECT index('a.1.2.3.4.5.6','1.2.3'); +SELECT index('a.1.2.3.4.5.6','6'); +SELECT index('a.1.2.3.4.5.6','6.1'); +SELECT index('a.1.2.3.4.5.6','5.6'); +SELECT index('0.1.2.3.5.4.5.6','5.6'); +SELECT index('0.1.2.3.5.4.5.6.8.5.6.8','5.6',3); +SELECT index('0.1.2.3.5.4.5.6.8.5.6.8','5.6',6); +SELECT index('0.1.2.3.5.4.5.6.8.5.6.8','5.6',7); +SELECT index('0.1.2.3.5.4.5.6.8.5.6.8','5.6',-7); +SELECT index('0.1.2.3.5.4.5.6.8.5.6.8','5.6',-4); +SELECT index('0.1.2.3.5.4.5.6.8.5.6.8','5.6',-3); +SELECT index('0.1.2.3.5.4.5.6.8.5.6.8','5.6',-2); +SELECT index('0.1.2.3.5.4.5.6.8.5.6.8','5.6',-20000); + + SELECT 'Top.Child1.Child2'::ltree || 'Child3'::text; SELECT 'Top.Child1.Child2'::ltree || 'Child3'::ltree; SELECT 'Top_0'::ltree || 'Top.Child1.Child2'::ltree;