diff options
Diffstat (limited to 'contrib/ltree/ltree_io.c')
-rw-r--r-- | contrib/ltree/ltree_io.c | 433 |
1 files changed, 433 insertions, 0 deletions
diff --git a/contrib/ltree/ltree_io.c b/contrib/ltree/ltree_io.c new file mode 100644 index 00000000000..845e61eaa77 --- /dev/null +++ b/contrib/ltree/ltree_io.c @@ -0,0 +1,433 @@ +/* + * in/out function for ltree and lquery + * Teodor Sigaev <teodor@stack.net> + */ + +#include "ltree.h" +#include <ctype.h> +#include "crc32.h" + +PG_FUNCTION_INFO_V1(ltree_in); +Datum ltree_in(PG_FUNCTION_ARGS); +PG_FUNCTION_INFO_V1(ltree_out); +Datum ltree_out(PG_FUNCTION_ARGS); + +PG_FUNCTION_INFO_V1(lquery_in); +Datum lquery_in(PG_FUNCTION_ARGS); +PG_FUNCTION_INFO_V1(lquery_out); +Datum lquery_out(PG_FUNCTION_ARGS); + + +#define UNCHAR elog(ERROR,"Syntax error in position %d near '%c'", ptr-buf, *ptr) + +typedef struct { + char* start; + int len; + int flag; +} nodeitem; + +#define LTPRS_WAITNAME 0 +#define LTPRS_WAITDELIM 1 + +Datum +ltree_in(PG_FUNCTION_ARGS) { + char *buf = (char *) PG_GETARG_POINTER(0); + char *ptr; + nodeitem *list, *lptr; + int num=0, totallen = 0; + int state = LTPRS_WAITNAME; + ltree *result; + ltree_level *curlevel; + + ptr=buf; + while( *ptr ) { + if ( *ptr == '.' ) + num++; + ptr++; + } + + list = lptr = (nodeitem*) palloc( sizeof(nodeitem)*(num+1) ); + ptr=buf; + while( *ptr ) { + if ( state == LTPRS_WAITNAME ) { + if ( ISALNUM(*ptr) ) { + lptr->start = ptr; + state = LTPRS_WAITDELIM; + } else + UNCHAR; + } else if ( state == LTPRS_WAITDELIM ) { + if ( *ptr == '.' ) { + lptr->len = ptr - lptr->start; + 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) ) + UNCHAR; + } else + elog(ERROR,"Inner error in parser"); + ptr++; + } + + if ( state == LTPRS_WAITDELIM ) { + lptr->len = ptr - lptr->start; + 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"); + + result = (ltree*)palloc( LTREE_HDRSIZE + totallen ); + result->len = LTREE_HDRSIZE + totallen; + result->numlevel = lptr-list; + curlevel = LTREE_FIRST(result); + lptr=list; + while( lptr-list < result->numlevel ) { + curlevel->len = (uint8) lptr->len; + memcpy( curlevel->name, lptr->start, lptr->len); + curlevel = LEVEL_NEXT(curlevel); + lptr++; + } + + pfree(list); + + PG_RETURN_POINTER(result); +} + +Datum +ltree_out(PG_FUNCTION_ARGS) { + ltree *in = PG_GETARG_LTREE(0); + char *buf,*ptr; + int i; + ltree_level *curlevel; + + ptr = buf = (char*)palloc( in->len ); + 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); + } + + *ptr='\0'; + PG_FREE_IF_COPY(in,0); + + PG_RETURN_POINTER(buf); +} + +#define LQPRS_WAITLEVEL 0 +#define LQPRS_WAITDELIM 1 +#define LQPRS_WAITOPEN 2 +#define LQPRS_WAITFNUM 3 +#define LQPRS_WAITSNUM 4 +#define LQPRS_WAITND 5 +#define LQPRS_WAITCLOSE 6 +#define LQPRS_WAITEND 7 +#define LQPRS_WAITVAR 8 + + +#define GETVAR(x) ( *((nodeitem**)LQL_FIRST(x)) ) + +Datum +lquery_in(PG_FUNCTION_ARGS) { + char *buf = (char *) PG_GETARG_POINTER(0); + char *ptr; + int num=0, totallen = 0, numOR=0; + int state = LQPRS_WAITLEVEL; + lquery *result; + nodeitem *lptr=NULL; + lquery_level *cur,*curqlevel, *tmpql; + lquery_variant *lrptr=NULL; + bool hasnot=false; + bool wasbad=false; + + ptr=buf; + while( *ptr ) { + if ( *ptr == '.' ) + num++; + else if ( *ptr == '|' ) + numOR++; + ptr++; + } + + 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 ) { + if ( ISALNUM(*ptr) ) { + GETVAR(curqlevel) = lptr = (nodeitem*)palloc( sizeof(nodeitem)*(numOR+1) ); + memset((void*)GETVAR(curqlevel), 0,sizeof(nodeitem)*(numOR+1) ); + lptr->start = ptr; + state = LQPRS_WAITDELIM; + curqlevel->numvar = 1; + } else if ( *ptr == '!' ) { + GETVAR(curqlevel) = lptr = (nodeitem*)palloc( sizeof(nodeitem)*(numOR+1) ); + memset((void*)GETVAR(curqlevel), 0,sizeof(nodeitem)*(numOR+1) ); + lptr->start = ptr+1; + state = LQPRS_WAITDELIM; + curqlevel->numvar = 1; + curqlevel->flag |= LQL_NOT; + hasnot=true; + } else if ( *ptr == '*' ) { + state = LQPRS_WAITOPEN; + } else + UNCHAR; + } else if ( state==LQPRS_WAITVAR ) { + if ( ISALNUM(*ptr) ) { + lptr++; + lptr->start = ptr; + state = LQPRS_WAITDELIM; + curqlevel->numvar++; + } else + UNCHAR; + } else if ( state==LQPRS_WAITDELIM ) { + if ( *ptr == '@' ) { + if ( lptr->start == ptr ) + UNCHAR; + lptr->flag |= LVAR_INCASE; + curqlevel->flag |= LVAR_INCASE; + } else if ( *ptr == '*' ) { + if ( lptr->start == ptr ) + UNCHAR; + lptr->flag |= LVAR_ANYEND; + curqlevel->flag |= LVAR_ANYEND; + } else if ( *ptr == '%' ) { + if ( lptr->start == ptr ) + UNCHAR; + lptr->flag |= LVAR_SUBLEXEM; + curqlevel->flag |= LVAR_SUBLEXEM; + } else if ( *ptr == '|' ) { + lptr->len = ptr - lptr->start - + ( ( lptr->flag & LVAR_SUBLEXEM ) ? 1 : 0 ) - + ( ( lptr->flag & LVAR_INCASE ) ? 1 : 0 ) - + ( ( lptr->flag & LVAR_ANYEND ) ? 1 : 0 ); + if ( lptr->len > 255 ) + elog(ERROR,"Name of level is too long (%d, must be < 256) in position %d", + lptr->len, lptr->start - buf); + state = LQPRS_WAITVAR; + } else if ( *ptr == '.' ) { + lptr->len = ptr - lptr->start - + ( ( lptr->flag & LVAR_SUBLEXEM ) ? 1 : 0 ) - + ( ( lptr->flag & LVAR_INCASE ) ? 1 : 0 ) - + ( ( lptr->flag & LVAR_ANYEND ) ? 1 : 0 ); + if ( lptr->len > 255 ) + 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; + } else + UNCHAR; + } else if ( state == LQPRS_WAITOPEN ) { + if ( *ptr == '{' ) { + state = LQPRS_WAITFNUM; + } else if ( *ptr == '.' ) { + curqlevel->low=0; + curqlevel->high=0xffff; + curqlevel++; + state = LQPRS_WAITLEVEL; + } else + UNCHAR; + } else if ( state == LQPRS_WAITFNUM ) { + if ( *ptr == ',' ) { + state = LQPRS_WAITSNUM; + } else if ( isdigit(*ptr) ) { + curqlevel->low = atoi( ptr ); + state = LQPRS_WAITND; + } else + UNCHAR; + } else if ( state == LQPRS_WAITSNUM ) { + if ( isdigit(*ptr) ) { + curqlevel->high = atoi( ptr ); + state = LQPRS_WAITCLOSE; + } else if ( *ptr == '}' ) { + curqlevel->high = 0xffff; + state = LQPRS_WAITEND; + } else + UNCHAR; + } else if ( state == LQPRS_WAITCLOSE ) { + if ( *ptr == '}' ) + state = LQPRS_WAITEND; + else if ( !isdigit(*ptr) ) + UNCHAR; + } else if ( state == LQPRS_WAITND ) { + if ( *ptr == '}' ) { + curqlevel->high = curqlevel->low; + state = LQPRS_WAITEND; + } else if ( *ptr == ',' ) + state = LQPRS_WAITSNUM; + else if ( !isdigit(*ptr) ) + UNCHAR; + } else if ( state == LQPRS_WAITEND ) { + if ( *ptr == '.' ) { + state = LQPRS_WAITLEVEL; + curqlevel++; + } else + UNCHAR; + } else + elog(ERROR,"Inner error in parser"); + ptr++; + } + + if ( state==LQPRS_WAITDELIM ) { + if ( lptr->start == ptr ) + elog(ERROR,"Unexpected end of line"); + lptr->len = ptr - lptr->start - + ( ( lptr->flag & LVAR_SUBLEXEM ) ? 1 : 0 ) - + ( ( lptr->flag & LVAR_INCASE ) ? 1 : 0 ) - + ( ( lptr->flag & LVAR_ANYEND ) ? 1 : 0 ); + if ( lptr->len==0 ) + elog(ERROR,"Unexpected end of line"); + if ( lptr->len > 255 ) + elog(ERROR,"Name of level is too long (%d, must be < 256) in position %d", + lptr->len, lptr->start - buf); + } else if ( state == LQPRS_WAITOPEN ) { + curqlevel->high = 0xffff; + } else if ( state != LQPRS_WAITEND ) + elog(ERROR,"Unexpected end of line"); + + 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; + result->firstgood = 0; + result->flag=0; + if ( hasnot ) + 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); + memcpy( lrptr->name, lptr->start, lptr->len); + lptr++; + lrptr = LVAR_NEXT( lrptr ); + } + pfree( GETVAR(curqlevel) ); + if ( cur->numvar > 1 || cur->flag != 0 ) + wasbad=true; + else if ( wasbad==false ) + (result->firstgood)++; + } else + wasbad=true; + curqlevel++; + cur = LQL_NEXT(cur); + } + + pfree(tmpql); + PG_RETURN_POINTER(result); +} + +Datum +lquery_out(PG_FUNCTION_ARGS) { + lquery *in = PG_GETARG_LQUERY(0); + char *buf,*ptr; + int i,j,totallen=0; + lquery_level *curqlevel; + lquery_variant *curtlevel; + + curqlevel = LQUERY_FIRST(in); + for(i=0;i<in->numlevel;i++) { + if ( curqlevel->numvar ) + totallen = (curqlevel->numvar*4) + 1 + curqlevel->totallen; + else + totallen = 2*11 + 4; + totallen++; + curqlevel = LQL_NEXT(curqlevel); + } + + + ptr = buf = (char*)palloc( totallen ); + curqlevel = LQUERY_FIRST(in); + for(i=0;i<in->numlevel;i++) { + if ( i!=0 ) { + *ptr = '.'; + ptr++; + } + if ( curqlevel->numvar ) { + if ( curqlevel->flag & LQL_NOT ) { + *ptr = '!'; + ptr++; + } + curtlevel = LQL_FIRST(curqlevel); + for(j=0;j<curqlevel->numvar;j++) { + if ( j!=0 ) { + *ptr = '|'; + ptr++; + } + memcpy( ptr, curtlevel->name, curtlevel->len ); + ptr+=curtlevel->len; + if ( (curtlevel->flag & LVAR_SUBLEXEM) ) { + *ptr = '%'; + ptr++; + } + if ( (curtlevel->flag & LVAR_INCASE) ) { + *ptr = '@'; + ptr++; + } + if ( (curtlevel->flag & LVAR_ANYEND) ) { + *ptr = '*'; + ptr++; + } + curtlevel = LVAR_NEXT(curtlevel); + } + } else { + if ( curqlevel->low == curqlevel->high ) { + sprintf(ptr,"*{%d}",curqlevel->low); + } else if ( curqlevel->low == 0 ) { + if ( curqlevel->high == 0xffff ) { + *ptr='*'; + *(ptr+1)='\0'; + } else + sprintf(ptr,"*{,%d}",curqlevel->high); + } else if ( curqlevel->high == 0xffff ) { + sprintf(ptr,"*{%d,}",curqlevel->low); + } else + sprintf(ptr,"*{%d,%d}", curqlevel->low, curqlevel->high); + ptr = strchr(ptr,'\0'); + } + + curqlevel = LQL_NEXT(curqlevel); + } + + *ptr='\0'; + PG_FREE_IF_COPY(in,0); + + PG_RETURN_POINTER(buf); +} + + |