aboutsummaryrefslogtreecommitdiff
path: root/contrib/hstore/hstore_op.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/hstore/hstore_op.c')
-rw-r--r--contrib/hstore/hstore_op.c567
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);
+}
+
+