diff options
Diffstat (limited to 'contrib/hstore/hstore_op.c')
-rw-r--r-- | contrib/hstore/hstore_op.c | 567 |
1 files changed, 567 insertions, 0 deletions
diff --git a/contrib/hstore/hstore_op.c b/contrib/hstore/hstore_op.c new file mode 100644 index 00000000000..12f783cb256 --- /dev/null +++ b/contrib/hstore/hstore_op.c @@ -0,0 +1,567 @@ +#include "hstore.h" +#include "utils/array.h" +#include "catalog/pg_type.h" +#include "funcapi.h" +#include <access/heapam.h> +#include <fmgr.h> + + +static HEntry * +findkey(HStore *hs, char *key, int keylen) { + HEntry *StopLow = ARRPTR(hs); + HEntry *StopHigh = StopLow + hs->size; + HEntry *StopMiddle; + int difference; + char *base = STRPTR(hs); + + while (StopLow < StopHigh) { + StopMiddle = StopLow + (StopHigh - StopLow) / 2; + + if ( StopMiddle->keylen == keylen ) + difference=strncmp(base+StopMiddle->pos, key, StopMiddle->keylen); + else + difference=(StopMiddle->keylen > keylen) ? 1 : -1; + + if (difference == 0) + return StopMiddle; + else if (difference < 0) + StopLow = StopMiddle + 1; + else + StopHigh = StopMiddle; + } + + return NULL; +} + +PG_FUNCTION_INFO_V1(fetchval); +Datum fetchval(PG_FUNCTION_ARGS); +Datum +fetchval(PG_FUNCTION_ARGS) { + HStore *hs = PG_GETARG_HS(0); + text *key = PG_GETARG_TEXT_P(1); + HEntry *entry; + text *out; + + if ((entry=findkey(hs,VARDATA(key), VARSIZE(key)-VARHDRSZ))==NULL || entry->valisnull) { + PG_FREE_IF_COPY(hs,0); + PG_FREE_IF_COPY(key,1); + PG_RETURN_NULL(); + } + + out=palloc(VARHDRSZ+entry->vallen); + memcpy(VARDATA(out),STRPTR(hs) + entry->pos + entry->keylen, entry->vallen); + VARATT_SIZEP(out) = VARHDRSZ+entry->vallen; + + PG_FREE_IF_COPY(hs,0); + PG_FREE_IF_COPY(key,1); + PG_RETURN_POINTER(out); +} + +PG_FUNCTION_INFO_V1(exists); +Datum exists(PG_FUNCTION_ARGS); +Datum +exists(PG_FUNCTION_ARGS) { + HStore *hs = PG_GETARG_HS(0); + text *key = PG_GETARG_TEXT_P(1); + HEntry *entry; + + entry=findkey(hs,VARDATA(key), VARSIZE(key)-VARHDRSZ); + + PG_FREE_IF_COPY(hs,0); + PG_FREE_IF_COPY(key,1); + + PG_RETURN_BOOL(entry); +} + +PG_FUNCTION_INFO_V1(defined); +Datum defined(PG_FUNCTION_ARGS); +Datum +defined(PG_FUNCTION_ARGS) { + HStore *hs = PG_GETARG_HS(0); + text *key = PG_GETARG_TEXT_P(1); + HEntry *entry; + bool res; + + entry=findkey(hs,VARDATA(key), VARSIZE(key)-VARHDRSZ); + + res = ( entry && !entry->valisnull ) ? true : false; + + PG_FREE_IF_COPY(hs,0); + PG_FREE_IF_COPY(key,1); + + PG_RETURN_BOOL(res); +} + +PG_FUNCTION_INFO_V1(delete); +Datum delete(PG_FUNCTION_ARGS); +Datum +delete(PG_FUNCTION_ARGS) { + HStore *hs = PG_GETARG_HS(0); + text *key = PG_GETARG_TEXT_P(1); + HStore *out = palloc(hs->len); + char *ptrs, *ptrd; + HEntry *es, *ed; + + out->len=hs->len; + out->size=hs->size; /* temprorary! */ + + ptrs=STRPTR(hs); + es =ARRPTR(hs); + ptrd=STRPTR(out); + ed =ARRPTR(out); + + while( es - ARRPTR(hs) < hs->size ) { + if ( !(es->keylen == VARSIZE(key) - VARHDRSZ && strncmp(ptrs, VARDATA(key), es->keylen)==0) ) { + memcpy( ed, es, sizeof(HEntry) ); + memcpy( ptrd, ptrs, es->keylen + ( (es->valisnull) ? 0 : es->vallen ) ); + ed->pos = ptrd - STRPTR(out); + ptrd += es->keylen + ( (es->valisnull) ? 0 : es->vallen ); + ed++; + } + ptrs += es->keylen + ( (es->valisnull) ? 0 : es->vallen ); + es++; + } + + if ( ed - ARRPTR(out) != out->size ) { + int buflen=ptrd-STRPTR(out); + ptrd = STRPTR(out); + + out->size = ed - ARRPTR(out); + + memmove( STRPTR(out), ptrd, buflen); + out->len = CALCDATASIZE(out->size, buflen); + } + + + PG_FREE_IF_COPY(hs,0); + PG_FREE_IF_COPY(key,1); + + PG_RETURN_POINTER(out); +} + +PG_FUNCTION_INFO_V1(hs_concat); +Datum hs_concat(PG_FUNCTION_ARGS); +Datum +hs_concat(PG_FUNCTION_ARGS) { + HStore *s1 = PG_GETARG_HS(0); + HStore *s2 = PG_GETARG_HS(1); + HStore *out = palloc( s1->len + s2->len ); + char *ps1, *ps2, *pd; + HEntry *es1, *es2, *ed; + + out->len = s1->len + s2->len; + out->size = s1->size + s2->size; + + ps1=STRPTR(s1); + ps2=STRPTR(s2); + pd=STRPTR(out); + es1=ARRPTR(s1); + es2=ARRPTR(s2); + ed=ARRPTR(out); + + while( es1 - ARRPTR(s1) < s1->size && es2 - ARRPTR(s2) < s2->size ) { + int difference; + if ( es1->keylen == es2->keylen ) + difference=strncmp(ps1, ps2, es1->keylen); + else + difference=(es1->keylen > es2->keylen) ? 1 : -1; + + if ( difference == 0 ) { + memcpy( ed, es2, sizeof(HEntry) ); + memcpy( pd, ps2, es2->keylen + ( (es2->valisnull) ? 0 : es2->vallen ) ); + ed->pos = pd - STRPTR(out); + pd += es2->keylen + ( (es2->valisnull) ? 0 : es2->vallen ); + ed++; + + ps1 += es1->keylen + ( (es1->valisnull) ? 0 : es1->vallen ); + es1++; + ps2 += es2->keylen + ( (es2->valisnull) ? 0 : es2->vallen ); + es2++; + } else if ( difference > 0 ) { + memcpy( ed, es2, sizeof(HEntry) ); + memcpy( pd, ps2, es2->keylen + ( (es2->valisnull) ? 0 : es2->vallen ) ); + ed->pos = pd - STRPTR(out); + pd += es2->keylen + ( (es2->valisnull) ? 0 : es2->vallen ); + ed++; + + ps2 += es2->keylen + ( (es2->valisnull) ? 0 : es2->vallen ); + es2++; + } else { + memcpy( ed, es1, sizeof(HEntry) ); + memcpy( pd, ps1, es1->keylen + ( (es1->valisnull) ? 0 : es1->vallen ) ); + ed->pos = pd - STRPTR(out); + pd += es1->keylen + ( (es1->valisnull) ? 0 : es1->vallen ); + ed++; + + ps1 += es1->keylen + ( (es1->valisnull) ? 0 : es1->vallen ); + es1++; + } + } + + while( es1 - ARRPTR(s1) < s1->size ) { + memcpy( ed, es1, sizeof(HEntry) ); + memcpy( pd, ps1, es1->keylen + ( (es1->valisnull) ? 0 : es1->vallen ) ); + ed->pos = pd - STRPTR(out); + pd += es1->keylen + ( (es1->valisnull) ? 0 : es1->vallen ); + ed++; + + ps1 += es1->keylen + ( (es1->valisnull) ? 0 : es1->vallen ); + es1++; + } + + while( es2 - ARRPTR(s2) < s2->size ) { + memcpy( ed, es2, sizeof(HEntry) ); + memcpy( pd, ps2, es2->keylen + ( (es2->valisnull) ? 0 : es2->vallen ) ); + ed->pos = pd - STRPTR(out); + pd += es2->keylen + ( (es2->valisnull) ? 0 : es2->vallen ); + ed++; + + ps2 += es2->keylen + ( (es2->valisnull) ? 0 : es2->vallen ); + es2++; + } + + if ( ed - ARRPTR(out) != out->size ) { + int buflen=pd-STRPTR(out); + pd = STRPTR(out); + + out->size = ed - ARRPTR(out); + + memmove( STRPTR(out), pd, buflen); + out->len = CALCDATASIZE(out->size, buflen); + } + + PG_FREE_IF_COPY(s1,0); + PG_FREE_IF_COPY(s2,1); + + PG_RETURN_POINTER(out); +} + +PG_FUNCTION_INFO_V1(tconvert); +Datum tconvert(PG_FUNCTION_ARGS); +Datum +tconvert(PG_FUNCTION_ARGS) { + text *key = PG_GETARG_TEXT_P(0); + text *val = PG_GETARG_TEXT_P(1); + int len; + HStore *out; + + len=CALCDATASIZE(1, VARSIZE(key) + VARSIZE(val) - 2*VARHDRSZ); + out = palloc(len); + out->len=len; + out->size=1; + + ARRPTR(out)->keylen = VARSIZE(key) - VARHDRSZ; + ARRPTR(out)->vallen = VARSIZE(val) - VARHDRSZ; + ARRPTR(out)->valisnull = false; + ARRPTR(out)->pos=0; + + memcpy( STRPTR(out), VARDATA(key), ARRPTR(out)->keylen ); + memcpy( STRPTR(out) + ARRPTR(out)->keylen, VARDATA(val), ARRPTR(out)->vallen ); + + PG_FREE_IF_COPY(key,0); + PG_FREE_IF_COPY(val,1); + + PG_RETURN_POINTER(out); +} + +PG_FUNCTION_INFO_V1(akeys); +Datum akeys(PG_FUNCTION_ARGS); +Datum +akeys(PG_FUNCTION_ARGS) { + HStore *hs = PG_GETARG_HS(0); + Datum *d; + ArrayType *a; + HEntry *ptr=ARRPTR(hs); + char *base=STRPTR(hs); + + d=(Datum*)palloc(sizeof(Datum)*(hs->size+1)); + while( ptr-ARRPTR(hs) < hs->size ) { + text *item=(text*)palloc(VARHDRSZ + ptr->keylen); + VARATT_SIZEP(item) = VARHDRSZ+ptr->keylen; + memcpy(VARDATA(item), base + ptr->pos, ptr->keylen); + d[ ptr-ARRPTR(hs) ] = PointerGetDatum(item); + ptr++; + } + + a = construct_array( + d, + hs->size, + TEXTOID, + -1, + false, + 'i' + ); + + ptr=ARRPTR(hs); + while( ptr-ARRPTR(hs) < hs->size ) { + pfree(DatumGetPointer(d[ ptr-ARRPTR(hs) ])); + ptr++; + } + + pfree(d); + PG_FREE_IF_COPY(hs,0); + + PG_RETURN_POINTER(a); +} + +PG_FUNCTION_INFO_V1(avals); +Datum avals(PG_FUNCTION_ARGS); +Datum +avals(PG_FUNCTION_ARGS) { + HStore *hs = PG_GETARG_HS(0); + Datum *d; + ArrayType *a; + HEntry *ptr=ARRPTR(hs); + char *base=STRPTR(hs); + + d=(Datum*)palloc(sizeof(Datum)*(hs->size+1)); + while( ptr-ARRPTR(hs) < hs->size ) { + int vallen = (ptr->valisnull) ? 0 : ptr->vallen; + text *item=(text*)palloc(VARHDRSZ + vallen); + VARATT_SIZEP(item) = VARHDRSZ+vallen; + memcpy(VARDATA(item), base + ptr->pos + ptr->keylen, vallen); + d[ ptr-ARRPTR(hs) ] = PointerGetDatum(item); + ptr++; + } + + a = construct_array( + d, + hs->size, + TEXTOID, + -1, + false, + 'i' + ); + + ptr=ARRPTR(hs); + while( ptr-ARRPTR(hs) < hs->size ) { + pfree(DatumGetPointer(d[ ptr-ARRPTR(hs) ])); + ptr++; + } + + pfree(d); + PG_FREE_IF_COPY(hs,0); + + PG_RETURN_POINTER(a); +} + +typedef struct { + HStore *hs; + int i; +} AKStore; + +static void +setup_firstcall(FuncCallContext *funcctx, HStore *hs) { + MemoryContext oldcontext; + AKStore *st; + + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + + st=(AKStore*)palloc( sizeof(AKStore) ); + st->i=0; + st->hs = (HStore*)palloc(hs->len); + memcpy( st->hs, hs, hs->len ); + + funcctx->user_fctx = (void*)st; + MemoryContextSwitchTo(oldcontext); +} + +PG_FUNCTION_INFO_V1(skeys); +Datum skeys(PG_FUNCTION_ARGS); +Datum +skeys(PG_FUNCTION_ARGS) { + FuncCallContext *funcctx; + AKStore *st; + + if (SRF_IS_FIRSTCALL()) { + HStore *hs = PG_GETARG_HS(0); + funcctx = SRF_FIRSTCALL_INIT(); + setup_firstcall(funcctx, hs); + PG_FREE_IF_COPY(hs,0); + } + + funcctx = SRF_PERCALL_SETUP(); + st = (AKStore*)funcctx->user_fctx; + + if ( st->i < st->hs->size ) { + HEntry *ptr = &(ARRPTR(st->hs)[st->i]); + text *item=(text*)palloc(VARHDRSZ + ptr->keylen); + + VARATT_SIZEP(item) = VARHDRSZ+ptr->keylen; + memcpy(VARDATA(item), STRPTR(st->hs) + ptr->pos, ptr->keylen); + st->i++; + + SRF_RETURN_NEXT(funcctx, PointerGetDatum(item)); + } + + pfree( st->hs ); + pfree( st ); + + SRF_RETURN_DONE(funcctx); +} + +PG_FUNCTION_INFO_V1(svals); +Datum svals(PG_FUNCTION_ARGS); +Datum +svals(PG_FUNCTION_ARGS) { + FuncCallContext *funcctx; + AKStore *st; + + if (SRF_IS_FIRSTCALL()) { + HStore *hs = PG_GETARG_HS(0); + funcctx = SRF_FIRSTCALL_INIT(); + setup_firstcall(funcctx, hs); + PG_FREE_IF_COPY(hs,0); + } + + funcctx = SRF_PERCALL_SETUP(); + st = (AKStore*)funcctx->user_fctx; + + if ( st->i < st->hs->size ) { + HEntry *ptr = &(ARRPTR(st->hs)[st->i]); + + if ( ptr->valisnull ) { + ReturnSetInfo *rsi; + + st->i++; + (funcctx)->call_cntr++; + rsi = (ReturnSetInfo *) fcinfo->resultinfo; + rsi->isDone = ExprMultipleResult; + PG_RETURN_NULL(); + } else { + int vallen = ptr->vallen; + text *item=(text*)palloc(VARHDRSZ + vallen); + + VARATT_SIZEP(item) = VARHDRSZ+vallen; + memcpy(VARDATA(item), STRPTR(st->hs) + ptr->pos + ptr->keylen, vallen); + st->i++; + + SRF_RETURN_NEXT(funcctx, PointerGetDatum(item)); + } + } + + pfree( st->hs ); + pfree( st ); + + SRF_RETURN_DONE(funcctx); +} + +PG_FUNCTION_INFO_V1(hs_contains); +Datum hs_contains(PG_FUNCTION_ARGS); +Datum +hs_contains(PG_FUNCTION_ARGS) { + HStore *val = PG_GETARG_HS(0); + HStore *tmpl = PG_GETARG_HS(1); + bool res = true; + HEntry *te = ARRPTR(tmpl); + char *vv = STRPTR(val); + char *tv = STRPTR(tmpl); + + while(res && te-ARRPTR(tmpl) < tmpl->size) { + HEntry *entry = findkey(val, tv + te->pos, te->keylen); + if ( entry ) { + if ( ! te->valisnull ) { + if ( entry->valisnull || !( + te->vallen==entry->vallen && + strncmp( + vv + entry->pos + entry->keylen, + tv + te->pos + te->keylen, + te->vallen ) == 0 + ) ) + res=false; + } + } else + res = false; + te++; + } + + PG_FREE_IF_COPY(val,0); + PG_FREE_IF_COPY(tmpl,1); + + PG_RETURN_BOOL(res); +} + +PG_FUNCTION_INFO_V1(hs_contained); +Datum hs_contained(PG_FUNCTION_ARGS); +Datum +hs_contained(PG_FUNCTION_ARGS) { + PG_RETURN_DATUM( DirectFunctionCall2( + hs_contains, + PG_GETARG_DATUM(1), + PG_GETARG_DATUM(0) + )); +} + +PG_FUNCTION_INFO_V1(each); +Datum each(PG_FUNCTION_ARGS); +Datum +each(PG_FUNCTION_ARGS) { + FuncCallContext *funcctx; + AKStore *st; + + if (SRF_IS_FIRSTCALL()) { + TupleDesc tupdesc; + MemoryContext oldcontext; + HStore *hs = PG_GETARG_HS(0); + + funcctx = SRF_FIRSTCALL_INIT(); + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + st=(AKStore*)palloc( sizeof(AKStore) ); + st->i=0; + st->hs = (HStore*)palloc(hs->len); + memcpy( st->hs, hs, hs->len ); + funcctx->user_fctx = (void*)st; + + tupdesc = RelationNameGetTupleDesc("hs_each"); + funcctx->slot = TupleDescGetSlot(tupdesc); + funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc); + + MemoryContextSwitchTo(oldcontext); + PG_FREE_IF_COPY(hs,0); + } + + funcctx = SRF_PERCALL_SETUP(); + st = (AKStore*)funcctx->user_fctx; + + if ( st->i < st->hs->size ) { + HEntry *ptr = &(ARRPTR(st->hs)[st->i]); + Datum res, dvalues[2]; + char nulls[] = {' ', ' '}; + text *item; + HeapTuple tuple; + + item=(text*)palloc(VARHDRSZ + ptr->keylen); + VARATT_SIZEP(item) = VARHDRSZ+ptr->keylen; + memcpy(VARDATA(item), STRPTR(st->hs) + ptr->pos, ptr->keylen); + dvalues[0] = PointerGetDatum(item); + + if ( ptr->valisnull ) { + dvalues[1]=(Datum)0; + nulls[1]='n'; + } else { + int vallen = ptr->vallen; + + item=(text*)palloc(VARHDRSZ + vallen); + VARATT_SIZEP(item) = VARHDRSZ+vallen; + memcpy(VARDATA(item), STRPTR(st->hs) + ptr->pos + ptr->keylen, vallen); + dvalues[1] = PointerGetDatum(item); + } + st->i++; + + tuple = heap_formtuple(funcctx->attinmeta->tupdesc, dvalues, nulls); + res = TupleGetDatum(funcctx->slot, tuple); + + pfree( DatumGetPointer(dvalues[0]) ); + if ( nulls[1] != 'n' ) + pfree( DatumGetPointer(dvalues[1]) ); + + SRF_RETURN_NEXT(funcctx, PointerGetDatum(res)); + } + + pfree( st->hs ); + pfree( st ); + + SRF_RETURN_DONE(funcctx); +} + + |