OSDN Git Service

6fb6d3db9e25665365a51e1389b573a01c37daf7
[pg-rex/syncrep.git] / contrib / ltree / ltree_io.c
1 /*
2  * in/out function for ltree and lquery
3  * Teodor Sigaev <teodor@stack.net>
4  */
5
6 #include "ltree.h"
7 #include <ctype.h>
8 #include "crc32.h" 
9
10 PG_FUNCTION_INFO_V1(ltree_in);
11 Datum ltree_in(PG_FUNCTION_ARGS);
12 PG_FUNCTION_INFO_V1(ltree_out);
13 Datum ltree_out(PG_FUNCTION_ARGS);
14
15 PG_FUNCTION_INFO_V1(lquery_in);
16 Datum lquery_in(PG_FUNCTION_ARGS);
17 PG_FUNCTION_INFO_V1(lquery_out);
18 Datum lquery_out(PG_FUNCTION_ARGS);
19
20
21 #define UNCHAR elog(ERROR,"Syntax error in position %d near '%c'", (int)(ptr-buf), *ptr)
22
23 typedef struct {
24         char*   start;
25         int     len;
26         int     flag;
27 } nodeitem;
28
29 #define LTPRS_WAITNAME  0
30 #define LTPRS_WAITDELIM 1
31
32 Datum 
33 ltree_in(PG_FUNCTION_ARGS) {
34         char    *buf = (char *) PG_GETARG_POINTER(0);
35         char    *ptr;
36         nodeitem *list, *lptr; 
37         int num=0, totallen = 0;
38         int state = LTPRS_WAITNAME;
39         ltree   *result;
40         ltree_level     *curlevel;
41
42         ptr=buf;
43         while( *ptr ) {
44                 if ( *ptr == '.' )
45                         num++;
46                 ptr++;
47         }
48
49         list = lptr = (nodeitem*) palloc( sizeof(nodeitem)*(num+1) );
50         ptr=buf;
51         while( *ptr ) {
52                 if ( state == LTPRS_WAITNAME ) {
53                         if ( ISALNUM(*ptr) ) {
54                                 lptr->start = ptr;
55                                 state = LTPRS_WAITDELIM;
56                         } else
57                                 UNCHAR;
58                 } else if ( state == LTPRS_WAITDELIM ) {
59                         if ( *ptr == '.' ) {
60                                 lptr->len = ptr - lptr->start;
61                                 if ( lptr->len > 255 ) 
62                                         elog(ERROR,"Name of level is too long (%d, must be < 256) in position %d", 
63                                                 lptr->len, (int)(lptr->start - buf));
64                                 totallen += MAXALIGN(lptr->len + LEVEL_HDRSIZE);
65                                 lptr++;
66                                 state = LTPRS_WAITNAME;
67                         } else if ( !ISALNUM(*ptr) )
68                                 UNCHAR;
69                 } else
70                         elog(ERROR,"Inner error in parser");
71                 ptr++;
72         }
73
74         if ( state == LTPRS_WAITDELIM ) {
75                 lptr->len = ptr - lptr->start;
76                 if ( lptr->len > 255 ) 
77                         elog(ERROR,"Name of level is too long (%d, must be < 256) in position %d", 
78                                 lptr->len, (int)(lptr->start - buf));
79                 totallen += MAXALIGN(lptr->len + LEVEL_HDRSIZE);
80                 lptr++;
81         } else if ( ! (state == LTPRS_WAITNAME && lptr == list) )
82                 elog(ERROR,"Unexpected end of line");
83
84         result = (ltree*)palloc( LTREE_HDRSIZE + totallen );
85         result->len = LTREE_HDRSIZE + totallen;
86         result->numlevel = lptr-list;
87         curlevel = LTREE_FIRST(result);
88         lptr=list;
89         while( lptr-list < result->numlevel ) {
90                 curlevel->len = (uint8) lptr->len;
91                 memcpy( curlevel->name, lptr->start, lptr->len);
92                 curlevel = LEVEL_NEXT(curlevel); 
93                 lptr++;
94         }
95
96         pfree(list);
97         PG_RETURN_POINTER(result);
98 }
99
100 Datum 
101 ltree_out(PG_FUNCTION_ARGS) {
102         ltree *in = PG_GETARG_LTREE(0);
103         char *buf,*ptr;
104         int i;
105         ltree_level     *curlevel;
106         
107         ptr = buf = (char*)palloc( in->len );
108         curlevel = LTREE_FIRST(in);
109         for(i=0;i<in->numlevel;i++) {
110                 if ( i!=0 ) {
111                         *ptr = '.';
112                         ptr++;
113                 }
114                 memcpy( ptr, curlevel->name, curlevel->len );
115                 ptr+=curlevel->len;
116                 curlevel = LEVEL_NEXT(curlevel);
117         }
118
119         *ptr='\0';
120         PG_FREE_IF_COPY(in,0);
121
122         PG_RETURN_POINTER(buf);
123 }
124
125 #define LQPRS_WAITLEVEL 0
126 #define LQPRS_WAITDELIM 1
127 #define LQPRS_WAITOPEN  2
128 #define LQPRS_WAITFNUM  3
129 #define LQPRS_WAITSNUM  4
130 #define LQPRS_WAITND    5
131 #define LQPRS_WAITCLOSE 6
132 #define LQPRS_WAITEND   7
133 #define LQPRS_WAITVAR   8
134
135
136 #define GETVAR(x) ( *((nodeitem**)LQL_FIRST(x)) ) 
137 #define ITEMSIZE        MAXALIGN(LQL_HDRSIZE+sizeof(nodeitem*)) 
138 #define NEXTLEV(x) ( (lquery_level*)( ((char*)(x)) + ITEMSIZE) ) 
139
140 Datum 
141 lquery_in(PG_FUNCTION_ARGS) {
142         char    *buf = (char *) PG_GETARG_POINTER(0);
143         char    *ptr;
144         int num=0, totallen = 0, numOR=0;
145         int state = LQPRS_WAITLEVEL;
146         lquery  *result;
147         nodeitem  *lptr=NULL;
148         lquery_level    *cur,*curqlevel, *tmpql;
149         lquery_variant  *lrptr=NULL;
150         bool hasnot=false;
151         bool wasbad=false;
152
153         ptr=buf;
154         while( *ptr ) {
155                 if ( *ptr == '.' )
156                         num++;
157                 else if ( *ptr == '|' )
158                         numOR++;
159                 ptr++;
160         }
161         
162         num++;
163         curqlevel = tmpql = (lquery_level*) palloc( ITEMSIZE*num );
164         memset((void*)tmpql,0, ITEMSIZE*num );
165         ptr=buf;
166         while( *ptr ) {
167                 if ( state==LQPRS_WAITLEVEL ) {
168                         if ( ISALNUM(*ptr) ) {
169                                 GETVAR(curqlevel) = lptr = (nodeitem*)palloc( sizeof(nodeitem)*(numOR+1) );
170                                 memset((void*)GETVAR(curqlevel),  0,sizeof(nodeitem)*(numOR+1) );
171                                 lptr->start = ptr;
172                                 state = LQPRS_WAITDELIM;
173                                 curqlevel->numvar = 1;
174                         } else if ( *ptr == '!' ) {
175                                 GETVAR(curqlevel) = lptr = (nodeitem*)palloc( sizeof(nodeitem)*(numOR+1) );
176                                 memset((void*)GETVAR(curqlevel),  0,sizeof(nodeitem)*(numOR+1) );
177                                 lptr->start = ptr+1;
178                                 state = LQPRS_WAITDELIM;
179                                 curqlevel->numvar = 1;
180                                 curqlevel->flag |= LQL_NOT;
181                                 hasnot=true;
182                         } else if ( *ptr == '*' ) {
183                                 state = LQPRS_WAITOPEN;
184                         } else
185                                 UNCHAR;
186                 } else if ( state==LQPRS_WAITVAR ) {
187                         if ( ISALNUM(*ptr) ) {
188                                 lptr++;
189                                 lptr->start = ptr;
190                                 state = LQPRS_WAITDELIM;
191                                 curqlevel->numvar++;
192                         } else
193                                 UNCHAR;
194                 } else if ( state==LQPRS_WAITDELIM ) {
195                         if ( *ptr == '@' ) {
196                                 if ( lptr->start == ptr )
197                                         UNCHAR;
198                                 lptr->flag |= LVAR_INCASE;
199                                 curqlevel->flag |= LVAR_INCASE;
200                         } else if ( *ptr == '*' ) {
201                                 if ( lptr->start == ptr )
202                                         UNCHAR;
203                                 lptr->flag |= LVAR_ANYEND;
204                                 curqlevel->flag |= LVAR_ANYEND;
205                         } else if ( *ptr == '%' ) {
206                                 if ( lptr->start == ptr )
207                                         UNCHAR;
208                                 lptr->flag |= LVAR_SUBLEXEM;
209                                 curqlevel->flag |= LVAR_SUBLEXEM;
210                         } else if ( *ptr == '|' ) {
211                                 lptr->len = ptr - lptr->start - 
212                                         ( ( lptr->flag & LVAR_SUBLEXEM ) ? 1 : 0 ) - 
213                                         ( ( lptr->flag & LVAR_INCASE ) ? 1 : 0 ) - 
214                                         ( ( lptr->flag & LVAR_ANYEND ) ? 1 : 0 );
215                                 if ( lptr->len > 255 ) 
216                                         elog(ERROR,"Name of level is too long (%d, must be < 256) in position %d", 
217                                                 lptr->len, (int)(lptr->start - buf));
218                                 state = LQPRS_WAITVAR;
219                         } else if ( *ptr == '.' ) {
220                                 lptr->len = ptr - lptr->start - 
221                                         ( ( lptr->flag & LVAR_SUBLEXEM ) ? 1 : 0 ) - 
222                                         ( ( lptr->flag & LVAR_INCASE ) ? 1 : 0 ) - 
223                                         ( ( lptr->flag & LVAR_ANYEND ) ? 1 : 0 );
224                                 if ( lptr->len > 255 ) 
225                                         elog(ERROR,"Name of level is too long (%d, must be < 256) in position %d", 
226                                                 lptr->len, (int)(lptr->start - buf));
227                                 state = LQPRS_WAITLEVEL;
228                                 curqlevel = NEXTLEV(curqlevel);
229                         } else if ( ISALNUM(*ptr) ) {
230                                 if ( lptr->flag )
231                                         UNCHAR;
232                         } else
233                                 UNCHAR;
234                 } else if ( state == LQPRS_WAITOPEN ) {
235                         if ( *ptr == '{' ) {
236                                 state = LQPRS_WAITFNUM;
237                         } else if ( *ptr == '.' ) {
238                                 curqlevel->low=0;
239                                 curqlevel->high=0xffff;
240                                 curqlevel = NEXTLEV(curqlevel);
241                                 state = LQPRS_WAITLEVEL;
242                         } else
243                                 UNCHAR;
244                 } else if ( state == LQPRS_WAITFNUM ) {
245                         if ( *ptr == ',' ) {
246                                 state = LQPRS_WAITSNUM; 
247                         } else if ( isdigit((unsigned int)*ptr) ) {
248                                 curqlevel->low = atoi( ptr );
249                                 state = LQPRS_WAITND;
250                         } else
251                                 UNCHAR; 
252                 } else if ( state == LQPRS_WAITSNUM ) {
253                         if ( isdigit((unsigned int)*ptr) ) {
254                                 curqlevel->high = atoi( ptr );
255                                 state =  LQPRS_WAITCLOSE;
256                         } else if ( *ptr == '}' ) {
257                                 curqlevel->high = 0xffff;
258                                 state = LQPRS_WAITEND;
259                         } else
260                                 UNCHAR;
261                 } else if ( state == LQPRS_WAITCLOSE ) {
262                         if ( *ptr == '}' )
263                                 state = LQPRS_WAITEND;
264                         else if ( !isdigit((unsigned int)*ptr) )
265                                 UNCHAR;
266                 } else if ( state == LQPRS_WAITND ) { 
267                         if ( *ptr == '}' ) {
268                                 curqlevel->high = curqlevel->low;
269                                 state = LQPRS_WAITEND;
270                         } else if ( *ptr == ',' )
271                                 state = LQPRS_WAITSNUM;
272                         else if ( !isdigit((unsigned int)*ptr) )
273                                 UNCHAR;
274                 } else if ( state == LQPRS_WAITEND ) {
275                         if ( *ptr == '.' ) {
276                                 state = LQPRS_WAITLEVEL;
277                                 curqlevel = NEXTLEV(curqlevel);
278                         } else
279                                 UNCHAR;
280                 } else
281                         elog(ERROR,"Inner error in parser");
282                 ptr++;
283         }
284                         
285         if ( state==LQPRS_WAITDELIM ) {
286                 if ( lptr->start == ptr )
287                         elog(ERROR,"Unexpected end of line");
288                 lptr->len = ptr - lptr->start -
289                         ( ( lptr->flag & LVAR_SUBLEXEM ) ? 1 : 0 ) - 
290                         ( ( lptr->flag & LVAR_INCASE ) ? 1 : 0 ) - 
291                         ( ( lptr->flag & LVAR_ANYEND ) ? 1 : 0 );
292                 if ( lptr->len==0 )
293                         elog(ERROR,"Unexpected end of line");
294                 if ( lptr->len > 255 ) 
295                         elog(ERROR,"Name of level is too long (%d, must be < 256) in position %d", 
296                                 lptr->len, (int)(lptr->start - buf));
297         } else if ( state == LQPRS_WAITOPEN ) {
298                 curqlevel->high = 0xffff;
299         } else if ( state != LQPRS_WAITEND ) 
300                 elog(ERROR,"Unexpected end of line");
301                  
302         curqlevel = tmpql;
303         totallen = LQUERY_HDRSIZE; 
304         while( (char*)curqlevel-(char*)tmpql < num*ITEMSIZE ) {
305                 totallen += LQL_HDRSIZE; 
306                 if ( curqlevel->numvar ) {
307                         lptr = GETVAR(curqlevel);
308                         while( lptr-GETVAR(curqlevel) < curqlevel->numvar ) {
309                                 totallen += MAXALIGN(LVAR_HDRSIZE + lptr->len);
310                                 lptr++;
311                         }
312                 } else if ( curqlevel->low > curqlevel->high )
313                         elog(ERROR,"Low limit(%d) is greater than upper(%d)",curqlevel->low,curqlevel->high ); 
314                 curqlevel = NEXTLEV(curqlevel);
315         }
316
317         result = (lquery*)palloc( totallen );
318         result->len = totallen;
319         result->numlevel = num;
320         result->firstgood = 0;
321         result->flag=0;
322         if ( hasnot )
323                 result->flag |= LQUERY_HASNOT;
324         cur = LQUERY_FIRST(result);
325         curqlevel = tmpql;
326         while( (char*)curqlevel-(char*)tmpql < num*ITEMSIZE ) {
327                 memcpy(cur,curqlevel,LQL_HDRSIZE);
328                 cur->totallen=LQL_HDRSIZE;
329                 if ( curqlevel->numvar ) {
330                         lrptr = LQL_FIRST(cur);
331                         lptr = GETVAR(curqlevel);
332                         while( lptr-GETVAR(curqlevel) < curqlevel->numvar ) {
333                                 cur->totallen += MAXALIGN(LVAR_HDRSIZE + lptr->len);
334                                 lrptr->len  = lptr->len;
335                                 lrptr->flag = lptr->flag;
336                                 lrptr->val = ltree_crc32_sz((uint8 *) lptr->start, lptr->len);
337                                 memcpy( lrptr->name, lptr->start, lptr->len);
338                                 lptr++;
339                                 lrptr = LVAR_NEXT( lrptr );
340                         }
341                         pfree( GETVAR(curqlevel) );
342                         if ( cur->numvar > 1 || cur->flag != 0 )
343                                 wasbad=true;
344                         else if ( wasbad==false )
345                                 (result->firstgood)++;   
346                 } else
347                         wasbad=true;
348                 curqlevel = NEXTLEV(curqlevel);
349                 cur = LQL_NEXT(cur);
350         }
351
352         pfree(tmpql);
353         PG_RETURN_POINTER(result);
354 }
355
356 Datum 
357 lquery_out(PG_FUNCTION_ARGS) {
358         lquery *in = PG_GETARG_LQUERY(0);
359         char *buf,*ptr;
360         int i,j,totallen=0;
361         lquery_level     *curqlevel;
362         lquery_variant     *curtlevel;
363
364         curqlevel = LQUERY_FIRST(in);
365         for(i=0;i<in->numlevel;i++) {
366                 if ( curqlevel->numvar )
367                         totallen = (curqlevel->numvar*4) + 1 + curqlevel->totallen;
368                 else
369                         totallen = 2*11 + 4;
370                 totallen++;
371                 curqlevel = LQL_NEXT(curqlevel);
372         }
373         
374         
375         ptr = buf = (char*)palloc( totallen );
376         curqlevel = LQUERY_FIRST(in);
377         for(i=0;i<in->numlevel;i++) {
378                 if ( i!=0 ) {
379                         *ptr = '.';
380                         ptr++;
381                 }
382                 if ( curqlevel->numvar ) {
383                         if ( curqlevel->flag & LQL_NOT ) {
384                                 *ptr = '!';
385                                 ptr++;
386                         }
387                         curtlevel = LQL_FIRST(curqlevel);
388                         for(j=0;j<curqlevel->numvar;j++) {
389                                 if ( j!=0 ) {
390                                         *ptr = '|';
391                                         ptr++;
392                                 }
393                                 memcpy( ptr, curtlevel->name, curtlevel->len );
394                                 ptr+=curtlevel->len;
395                                 if ( (curtlevel->flag & LVAR_SUBLEXEM) ) {
396                                         *ptr = '%';
397                                         ptr++;
398                                 }
399                                 if ( (curtlevel->flag & LVAR_INCASE) ) {
400                                         *ptr = '@';
401                                         ptr++;
402                                 }
403                                 if ( (curtlevel->flag & LVAR_ANYEND) ) {
404                                         *ptr = '*';
405                                         ptr++;
406                                 }
407                                 curtlevel = LVAR_NEXT(curtlevel);
408                         }
409                 } else {
410                         if ( curqlevel->low == curqlevel->high ) {
411                                 sprintf(ptr,"*{%d}",curqlevel->low);
412                         } else if ( curqlevel->low == 0 ) {
413                                 if ( curqlevel->high == 0xffff ) {
414                                         *ptr='*';
415                                         *(ptr+1)='\0';
416                                 } else
417                                         sprintf(ptr,"*{,%d}",curqlevel->high);
418                         } else if ( curqlevel->high == 0xffff ) {
419                                 sprintf(ptr,"*{%d,}",curqlevel->low);
420                         } else 
421                                 sprintf(ptr,"*{%d,%d}", curqlevel->low, curqlevel->high);
422                         ptr = strchr(ptr,'\0');
423                 }
424
425                 curqlevel = LQL_NEXT(curqlevel);
426         }
427
428         *ptr='\0';
429         PG_FREE_IF_COPY(in,0);
430
431         PG_RETURN_POINTER(buf);
432 }
433
434