summaryrefslogtreecommitdiff
path: root/contrib/ltree/ltree_op.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/ltree/ltree_op.c')
-rw-r--r--contrib/ltree/ltree_op.c310
1 files changed, 310 insertions, 0 deletions
diff --git a/contrib/ltree/ltree_op.c b/contrib/ltree/ltree_op.c
new file mode 100644
index 0000000000..6d504713e5
--- /dev/null
+++ b/contrib/ltree/ltree_op.c
@@ -0,0 +1,310 @@
+/*
+ * op function for ltree
+ * Teodor Sigaev <teodor@stack.net>
+ */
+
+#include "ltree.h"
+#include <ctype.h>
+
+/* compare functions */
+PG_FUNCTION_INFO_V1(ltree_cmp);
+PG_FUNCTION_INFO_V1(ltree_lt);
+PG_FUNCTION_INFO_V1(ltree_le);
+PG_FUNCTION_INFO_V1(ltree_eq);
+PG_FUNCTION_INFO_V1(ltree_ne);
+PG_FUNCTION_INFO_V1(ltree_ge);
+PG_FUNCTION_INFO_V1(ltree_gt);
+PG_FUNCTION_INFO_V1(nlevel);
+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_addltree);
+PG_FUNCTION_INFO_V1(ltree_addtext);
+PG_FUNCTION_INFO_V1(ltree_textadd);
+Datum ltree_cmp(PG_FUNCTION_ARGS);
+Datum ltree_lt(PG_FUNCTION_ARGS);
+Datum ltree_le(PG_FUNCTION_ARGS);
+Datum ltree_eq(PG_FUNCTION_ARGS);
+Datum ltree_ne(PG_FUNCTION_ARGS);
+Datum ltree_ge(PG_FUNCTION_ARGS);
+Datum ltree_gt(PG_FUNCTION_ARGS);
+Datum nlevel(PG_FUNCTION_ARGS);
+Datum subltree(PG_FUNCTION_ARGS);
+Datum subpath(PG_FUNCTION_ARGS);
+Datum ltree_addltree(PG_FUNCTION_ARGS);
+Datum ltree_addtext(PG_FUNCTION_ARGS);
+Datum ltree_textadd(PG_FUNCTION_ARGS);
+
+int
+ltree_compare(const ltree *a, const ltree *b) {
+ ltree_level *al = LTREE_FIRST(a);
+ ltree_level *bl = LTREE_FIRST(b);
+ int an = a->numlevel;
+ int bn = b->numlevel;
+ int res = 0;
+
+ while( an>0 && bn>0 ) {
+ if ( (res = strncmp( al->name, bl->name, min(al->len, bl->len))) == 0 ) {
+ if ( al->len != bl->len )
+ return (al->len - bl->len)*10*(an+1);
+ } else
+ return res*10*(an+1);
+
+ an--; bn--;
+ al = LEVEL_NEXT(al);
+ bl = LEVEL_NEXT(bl);
+ }
+
+ return (a->numlevel - b->numlevel)*10*(an+1);
+}
+
+#define RUNCMP \
+ltree *a = PG_GETARG_LTREE(0); \
+ltree *b = PG_GETARG_LTREE(1); \
+int res = ltree_compare(a,b); \
+PG_FREE_IF_COPY(a,0); \
+PG_FREE_IF_COPY(b,1); \
+
+Datum
+ltree_cmp(PG_FUNCTION_ARGS) {
+ RUNCMP
+ PG_RETURN_INT32(res);
+}
+
+Datum
+ltree_lt(PG_FUNCTION_ARGS) {
+ RUNCMP
+ PG_RETURN_BOOL( (res<0) ? true : false );
+}
+
+Datum
+ltree_le(PG_FUNCTION_ARGS) {
+ RUNCMP
+ PG_RETURN_BOOL( (res<=0) ? true : false );
+}
+
+Datum
+ltree_eq(PG_FUNCTION_ARGS) {
+ RUNCMP
+ PG_RETURN_BOOL( (res==0) ? true : false );
+}
+
+Datum
+ltree_ge(PG_FUNCTION_ARGS) {
+ RUNCMP
+ PG_RETURN_BOOL( (res>=0) ? true : false );
+}
+
+Datum
+ltree_gt(PG_FUNCTION_ARGS) {
+ RUNCMP
+ PG_RETURN_BOOL( (res>0) ? true : false );
+}
+
+Datum
+ltree_ne(PG_FUNCTION_ARGS) {
+ RUNCMP
+ PG_RETURN_BOOL( (res!=0) ? true : false );
+}
+
+Datum
+nlevel(PG_FUNCTION_ARGS) {
+ ltree *a = PG_GETARG_LTREE(0);
+ int res = a->numlevel;
+ PG_FREE_IF_COPY(a,0);
+ PG_RETURN_INT32(res);
+}
+
+bool
+inner_isparent(const ltree *c, const ltree *p) {
+ ltree_level *cl = LTREE_FIRST(c);
+ ltree_level *pl = LTREE_FIRST(p);
+ int pn = p->numlevel;
+
+ if ( pn > c->numlevel )
+ return false;
+
+ while( pn>0 ) {
+ if ( cl->len != pl->len )
+ return false;
+ if ( strncmp( cl->name, pl->name, cl->len ) )
+ return false;
+
+ pn--;
+ cl = LEVEL_NEXT(cl);
+ pl = LEVEL_NEXT(pl);
+ }
+ return true;
+}
+
+Datum
+ltree_isparent(PG_FUNCTION_ARGS) {
+ ltree *c = PG_GETARG_LTREE(1);
+ ltree *p = PG_GETARG_LTREE(0);
+ bool res = inner_isparent(c,p);
+ PG_FREE_IF_COPY(c,1);
+ PG_FREE_IF_COPY(p,0);
+ PG_RETURN_BOOL( res );
+}
+
+Datum
+ltree_risparent(PG_FUNCTION_ARGS) {
+ ltree *c = PG_GETARG_LTREE(0);
+ ltree *p = PG_GETARG_LTREE(1);
+ bool res = inner_isparent(c,p);
+ PG_FREE_IF_COPY(c,0);
+ PG_FREE_IF_COPY(p,1);
+ PG_RETURN_BOOL( res );
+}
+
+
+static ltree*
+inner_subltree(ltree *t, int4 startpos, int4 endpos) {
+ char *start=NULL,*end=NULL;
+ ltree_level *ptr = LTREE_FIRST(t);
+ ltree *res;
+ int i;
+
+ if ( startpos <0 || endpos <0 || startpos>=t->numlevel || startpos >= endpos )
+ elog(ERROR,"Wrong positions");
+
+ if ( endpos > t->numlevel )
+ endpos = t->numlevel;
+
+ for(i=0;i<endpos ;i++) {
+ if ( i==startpos )
+ start = (char*)ptr;
+ if ( i==endpos-1 ) {
+ end = (char*)LEVEL_NEXT(ptr);
+ break;
+ }
+ ptr = LEVEL_NEXT(ptr);
+ }
+
+ res=(ltree*)palloc( LTREE_HDRSIZE + (end-start) );
+ res->len = LTREE_HDRSIZE + (end-start);
+ res->numlevel = endpos-startpos;
+
+ memcpy( LTREE_FIRST(res), start, end-start);
+
+ return res;
+}
+
+Datum
+subltree(PG_FUNCTION_ARGS) {
+ ltree *t = PG_GETARG_LTREE(0);
+ ltree *res = inner_subltree(t,PG_GETARG_INT32(1),PG_GETARG_INT32(2));
+
+ PG_FREE_IF_COPY(t,0);
+ PG_RETURN_POINTER(res);
+}
+
+Datum
+subpath(PG_FUNCTION_ARGS) {
+ ltree *t = PG_GETARG_LTREE(0);
+ int4 start = PG_GETARG_INT32(1);
+ int4 len = ( fcinfo->nargs==3 ) ? PG_GETARG_INT32(2) : 0;
+ int4 end;
+ ltree *res;
+
+ end = start+len;
+
+ if ( start < 0 ) {
+ start = t->numlevel + start;
+ end = start+len;
+ }
+ if ( start < 0 ) { /* start > t->numlevel */
+ start = t->numlevel + start;
+ end = start+len;
+ }
+
+ if ( len < 0 )
+ end = t->numlevel + len;
+ else if ( len == 0 )
+ end = 0xffff;
+
+ res = inner_subltree(t,start,end);
+
+ PG_FREE_IF_COPY(t,0);
+ PG_RETURN_POINTER(res);
+}
+
+static ltree*
+ltree_concat( ltree *a, ltree *b) {
+ ltree *r;
+ r=(ltree*)palloc( a->len + b->len - LTREE_HDRSIZE);
+ r->len = a->len + b->len - LTREE_HDRSIZE;
+ r->numlevel = a->numlevel + b->numlevel;
+
+ memcpy( LTREE_FIRST(r), LTREE_FIRST(a), a->len - LTREE_HDRSIZE);
+ memcpy( ((char*)LTREE_FIRST(r))+ a->len - LTREE_HDRSIZE, LTREE_FIRST(b), b->len -
+ LTREE_HDRSIZE);
+ return r;
+}
+
+Datum
+ltree_addltree(PG_FUNCTION_ARGS) {
+ ltree *a = PG_GETARG_LTREE(0);
+ ltree *b = PG_GETARG_LTREE(1);
+ ltree *r;
+
+ r = ltree_concat(a, b);
+ PG_FREE_IF_COPY(a,0);
+ PG_FREE_IF_COPY(b,1);
+ PG_RETURN_POINTER(r);
+}
+
+Datum
+ltree_addtext(PG_FUNCTION_ARGS) {
+ ltree *a = PG_GETARG_LTREE(0);
+ text *b = PG_GETARG_TEXT_P(1);
+ char *s;
+ ltree *r,*tmp;
+
+ s = (char*)palloc( VARSIZE(b) - VARHDRSZ+1 );
+ memcpy(s, VARDATA(b), VARSIZE(b) - VARHDRSZ );
+ s[VARSIZE(b) - VARHDRSZ] = '\0';
+
+ tmp = (ltree*)DatumGetPointer( DirectFunctionCall1(
+ ltree_in,
+ PointerGetDatum(s)
+ ) );
+
+ pfree(s);
+
+ r = ltree_concat(a,tmp);
+
+ pfree( tmp );
+
+ PG_FREE_IF_COPY(a,0);
+ PG_FREE_IF_COPY(b,1);
+ PG_RETURN_POINTER(r);
+}
+
+Datum
+ltree_textadd(PG_FUNCTION_ARGS) {
+ ltree *a = PG_GETARG_LTREE(1);
+ text *b = PG_GETARG_TEXT_P(0);
+ char *s;
+ ltree *r,*tmp;
+
+ s = (char*)palloc( VARSIZE(b) - VARHDRSZ + 1 );
+ memcpy(s, VARDATA(b), VARSIZE(b) - VARHDRSZ );
+ s[VARSIZE(b) - VARHDRSZ] = '\0';
+
+ tmp = (ltree*)DatumGetPointer( DirectFunctionCall1(
+ ltree_in,
+ PointerGetDatum(s)
+ ) );
+
+ pfree(s);
+
+ r = ltree_concat(tmp,a);
+
+ pfree( tmp );
+
+ PG_FREE_IF_COPY(a,1);
+ PG_FREE_IF_COPY(b,0);
+ PG_RETURN_POINTER(r);
+}