]> granicus.if.org Git - postgresql/commitdiff
please find attached patch to current CVS ( contrib/ltree )
authorBruce Momjian <bruce@momjian.us>
Sun, 4 Aug 2002 05:02:50 +0000 (05:02 +0000)
committerBruce Momjian <bruce@momjian.us>
Sun, 4 Aug 2002 05:02:50 +0000 (05:02 +0000)
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

Oleg Bartunov

contrib/ltree/README.ltree
contrib/ltree/_ltree_op.c
contrib/ltree/ltree.h
contrib/ltree/ltree.sql.in
contrib/ltree/ltree_io.c
contrib/ltree/ltree_op.c

index 568704b97fb0ebc6120120258a0dfe0662832fdb..b11a27bc30d88b0dff86cb7b7464b0a234752dff 100644 (file)
@@ -4,7 +4,7 @@ ltree - is a PostgreSQL contrib module which contains implementation of data
 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)
+(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,9 +184,21 @@ int4 nlevel
         nlevel 
         --------
           3
-
-Note, that arguments start, end, OFFSET, LEN have meaning of level of the node
-!
+    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,8 +207,6 @@ INSTALLATION
   make install
   make installcheck
 
-for 7.2 one needs to apply patch ( patch < patch.72) before installation !
-
 EXAMPLE OF USAGE
 
  createdb ltreetest
@@ -416,6 +426,11 @@ appreciate your input. So far, below some (rather obvious) results:
 
 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.
 
index 3df2d962d4e453f0a7751a24de797a73e5035293..cc7a16c27e664dda776f2ed7f635940cae015a93 100644 (file)
@@ -28,6 +28,8 @@ Datum _ltree_extract_risparent(PG_FUNCTION_ARGS);
 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) ) ) )
@@ -210,3 +212,27 @@ _ltxtq_extract_exec(PG_FUNCTION_ARGS) {
        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();
+}
+
index 750802d09362ffb5a756fd108bc60a9d5c1e2f38..22134befafcee93e9a9e10b446f59ce9db067b9e 100644 (file)
@@ -12,7 +12,7 @@ typedef struct {
 } ltree_level;
 
 #define LEVEL_HDRSIZE   (sizeof(uint8))
-#define LEVEL_NEXT(x)  ( (ltree_level*)( ((char*)(x)) + ((ltree_level*)(x))->len + LEVEL_HDRSIZE ) )
+#define LEVEL_NEXT(x)  ( (ltree_level*)( ((char*)(x)) + MAXALIGN(((ltree_level*)(x))->len + LEVEL_HDRSIZE) ) )
 
 typedef struct {
        int32   len;
@@ -20,8 +20,8 @@ typedef struct {
        char    data[1];
 } ltree;
 
-#define LTREE_HDRSIZE  ( sizeof(int32) + sizeof(uint16) )
-#define LTREE_FIRST(x) ( (ltree_level*)( ((ltree*)(x))->data ) )
+#define LTREE_HDRSIZE  MAXALIGN( sizeof(int32) + sizeof(uint16) )
+#define LTREE_FIRST(x) ( (ltree_level*)( ((char*)(x))+LTREE_HDRSIZE ) )
 
 
 /* lquery */
@@ -33,8 +33,8 @@ typedef struct {
        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_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,9 +49,9 @@ typedef struct {
        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_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,8 +69,8 @@ typedef struct {
        char    data[1];
 } lquery; 
 
-#define LQUERY_HDRSIZE   ( sizeof(int32) + 3*sizeof(uint16) )
-#define LQUERY_FIRST(x)   ( (lquery_level*)( ((lquery*)(x))->data ) )
+#define LQUERY_HDRSIZE   MAXALIGN( sizeof(int32) + 3*sizeof(uint16) )
+#define LQUERY_FIRST(x)   ( (lquery_level*)( ((char*)(x))+LQUERY_HDRSIZE ) )
 
 #define LQUERY_HASNOT          0x01
 
@@ -113,7 +113,7 @@ typedef struct
         char            data[1];
 }       ltxtquery;
 
-#define HDRSIZEQT       ( 2*sizeof(int4) )
+#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,6 +159,7 @@ int ltree_compare(const ltree *a, const ltree *b);
 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,14 +213,14 @@ typedef struct {
 #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_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*)( ((ltree_gist*)(x))->data ) ) + ( LTG_ISALLTRUE(x) ? 0 : SIGLEN ) ) )
-#define LTG_RENODE(x)  ( (ltree*)( ((char*)LTG_LNODE(x)) + LTG_LNODE(x)->len ) )
+#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) ) 
index abc6d46f52d3b0fb4f885574b693cf4f83a0f86a..4e0f52ea1f6f3d3f8c77e569fe7b97fe1cf44fcc 100644 (file)
@@ -117,6 +117,46 @@ RETURNS int4
 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'
index 845e61eaa779f43684baf35669b6d3c05aba722c..0315dca06b2b359b9eac8f379983ec5ce3c426db 100644 (file)
@@ -61,7 +61,7 @@ ltree_in(PG_FUNCTION_ARGS) {
                                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;
+                               totallen += MAXALIGN(lptr->len + LEVEL_HDRSIZE);
                                lptr++;
                                state = LTPRS_WAITNAME;
                        } else if ( !ISALNUM(*ptr) )
@@ -76,7 +76,7 @@ ltree_in(PG_FUNCTION_ARGS) {
                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;
+               totallen += MAXALIGN(lptr->len + LEVEL_HDRSIZE);
                lptr++;
        } else if ( ! (state == LTPRS_WAITNAME && lptr == list) )
                elog(ERROR,"Unexpected end of line");
@@ -94,7 +94,6 @@ ltree_in(PG_FUNCTION_ARGS) {
        }
 
        pfree(list);
-
        PG_RETURN_POINTER(result);
 }
 
@@ -134,7 +133,9 @@ ltree_out(PG_FUNCTION_ARGS) {
 #define LQPRS_WAITVAR  8
 
 
-#define GETVAR(x) ( *((nodeitem**)LQL_FIRST(x)) )  
+#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,8 +160,8 @@ lquery_in(PG_FUNCTION_ARGS) {
        }
        
        num++;
-       curqlevel = tmpql = (lquery_level*) palloc( ( LQL_HDRSIZE+sizeof(nodeitem*) )*(num) );
-       memset((void*)tmpql,0, ( LQL_HDRSIZE+sizeof(nodeitem*) )*(num) );
+       curqlevel = tmpql = (lquery_level*) palloc( ITEMSIZE*num );
+       memset((void*)tmpql,0, ITEMSIZE*num );
        ptr=buf;
        while( *ptr ) {
                if ( state==LQPRS_WAITLEVEL ) {
@@ -224,7 +225,7 @@ lquery_in(PG_FUNCTION_ARGS) {
                                        elog(ERROR,"Name of level is too long (%d, must be < 256) in position %d", 
                                                lptr->len, lptr->start - buf);
                                state = LQPRS_WAITLEVEL;
-                               curqlevel++;
+                               curqlevel = NEXTLEV(curqlevel);
                        } else if ( ISALNUM(*ptr) ) {
                                if ( lptr->flag )
                                        UNCHAR;
@@ -236,7 +237,7 @@ lquery_in(PG_FUNCTION_ARGS) {
                        } else if ( *ptr == '.' ) {
                                curqlevel->low=0;
                                curqlevel->high=0xffff;
-                               curqlevel++;
+                               curqlevel = NEXTLEV(curqlevel);
                                state = LQPRS_WAITLEVEL;
                        } else
                                UNCHAR;
@@ -273,7 +274,7 @@ lquery_in(PG_FUNCTION_ARGS) {
                } else if ( state == LQPRS_WAITEND ) {
                        if ( *ptr == '.' ) {
                                state = LQPRS_WAITLEVEL;
-                               curqlevel++;
+                               curqlevel = NEXTLEV(curqlevel);
                        } else
                                UNCHAR;
                } else
@@ -300,19 +301,19 @@ lquery_in(PG_FUNCTION_ARGS) {
                 
        curqlevel = tmpql;
        totallen = LQUERY_HDRSIZE; 
-       while( curqlevel-tmpql < num ) {
+       while( (char*)curqlevel-(char*)tmpql < num*ITEMSIZE ) {
                totallen += LQL_HDRSIZE; 
                if ( curqlevel->numvar ) {
                        lptr = GETVAR(curqlevel);
                        while( lptr-GETVAR(curqlevel) < curqlevel->numvar ) {
-                               totallen += LVAR_HDRSIZE + lptr->len;
+                               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++;
+               curqlevel = NEXTLEV(curqlevel);
        }
-       
+
        result = (lquery*)palloc( totallen );
        result->len = totallen;
        result->numlevel = num;
@@ -322,14 +323,14 @@ lquery_in(PG_FUNCTION_ARGS) {
                result->flag |= LQUERY_HASNOT;
        cur = LQUERY_FIRST(result);
        curqlevel = tmpql;
-       while( curqlevel-tmpql < num ) {
+       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 += LVAR_HDRSIZE + lptr->len;
+                               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,7 +345,7 @@ lquery_in(PG_FUNCTION_ARGS) {
                                (result->firstgood)++;   
                } else
                        wasbad=true;
-               curqlevel++;
+               curqlevel = NEXTLEV(curqlevel);
                cur = LQL_NEXT(cur);
        }
 
index 6d504713e5b75dcd93658168a94ab8b09dadad04..b954908b6c54216000417a7688e77a7906a59aa7 100644 (file)
@@ -22,6 +22,7 @@ PG_FUNCTION_INFO_V1(subpath);
 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,6 +36,7 @@ Datum   subpath(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);
 
 int
 ltree_compare(const ltree *a, const ltree *b) {
@@ -308,3 +310,79 @@ ltree_textadd(PG_FUNCTION_ARGS) {
         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;i<min(tmp, (*ptr)->numlevel-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;i<num;i++) {
+               reslen += MAXALIGN(l1->len + 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;i<num;i++) {
+               memcpy(l2,l1, MAXALIGN(l1->len + 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;i<fcinfo->nargs;i++)
+               a[i] = PG_GETARG_LTREE(i);
+       res = lca_inner(a, (int) fcinfo->nargs); 
+       for(i=0;i<fcinfo->nargs;i++)
+               PG_FREE_IF_COPY(a[i],i);
+       pfree(a);
+                
+       if ( res )
+               PG_RETURN_POINTER(res);
+       else
+               PG_RETURN_NULL();
+}
+
+