#include "postgres.h"
#include "catalog/namespace.h"
#include "catalog/pg_type.h"
#include "commands/trigger.h"
#include "executor/spi.h"
#include "funcapi.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "tsearch/ts_utils.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
Go to the source code of this file.
Data Structures | |
struct | CHKVAL |
struct | StatEntry |
struct | TSVectorStat |
Defines | |
#define | STATENTRYHDRSZ (offsetof(StatEntry, lexeme)) |
#define | STATHDRSIZE (offsetof(TSVectorStat, data)) |
#define | TSVECTORCMPFUNC(type, action, ret) |
#define | compareEntry(pa, a, pb, b) |
#define | compareStatWord(a, e, t) |
Typedefs | |
typedef struct StatEntry | StatEntry |
Functions | |
static Datum | tsvector_update_trigger (PG_FUNCTION_ARGS, bool config_column) |
static bool | is_expected_type (Oid typid, Oid expected_type) |
static bool | is_text_type (Oid typid) |
static int | silly_cmp_tsvector (const TSVector a, const TSVector b) |
TSVECTORCMPFUNC (lt,<, BOOL) | |
TSVECTORCMPFUNC (le,<=, BOOL) | |
TSVECTORCMPFUNC (eq,==, BOOL) | |
TSVECTORCMPFUNC (ge, >=, BOOL) | |
TSVECTORCMPFUNC (gt, >, BOOL) | |
TSVECTORCMPFUNC (ne,!=, BOOL) | |
TSVECTORCMPFUNC (cmp,+, INT32) | |
Datum | tsvector_strip (PG_FUNCTION_ARGS) |
Datum | tsvector_length (PG_FUNCTION_ARGS) |
Datum | tsvector_setweight (PG_FUNCTION_ARGS) |
static int32 | add_pos (TSVector src, WordEntry *srcptr, TSVector dest, WordEntry *destptr, int32 maxpos) |
Datum | tsvector_concat (PG_FUNCTION_ARGS) |
int32 | tsCompareString (char *a, int lena, char *b, int lenb, bool prefix) |
static bool | checkclass_str (CHKVAL *chkval, WordEntry *val, QueryOperand *item) |
static bool | checkcondition_str (void *checkval, QueryOperand *val) |
bool | TS_execute (QueryItem *curitem, void *checkval, bool calcnot, bool(*chkcond)(void *checkval, QueryOperand *val)) |
bool | tsquery_requires_match (QueryItem *curitem) |
Datum | ts_match_qv (PG_FUNCTION_ARGS) |
Datum | ts_match_vq (PG_FUNCTION_ARGS) |
Datum | ts_match_tt (PG_FUNCTION_ARGS) |
Datum | ts_match_tq (PG_FUNCTION_ARGS) |
static int | check_weight (TSVector txt, WordEntry *wptr, int8 weight) |
static void | insertStatEntry (MemoryContext persistentContext, TSVectorStat *stat, TSVector txt, uint32 off) |
static void | chooseNextStatEntry (MemoryContext persistentContext, TSVectorStat *stat, TSVector txt, uint32 low, uint32 high, uint32 offset) |
static TSVectorStat * | ts_accum (MemoryContext persistentContext, TSVectorStat *stat, Datum data) |
static void | ts_setup_firstcall (FunctionCallInfo fcinfo, FuncCallContext *funcctx, TSVectorStat *stat) |
static StatEntry * | walkStatEntryTree (TSVectorStat *stat) |
static Datum | ts_process_call (FuncCallContext *funcctx) |
static TSVectorStat * | ts_stat_sql (MemoryContext persistentContext, text *txt, text *ws) |
Datum | ts_stat1 (PG_FUNCTION_ARGS) |
Datum | ts_stat2 (PG_FUNCTION_ARGS) |
Datum | tsvector_update_trigger_byid (PG_FUNCTION_ARGS) |
Datum | tsvector_update_trigger_bycolumn (PG_FUNCTION_ARGS) |
#define compareEntry | ( | pa, | ||
a, | ||||
pb, | ||||
b | ||||
) |
tsCompareString((pa) + (a)->pos, (a)->len, \ (pb) + (b)->pos, (b)->len, \ false)
Definition at line 287 of file tsvector_op.c.
Referenced by tsvector_concat().
#define compareStatWord | ( | a, | ||
e, | ||||
t | ||||
) |
tsCompareString((a)->lexeme, (a)->lenlexeme, \ STRPTR(t) + (e)->pos, (e)->len, \ false)
Definition at line 877 of file tsvector_op.c.
Referenced by insertStatEntry().
#define STATENTRYHDRSZ (offsetof(StatEntry, lexeme)) |
Definition at line 50 of file tsvector_op.c.
Referenced by insertStatEntry().
#define STATHDRSIZE (offsetof(TSVectorStat, data)) |
Definition at line 64 of file tsvector_op.c.
#define TSVECTORCMPFUNC | ( | type, | ||
action, | ||||
ret | ||||
) |
Datum \ tsvector_##type(PG_FUNCTION_ARGS) \ { \ TSVector a = PG_GETARG_TSVECTOR(0); \ TSVector b = PG_GETARG_TSVECTOR(1); \ int res = silly_cmp_tsvector(a, b); \ PG_FREE_IF_COPY(a,0); \ PG_FREE_IF_COPY(b,1); \ PG_RETURN_##ret( res action 0 ); \ } \ /* keep compiler quiet - no extra ; */ \ extern int no_such_variable
Definition at line 165 of file tsvector_op.c.
static int32 add_pos | ( | TSVector | src, | |
WordEntry * | srcptr, | |||
TSVector | dest, | |||
WordEntry * | destptr, | |||
int32 | maxpos | |||
) | [static] |
Definition at line 297 of file tsvector_op.c.
References _POSVECPTR, WordEntry::haspos, i, LIMITPOS, MAXENTRYPOS, MAXNUMPOS, POSDATALEN, POSDATAPTR, WEP_GETPOS, WEP_GETWEIGHT, WEP_SETPOS, and WEP_SETWEIGHT.
Referenced by tsvector_concat().
{ uint16 *clen = &_POSVECPTR(dest, destptr)->npos; int i; uint16 slen = POSDATALEN(src, srcptr), startlen; WordEntryPos *spos = POSDATAPTR(src, srcptr), *dpos = POSDATAPTR(dest, destptr); if (!destptr->haspos) *clen = 0; startlen = *clen; for (i = 0; i < slen && *clen < MAXNUMPOS && (*clen == 0 || WEP_GETPOS(dpos[*clen - 1]) != MAXENTRYPOS - 1); i++) { WEP_SETWEIGHT(dpos[*clen], WEP_GETWEIGHT(spos[i])); WEP_SETPOS(dpos[*clen], LIMITPOS(WEP_GETPOS(spos[i]) + maxpos)); (*clen)++; } if (*clen != startlen) destptr->haspos = 1; return *clen - startlen; }
Definition at line 862 of file tsvector_op.c.
References POSDATALEN, POSDATAPTR, and WEP_GETWEIGHT.
Referenced by insertStatEntry().
{ int len = POSDATALEN(txt, wptr); int num = 0; WordEntryPos *ptr = POSDATAPTR(txt, wptr); while (len--) { if (weight & (1 << WEP_GETWEIGHT(*ptr))) num++; ptr++; } return num; }
static bool checkclass_str | ( | CHKVAL * | chkval, | |
WordEntry * | val, | |||
QueryOperand * | item | |||
) | [static] |
Definition at line 593 of file tsvector_op.c.
References WordEntry::len, WordEntryPosVector::npos, WordEntryPosVector::pos, WordEntry::pos, SHORTALIGN, CHKVAL::values, QueryOperand::weight, and WEP_GETWEIGHT.
Referenced by checkcondition_str().
{ WordEntryPosVector *posvec; WordEntryPos *ptr; uint16 len; posvec = (WordEntryPosVector *) (chkval->values + SHORTALIGN(val->pos + val->len)); len = posvec->npos; ptr = posvec->pos; while (len--) { if (item->weight & (1 << WEP_GETWEIGHT(*ptr))) return true; ptr++; } return false; }
static bool checkcondition_str | ( | void * | checkval, | |
QueryOperand * | val | |||
) | [static] |
Definition at line 618 of file tsvector_op.c.
References CHKVAL::arrb, CHKVAL::arre, checkclass_str(), difference(), QueryOperand::distance, WordEntry::haspos, WordEntry::len, QueryOperand::length, CHKVAL::operand, WordEntry::pos, QueryOperand::prefix, tsCompareString(), CHKVAL::values, and QueryOperand::weight.
Referenced by ts_match_vq().
{ CHKVAL *chkval = (CHKVAL *) checkval; WordEntry *StopLow = chkval->arrb; WordEntry *StopHigh = chkval->arre; WordEntry *StopMiddle = StopHigh; int difference = -1; bool res = false; /* Loop invariant: StopLow <= val < StopHigh */ while (StopLow < StopHigh) { StopMiddle = StopLow + (StopHigh - StopLow) / 2; difference = tsCompareString(chkval->operand + val->distance, val->length, chkval->values + StopMiddle->pos, StopMiddle->len, false); if (difference == 0) { res = (val->weight && StopMiddle->haspos) ? checkclass_str(chkval, StopMiddle, val) : true; break; } else if (difference > 0) StopLow = StopMiddle + 1; else StopHigh = StopMiddle; } if (!res && val->prefix) { /* * there was a failed exact search, so we should scan further to find * a prefix match. */ if (StopLow >= StopHigh) StopMiddle = StopHigh; while (res == false && StopMiddle < chkval->arre && tsCompareString(chkval->operand + val->distance, val->length, chkval->values + StopMiddle->pos, StopMiddle->len, true) == 0) { res = (val->weight && StopMiddle->haspos) ? checkclass_str(chkval, StopMiddle, val) : true; StopMiddle++; } } return res; }
static void chooseNextStatEntry | ( | MemoryContext | persistentContext, | |
TSVectorStat * | stat, | |||
TSVector | txt, | |||
uint32 | low, | |||
uint32 | high, | |||
uint32 | offset | |||
) | [static] |
Definition at line 949 of file tsvector_op.c.
References insertStatEntry().
Referenced by ts_accum().
{ uint32 pos; uint32 middle = (low + high) >> 1; pos = (low + middle) >> 1; if (low != middle && pos >= offset && pos - offset < txt->size) insertStatEntry(persistentContext, stat, txt, pos - offset); pos = (high + middle + 1) >> 1; if (middle + 1 != high && pos >= offset && pos - offset < txt->size) insertStatEntry(persistentContext, stat, txt, pos - offset); if (low != middle) chooseNextStatEntry(persistentContext, stat, txt, low, middle, offset); if (high != middle + 1) chooseNextStatEntry(persistentContext, stat, txt, middle + 1, high, offset); }
static void insertStatEntry | ( | MemoryContext | persistentContext, | |
TSVectorStat * | stat, | |||
TSVector | txt, | |||
uint32 | off | |||
) | [static] |
Definition at line 883 of file tsvector_op.c.
References ARRPTR, check_weight(), compareStatWord, WordEntry::haspos, StatEntry::left, WordEntry::len, StatEntry::lenlexeme, StatEntry::lexeme, TSVectorStat::maxdepth, MemoryContextAlloc(), StatEntry::ndoc, StatEntry::nentry, NULL, WordEntry::pos, POSDATALEN, StatEntry::right, TSVectorStat::root, STATENTRYHDRSZ, STRPTR, and TSVectorStat::weight.
Referenced by chooseNextStatEntry(), and ts_accum().
{ WordEntry *we = ARRPTR(txt) + off; StatEntry *node = stat->root, *pnode = NULL; int n, res = 0; uint32 depth = 1; if (stat->weight == 0) n = (we->haspos) ? POSDATALEN(txt, we) : 1; else n = (we->haspos) ? check_weight(txt, we, stat->weight) : 0; if (n == 0) return; /* nothing to insert */ while (node) { res = compareStatWord(node, we, txt); if (res == 0) { break; } else { pnode = node; node = (res < 0) ? node->left : node->right; } depth++; } if (depth > stat->maxdepth) stat->maxdepth = depth; if (node == NULL) { node = MemoryContextAlloc(persistentContext, STATENTRYHDRSZ + we->len); node->left = node->right = NULL; node->ndoc = 1; node->nentry = n; node->lenlexeme = we->len; memcpy(node->lexeme, STRPTR(txt) + we->pos, node->lenlexeme); if (pnode == NULL) { stat->root = node; } else { if (res < 0) pnode->left = node; else pnode->right = node; } } else { node->ndoc++; node->nentry += n; } }
Definition at line 77 of file tsvector_op.c.
References getBaseType().
Referenced by ts_stat_sql(), and tsvector_update_trigger().
{ if (typid == expected_type) return true; typid = getBaseType(typid); if (typid == expected_type) return true; return false; }
Definition at line 89 of file tsvector_op.c.
References BPCHAROID, getBaseType(), TEXTOID, and VARCHAROID.
Referenced by tsvector_update_trigger().
{ /* varchar(n) and char(n) are binary-compatible with text */ if (typid == TEXTOID || typid == VARCHAROID || typid == BPCHAROID) return true; /* Allow domains over these types, too */ typid = getBaseType(typid); if (typid == TEXTOID || typid == VARCHAROID || typid == BPCHAROID) return true; return false; }
Definition at line 106 of file tsvector_op.c.
References ARRPTR, WordEntry::haspos, i, WordEntry::len, WordEntry::pos, POSDATALEN, POSDATAPTR, TSVectorData::size, STRPTR, tsCompareString(), VARSIZE, WEP_GETPOS, and WEP_GETWEIGHT.
{ if (VARSIZE(a) < VARSIZE(b)) return -1; else if (VARSIZE(a) > VARSIZE(b)) return 1; else if (a->size < b->size) return -1; else if (a->size > b->size) return 1; else { WordEntry *aptr = ARRPTR(a); WordEntry *bptr = ARRPTR(b); int i = 0; int res; for (i = 0; i < a->size; i++) { if (aptr->haspos != bptr->haspos) { return (aptr->haspos > bptr->haspos) ? -1 : 1; } else if ((res = tsCompareString(STRPTR(a) + aptr->pos, aptr->len, STRPTR(b) + bptr->pos, bptr->len, false)) != 0) { return res; } else if (aptr->haspos) { WordEntryPos *ap = POSDATAPTR(a, aptr); WordEntryPos *bp = POSDATAPTR(b, bptr); int j; if (POSDATALEN(a, aptr) != POSDATALEN(b, bptr)) return (POSDATALEN(a, aptr) > POSDATALEN(b, bptr)) ? -1 : 1; for (j = 0; j < POSDATALEN(a, aptr); j++) { if (WEP_GETPOS(*ap) != WEP_GETPOS(*bp)) { return (WEP_GETPOS(*ap) > WEP_GETPOS(*bp)) ? -1 : 1; } else if (WEP_GETWEIGHT(*ap) != WEP_GETWEIGHT(*bp)) { return (WEP_GETWEIGHT(*ap) > WEP_GETWEIGHT(*bp)) ? -1 : 1; } ap++, bp++; } } aptr++; bptr++; } } return 0; }
static TSVectorStat* ts_accum | ( | MemoryContext | persistentContext, | |
TSVectorStat * | stat, | |||
Datum | data | |||
) | [static] |
Definition at line 981 of file tsvector_op.c.
References chooseNextStatEntry(), DatumGetPointer, DatumGetTSVector, i, insertStatEntry(), TSVectorStat::maxdepth, MemoryContextAllocZero(), NULL, pfree(), and TSVectorData::size.
Referenced by ts_stat_sql().
{ TSVector txt = DatumGetTSVector(data); uint32 i, nbit = 0, offset; if (stat == NULL) { /* Init in first */ stat = MemoryContextAllocZero(persistentContext, sizeof(TSVectorStat)); stat->maxdepth = 1; } /* simple check of correctness */ if (txt == NULL || txt->size == 0) { if (txt && txt != (TSVector) DatumGetPointer(data)) pfree(txt); return stat; } i = txt->size - 1; for (; i > 0; i >>= 1) nbit++; nbit = 1 << nbit; offset = (nbit - txt->size) / 2; insertStatEntry(persistentContext, stat, txt, (nbit >> 1) - offset); chooseNextStatEntry(persistentContext, stat, txt, 0, nbit, offset); return stat; }
bool TS_execute | ( | QueryItem * | curitem, | |
void * | checkval, | |||
bool | calcnot, | |||
bool(*)(void *checkval, QueryOperand *val) | chkcond | |||
) |
Definition at line 681 of file tsvector_op.c.
References check_stack_depth(), elog, ERROR, OP_AND, OP_NOT, OP_OR, QI_VAL, TS_execute(), and QueryItem::type.
Referenced by Cover(), gin_tsquery_consistent(), gtsvector_consistent(), hlCover(), TS_execute(), and ts_match_vq().
{ /* since this function recurses, it could be driven to stack overflow */ check_stack_depth(); if (curitem->type == QI_VAL) return chkcond(checkval, (QueryOperand *) curitem); switch (curitem->qoperator.oper) { case OP_NOT: if (calcnot) return !TS_execute(curitem + 1, checkval, calcnot, chkcond); else return true; case OP_AND: if (TS_execute(curitem + curitem->qoperator.left, checkval, calcnot, chkcond)) return TS_execute(curitem + 1, checkval, calcnot, chkcond); else return false; case OP_OR: if (TS_execute(curitem + curitem->qoperator.left, checkval, calcnot, chkcond)) return true; else return TS_execute(curitem + 1, checkval, calcnot, chkcond); default: elog(ERROR, "unrecognized operator: %d", curitem->qoperator.oper); } /* not reachable, but keep compiler quiet */ return false; }
Datum ts_match_qv | ( | PG_FUNCTION_ARGS | ) |
Definition at line 772 of file tsvector_op.c.
References DirectFunctionCall2, PG_GETARG_DATUM, PG_RETURN_DATUM, and ts_match_vq().
{ PG_RETURN_DATUM(DirectFunctionCall2(ts_match_vq, PG_GETARG_DATUM(1), PG_GETARG_DATUM(0))); }
Datum ts_match_tq | ( | PG_FUNCTION_ARGS | ) |
Definition at line 833 of file tsvector_op.c.
References DatumGetBool, DatumGetTSVector, DirectFunctionCall1, DirectFunctionCall2, pfree(), PG_FREE_IF_COPY, PG_GETARG_DATUM, PG_GETARG_TSQUERY, PG_RETURN_BOOL, to_tsvector(), ts_match_vq(), TSQueryGetDatum, and TSVectorGetDatum.
{ TSVector vector; TSQuery query = PG_GETARG_TSQUERY(1); bool res; vector = DatumGetTSVector(DirectFunctionCall1(to_tsvector, PG_GETARG_DATUM(0))); res = DatumGetBool(DirectFunctionCall2(ts_match_vq, TSVectorGetDatum(vector), TSQueryGetDatum(query))); pfree(vector); PG_FREE_IF_COPY(query, 1); PG_RETURN_BOOL(res); }
Datum ts_match_tt | ( | PG_FUNCTION_ARGS | ) |
Definition at line 811 of file tsvector_op.c.
References DatumGetBool, DatumGetTSQuery, DatumGetTSVector, DirectFunctionCall1, DirectFunctionCall2, pfree(), PG_GETARG_DATUM, PG_RETURN_BOOL, plainto_tsquery(), to_tsvector(), ts_match_vq(), TSQueryGetDatum, and TSVectorGetDatum.
{ TSVector vector; TSQuery query; bool res; vector = DatumGetTSVector(DirectFunctionCall1(to_tsvector, PG_GETARG_DATUM(0))); query = DatumGetTSQuery(DirectFunctionCall1(plainto_tsquery, PG_GETARG_DATUM(1))); res = DatumGetBool(DirectFunctionCall2(ts_match_vq, TSVectorGetDatum(vector), TSQueryGetDatum(query))); pfree(vector); pfree(query); PG_RETURN_BOOL(res); }
Datum ts_match_vq | ( | PG_FUNCTION_ARGS | ) |
Definition at line 780 of file tsvector_op.c.
References CHKVAL::arrb, CHKVAL::arre, ARRPTR, checkcondition_str(), GETOPERAND, GETQUERY, CHKVAL::operand, PG_FREE_IF_COPY, PG_GETARG_TSQUERY, PG_GETARG_TSVECTOR, PG_RETURN_BOOL, TSQueryData::size, TSVectorData::size, STRPTR, TS_execute(), val, and CHKVAL::values.
Referenced by ts_match_qv(), ts_match_tq(), and ts_match_tt().
{ TSVector val = PG_GETARG_TSVECTOR(0); TSQuery query = PG_GETARG_TSQUERY(1); CHKVAL chkval; bool result; if (!val->size || !query->size) { PG_FREE_IF_COPY(val, 0); PG_FREE_IF_COPY(query, 1); PG_RETURN_BOOL(false); } chkval.arrb = ARRPTR(val); chkval.arre = chkval.arrb + val->size; chkval.values = STRPTR(val); chkval.operand = GETOPERAND(query); result = TS_execute( GETQUERY(query), &chkval, true, checkcondition_str ); PG_FREE_IF_COPY(val, 0); PG_FREE_IF_COPY(query, 1); PG_RETURN_BOOL(result); }
static Datum ts_process_call | ( | FuncCallContext * | funcctx | ) | [static] |
Definition at line 1108 of file tsvector_op.c.
References FuncCallContext::attinmeta, BuildTupleFromCStrings(), HeapTupleGetDatum, StatEntry::lenlexeme, StatEntry::lexeme, StatEntry::ndoc, StatEntry::nentry, NULL, palloc(), pfree(), FuncCallContext::user_fctx, values, and walkStatEntryTree().
Referenced by ts_stat1(), and ts_stat2().
{ TSVectorStat *st; StatEntry *entry; st = (TSVectorStat *) funcctx->user_fctx; entry = walkStatEntryTree(st); if (entry != NULL) { Datum result; char *values[3]; char ndoc[16]; char nentry[16]; HeapTuple tuple; values[0] = palloc(entry->lenlexeme + 1); memcpy(values[0], entry->lexeme, entry->lenlexeme); (values[0])[entry->lenlexeme] = '\0'; sprintf(ndoc, "%d", entry->ndoc); values[1] = ndoc; sprintf(nentry, "%d", entry->nentry); values[2] = nentry; tuple = BuildTupleFromCStrings(funcctx->attinmeta, values); result = HeapTupleGetDatum(tuple); pfree(values[0]); /* mark entry as already visited */ entry->ndoc = 0; return result; } return (Datum) 0; }
static void ts_setup_firstcall | ( | FunctionCallInfo | fcinfo, | |
FuncCallContext * | funcctx, | |||
TSVectorStat * | stat | |||
) | [static] |
Definition at line 1016 of file tsvector_op.c.
References Assert, FuncCallContext::attinmeta, BlessTupleDesc(), CreateTemplateTupleDesc(), INT4OID, StatEntry::left, TSVectorStat::maxdepth, MemoryContextSwitchTo(), FuncCallContext::multi_call_memory_ctx, NULL, palloc0(), TSVectorStat::root, TSVectorStat::stack, TSVectorStat::stackpos, TEXTOID, FuncCallContext::tuple_desc, TupleDescGetAttInMetadata(), TupleDescInitEntry(), and FuncCallContext::user_fctx.
Referenced by ts_stat1(), and ts_stat2().
{ TupleDesc tupdesc; MemoryContext oldcontext; StatEntry *node; funcctx->user_fctx = (void *) stat; oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); stat->stack = palloc0(sizeof(StatEntry *) * (stat->maxdepth + 1)); stat->stackpos = 0; node = stat->root; /* find leftmost value */ if (node == NULL) stat->stack[stat->stackpos] = NULL; else for (;;) { stat->stack[stat->stackpos] = node; if (node->left) { stat->stackpos++; node = node->left; } else break; } Assert(stat->stackpos <= stat->maxdepth); tupdesc = CreateTemplateTupleDesc(3, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "word", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "ndoc", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 3, "nentry", INT4OID, -1, 0); funcctx->tuple_desc = BlessTupleDesc(tupdesc); funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc); MemoryContextSwitchTo(oldcontext); }
Datum ts_stat1 | ( | PG_FUNCTION_ARGS | ) |
Definition at line 1236 of file tsvector_op.c.
References FuncCallContext::multi_call_memory_ctx, NULL, PG_FREE_IF_COPY, PG_GETARG_TEXT_P, SPI_connect(), SPI_finish(), SRF_FIRSTCALL_INIT, SRF_IS_FIRSTCALL, SRF_PERCALL_SETUP, SRF_RETURN_DONE, SRF_RETURN_NEXT, ts_process_call(), ts_setup_firstcall(), and ts_stat_sql().
{ FuncCallContext *funcctx; Datum result; if (SRF_IS_FIRSTCALL()) { TSVectorStat *stat; text *txt = PG_GETARG_TEXT_P(0); funcctx = SRF_FIRSTCALL_INIT(); SPI_connect(); stat = ts_stat_sql(funcctx->multi_call_memory_ctx, txt, NULL); PG_FREE_IF_COPY(txt, 0); ts_setup_firstcall(fcinfo, 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); }
Datum ts_stat2 | ( | PG_FUNCTION_ARGS | ) |
Definition at line 1261 of file tsvector_op.c.
References FuncCallContext::multi_call_memory_ctx, PG_FREE_IF_COPY, PG_GETARG_TEXT_P, SPI_connect(), SPI_finish(), SRF_FIRSTCALL_INIT, SRF_IS_FIRSTCALL, SRF_PERCALL_SETUP, SRF_RETURN_DONE, SRF_RETURN_NEXT, ts_process_call(), ts_setup_firstcall(), and ts_stat_sql().
{ FuncCallContext *funcctx; Datum result; if (SRF_IS_FIRSTCALL()) { TSVectorStat *stat; text *txt = PG_GETARG_TEXT_P(0); text *ws = PG_GETARG_TEXT_P(1); funcctx = SRF_FIRSTCALL_INIT(); SPI_connect(); stat = ts_stat_sql(funcctx->multi_call_memory_ctx, txt, ws); PG_FREE_IF_COPY(txt, 0); PG_FREE_IF_COPY(ws, 1); ts_setup_firstcall(fcinfo, 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); }
static TSVectorStat* ts_stat_sql | ( | MemoryContext | persistentContext, | |
text * | txt, | |||
text * | ws | |||
) | [static] |
Definition at line 1148 of file tsvector_op.c.
References buf, elog, ereport, errcode(), errmsg(), ERROR, i, is_expected_type(), TSVectorStat::maxdepth, MemoryContextAllocZero(), tupleDesc::natts, NULL, pfree(), pg_mblen(), SPI_cursor_close(), SPI_cursor_fetch(), SPI_cursor_open(), SPI_freeplan(), SPI_freetuptable(), SPI_getbinval(), SPI_gettypeid(), SPI_prepare(), SPI_processed, SPI_tuptable, text_to_cstring(), ts_accum(), TSVECTOROID, SPITupleTable::tupdesc, SPITupleTable::vals, VARDATA, VARHDRSZ, VARSIZE, and TSVectorStat::weight.
Referenced by ts_stat1(), and ts_stat2().
{ char *query = text_to_cstring(txt); int i; TSVectorStat *stat; bool isnull; Portal portal; SPIPlanPtr plan; if ((plan = SPI_prepare(query, 0, NULL)) == NULL) /* internal error */ elog(ERROR, "SPI_prepare(\"%s\") failed", query); if ((portal = SPI_cursor_open(NULL, plan, NULL, NULL, true)) == NULL) /* internal error */ elog(ERROR, "SPI_cursor_open(\"%s\") failed", query); SPI_cursor_fetch(portal, true, 100); if (SPI_tuptable == NULL || SPI_tuptable->tupdesc->natts != 1 || !is_expected_type(SPI_gettypeid(SPI_tuptable->tupdesc, 1), TSVECTOROID)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("ts_stat query must return one tsvector column"))); stat = MemoryContextAllocZero(persistentContext, sizeof(TSVectorStat)); stat->maxdepth = 1; if (ws) { char *buf; buf = VARDATA(ws); while (buf - VARDATA(ws) < VARSIZE(ws) - VARHDRSZ) { if (pg_mblen(buf) == 1) { switch (*buf) { case 'A': case 'a': stat->weight |= 1 << 3; break; case 'B': case 'b': stat->weight |= 1 << 2; break; case 'C': case 'c': stat->weight |= 1 << 1; break; case 'D': case 'd': stat->weight |= 1; break; default: stat->weight |= 0; } } buf += pg_mblen(buf); } } 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) stat = ts_accum(persistentContext, stat, data); } 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; }
Definition at line 556 of file tsvector_op.c.
Referenced by checkcondition_str(), compareentry(), compareQueryOperand(), compareWORD(), gin_cmp_prefix(), gin_cmp_tslexeme(), hlfinditem(), QTNodeCompare(), and silly_cmp_tsvector().
{ int cmp; if (lena == 0) { if (prefix) cmp = 0; /* empty string is prefix of anything */ else cmp = (lenb > 0) ? -1 : 0; } else if (lenb == 0) { cmp = (lena > 0) ? 1 : 0; } else { cmp = memcmp(a, b, Min(lena, lenb)); if (prefix) { if (cmp == 0 && lena > lenb) cmp = 1; /* a is longer, so not a prefix of b */ } else if (cmp == 0 && lena != lenb) { cmp = (lena < lenb) ? -1 : 1; } } return cmp; }
Definition at line 727 of file tsvector_op.c.
References check_stack_depth(), elog, ERROR, QueryOperator::left, OP_AND, OP_NOT, OP_OR, QueryOperator::oper, QI_VAL, QueryItem::qoperator, tsquery_requires_match(), and QueryItem::type.
Referenced by gin_extract_tsquery(), and tsquery_requires_match().
{ /* since this function recurses, it could be driven to stack overflow */ check_stack_depth(); if (curitem->type == QI_VAL) return true; switch (curitem->qoperator.oper) { case OP_NOT: /* * Assume there are no required matches underneath a NOT. For * some cases with nested NOTs, we could prove there's a required * match, but it seems unlikely to be worth the trouble. */ return false; case OP_AND: /* If either side requires a match, we're good */ if (tsquery_requires_match(curitem + curitem->qoperator.left)) return true; else return tsquery_requires_match(curitem + 1); case OP_OR: /* Both sides must require a match */ if (tsquery_requires_match(curitem + curitem->qoperator.left)) return tsquery_requires_match(curitem + 1); else return false; default: elog(ERROR, "unrecognized operator: %d", curitem->qoperator.oper); } /* not reachable, but keep compiler quiet */ return false; }
Datum tsvector_concat | ( | PG_FUNCTION_ARGS | ) |
Definition at line 329 of file tsvector_op.c.
References _POSVECPTR, add_pos(), ARRPTR, Assert, CALCDATASIZE, cmp(), compareEntry, ereport, errcode(), errmsg(), ERROR, WordEntry::haspos, i, WordEntry::len, MAXSTRPOS, memmove, palloc0(), PG_FREE_IF_COPY, PG_GETARG_TSVECTOR, PG_RETURN_POINTER, WordEntry::pos, POSDATALEN, POSDATAPTR, SET_VARSIZE, SHORTALIGN, TSVectorData::size, STRPTR, VARSIZE, and WEP_GETPOS.
{ TSVector in1 = PG_GETARG_TSVECTOR(0); TSVector in2 = PG_GETARG_TSVECTOR(1); TSVector out; WordEntry *ptr; WordEntry *ptr1, *ptr2; WordEntryPos *p; int maxpos = 0, i, j, i1, i2, dataoff, output_bytes, output_size; char *data, *data1, *data2; /* Get max position in in1; we'll need this to offset in2's positions */ ptr = ARRPTR(in1); i = in1->size; while (i--) { if ((j = POSDATALEN(in1, ptr)) != 0) { p = POSDATAPTR(in1, ptr); while (j--) { if (WEP_GETPOS(*p) > maxpos) maxpos = WEP_GETPOS(*p); p++; } } ptr++; } ptr1 = ARRPTR(in1); ptr2 = ARRPTR(in2); data1 = STRPTR(in1); data2 = STRPTR(in2); i1 = in1->size; i2 = in2->size; /* * Conservative estimate of space needed. We might need all the data in * both inputs, and conceivably add a pad byte before position data for * each item where there was none before. */ output_bytes = VARSIZE(in1) + VARSIZE(in2) + i1 + i2; out = (TSVector) palloc0(output_bytes); SET_VARSIZE(out, output_bytes); /* * We must make out->size valid so that STRPTR(out) is sensible. We'll * collapse out any unused space at the end. */ out->size = in1->size + in2->size; ptr = ARRPTR(out); data = STRPTR(out); dataoff = 0; while (i1 && i2) { int cmp = compareEntry(data1, ptr1, data2, ptr2); if (cmp < 0) { /* in1 first */ ptr->haspos = ptr1->haspos; ptr->len = ptr1->len; memcpy(data + dataoff, data1 + ptr1->pos, ptr1->len); ptr->pos = dataoff; dataoff += ptr1->len; if (ptr->haspos) { dataoff = SHORTALIGN(dataoff); memcpy(data + dataoff, _POSVECPTR(in1, ptr1), POSDATALEN(in1, ptr1) * sizeof(WordEntryPos) + sizeof(uint16)); dataoff += POSDATALEN(in1, ptr1) * sizeof(WordEntryPos) + sizeof(uint16); } ptr++; ptr1++; i1--; } else if (cmp > 0) { /* in2 first */ ptr->haspos = ptr2->haspos; ptr->len = ptr2->len; memcpy(data + dataoff, data2 + ptr2->pos, ptr2->len); ptr->pos = dataoff; dataoff += ptr2->len; if (ptr->haspos) { int addlen = add_pos(in2, ptr2, out, ptr, maxpos); if (addlen == 0) ptr->haspos = 0; else { dataoff = SHORTALIGN(dataoff); dataoff += addlen * sizeof(WordEntryPos) + sizeof(uint16); } } ptr++; ptr2++; i2--; } else { ptr->haspos = ptr1->haspos | ptr2->haspos; ptr->len = ptr1->len; memcpy(data + dataoff, data1 + ptr1->pos, ptr1->len); ptr->pos = dataoff; dataoff += ptr1->len; if (ptr->haspos) { if (ptr1->haspos) { dataoff = SHORTALIGN(dataoff); memcpy(data + dataoff, _POSVECPTR(in1, ptr1), POSDATALEN(in1, ptr1) * sizeof(WordEntryPos) + sizeof(uint16)); dataoff += POSDATALEN(in1, ptr1) * sizeof(WordEntryPos) + sizeof(uint16); if (ptr2->haspos) dataoff += add_pos(in2, ptr2, out, ptr, maxpos) * sizeof(WordEntryPos); } else /* must have ptr2->haspos */ { int addlen = add_pos(in2, ptr2, out, ptr, maxpos); if (addlen == 0) ptr->haspos = 0; else { dataoff = SHORTALIGN(dataoff); dataoff += addlen * sizeof(WordEntryPos) + sizeof(uint16); } } } ptr++; ptr1++; ptr2++; i1--; i2--; } } while (i1) { ptr->haspos = ptr1->haspos; ptr->len = ptr1->len; memcpy(data + dataoff, data1 + ptr1->pos, ptr1->len); ptr->pos = dataoff; dataoff += ptr1->len; if (ptr->haspos) { dataoff = SHORTALIGN(dataoff); memcpy(data + dataoff, _POSVECPTR(in1, ptr1), POSDATALEN(in1, ptr1) * sizeof(WordEntryPos) + sizeof(uint16)); dataoff += POSDATALEN(in1, ptr1) * sizeof(WordEntryPos) + sizeof(uint16); } ptr++; ptr1++; i1--; } while (i2) { ptr->haspos = ptr2->haspos; ptr->len = ptr2->len; memcpy(data + dataoff, data2 + ptr2->pos, ptr2->len); ptr->pos = dataoff; dataoff += ptr2->len; if (ptr->haspos) { int addlen = add_pos(in2, ptr2, out, ptr, maxpos); if (addlen == 0) ptr->haspos = 0; else { dataoff = SHORTALIGN(dataoff); dataoff += addlen * sizeof(WordEntryPos) + sizeof(uint16); } } ptr++; ptr2++; i2--; } /* * Instead of checking each offset individually, we check for overflow of * pos fields once at the end. */ if (dataoff > MAXSTRPOS) ereport(ERROR, (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), errmsg("string is too long for tsvector (%d bytes, max %d bytes)", dataoff, MAXSTRPOS))); /* * Adjust sizes (asserting that we didn't overrun the original estimates) * and collapse out any unused array entries. */ output_size = ptr - ARRPTR(out); Assert(output_size <= out->size); out->size = output_size; if (data != STRPTR(out)) memmove(STRPTR(out), data, dataoff); output_bytes = CALCDATASIZE(out->size, dataoff); Assert(output_bytes <= VARSIZE(out)); SET_VARSIZE(out, output_bytes); PG_FREE_IF_COPY(in1, 0); PG_FREE_IF_COPY(in2, 1); PG_RETURN_POINTER(out); }
Datum tsvector_length | ( | PG_FUNCTION_ARGS | ) |
Definition at line 221 of file tsvector_op.c.
References PG_FREE_IF_COPY, PG_GETARG_TSVECTOR, PG_RETURN_INT32, and TSVectorData::size.
{ TSVector in = PG_GETARG_TSVECTOR(0); int32 ret = in->size; PG_FREE_IF_COPY(in, 0); PG_RETURN_INT32(ret); }
Datum tsvector_setweight | ( | PG_FUNCTION_ARGS | ) |
Definition at line 231 of file tsvector_op.c.
References ARRPTR, elog, ERROR, i, palloc(), PG_FREE_IF_COPY, PG_GETARG_CHAR, PG_GETARG_TSVECTOR, PG_RETURN_POINTER, POSDATALEN, POSDATAPTR, TSVectorData::size, VARSIZE, and WEP_SETWEIGHT.
{ TSVector in = PG_GETARG_TSVECTOR(0); char cw = PG_GETARG_CHAR(1); TSVector out; int i, j; WordEntry *entry; WordEntryPos *p; int w = 0; switch (cw) { case 'A': case 'a': w = 3; break; case 'B': case 'b': w = 2; break; case 'C': case 'c': w = 1; break; case 'D': case 'd': w = 0; break; default: /* internal error */ elog(ERROR, "unrecognized weight: %d", cw); } out = (TSVector) palloc(VARSIZE(in)); memcpy(out, in, VARSIZE(in)); entry = ARRPTR(out); i = out->size; while (i--) { if ((j = POSDATALEN(out, entry)) != 0) { p = POSDATAPTR(out, entry); while (j--) { WEP_SETWEIGHT(*p, w); p++; } } entry++; } PG_FREE_IF_COPY(in, 0); PG_RETURN_POINTER(out); }
Datum tsvector_strip | ( | PG_FUNCTION_ARGS | ) |
Definition at line 188 of file tsvector_op.c.
References ARRPTR, CALCDATASIZE, cur, i, WordEntry::len, palloc0(), PG_FREE_IF_COPY, PG_GETARG_TSVECTOR, PG_RETURN_POINTER, SET_VARSIZE, TSVectorData::size, and STRPTR.
{ TSVector in = PG_GETARG_TSVECTOR(0); TSVector out; int i, len = 0; WordEntry *arrin = ARRPTR(in), *arrout; char *cur; for (i = 0; i < in->size; i++) len += arrin[i].len; len = CALCDATASIZE(in->size, len); out = (TSVector) palloc0(len); SET_VARSIZE(out, len); out->size = in->size; arrout = ARRPTR(out); cur = STRPTR(out); for (i = 0; i < in->size; i++) { memcpy(cur, STRPTR(in) + arrin[i].pos, arrin[i].len); arrout[i].haspos = 0; arrout[i].len = arrin[i].len; arrout[i].pos = cur - STRPTR(out); cur += arrout[i].len; } PG_FREE_IF_COPY(in, 0); PG_RETURN_POINTER(out); }
Definition at line 1311 of file tsvector_op.c.
References CALCDATASIZE, CALLED_AS_TRIGGER, ParsedText::curwords, DatumGetObjectId, DatumGetPointer, DatumGetTextP, elog, ereport, errcode(), errmsg(), ERROR, get_ts_config_oid(), i, is_expected_type(), is_text_type(), ParsedText::lenwords, list_length(), make_tsvector(), NULL, numattr, palloc(), parsetext(), pfree(), PointerGetDatum, ParsedText::pos, RelationData::rd_att, REGCONFIGOID, SET_VARSIZE, TSVectorData::size, SPI_ERROR_NOATTRIBUTE, SPI_fnumber(), SPI_getbinval(), SPI_gettypeid(), SPI_modifytuple(), SPI_result, stringToQualifiedNameList(), TriggerData::tg_event, TriggerData::tg_newtuple, TriggerData::tg_relation, TriggerData::tg_trigger, TriggerData::tg_trigtuple, Trigger::tgargs, Trigger::tgnargs, TRIGGER_FIRED_BEFORE, TRIGGER_FIRED_BY_INSERT, TRIGGER_FIRED_BY_UPDATE, TRIGGER_FIRED_FOR_ROW, TSVECTOROID, VARDATA, VARHDRSZ, VARSIZE, and ParsedText::words.
Referenced by tsvector_update_trigger_bycolumn(), and tsvector_update_trigger_byid().
{ TriggerData *trigdata; Trigger *trigger; Relation rel; HeapTuple rettuple = NULL; int tsvector_attr_num, i; ParsedText prs; Datum datum; bool isnull; text *txt; Oid cfgId; /* Check call context */ if (!CALLED_AS_TRIGGER(fcinfo)) /* internal error */ elog(ERROR, "tsvector_update_trigger: not fired by trigger manager"); trigdata = (TriggerData *) fcinfo->context; if (!TRIGGER_FIRED_FOR_ROW(trigdata->tg_event)) elog(ERROR, "tsvector_update_trigger: must be fired for row"); if (!TRIGGER_FIRED_BEFORE(trigdata->tg_event)) elog(ERROR, "tsvector_update_trigger: must be fired BEFORE event"); if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event)) rettuple = trigdata->tg_trigtuple; else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event)) rettuple = trigdata->tg_newtuple; else elog(ERROR, "tsvector_update_trigger: must be fired for INSERT or UPDATE"); trigger = trigdata->tg_trigger; rel = trigdata->tg_relation; if (trigger->tgnargs < 3) elog(ERROR, "tsvector_update_trigger: arguments must be tsvector_field, ts_config, text_field1, ...)"); /* Find the target tsvector column */ tsvector_attr_num = SPI_fnumber(rel->rd_att, trigger->tgargs[0]); if (tsvector_attr_num == SPI_ERROR_NOATTRIBUTE) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("tsvector column \"%s\" does not exist", trigger->tgargs[0]))); if (!is_expected_type(SPI_gettypeid(rel->rd_att, tsvector_attr_num), TSVECTOROID)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("column \"%s\" is not of tsvector type", trigger->tgargs[0]))); /* Find the configuration to use */ if (config_column) { int config_attr_num; config_attr_num = SPI_fnumber(rel->rd_att, trigger->tgargs[1]); if (config_attr_num == SPI_ERROR_NOATTRIBUTE) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("configuration column \"%s\" does not exist", trigger->tgargs[1]))); if (!is_expected_type(SPI_gettypeid(rel->rd_att, config_attr_num), REGCONFIGOID)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("column \"%s\" is not of regconfig type", trigger->tgargs[1]))); datum = SPI_getbinval(rettuple, rel->rd_att, config_attr_num, &isnull); if (isnull) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("configuration column \"%s\" must not be null", trigger->tgargs[1]))); cfgId = DatumGetObjectId(datum); } else { List *names; names = stringToQualifiedNameList(trigger->tgargs[1]); /* require a schema so that results are not search path dependent */ if (list_length(names) < 2) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("text search configuration name \"%s\" must be schema-qualified", trigger->tgargs[1]))); cfgId = get_ts_config_oid(names, false); } /* initialize parse state */ prs.lenwords = 32; prs.curwords = 0; prs.pos = 0; prs.words = (ParsedWord *) palloc(sizeof(ParsedWord) * prs.lenwords); /* find all words in indexable column(s) */ for (i = 2; i < trigger->tgnargs; i++) { int numattr; numattr = SPI_fnumber(rel->rd_att, trigger->tgargs[i]); if (numattr == SPI_ERROR_NOATTRIBUTE) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("column \"%s\" does not exist", trigger->tgargs[i]))); if (!is_text_type(SPI_gettypeid(rel->rd_att, numattr))) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("column \"%s\" is not of a character type", trigger->tgargs[i]))); datum = SPI_getbinval(rettuple, rel->rd_att, numattr, &isnull); if (isnull) continue; txt = DatumGetTextP(datum); parsetext(cfgId, &prs, VARDATA(txt), VARSIZE(txt) - VARHDRSZ); if (txt != (text *) DatumGetPointer(datum)) pfree(txt); } /* make tsvector value */ if (prs.curwords) { datum = PointerGetDatum(make_tsvector(&prs)); rettuple = SPI_modifytuple(rel, rettuple, 1, &tsvector_attr_num, &datum, NULL); pfree(DatumGetPointer(datum)); } else { TSVector out = palloc(CALCDATASIZE(0, 0)); SET_VARSIZE(out, CALCDATASIZE(0, 0)); out->size = 0; datum = PointerGetDatum(out); rettuple = SPI_modifytuple(rel, rettuple, 1, &tsvector_attr_num, &datum, NULL); pfree(prs.words); } if (rettuple == NULL) /* internal error */ elog(ERROR, "tsvector_update_trigger: %d returned by SPI_modifytuple", SPI_result); return PointerGetDatum(rettuple); }
Datum tsvector_update_trigger_bycolumn | ( | PG_FUNCTION_ARGS | ) |
Definition at line 1305 of file tsvector_op.c.
References tsvector_update_trigger().
{ return tsvector_update_trigger(fcinfo, true); }
Datum tsvector_update_trigger_byid | ( | PG_FUNCTION_ARGS | ) |
Definition at line 1299 of file tsvector_op.c.
References tsvector_update_trigger().
Referenced by tsa_tsearch2().
{ return tsvector_update_trigger(fcinfo, false); }
TSVECTORCMPFUNC | ( | gt | , | |
BOOL | ||||
) |
TSVECTORCMPFUNC | ( | lt | ) |
TSVECTORCMPFUNC | ( | eq | , | |
BOOL | ||||
) |
TSVECTORCMPFUNC | ( | ge | , | |
>= | , | |||
BOOL | ||||
) |
TSVECTORCMPFUNC | ( | le | , | |
<= | , | |||
BOOL | ||||
) |
TSVECTORCMPFUNC | ( | cmp | , | |
+ | , | |||
INT32 | ||||
) |
TSVECTORCMPFUNC | ( | ne | , | |
! | , | |||
BOOL | ||||
) |
static StatEntry* walkStatEntryTree | ( | TSVectorStat * | stat | ) | [static] |
Definition at line 1062 of file tsvector_op.c.
References Assert, StatEntry::left, TSVectorStat::maxdepth, StatEntry::ndoc, NULL, StatEntry::right, TSVectorStat::stack, and TSVectorStat::stackpos.
Referenced by ts_process_call().
{ StatEntry *node = stat->stack[stat->stackpos]; if (node == NULL) return NULL; if (node->ndoc != 0) { /* return entry itself: we already was at left sublink */ return node; } else if (node->right && node->right != stat->stack[stat->stackpos + 1]) { /* go on right sublink */ stat->stackpos++; node = node->right; /* find most-left value */ for (;;) { stat->stack[stat->stackpos] = node; if (node->left) { stat->stackpos++; node = node->left; } else break; } Assert(stat->stackpos <= stat->maxdepth); } else { /* we already return all left subtree, itself and right subtree */ if (stat->stackpos == 0) return NULL; stat->stackpos--; return walkStatEntryTree(stat); } return node; }