add .gitignore
[hstore.git] / hstore_op.c
1 #include "hstore.h"
2 #include "utils/array.h"
3 #include "catalog/pg_type.h"
4 #include "funcapi.h"
5 #include <access/heapam.h>
6 #include <fmgr.h>
7
8
9 static HEntry *
10 findkey(HStore *hs, char *key, int keylen) {
11         HEntry *StopLow  = ARRPTR(hs);
12         HEntry *StopHigh = StopLow + hs->size;
13         HEntry  *StopMiddle;
14         int    difference;
15         char *base = STRPTR(hs);
16
17         while (StopLow < StopHigh) {
18                 StopMiddle = StopLow + (StopHigh - StopLow) / 2;
19
20                 if ( StopMiddle->keylen == keylen )
21                         difference=strncmp(base+StopMiddle->pos, key, StopMiddle->keylen);
22                 else
23                         difference=(StopMiddle->keylen > keylen) ? 1 : -1;
24
25                 if (difference == 0)
26                         return StopMiddle;
27                 else if (difference < 0)
28                         StopLow = StopMiddle + 1;
29                 else
30                         StopHigh = StopMiddle;
31         }
32                 
33         return NULL;
34 }
35
36 PG_FUNCTION_INFO_V1(fetchval);
37 Datum           fetchval(PG_FUNCTION_ARGS);
38 Datum
39 fetchval(PG_FUNCTION_ARGS) {
40         HStore *hs  = PG_GETARG_HS(0);
41         text   *key = PG_GETARG_TEXT_P(1);
42         HEntry  *entry;
43         text *out;
44
45         if ((entry=findkey(hs,VARDATA(key), VARSIZE(key)-VARHDRSZ))==NULL || entry->valisnull) {
46                 PG_FREE_IF_COPY(hs,0);
47                 PG_FREE_IF_COPY(key,1);
48                 PG_RETURN_NULL();
49         }
50
51         out=palloc(VARHDRSZ+entry->vallen);
52         memcpy(VARDATA(out),STRPTR(hs) + entry->pos + entry->keylen, entry->vallen);
53         VARATT_SIZEP(out) = VARHDRSZ+entry->vallen;
54
55         PG_FREE_IF_COPY(hs,0);
56         PG_FREE_IF_COPY(key,1);
57         PG_RETURN_POINTER(out);
58 }
59
60 PG_FUNCTION_INFO_V1(exists);
61 Datum           exists(PG_FUNCTION_ARGS);
62 Datum
63 exists(PG_FUNCTION_ARGS) {
64         HStore *hs  = PG_GETARG_HS(0);
65         text   *key = PG_GETARG_TEXT_P(1);
66         HEntry  *entry;
67
68         entry=findkey(hs,VARDATA(key), VARSIZE(key)-VARHDRSZ);
69
70         PG_FREE_IF_COPY(hs,0);
71         PG_FREE_IF_COPY(key,1);
72
73         PG_RETURN_BOOL(entry);
74 }
75
76 PG_FUNCTION_INFO_V1(defined);
77 Datum           defined(PG_FUNCTION_ARGS);
78 Datum
79 defined(PG_FUNCTION_ARGS) {
80         HStore *hs  = PG_GETARG_HS(0);
81         text   *key = PG_GETARG_TEXT_P(1);
82         HEntry  *entry;
83         bool res;
84
85         entry=findkey(hs,VARDATA(key), VARSIZE(key)-VARHDRSZ);
86
87         res = ( entry && !entry->valisnull ) ? true : false;
88
89         PG_FREE_IF_COPY(hs,0);
90         PG_FREE_IF_COPY(key,1);
91
92         PG_RETURN_BOOL(res);
93 }
94
95 PG_FUNCTION_INFO_V1(delete);
96 Datum           delete(PG_FUNCTION_ARGS);
97 Datum
98 delete(PG_FUNCTION_ARGS) {
99         HStore *hs  = PG_GETARG_HS(0);
100         text   *key = PG_GETARG_TEXT_P(1);
101         HStore *out = palloc(hs->len);
102         char *ptrs, *ptrd;
103         HEntry  *es, *ed;
104
105         out->len=hs->len;
106         out->size=hs->size; /* temprorary! */
107
108         ptrs=STRPTR(hs);
109         es  =ARRPTR(hs);
110         ptrd=STRPTR(out);
111         ed  =ARRPTR(out);
112
113         while( es - ARRPTR(hs) < hs->size ) { 
114                 if ( !(es->keylen == VARSIZE(key) - VARHDRSZ && strncmp(ptrs, VARDATA(key), es->keylen)==0) ) {
115                         memcpy( ed, es, sizeof(HEntry) );
116                         memcpy( ptrd, ptrs, es->keylen + ( (es->valisnull) ? 0 : es->vallen ) );
117                         ed->pos = ptrd - STRPTR(out);
118                         ptrd += es->keylen + ( (es->valisnull) ? 0 : es->vallen );
119                         ed++;
120                 }
121                 ptrs += es->keylen + ( (es->valisnull) ? 0 : es->vallen );
122                 es++;
123         }
124
125         if ( ed - ARRPTR(out) != out->size ) {
126                 int buflen=ptrd-STRPTR(out);
127                 ptrd = STRPTR(out);
128
129                 out->size = ed - ARRPTR(out);
130
131                 memmove( STRPTR(out), ptrd, buflen);
132                 out->len = CALCDATASIZE(out->size, buflen);
133         }
134                 
135
136         PG_FREE_IF_COPY(hs,0);
137         PG_FREE_IF_COPY(key,1);
138
139         PG_RETURN_POINTER(out);
140 }
141
142 PG_FUNCTION_INFO_V1(hs_concat);
143 Datum           hs_concat(PG_FUNCTION_ARGS);
144 Datum
145 hs_concat(PG_FUNCTION_ARGS) {
146         HStore *s1  = PG_GETARG_HS(0);
147         HStore *s2  = PG_GETARG_HS(1);
148         HStore *out = palloc( s1->len + s2->len );
149         char *ps1, *ps2, *pd;
150         HEntry *es1, *es2, *ed;
151
152         out->len = s1->len + s2->len;
153         out->size = s1->size + s2->size;
154
155         ps1=STRPTR(s1);
156         ps2=STRPTR(s2);
157         pd=STRPTR(out);
158         es1=ARRPTR(s1);
159         es2=ARRPTR(s2);
160         ed=ARRPTR(out);
161
162         while( es1 - ARRPTR(s1) < s1->size && es2 - ARRPTR(s2) < s2->size ) {
163                 int difference;
164                 if ( es1->keylen == es2->keylen )
165                         difference=strncmp(ps1, ps2, es1->keylen);
166                 else
167                         difference=(es1->keylen > es2->keylen) ? 1 : -1;
168
169                 if ( difference == 0 ) {
170                         memcpy( ed, es2, sizeof(HEntry) );
171                         memcpy( pd, ps2, es2->keylen + ( (es2->valisnull) ? 0 : es2->vallen ) );
172                         ed->pos = pd - STRPTR(out);
173                         pd += es2->keylen + ( (es2->valisnull) ? 0 : es2->vallen );
174                         ed++;
175                         
176                         ps1 += es1->keylen + ( (es1->valisnull) ? 0 : es1->vallen );
177                         es1++;
178                         ps2 += es2->keylen + ( (es2->valisnull) ? 0 : es2->vallen );
179                         es2++;
180                 } else if ( difference > 0 ) {
181                         memcpy( ed, es2, sizeof(HEntry) );
182                         memcpy( pd, ps2, es2->keylen + ( (es2->valisnull) ? 0 : es2->vallen ) );
183                         ed->pos = pd - STRPTR(out);
184                         pd += es2->keylen + ( (es2->valisnull) ? 0 : es2->vallen );
185                         ed++;
186                         
187                         ps2 += es2->keylen + ( (es2->valisnull) ? 0 : es2->vallen );
188                         es2++;
189                 } else {
190                         memcpy( ed, es1, sizeof(HEntry) );
191                         memcpy( pd, ps1, es1->keylen + ( (es1->valisnull) ? 0 : es1->vallen ) );
192                         ed->pos = pd - STRPTR(out);
193                         pd += es1->keylen + ( (es1->valisnull) ? 0 : es1->vallen );
194                         ed++;
195                         
196                         ps1 += es1->keylen + ( (es1->valisnull) ? 0 : es1->vallen );
197                         es1++;
198                 }
199         }
200
201         while( es1 - ARRPTR(s1) < s1->size ) {
202                 memcpy( ed, es1, sizeof(HEntry) );
203                 memcpy( pd, ps1, es1->keylen + ( (es1->valisnull) ? 0 : es1->vallen ) );
204                 ed->pos = pd - STRPTR(out);
205                 pd += es1->keylen + ( (es1->valisnull) ? 0 : es1->vallen );
206                 ed++;
207                         
208                 ps1 += es1->keylen + ( (es1->valisnull) ? 0 : es1->vallen );
209                 es1++;
210         }
211
212         while( es2 - ARRPTR(s2) < s2->size ) {
213                 memcpy( ed, es2, sizeof(HEntry) );
214                 memcpy( pd, ps2, es2->keylen + ( (es2->valisnull) ? 0 : es2->vallen ) );
215                 ed->pos = pd - STRPTR(out);
216                 pd += es2->keylen + ( (es2->valisnull) ? 0 : es2->vallen );
217                 ed++;
218                 
219                 ps2 += es2->keylen + ( (es2->valisnull) ? 0 : es2->vallen );
220                 es2++;
221         }
222
223         if ( ed - ARRPTR(out) != out->size ) {
224                 int buflen=pd-STRPTR(out);
225                 pd = STRPTR(out);
226
227                 out->size = ed - ARRPTR(out);
228
229                 memmove( STRPTR(out), pd, buflen);
230                 out->len = CALCDATASIZE(out->size, buflen);
231         }
232                 
233         PG_FREE_IF_COPY(s1,0);
234         PG_FREE_IF_COPY(s2,1);
235
236         PG_RETURN_POINTER(out);
237 }
238
239 PG_FUNCTION_INFO_V1(tconvert);
240 Datum           tconvert(PG_FUNCTION_ARGS);
241 Datum
242 tconvert(PG_FUNCTION_ARGS) {
243         text   *key = PG_GETARG_TEXT_P(0);
244         text   *val = PG_GETARG_TEXT_P(1);
245         int len;
246         HStore  *out;
247
248         len=CALCDATASIZE(1, VARSIZE(key) + VARSIZE(val) - 2*VARHDRSZ);
249         out = palloc(len);
250         out->len=len;
251         out->size=1;
252
253         ARRPTR(out)->keylen = VARSIZE(key) - VARHDRSZ;
254         ARRPTR(out)->vallen = VARSIZE(val) - VARHDRSZ;
255         ARRPTR(out)->valisnull = false;
256         ARRPTR(out)->pos=0;
257
258         memcpy( STRPTR(out), VARDATA(key), ARRPTR(out)->keylen );
259         memcpy( STRPTR(out) + ARRPTR(out)->keylen, VARDATA(val), ARRPTR(out)->vallen );
260                 
261         PG_FREE_IF_COPY(key,0);
262         PG_FREE_IF_COPY(val,1);
263
264         PG_RETURN_POINTER(out);
265 }
266
267 PG_FUNCTION_INFO_V1(akeys);
268 Datum           akeys(PG_FUNCTION_ARGS);
269 Datum
270 akeys(PG_FUNCTION_ARGS) { 
271         HStore *hs  = PG_GETARG_HS(0);
272         Datum   *d;
273         ArrayType *a;
274         HEntry *ptr=ARRPTR(hs);
275         char *base=STRPTR(hs);
276
277         d=(Datum*)palloc(sizeof(Datum)*(hs->size+1));
278         while( ptr-ARRPTR(hs) < hs->size ) {
279                 text *item=(text*)palloc(VARHDRSZ + ptr->keylen);
280                 VARATT_SIZEP(item) = VARHDRSZ+ptr->keylen;
281                 memcpy(VARDATA(item), base + ptr->pos, ptr->keylen);
282                 d[ ptr-ARRPTR(hs) ] = PointerGetDatum(item);
283                 ptr++;
284         }
285         
286         a = construct_array(
287                 d,
288                 hs->size,
289                 TEXTOID,
290                 -1,
291                 false,
292                 'i'
293         );
294
295         ptr=ARRPTR(hs);
296         while( ptr-ARRPTR(hs) < hs->size ) {
297                 pfree(DatumGetPointer(d[ ptr-ARRPTR(hs) ]));
298                 ptr++;
299         }
300
301         pfree(d);
302         PG_FREE_IF_COPY(hs,0);
303
304         PG_RETURN_POINTER(a);
305 }
306
307 PG_FUNCTION_INFO_V1(avals);
308 Datum           avals(PG_FUNCTION_ARGS);
309 Datum
310 avals(PG_FUNCTION_ARGS) { 
311         HStore *hs  = PG_GETARG_HS(0);
312         Datum   *d;
313         ArrayType *a;
314         HEntry *ptr=ARRPTR(hs);
315         char *base=STRPTR(hs);
316
317         d=(Datum*)palloc(sizeof(Datum)*(hs->size+1));
318         while( ptr-ARRPTR(hs) < hs->size ) {
319                 int vallen = (ptr->valisnull) ? 0 : ptr->vallen; 
320                 text *item=(text*)palloc(VARHDRSZ + vallen);
321                 VARATT_SIZEP(item) = VARHDRSZ+vallen;
322                 memcpy(VARDATA(item), base + ptr->pos + ptr->keylen, vallen);
323                 d[ ptr-ARRPTR(hs) ] = PointerGetDatum(item);
324                 ptr++;
325         }
326         
327         a = construct_array(
328                 d,
329                 hs->size,
330                 TEXTOID,
331                 -1,
332                 false,
333                 'i'
334         );
335
336         ptr=ARRPTR(hs);
337         while( ptr-ARRPTR(hs) < hs->size ) {
338                 pfree(DatumGetPointer(d[ ptr-ARRPTR(hs) ]));
339                 ptr++;
340         }
341
342         pfree(d);
343         PG_FREE_IF_COPY(hs,0);
344
345         PG_RETURN_POINTER(a);
346 }
347
348 typedef struct {
349         HStore  *hs;
350         int     i;
351 } AKStore;
352
353 static void
354 setup_firstcall(FuncCallContext  *funcctx, HStore *hs) {
355         MemoryContext     oldcontext;
356         AKStore     *st;
357
358         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
359
360         st=(AKStore*)palloc( sizeof(AKStore) );
361         st->i=0;
362         st->hs = (HStore*)palloc(hs->len);
363         memcpy( st->hs, hs, hs->len );
364
365         funcctx->user_fctx = (void*)st;
366         MemoryContextSwitchTo(oldcontext);
367 }
368
369 PG_FUNCTION_INFO_V1(skeys);
370 Datum           skeys(PG_FUNCTION_ARGS);
371 Datum
372 skeys(PG_FUNCTION_ARGS) {
373         FuncCallContext  *funcctx;
374         AKStore *st;
375
376         if (SRF_IS_FIRSTCALL()) {
377                 HStore *hs = PG_GETARG_HS(0);
378                 funcctx = SRF_FIRSTCALL_INIT();
379                 setup_firstcall(funcctx, hs);
380                 PG_FREE_IF_COPY(hs,0);
381         }
382
383         funcctx = SRF_PERCALL_SETUP();
384         st = (AKStore*)funcctx->user_fctx;
385         
386         if ( st->i < st->hs->size ) {
387                 HEntry *ptr = &(ARRPTR(st->hs)[st->i]);
388                 text *item=(text*)palloc(VARHDRSZ + ptr->keylen);
389
390                 VARATT_SIZEP(item) = VARHDRSZ+ptr->keylen;
391                 memcpy(VARDATA(item), STRPTR(st->hs) + ptr->pos, ptr->keylen);
392                 st->i++;
393
394                 SRF_RETURN_NEXT(funcctx, PointerGetDatum(item));
395         }
396         
397         pfree( st->hs );
398         pfree( st );
399
400         SRF_RETURN_DONE(funcctx); 
401 }
402
403 PG_FUNCTION_INFO_V1(svals);
404 Datum           svals(PG_FUNCTION_ARGS);
405 Datum
406 svals(PG_FUNCTION_ARGS) {
407         FuncCallContext  *funcctx;
408         AKStore *st;
409
410         if (SRF_IS_FIRSTCALL()) {
411                 HStore *hs = PG_GETARG_HS(0);
412                 funcctx = SRF_FIRSTCALL_INIT();
413                 setup_firstcall(funcctx, hs);
414                 PG_FREE_IF_COPY(hs,0);
415         }
416
417         funcctx = SRF_PERCALL_SETUP();
418         st = (AKStore*)funcctx->user_fctx;
419         
420         if ( st->i < st->hs->size ) {
421                 HEntry *ptr = &(ARRPTR(st->hs)[st->i]);
422
423                 if ( ptr->valisnull ) {
424                         ReturnSetInfo      *rsi;
425
426                         st->i++;
427                         (funcctx)->call_cntr++;
428                         rsi = (ReturnSetInfo *) fcinfo->resultinfo;
429                         rsi->isDone = ExprMultipleResult;
430                         PG_RETURN_NULL();
431                 } else {
432                         int vallen = ptr->vallen; 
433                         text *item=(text*)palloc(VARHDRSZ + vallen);
434
435                         VARATT_SIZEP(item) = VARHDRSZ+vallen;
436                         memcpy(VARDATA(item), STRPTR(st->hs) + ptr->pos + ptr->keylen, vallen);
437                         st->i++;
438
439                         SRF_RETURN_NEXT(funcctx, PointerGetDatum(item));
440                 }
441         }
442         
443         pfree( st->hs );
444         pfree( st );
445
446         SRF_RETURN_DONE(funcctx); 
447 }
448
449 PG_FUNCTION_INFO_V1(hs_contains);
450 Datum           hs_contains(PG_FUNCTION_ARGS);
451 Datum
452 hs_contains(PG_FUNCTION_ARGS) {
453         HStore *val   = PG_GETARG_HS(0);
454         HStore *tmpl  = PG_GETARG_HS(1);
455         bool res = true;
456         HEntry  *te = ARRPTR(tmpl);
457         char    *vv = STRPTR(val);
458         char    *tv = STRPTR(tmpl);
459
460         while(res && te-ARRPTR(tmpl) < tmpl->size) {
461                 HEntry *entry = findkey(val, tv + te->pos, te->keylen);
462                 if ( entry ) {
463                         if ( ! te->valisnull ) {
464                                 if ( entry->valisnull || !(
465                                                 te->vallen==entry->vallen && 
466                                                 strncmp( 
467                                                         vv + entry->pos + entry->keylen, 
468                                                         tv + te->pos + te->keylen, 
469                                                 te->vallen ) == 0
470                                         ) )
471                                         res=false;
472                         }
473                 } else
474                         res = false;
475                 te++;
476         }
477
478         PG_FREE_IF_COPY(val,0);
479         PG_FREE_IF_COPY(tmpl,1);
480
481         PG_RETURN_BOOL(res);
482 }
483         
484 PG_FUNCTION_INFO_V1(hs_contained);
485 Datum           hs_contained(PG_FUNCTION_ARGS);
486 Datum
487 hs_contained(PG_FUNCTION_ARGS) {
488         PG_RETURN_DATUM( DirectFunctionCall2(
489                 hs_contains,
490                 PG_GETARG_DATUM(1),
491                 PG_GETARG_DATUM(0)
492         ));
493 }
494
495 PG_FUNCTION_INFO_V1(each);
496 Datum           each(PG_FUNCTION_ARGS);
497 Datum
498 each(PG_FUNCTION_ARGS) {
499         FuncCallContext  *funcctx;
500         AKStore *st;
501
502         if (SRF_IS_FIRSTCALL()) {
503                 TupleDesc            tupdesc;
504                 MemoryContext     oldcontext;
505                 HStore *hs = PG_GETARG_HS(0);
506
507                 funcctx = SRF_FIRSTCALL_INIT();
508                 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
509                 st=(AKStore*)palloc( sizeof(AKStore) );
510                 st->i=0;
511                 st->hs = (HStore*)palloc(hs->len);
512                 memcpy( st->hs, hs, hs->len );
513                 funcctx->user_fctx = (void*)st;         
514                         
515                 tupdesc = RelationNameGetTupleDesc("hs_each");
516                 funcctx->slot = TupleDescGetSlot(tupdesc);
517                 funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
518
519                 MemoryContextSwitchTo(oldcontext);
520                 PG_FREE_IF_COPY(hs,0);
521         }
522
523         funcctx = SRF_PERCALL_SETUP();
524         st = (AKStore*)funcctx->user_fctx;
525         
526         if ( st->i < st->hs->size ) {
527                 HEntry *ptr = &(ARRPTR(st->hs)[st->i]);
528                 Datum res, dvalues[2];
529                 char       nulls[] = {' ', ' '};
530                 text *item;
531                 HeapTuple       tuple;
532
533                 item=(text*)palloc(VARHDRSZ + ptr->keylen);
534                 VARATT_SIZEP(item) = VARHDRSZ+ptr->keylen;
535                 memcpy(VARDATA(item), STRPTR(st->hs) + ptr->pos, ptr->keylen);
536                 dvalues[0] = PointerGetDatum(item);
537
538                 if ( ptr->valisnull ) {
539                         dvalues[1]=(Datum)0;
540                         nulls[1]='n';
541                 } else {
542                         int vallen = ptr->vallen; 
543
544                         item=(text*)palloc(VARHDRSZ + vallen);
545                         VARATT_SIZEP(item) = VARHDRSZ+vallen;
546                         memcpy(VARDATA(item), STRPTR(st->hs) + ptr->pos + ptr->keylen, vallen);
547                         dvalues[1] = PointerGetDatum(item);
548                 }
549                 st->i++;
550
551                 tuple = heap_formtuple(funcctx->attinmeta->tupdesc, dvalues, nulls);
552                 res = TupleGetDatum(funcctx->slot, tuple);
553
554                 pfree( DatumGetPointer(dvalues[0]) );
555                 if ( nulls[1] != 'n' ) 
556                         pfree( DatumGetPointer(dvalues[1]) );
557
558                 SRF_RETURN_NEXT(funcctx, PointerGetDatum(res));
559         }
560         
561         pfree( st->hs );
562         pfree( st );
563
564         SRF_RETURN_DONE(funcctx); 
565 }
566
567