summaryrefslogtreecommitdiff
path: root/contrib/tsearch2/ts_stat.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/tsearch2/ts_stat.c')
-rw-r--r--contrib/tsearch2/ts_stat.c412
1 files changed, 412 insertions, 0 deletions
diff --git a/contrib/tsearch2/ts_stat.c b/contrib/tsearch2/ts_stat.c
new file mode 100644
index 0000000000..9099981e5c
--- /dev/null
+++ b/contrib/tsearch2/ts_stat.c
@@ -0,0 +1,412 @@
+/*
+ * stat functions
+ */
+
+#include "tsvector.h"
+#include "ts_stat.h"
+#include "funcapi.h"
+#include "catalog/pg_type.h"
+#include "executor/spi.h"
+#include "common.h"
+
+PG_FUNCTION_INFO_V1(tsstat_in);
+Datum tsstat_in(PG_FUNCTION_ARGS);
+Datum
+tsstat_in(PG_FUNCTION_ARGS) {
+ tsstat *stat=palloc(STATHDRSIZE);
+ stat->len=STATHDRSIZE;
+ stat->size=0;
+ PG_RETURN_POINTER(stat);
+}
+
+PG_FUNCTION_INFO_V1(tsstat_out);
+Datum tsstat_out(PG_FUNCTION_ARGS);
+Datum
+tsstat_out(PG_FUNCTION_ARGS) {
+ elog(ERROR,"Unimplemented");
+ PG_RETURN_NULL();
+}
+
+static WordEntry**
+SEI_realloc( WordEntry** in, uint32 *len ) {
+ if ( *len==0 || in==NULL ) {
+ *len=8;
+ in=palloc( sizeof(WordEntry*)* (*len) );
+ } else {
+ *len *= 2;
+ in=repalloc( in, sizeof(WordEntry*)* (*len) );
+ }
+ return in;
+}
+
+static int
+compareStatWord(StatEntry *a, WordEntry *b, tsstat *stat, tsvector *txt) {
+ if ( a->len == b->len )
+ return strncmp(
+ STATSTRPTR(stat) + a->pos,
+ STRPTR(txt) + b->pos,
+ a->len
+ );
+ return ( a->len > b->len ) ? 1 : -1;
+}
+
+static tsstat*
+formstat(tsstat *stat, tsvector *txt, WordEntry** entry, uint32 len) {
+ tsstat *newstat;
+ uint32 totallen, nentry;
+ uint32 slen=0;
+ WordEntry **ptr=entry;
+ char *curptr;
+ StatEntry *sptr,*nptr;
+
+ while(ptr-entry<len) {
+ slen += (*ptr)->len;
+ ptr++;
+ }
+
+ nentry=stat->size + len;
+ slen+=STATSTRSIZE(stat);
+ totallen=CALCSTATSIZE(nentry,slen);
+ newstat=palloc(totallen);
+ newstat->len=totallen;
+ newstat->size=nentry;
+
+ memcpy(STATSTRPTR(newstat), STATSTRPTR(stat), STATSTRSIZE(stat));
+ curptr=STATSTRPTR(newstat) + STATSTRSIZE(stat);
+
+ ptr=entry;
+ sptr=STATPTR(stat);
+ nptr=STATPTR(newstat);
+
+ if ( len == 1 ) {
+ StatEntry *StopLow = STATPTR(stat);
+ StatEntry *StopHigh = (StatEntry*)STATSTRPTR(stat);
+
+ while (StopLow < StopHigh) {
+ sptr=StopLow + (StopHigh - StopLow) / 2;
+ if ( compareStatWord(sptr,*ptr,stat,txt) < 0 )
+ StopLow = sptr + 1;
+ else
+ StopHigh = sptr;
+ }
+ nptr =STATPTR(newstat) + (StopLow-STATPTR(stat));
+ memcpy( STATPTR(newstat), STATPTR(stat), sizeof(StatEntry) * (StopLow-STATPTR(stat)) );
+ nptr->nentry=POSDATALEN(txt,*ptr);
+ if ( nptr->nentry==0 )
+ nptr->nentry=1;
+ nptr->ndoc=1;
+ nptr->len=(*ptr)->len;
+ memcpy(curptr, STRPTR(txt) + (*ptr)->pos, nptr->len);
+ nptr->pos = curptr - STATSTRPTR(newstat);
+ memcpy( nptr+1, StopLow, sizeof(StatEntry) * ( ((StatEntry*)STATSTRPTR(stat))-StopLow ) );
+ } else {
+ while( sptr-STATPTR(stat) < stat->size && ptr-entry<len) {
+ if ( compareStatWord(sptr,*ptr,stat,txt) < 0 ) {
+ memcpy(nptr, sptr, sizeof(StatEntry));
+ sptr++;
+ } else {
+ nptr->nentry=POSDATALEN(txt,*ptr);
+ if ( nptr->nentry==0 )
+ nptr->nentry=1;
+ nptr->ndoc=1;
+ nptr->len=(*ptr)->len;
+ memcpy(curptr, STRPTR(txt) + (*ptr)->pos, nptr->len);
+ nptr->pos = curptr - STATSTRPTR(newstat);
+ curptr += nptr->len;
+ ptr++;
+ }
+ nptr++;
+ }
+
+ memcpy( nptr, sptr, sizeof(StatEntry)*( stat->size - (sptr-STATPTR(stat)) ) );
+
+ while(ptr-entry<len) {
+ nptr->nentry=POSDATALEN(txt,*ptr);
+ if ( nptr->nentry==0 )
+ nptr->nentry=1;
+ nptr->ndoc=1;
+ nptr->len=(*ptr)->len;
+ memcpy(curptr, STRPTR(txt) + (*ptr)->pos, nptr->len);
+ nptr->pos = curptr - STATSTRPTR(newstat);
+ curptr += nptr->len;
+ ptr++; nptr++;
+ }
+ }
+
+ return newstat;
+}
+
+PG_FUNCTION_INFO_V1(ts_accum);
+Datum ts_accum(PG_FUNCTION_ARGS);
+Datum
+ts_accum(PG_FUNCTION_ARGS) {
+ tsstat *newstat,*stat= (tsstat*)PG_GETARG_POINTER(0);
+ tsvector *txt = (tsvector *) PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+ WordEntry **newentry=NULL;
+ uint32 len=0, cur=0;
+ StatEntry *sptr;
+ WordEntry *wptr;
+
+ if ( stat==NULL || PG_ARGISNULL(0) ) { /* Init in first */
+ stat=palloc(STATHDRSIZE);
+ stat->len=STATHDRSIZE;
+ stat->size=0;
+ }
+
+ /* simple check of correctness */
+ if ( txt==NULL || PG_ARGISNULL(1) || txt->size==0 ) {
+ PG_FREE_IF_COPY(txt,1);
+ PG_RETURN_POINTER(stat);
+ }
+
+ sptr=STATPTR(stat);
+ wptr=ARRPTR(txt);
+
+ if ( stat->size < 100*txt->size ) { /* merge */
+ while( sptr-STATPTR(stat) < stat->size && wptr-ARRPTR(txt) < txt->size ) {
+ int cmp = compareStatWord(sptr,wptr,stat,txt);
+ if ( cmp<0 ) {
+ sptr++;
+ } else if ( cmp==0 ) {
+ int n=POSDATALEN(txt,wptr);
+
+ if (n==0) n=1;
+ sptr->ndoc++;
+ sptr->nentry +=n ;
+ sptr++; wptr++;
+ } else {
+ if ( cur==len )
+ newentry=SEI_realloc(newentry, &len);
+ newentry[cur]=wptr;
+ wptr++; cur++;
+ }
+ }
+
+ while( wptr-ARRPTR(txt) < txt->size ) {
+ if ( cur==len )
+ newentry=SEI_realloc(newentry, &len);
+ newentry[cur]=wptr;
+ wptr++; cur++;
+ }
+ } else { /* search */
+ while( wptr-ARRPTR(txt) < txt->size ) {
+ StatEntry *StopLow = STATPTR(stat);
+ StatEntry *StopHigh = (StatEntry*)STATSTRPTR(stat);
+ int cmp;
+
+ while (StopLow < StopHigh) {
+ sptr=StopLow + (StopHigh - StopLow) / 2;
+ cmp = compareStatWord(sptr,wptr,stat,txt);
+ if (cmp==0) {
+ int n=POSDATALEN(txt,wptr);
+ if (n==0) n=1;
+ sptr->ndoc++;
+ sptr->nentry +=n ;
+ break;
+ } else if ( cmp < 0 )
+ StopLow = sptr + 1;
+ else
+ StopHigh = sptr;
+ }
+
+ if ( StopLow >= StopHigh ) { /* not found */
+ if ( cur==len )
+ newentry=SEI_realloc(newentry, &len);
+ newentry[cur]=wptr;
+ cur++;
+ }
+ wptr++;
+ }
+ }
+
+
+ if ( cur==0 ) { /* no new words */
+ PG_FREE_IF_COPY(txt,1);
+ PG_RETURN_POINTER(stat);
+ }
+
+ newstat = formstat(stat, txt, newentry, cur);
+ pfree(newentry);
+ PG_FREE_IF_COPY(txt,1);
+ /* pfree(stat); */
+
+ PG_RETURN_POINTER(newstat);
+}
+
+typedef struct {
+ uint32 cur;
+ tsvector *stat;
+} StatStorage;
+
+static void
+ts_setup_firstcall(FuncCallContext *funcctx, tsstat *stat) {
+ TupleDesc tupdesc;
+ MemoryContext oldcontext;
+ StatStorage *st;
+
+ oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+ st=palloc( sizeof(StatStorage) );
+ st->cur=0;
+ st->stat=palloc( stat->len );
+ memcpy(st->stat, stat, stat->len);
+ funcctx->user_fctx = (void*)st;
+ tupdesc = RelationNameGetTupleDesc("statinfo");
+ funcctx->slot = TupleDescGetSlot(tupdesc);
+ funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
+ MemoryContextSwitchTo(oldcontext);
+}
+
+
+static Datum
+ts_process_call(FuncCallContext *funcctx) {
+ StatStorage *st;
+ st=(StatStorage*)funcctx->user_fctx;
+
+ if ( st->cur < st->stat->size ) {
+ Datum result;
+ char* values[3];
+ char ndoc[16];
+ char nentry[16];
+ StatEntry *entry=STATPTR(st->stat) + st->cur;
+ HeapTuple tuple;
+
+ values[1]=ndoc;
+ sprintf(ndoc,"%d",entry->ndoc);
+ values[2]=nentry;
+ sprintf(nentry,"%d",entry->nentry);
+ values[0]=palloc( entry->len+1 );
+ memcpy( values[0], STATSTRPTR(st->stat)+entry->pos, entry->len);
+ (values[0])[entry->len]='\0';
+
+ tuple = BuildTupleFromCStrings(funcctx->attinmeta, values);
+ result = TupleGetDatum(funcctx->slot, tuple);
+
+ pfree(values[0]);
+ st->cur++;
+ return result;
+ } else {
+ pfree(st->stat);
+ pfree(st);
+ }
+
+ return (Datum)0;
+}
+
+PG_FUNCTION_INFO_V1(ts_accum_finish);
+Datum ts_accum_finish(PG_FUNCTION_ARGS);
+Datum
+ts_accum_finish(PG_FUNCTION_ARGS) {
+ FuncCallContext *funcctx;
+ Datum result;
+
+ if (SRF_IS_FIRSTCALL()) {
+ funcctx = SRF_FIRSTCALL_INIT();
+ ts_setup_firstcall(funcctx, (tsstat*)PG_GETARG_POINTER(0) );
+ }
+
+ funcctx = SRF_PERCALL_SETUP();
+ if ( (result=ts_process_call(funcctx)) != (Datum)0 )
+ SRF_RETURN_NEXT(funcctx, result);
+ SRF_RETURN_DONE(funcctx);
+}
+
+static Oid tiOid=InvalidOid;
+static void
+get_ti_Oid(void) {
+ int ret;
+ bool isnull;
+
+ if ( (ret = SPI_exec("select oid from pg_type where typname='tsvector'",1)) < 0 )
+ elog(ERROR, "SPI_exec to get tsvector oid returns %d", ret);
+
+ if ( SPI_processed<0 )
+ elog(ERROR, "There is no tsvector type");
+ tiOid = DatumGetObjectId( SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &isnull) );
+ if ( tiOid==InvalidOid )
+ elog(ERROR, "tsvector type has InvalidOid");
+}
+
+static tsstat*
+ts_stat_sql(text *txt) {
+ char *query=text2char(txt);
+ int i;
+ tsstat *newstat,*stat;
+ bool isnull;
+ Portal portal;
+ void *plan;
+
+ if ( tiOid==InvalidOid )
+ get_ti_Oid();
+
+ if ( (plan = SPI_prepare(query,0,NULL))==NULL )
+ elog(ERROR, "SPI_prepare('%s') returns NULL",query);
+
+ if ( (portal = SPI_cursor_open(NULL, plan, NULL, NULL)) == NULL )
+ elog(ERROR, "SPI_cursor_open('%s') returns NULL",query);
+
+ SPI_cursor_fetch(portal, true, 100);
+
+ if ( SPI_tuptable->tupdesc->natts != 1 )
+ elog(ERROR, "Number of fields doesn't equal to 1");
+
+ if ( SPI_gettypeid(SPI_tuptable->tupdesc, 1) != tiOid )
+ elog(ERROR, "Column isn't of tsvector type");
+
+ stat=palloc(STATHDRSIZE);
+ stat->len=STATHDRSIZE;
+ stat->size=0;
+
+ while(SPI_processed>0) {
+ for(i=0;i<SPI_processed;i++) {
+ Datum data=SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 1, &isnull);
+
+ if ( !isnull ) {
+ newstat = (tsstat*)DatumGetPointer(DirectFunctionCall2(
+ ts_accum,
+ PointerGetDatum(stat),
+ data
+ ));
+ if ( stat!=newstat && stat )
+ pfree(stat);
+ stat=newstat;
+ }
+ }
+
+ SPI_freetuptable(SPI_tuptable);
+ SPI_cursor_fetch(portal, true, 100);
+ }
+
+ SPI_freetuptable(SPI_tuptable);
+ SPI_cursor_close(portal);
+ SPI_freeplan(plan);
+ pfree(query);
+
+ return stat;
+}
+
+PG_FUNCTION_INFO_V1(ts_stat);
+Datum ts_stat(PG_FUNCTION_ARGS);
+Datum
+ts_stat(PG_FUNCTION_ARGS) {
+ FuncCallContext *funcctx;
+ Datum result;
+
+ if (SRF_IS_FIRSTCALL()) {
+ tsstat *stat;
+ text *txt=PG_GETARG_TEXT_P(0);
+
+ funcctx = SRF_FIRSTCALL_INIT();
+ SPI_connect();
+ stat = ts_stat_sql(txt);
+ PG_FREE_IF_COPY(txt,0);
+ ts_setup_firstcall(funcctx, stat );
+ SPI_finish();
+ }
+
+ funcctx = SRF_PERCALL_SETUP();
+ if ( (result=ts_process_call(funcctx)) != (Datum)0 )
+ SRF_RETURN_NEXT(funcctx, result);
+ SRF_RETURN_DONE(funcctx);
+}
+
+