Common subdirectories: contrib/ltree.old/CVS and contrib/ltree/CVS diff -c contrib/ltree.old/README.ltree contrib/ltree/README.ltree *** contrib/ltree.old/README.ltree Tue Jul 30 20:40:34 2002 --- contrib/ltree/README.ltree Wed Jul 31 21:38:07 2002 *************** *** 4,10 **** types, indexed access methods and queries for data organized as a tree-like structures. This module will works for PostgreSQL version 7.3. ! (patch for 7.2 version is provided, see INSTALLATION) ------------------------------------------------------------------------------- All work was done by Teodor Sigaev (teodor@stack.net) and Oleg Bartunov (oleg@sai.msu.su). See http://www.sai.msu.su/~megera/postgres/gist for --- 4,10 ---- types, indexed access methods and queries for data organized as a tree-like structures. This module will works for PostgreSQL version 7.3. ! (version for 7.2 version is available from http://www.sai.msu.su/~megera/postgres/gist/ltree/ltree-7.2.tar.gz) ------------------------------------------------------------------------------- All work was done by Teodor Sigaev (teodor@stack.net) and Oleg Bartunov (oleg@sai.msu.su). See http://www.sai.msu.su/~megera/postgres/gist for *************** *** 184,192 **** nlevel -------- 3 ! ! Note, that arguments start, end, OFFSET, LEN have meaning of level of the node ! ! INSTALLATION --- 184,204 ---- nlevel -------- 3 ! Note, that arguments start, end, OFFSET, LEN have meaning of level of the ! node ! ! ! ltree lca(ltree,ltree,...) (up to 8 arguments) ! ltree lca(ltree[]) ! Returns Lowest Common Ancestor (lca) ! # select lca('1.2.2.3','1.2.3.4.5.6'); ! lca ! ----- ! 1.2 ! # select lca('{la.2.3,1.2.3.4.5.6}') is null; ! ?column? ! ---------- ! f ! INSTALLATION *************** *** 195,202 **** make install make installcheck - for 7.2 one needs to apply patch ( patch < patch.72) before installation ! - EXAMPLE OF USAGE createdb ltreetest --- 207,212 ---- *************** *** 416,421 **** --- 426,436 ---- CHANGES + July 31, 2002 + Now works on 64-bit platforms. + Added function lca - lowest common ancestor + Version for 7.2 is distributed as separate package - + http://www.sai.msu.su/~megera/postgres/gist/ltree/ltree-7.2.tar.gz July 13, 2002 Initial release. diff -c contrib/ltree.old/_ltree_op.c contrib/ltree/_ltree_op.c *** contrib/ltree.old/_ltree_op.c Tue Jul 30 20:40:34 2002 --- contrib/ltree/_ltree_op.c Wed Jul 31 21:31:47 2002 *************** *** 28,33 **** --- 28,35 ---- Datum _ltq_extract_regex(PG_FUNCTION_ARGS); Datum _ltxtq_extract_exec(PG_FUNCTION_ARGS); + PG_FUNCTION_INFO_V1(_lca); + Datum _lca(PG_FUNCTION_ARGS); typedef Datum (*PGCALL2)(PG_FUNCTION_ARGS); #define NEXTVAL(x) ( (ltree*)( (char*)(x) + INTALIGN( VARSIZE(x) ) ) ) *************** *** 208,212 **** --- 210,238 ---- PG_FREE_IF_COPY(la,0); PG_FREE_IF_COPY(query,1); PG_RETURN_POINTER(item); + } + + Datum + _lca(PG_FUNCTION_ARGS) { + ArrayType *la = (ArrayType *)DatumGetPointer(PG_DETOAST_DATUM(PG_GETARG_DATUM(0))); + int num=ArrayGetNItems( ARR_NDIM(la), ARR_DIMS(la)); + ltree *item = (ltree*)ARR_DATA_PTR(la); + ltree **a,*res; + + a=(ltree**)palloc( sizeof(ltree*) * num ); + while( num>0 ) { + num--; + a[num] = item; + item = NEXTVAL(item); + } + res = lca_inner(a, ArrayGetNItems( ARR_NDIM(la), ARR_DIMS(la))); + pfree(a); + + PG_FREE_IF_COPY(la,0); + + if ( res ) + PG_RETURN_POINTER(res); + else + PG_RETURN_NULL(); } Common subdirectories: contrib/ltree.old/data and contrib/ltree/data Common subdirectories: contrib/ltree.old/expected and contrib/ltree/expected diff -c contrib/ltree.old/ltree.h contrib/ltree/ltree.h *** contrib/ltree.old/ltree.h Tue Jul 30 20:40:34 2002 --- contrib/ltree/ltree.h Wed Jul 31 21:31:47 2002 *************** *** 12,18 **** } ltree_level; #define LEVEL_HDRSIZE (sizeof(uint8)) ! #define LEVEL_NEXT(x) ( (ltree_level*)( ((char*)(x)) + ((ltree_level*)(x))->len + LEVEL_HDRSIZE ) ) typedef struct { int32 len; --- 12,18 ---- } ltree_level; #define LEVEL_HDRSIZE (sizeof(uint8)) ! #define LEVEL_NEXT(x) ( (ltree_level*)( ((char*)(x)) + MAXALIGN(((ltree_level*)(x))->len + LEVEL_HDRSIZE) ) ) typedef struct { int32 len; *************** *** 20,27 **** char data[1]; } ltree; ! #define LTREE_HDRSIZE ( sizeof(int32) + sizeof(uint16) ) ! #define LTREE_FIRST(x) ( (ltree_level*)( ((ltree*)(x))->data ) ) /* lquery */ --- 20,27 ---- char data[1]; } ltree; ! #define LTREE_HDRSIZE MAXALIGN( sizeof(int32) + sizeof(uint16) ) ! #define LTREE_FIRST(x) ( (ltree_level*)( ((char*)(x))+LTREE_HDRSIZE ) ) /* lquery */ *************** *** 33,40 **** char name[1]; } lquery_variant; ! #define LVAR_HDRSIZE (sizeof(uint8)*2 + sizeof(int4)) ! #define LVAR_NEXT(x) ( (lquery_variant*)( ((char*)(x)) + ((lquery_variant*)(x))->len + LVAR_HDRSIZE ) ) #define LVAR_ANYEND 0x01 #define LVAR_INCASE 0x02 --- 33,40 ---- char name[1]; } lquery_variant; ! #define LVAR_HDRSIZE MAXALIGN(sizeof(uint8)*2 + sizeof(int4)) ! #define LVAR_NEXT(x) ( (lquery_variant*)( ((char*)(x)) + MAXALIGN(((lquery_variant*)(x))->len) + LVAR_HDRSIZE ) ) #define LVAR_ANYEND 0x01 #define LVAR_INCASE 0x02 *************** *** 49,57 **** char variants[1]; } lquery_level; ! #define LQL_HDRSIZE ( sizeof(uint16)*5 ) ! #define LQL_NEXT(x) ( (lquery_level*)( ((char*)(x)) + ((lquery_level*)(x))->totallen ) ) ! #define LQL_FIRST(x) ( (lquery_variant*)( ((lquery_level*)(x))->variants ) ) #define LQL_NOT 0x10 #ifdef LOWER_NODE --- 49,57 ---- char variants[1]; } lquery_level; ! #define LQL_HDRSIZE MAXALIGN( sizeof(uint16)*5 ) ! #define LQL_NEXT(x) ( (lquery_level*)( ((char*)(x)) + MAXALIGN(((lquery_level*)(x))->totallen) ) ) ! #define LQL_FIRST(x) ( (lquery_variant*)( ((char*)(x))+LQL_HDRSIZE ) ) #define LQL_NOT 0x10 #ifdef LOWER_NODE *************** *** 69,76 **** char data[1]; } lquery; ! #define LQUERY_HDRSIZE ( sizeof(int32) + 3*sizeof(uint16) ) ! #define LQUERY_FIRST(x) ( (lquery_level*)( ((lquery*)(x))->data ) ) #define LQUERY_HASNOT 0x01 --- 69,76 ---- char data[1]; } lquery; ! #define LQUERY_HDRSIZE MAXALIGN( sizeof(int32) + 3*sizeof(uint16) ) ! #define LQUERY_FIRST(x) ( (lquery_level*)( ((char*)(x))+LQUERY_HDRSIZE ) ) #define LQUERY_HASNOT 0x01 *************** *** 113,119 **** char data[1]; } ltxtquery; ! #define HDRSIZEQT ( 2*sizeof(int4) ) #define COMPUTESIZE(size,lenofoperand) ( HDRSIZEQT + size * sizeof(ITEM) + lenofoperand ) #define GETQUERY(x) (ITEM*)( (char*)(x)+HDRSIZEQT ) #define GETOPERAND(x) ( (char*)GETQUERY(x) + ((ltxtquery*)x)->size * sizeof(ITEM) ) --- 113,119 ---- char data[1]; } ltxtquery; ! #define HDRSIZEQT MAXALIGN( 2*sizeof(int4) ) #define COMPUTESIZE(size,lenofoperand) ( HDRSIZEQT + size * sizeof(ITEM) + lenofoperand ) #define GETQUERY(x) (ITEM*)( (char*)(x)+HDRSIZEQT ) #define GETOPERAND(x) ( (char*)GETQUERY(x) + ((ltxtquery*)x)->size * sizeof(ITEM) ) *************** *** 159,164 **** --- 159,165 ---- bool inner_isparent(const ltree *c, const ltree *p); bool compare_subnode( ltree_level *t, char *q, int len, int (*cmpptr)(const char *,const char *,size_t), bool anyend ); + ltree* lca_inner(ltree** a, int len); #define PG_GETARG_LTREE(x) ((ltree*)DatumGetPointer(PG_DETOAST_DATUM(PG_GETARG_DATUM(x)))) #define PG_GETARG_LQUERY(x) ((lquery*)DatumGetPointer(PG_DETOAST_DATUM(PG_GETARG_DATUM(x)))) *************** *** 212,225 **** #define LTG_ALLTRUE 0x02 #define LTG_NORIGHT 0x04 ! #define LTG_HDRSIZE ( sizeof(int4) + sizeof(uint32) ) ! #define LTG_SIGN(x) ( (BITVECP)( ((ltree_gist*)(x))->data ) ) ! #define LTG_NODE(x) ( (ltree*)( ((ltree_gist*)(x))->data ) ) #define LTG_ISONENODE(x) ( ((ltree_gist*)(x))->flag & LTG_ONENODE ) #define LTG_ISALLTRUE(x) ( ((ltree_gist*)(x))->flag & LTG_ALLTRUE ) #define LTG_ISNORIGHT(x) ( ((ltree_gist*)(x))->flag & LTG_NORIGHT ) ! #define LTG_LNODE(x) ( (ltree*)( ( (char*)( ((ltree_gist*)(x))->data ) ) + ( LTG_ISALLTRUE(x) ? 0 : SIGLEN ) ) ) ! #define LTG_RENODE(x) ( (ltree*)( ((char*)LTG_LNODE(x)) + LTG_LNODE(x)->len ) ) #define LTG_RNODE(x) ( LTG_ISNORIGHT(x) ? LTG_LNODE(x) : LTG_RENODE(x) ) #define LTG_GETLNODE(x) ( LTG_ISONENODE(x) ? LTG_NODE(x) : LTG_LNODE(x) ) --- 213,226 ---- #define LTG_ALLTRUE 0x02 #define LTG_NORIGHT 0x04 ! #define LTG_HDRSIZE MAXALIGN( sizeof(int4) + sizeof(uint32) ) ! #define LTG_SIGN(x) ( (BITVECP)( ((char*)(x))+LTG_HDRSIZE ) ) ! #define LTG_NODE(x) ( (ltree*)( ((char*)(x))+LTG_HDRSIZE ) ) #define LTG_ISONENODE(x) ( ((ltree_gist*)(x))->flag & LTG_ONENODE ) #define LTG_ISALLTRUE(x) ( ((ltree_gist*)(x))->flag & LTG_ALLTRUE ) #define LTG_ISNORIGHT(x) ( ((ltree_gist*)(x))->flag & LTG_NORIGHT ) ! #define LTG_LNODE(x) ( (ltree*)( ( ((char*)(x))+LTG_HDRSIZE ) + ( LTG_ISALLTRUE(x) ? 0 : SIGLEN ) ) ) ! #define LTG_RENODE(x) ( (ltree*)( ((char*)LTG_LNODE(x)) + LTG_LNODE(x)->len) ) #define LTG_RNODE(x) ( LTG_ISNORIGHT(x) ? LTG_LNODE(x) : LTG_RENODE(x) ) #define LTG_GETLNODE(x) ( LTG_ISONENODE(x) ? LTG_NODE(x) : LTG_LNODE(x) ) diff -c contrib/ltree.old/ltree.sql.in contrib/ltree/ltree.sql.in *** contrib/ltree.old/ltree.sql.in Tue Jul 30 22:48:34 2002 --- contrib/ltree/ltree.sql.in Wed Jul 31 21:31:47 2002 *************** *** 117,122 **** --- 117,162 ---- AS 'MODULE_PATHNAME' LANGUAGE 'c' with (isstrict,iscachable); + CREATE FUNCTION lca(_ltree) + RETURNS ltree + AS 'MODULE_PATHNAME','_lca' + LANGUAGE 'c' with (isstrict,iscachable); + + CREATE FUNCTION lca(ltree,ltree) + RETURNS ltree + AS 'MODULE_PATHNAME' + LANGUAGE 'c' with (isstrict,iscachable); + + CREATE FUNCTION lca(ltree,ltree,ltree) + RETURNS ltree + AS 'MODULE_PATHNAME' + LANGUAGE 'c' with (isstrict,iscachable); + + CREATE FUNCTION lca(ltree,ltree,ltree,ltree) + RETURNS ltree + AS 'MODULE_PATHNAME' + LANGUAGE 'c' with (isstrict,iscachable); + + CREATE FUNCTION lca(ltree,ltree,ltree,ltree,ltree) + RETURNS ltree + AS 'MODULE_PATHNAME' + LANGUAGE 'c' with (isstrict,iscachable); + + CREATE FUNCTION lca(ltree,ltree,ltree,ltree,ltree,ltree) + RETURNS ltree + AS 'MODULE_PATHNAME' + LANGUAGE 'c' with (isstrict,iscachable); + + CREATE FUNCTION lca(ltree,ltree,ltree,ltree,ltree,ltree,ltree) + RETURNS ltree + AS 'MODULE_PATHNAME' + LANGUAGE 'c' with (isstrict,iscachable); + + CREATE FUNCTION lca(ltree,ltree,ltree,ltree,ltree,ltree,ltree,ltree) + RETURNS ltree + AS 'MODULE_PATHNAME' + LANGUAGE 'c' with (isstrict,iscachable); + CREATE FUNCTION ltree_isparent(ltree,ltree) RETURNS bool AS 'MODULE_PATHNAME' diff -c contrib/ltree.old/ltree_io.c contrib/ltree/ltree_io.c *** contrib/ltree.old/ltree_io.c Tue Jul 30 20:40:34 2002 --- contrib/ltree/ltree_io.c Wed Jul 31 21:31:47 2002 *************** *** 61,67 **** if ( lptr->len > 255 ) elog(ERROR,"Name of level is too long (%d, must be < 256) in position %d", lptr->len, lptr->start - buf); ! totallen += lptr->len + LEVEL_HDRSIZE; lptr++; state = LTPRS_WAITNAME; } else if ( !ISALNUM(*ptr) ) --- 61,67 ---- if ( lptr->len > 255 ) elog(ERROR,"Name of level is too long (%d, must be < 256) in position %d", lptr->len, lptr->start - buf); ! totallen += MAXALIGN(lptr->len + LEVEL_HDRSIZE); lptr++; state = LTPRS_WAITNAME; } else if ( !ISALNUM(*ptr) ) *************** *** 76,82 **** if ( lptr->len > 255 ) elog(ERROR,"Name of level is too long (%d, must be < 256) in position %d", lptr->len, lptr->start - buf); ! totallen += lptr->len + LEVEL_HDRSIZE; lptr++; } else if ( ! (state == LTPRS_WAITNAME && lptr == list) ) elog(ERROR,"Unexpected end of line"); --- 76,82 ---- if ( lptr->len > 255 ) elog(ERROR,"Name of level is too long (%d, must be < 256) in position %d", lptr->len, lptr->start - buf); ! totallen += MAXALIGN(lptr->len + LEVEL_HDRSIZE); lptr++; } else if ( ! (state == LTPRS_WAITNAME && lptr == list) ) elog(ERROR,"Unexpected end of line"); *************** *** 94,100 **** } pfree(list); - PG_RETURN_POINTER(result); } --- 94,99 ---- *************** *** 134,140 **** #define LQPRS_WAITVAR 8 ! #define GETVAR(x) ( *((nodeitem**)LQL_FIRST(x)) ) Datum lquery_in(PG_FUNCTION_ARGS) { --- 133,141 ---- #define LQPRS_WAITVAR 8 ! #define GETVAR(x) ( *((nodeitem**)LQL_FIRST(x)) ) ! #define ITEMSIZE MAXALIGN(LQL_HDRSIZE+sizeof(nodeitem*)) ! #define NEXTLEV(x) ( (lquery_level*)( ((char*)(x)) + ITEMSIZE) ) Datum lquery_in(PG_FUNCTION_ARGS) { *************** *** 159,166 **** } num++; ! curqlevel = tmpql = (lquery_level*) palloc( ( LQL_HDRSIZE+sizeof(nodeitem*) )*(num) ); ! memset((void*)tmpql,0, ( LQL_HDRSIZE+sizeof(nodeitem*) )*(num) ); ptr=buf; while( *ptr ) { if ( state==LQPRS_WAITLEVEL ) { --- 160,167 ---- } num++; ! curqlevel = tmpql = (lquery_level*) palloc( ITEMSIZE*num ); ! memset((void*)tmpql,0, ITEMSIZE*num ); ptr=buf; while( *ptr ) { if ( state==LQPRS_WAITLEVEL ) { *************** *** 224,230 **** elog(ERROR,"Name of level is too long (%d, must be < 256) in position %d", lptr->len, lptr->start - buf); state = LQPRS_WAITLEVEL; ! curqlevel++; } else if ( ISALNUM(*ptr) ) { if ( lptr->flag ) UNCHAR; --- 225,231 ---- elog(ERROR,"Name of level is too long (%d, must be < 256) in position %d", lptr->len, lptr->start - buf); state = LQPRS_WAITLEVEL; ! curqlevel = NEXTLEV(curqlevel); } else if ( ISALNUM(*ptr) ) { if ( lptr->flag ) UNCHAR; *************** *** 236,242 **** } else if ( *ptr == '.' ) { curqlevel->low=0; curqlevel->high=0xffff; ! curqlevel++; state = LQPRS_WAITLEVEL; } else UNCHAR; --- 237,243 ---- } else if ( *ptr == '.' ) { curqlevel->low=0; curqlevel->high=0xffff; ! curqlevel = NEXTLEV(curqlevel); state = LQPRS_WAITLEVEL; } else UNCHAR; *************** *** 273,279 **** } else if ( state == LQPRS_WAITEND ) { if ( *ptr == '.' ) { state = LQPRS_WAITLEVEL; ! curqlevel++; } else UNCHAR; } else --- 274,280 ---- } else if ( state == LQPRS_WAITEND ) { if ( *ptr == '.' ) { state = LQPRS_WAITLEVEL; ! curqlevel = NEXTLEV(curqlevel); } else UNCHAR; } else *************** *** 300,318 **** curqlevel = tmpql; totallen = LQUERY_HDRSIZE; ! while( curqlevel-tmpql < num ) { totallen += LQL_HDRSIZE; if ( curqlevel->numvar ) { lptr = GETVAR(curqlevel); while( lptr-GETVAR(curqlevel) < curqlevel->numvar ) { ! totallen += LVAR_HDRSIZE + lptr->len; lptr++; } } else if ( curqlevel->low > curqlevel->high ) elog(ERROR,"Low limit(%d) is greater than upper(%d)",curqlevel->low,curqlevel->high ); ! curqlevel++; } ! result = (lquery*)palloc( totallen ); result->len = totallen; result->numlevel = num; --- 301,319 ---- curqlevel = tmpql; totallen = LQUERY_HDRSIZE; ! while( (char*)curqlevel-(char*)tmpql < num*ITEMSIZE ) { totallen += LQL_HDRSIZE; if ( curqlevel->numvar ) { lptr = GETVAR(curqlevel); while( lptr-GETVAR(curqlevel) < curqlevel->numvar ) { ! totallen += MAXALIGN(LVAR_HDRSIZE + lptr->len); lptr++; } } else if ( curqlevel->low > curqlevel->high ) elog(ERROR,"Low limit(%d) is greater than upper(%d)",curqlevel->low,curqlevel->high ); ! curqlevel = NEXTLEV(curqlevel); } ! result = (lquery*)palloc( totallen ); result->len = totallen; result->numlevel = num; *************** *** 322,335 **** result->flag |= LQUERY_HASNOT; cur = LQUERY_FIRST(result); curqlevel = tmpql; ! while( curqlevel-tmpql < num ) { memcpy(cur,curqlevel,LQL_HDRSIZE); cur->totallen=LQL_HDRSIZE; if ( curqlevel->numvar ) { lrptr = LQL_FIRST(cur); lptr = GETVAR(curqlevel); while( lptr-GETVAR(curqlevel) < curqlevel->numvar ) { ! cur->totallen += LVAR_HDRSIZE + lptr->len; lrptr->len = lptr->len; lrptr->flag = lptr->flag; lrptr->val = crc32_sz((uint8 *) lptr->start, lptr->len); --- 323,336 ---- result->flag |= LQUERY_HASNOT; cur = LQUERY_FIRST(result); curqlevel = tmpql; ! while( (char*)curqlevel-(char*)tmpql < num*ITEMSIZE ) { memcpy(cur,curqlevel,LQL_HDRSIZE); cur->totallen=LQL_HDRSIZE; if ( curqlevel->numvar ) { lrptr = LQL_FIRST(cur); lptr = GETVAR(curqlevel); while( lptr-GETVAR(curqlevel) < curqlevel->numvar ) { ! cur->totallen += MAXALIGN(LVAR_HDRSIZE + lptr->len); lrptr->len = lptr->len; lrptr->flag = lptr->flag; lrptr->val = crc32_sz((uint8 *) lptr->start, lptr->len); *************** *** 344,350 **** (result->firstgood)++; } else wasbad=true; ! curqlevel++; cur = LQL_NEXT(cur); } --- 345,351 ---- (result->firstgood)++; } else wasbad=true; ! curqlevel = NEXTLEV(curqlevel); cur = LQL_NEXT(cur); } diff -c contrib/ltree.old/ltree_op.c contrib/ltree/ltree_op.c *** contrib/ltree.old/ltree_op.c Tue Jul 30 20:40:34 2002 --- contrib/ltree/ltree_op.c Wed Jul 31 21:31:47 2002 *************** *** 22,27 **** --- 22,28 ---- PG_FUNCTION_INFO_V1(ltree_addltree); PG_FUNCTION_INFO_V1(ltree_addtext); PG_FUNCTION_INFO_V1(ltree_textadd); + PG_FUNCTION_INFO_V1(lca); Datum ltree_cmp(PG_FUNCTION_ARGS); Datum ltree_lt(PG_FUNCTION_ARGS); Datum ltree_le(PG_FUNCTION_ARGS); *************** *** 35,40 **** --- 36,42 ---- Datum ltree_addltree(PG_FUNCTION_ARGS); Datum ltree_addtext(PG_FUNCTION_ARGS); Datum ltree_textadd(PG_FUNCTION_ARGS); + Datum lca(PG_FUNCTION_ARGS); int ltree_compare(const ltree *a, const ltree *b) { *************** *** 308,310 **** --- 310,388 ---- PG_FREE_IF_COPY(b,0); PG_RETURN_POINTER(r); } + + ltree* + lca_inner(ltree** a, int len) { + int tmp,num=( (*a)->numlevel ) ? (*a)->numlevel-1 : 0; + ltree **ptr=a+1; + int i,reslen=LTREE_HDRSIZE; + ltree_level *l1, *l2; + ltree *res; + + + if ( (*a)->numlevel == 0 ) + return NULL; + + while( ptr-a < len ) { + if ( (*ptr)->numlevel == 0 ) + return NULL; + else if ( (*ptr)->numlevel == 1 ) + num=0; + else { + l1 = LTREE_FIRST(*a); + l2 = LTREE_FIRST(*ptr); + tmp=num; num=0; + for(i=0;inumlevel-1); i++) { + if ( l1->len == l2->len && strncmp(l1->name,l2->name,l1->len) == 0 ) + num=i+1; + else + break; + l1=LEVEL_NEXT(l1); + l2=LEVEL_NEXT(l2); + } + } + ptr++; + } + + l1 = LTREE_FIRST(*a); + for(i=0;ilen + LEVEL_HDRSIZE); + l1=LEVEL_NEXT(l1); + } + + res=(ltree*)palloc( reslen ); + res->len = reslen; + res->numlevel = num; + + l1 = LTREE_FIRST(*a); + l2 = LTREE_FIRST(res); + + for(i=0;ilen + LEVEL_HDRSIZE)); + l1=LEVEL_NEXT(l1); + l2=LEVEL_NEXT(l2); + } + + return res; + } + + Datum + lca(PG_FUNCTION_ARGS) { + int i; + ltree **a,*res; + + a=(ltree**)palloc( sizeof(ltree*) * fcinfo->nargs ); + for(i=0;inargs;i++) + a[i] = PG_GETARG_LTREE(i); + res = lca_inner(a, (int) fcinfo->nargs); + for(i=0;inargs;i++) + PG_FREE_IF_COPY(a[i],i); + pfree(a); + + if ( res ) + PG_RETURN_POINTER(res); + else + PG_RETURN_NULL(); + } + + Only in contrib/ltree.old: patch.72 Common subdirectories: contrib/ltree.old/sql and contrib/ltree/sql