postgresql/contrib/ltree/ltree_io.c

434 lines
11 KiB
C

/*
* 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);
}