Datum tsquery_rewrite(PG_FUNCTION_ARGS) { TSQuery query = PG_GETARG_TSQUERY_COPY(0); TSQuery ex = PG_GETARG_TSQUERY(1); TSQuery subst = PG_GETARG_TSQUERY(2); TSQuery rewrited = query; QTNode *tree, *qex, *subs = NULL; if (query->size == 0 || ex->size == 0) { PG_FREE_IF_COPY(ex, 1); PG_FREE_IF_COPY(subst, 2); PG_RETURN_POINTER(rewrited); } tree = QT2QTN(GETQUERY(query), GETOPERAND(query)); QTNTernary(tree); QTNSort(tree); qex = QT2QTN(GETQUERY(ex), GETOPERAND(ex)); QTNTernary(qex); QTNSort(qex); if (subst->size) subs = QT2QTN(GETQUERY(subst), GETOPERAND(subst)); tree = findsubquery(tree, qex, subs, NULL); QTNFree(qex); QTNFree(subs); if (!tree) { SET_VARSIZE(rewrited, HDRSIZETQ); rewrited->size = 0; PG_FREE_IF_COPY(ex, 1); PG_FREE_IF_COPY(subst, 2); PG_RETURN_POINTER(rewrited); } else { QTNBinary(tree); rewrited = QTN2QT(tree); QTNFree(tree); } PG_FREE_IF_COPY(query, 0); PG_FREE_IF_COPY(ex, 1); PG_FREE_IF_COPY(subst, 2); PG_RETURN_POINTER(rewrited); }
Datum tsquery_rewrite_query(PG_FUNCTION_ARGS) { QUERYTYPE *query = (QUERYTYPE *) DatumGetPointer(PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(0))); QUERYTYPE *ex = (QUERYTYPE *) DatumGetPointer(PG_DETOAST_DATUM(PG_GETARG_DATUM(1))); QUERYTYPE *subst = (QUERYTYPE *) DatumGetPointer(PG_DETOAST_DATUM(PG_GETARG_DATUM(2))); QUERYTYPE *rewrited = query; QTNode *tree, *qex, *subs = NULL; if (query->size == 0 || ex->size == 0) { PG_FREE_IF_COPY(ex, 1); PG_FREE_IF_COPY(subst, 2); PG_RETURN_POINTER(rewrited); } tree = QT2QTN(GETQUERY(query), GETOPERAND(query)); QTNTernary(tree); QTNSort(tree); qex = QT2QTN(GETQUERY(ex), GETOPERAND(ex)); QTNTernary(qex); QTNSort(qex); if (subst->size) subs = QT2QTN(GETQUERY(subst), GETOPERAND(subst)); tree = findsubquery(tree, qex, PlainMemory, subs, NULL); QTNFree(qex); QTNFree(subs); if (!tree) { rewrited->len = HDRSIZEQT; rewrited->size = 0; PG_FREE_IF_COPY(ex, 1); PG_FREE_IF_COPY(subst, 2); PG_RETURN_POINTER(rewrited); } else { QTNBinary(tree); rewrited = QTN2QT(tree, PlainMemory); QTNFree(tree); } PG_FREE_IF_COPY(query, 0); PG_FREE_IF_COPY(ex, 1); PG_FREE_IF_COPY(subst, 2); PG_RETURN_POINTER(rewrited); }
/* * Remove unnecessary intermediate nodes. For example: * * OR OR * a OR -> a b c * b c */ void QTNTernary(QTNode *in) { int i; /* since this function recurses, it could be driven to stack overflow. */ check_stack_depth(); if (in->valnode->type != QI_OPR) return; for (i = 0; i < in->nchild; i++) QTNTernary(in->child[i]); /* Only AND and OR are associative, so don't flatten other node types */ if (in->valnode->qoperator.oper != OP_AND && in->valnode->qoperator.oper != OP_OR) return; for (i = 0; i < in->nchild; i++) { QTNode *cc = in->child[i]; if (cc->valnode->type == QI_OPR && in->valnode->qoperator.oper == cc->valnode->qoperator.oper) { int oldnchild = in->nchild; in->nchild += cc->nchild - 1; in->child = (QTNode **) repalloc(in->child, in->nchild * sizeof(QTNode *)); if (i + 1 != oldnchild) memmove(in->child + i + cc->nchild, in->child + i + 1, (oldnchild - i - 1) * sizeof(QTNode *)); memcpy(in->child + i, cc->child, cc->nchild * sizeof(QTNode *)); i += cc->nchild - 1; if (cc->flags & QTN_NEEDFREE) pfree(cc->valnode); pfree(cc); } } }
Datum tsquery_rewrite(PG_FUNCTION_ARGS) { QUERYTYPE *query = (QUERYTYPE *) DatumGetPointer(PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(0))); text *in = PG_GETARG_TEXT_P(1); QUERYTYPE *rewrited = query; QTNode *tree; char *buf; void *plan; Portal portal; bool isnull; int i; if (query->size == 0) { PG_FREE_IF_COPY(in, 1); PG_RETURN_POINTER(rewrited); } tree = QT2QTN(GETQUERY(query), GETOPERAND(query)); QTNTernary(tree); QTNSort(tree); buf = (char *) palloc(VARSIZE(in)); memcpy(buf, VARDATA(in), VARSIZE(in) - VARHDRSZ); buf[VARSIZE(in) - VARHDRSZ] = '\0'; SPI_connect(); if (tsqOid == InvalidOid) get_tsq_Oid(); if ((plan = SPI_prepare(buf, 0, NULL)) == NULL) elog(ERROR, "SPI_prepare('%s') returns NULL", buf); if ((portal = SPI_cursor_open(NULL, plan, NULL, NULL, false)) == NULL) elog(ERROR, "SPI_cursor_open('%s') returns NULL", buf); SPI_cursor_fetch(portal, true, 100); if (SPI_tuptable->tupdesc->natts != 2) elog(ERROR, "number of fields doesn't equal to 2"); if (SPI_gettypeid(SPI_tuptable->tupdesc, 1) != tsqOid) elog(ERROR, "column #1 isn't of tsquery type"); if (SPI_gettypeid(SPI_tuptable->tupdesc, 2) != tsqOid) elog(ERROR, "column #2 isn't of tsquery type"); while (SPI_processed > 0 && tree) { for (i = 0; i < SPI_processed && tree; i++) { Datum qdata = SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 1, &isnull); Datum sdata; if (isnull) continue; sdata = SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 2, &isnull); if (!isnull) { QUERYTYPE *qtex = (QUERYTYPE *) DatumGetPointer(PG_DETOAST_DATUM(qdata)); QUERYTYPE *qtsubs = (QUERYTYPE *) DatumGetPointer(PG_DETOAST_DATUM(sdata)); QTNode *qex, *qsubs = NULL; if (qtex->size == 0) { if (qtex != (QUERYTYPE *) DatumGetPointer(qdata)) pfree(qtex); if (qtsubs != (QUERYTYPE *) DatumGetPointer(sdata)) pfree(qtsubs); continue; } qex = QT2QTN(GETQUERY(qtex), GETOPERAND(qtex)); QTNTernary(qex); QTNSort(qex); if (qtsubs->size) qsubs = QT2QTN(GETQUERY(qtsubs), GETOPERAND(qtsubs)); tree = findsubquery(tree, qex, SPIMemory, qsubs, NULL); QTNFree(qex); if (qtex != (QUERYTYPE *) DatumGetPointer(qdata)) pfree(qtex); QTNFree(qsubs); if (qtsubs != (QUERYTYPE *) DatumGetPointer(sdata)) pfree(qtsubs); } } SPI_freetuptable(SPI_tuptable); SPI_cursor_fetch(portal, true, 100); } SPI_freetuptable(SPI_tuptable); SPI_cursor_close(portal); SPI_freeplan(plan); SPI_finish(); if (tree) { QTNBinary(tree); rewrited = QTN2QT(tree, PlainMemory); QTNFree(tree); PG_FREE_IF_COPY(query, 0); } else { rewrited->len = HDRSIZEQT; rewrited->size = 0; } pfree(buf); PG_FREE_IF_COPY(in, 1); PG_RETURN_POINTER(rewrited); }
Datum rewrite_accum(PG_FUNCTION_ARGS) { QUERYTYPE *acc = (QUERYTYPE *) PG_GETARG_POINTER(0); ArrayType *qa = (ArrayType *) DatumGetPointer(PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(1))); QUERYTYPE *q; QTNode *qex, *subs = NULL, *acctree; bool isfind = false; Datum *elemsp; int nelemsp; AggregateContext = ((AggState *) fcinfo->context)->aggcontext; if (acc == NULL || PG_ARGISNULL(0)) { acc = (QUERYTYPE *) MEMALLOC(AggMemory, sizeof(QUERYTYPE)); acc->len = HDRSIZEQT; acc->size = 0; } if (qa == NULL || PG_ARGISNULL(1)) { PG_FREE_IF_COPY(qa, 1); PG_RETURN_POINTER(acc); } if (ARR_NDIM(qa) != 1) elog(ERROR, "array must be one-dimensional, not %d dimension", ARR_NDIM(qa)); if (ArrayGetNItems(ARR_NDIM(qa), ARR_DIMS(qa)) != 3) elog(ERROR, "array should have only three elements"); if (tsqOid == InvalidOid) { SPI_connect(); get_tsq_Oid(); SPI_finish(); } if (ARR_ELEMTYPE(qa) != tsqOid) elog(ERROR, "array should contain tsquery type"); deconstruct_array(qa, tsqOid, -1, false, 'i', &elemsp, NULL, &nelemsp); q = (QUERYTYPE *) DatumGetPointer(elemsp[0]); if (q->size == 0) { pfree(elemsp); PG_RETURN_POINTER(acc); } if (!acc->size) { if (acc->len > HDRSIZEQT) { pfree(elemsp); PG_RETURN_POINTER(acc); } else acctree = QT2QTN(GETQUERY(q), GETOPERAND(q)); } else acctree = QT2QTN(GETQUERY(acc), GETOPERAND(acc)); QTNTernary(acctree); QTNSort(acctree); q = (QUERYTYPE *) DatumGetPointer(elemsp[1]); if (q->size == 0) { pfree(elemsp); PG_RETURN_POINTER(acc); } qex = QT2QTN(GETQUERY(q), GETOPERAND(q)); QTNTernary(qex); QTNSort(qex); q = (QUERYTYPE *) DatumGetPointer(elemsp[2]); if (q->size) subs = QT2QTN(GETQUERY(q), GETOPERAND(q)); acctree = findsubquery(acctree, qex, PlainMemory, subs, &isfind); if (isfind || !acc->size) { /* pfree( acc ); do not pfree(p), because nodeAgg.c will */ if (acctree) { QTNBinary(acctree); acc = QTN2QT(acctree, AggMemory); } else { acc = (QUERYTYPE *) MEMALLOC(AggMemory, HDRSIZEQT * 2); acc->len = HDRSIZEQT * 2; acc->size = 0; } } pfree(elemsp); QTNFree(qex); QTNFree(subs); QTNFree(acctree); PG_RETURN_POINTER(acc); }
Datum tsa_rewrite_accum(PG_FUNCTION_ARGS) { TSQuery acc; ArrayType *qa; TSQuery q; QTNode *qex = NULL, *subs = NULL, *acctree = NULL; bool isfind = false; Datum *elemsp; int nelemsp; MemoryContext aggcontext; MemoryContext oldcontext; aggcontext = ((AggState *) fcinfo->context)->aggcontext; if (PG_ARGISNULL(0) || PG_GETARG_POINTER(0) == NULL) { acc = (TSQuery) MemoryContextAlloc(aggcontext, HDRSIZETQ); SET_VARSIZE(acc, HDRSIZETQ); acc->size = 0; } else acc = PG_GETARG_TSQUERY(0); if (PG_ARGISNULL(1) || PG_GETARG_POINTER(1) == NULL) PG_RETURN_TSQUERY(acc); else qa = PG_GETARG_ARRAYTYPE_P_COPY(1); if (ARR_NDIM(qa) != 1) elog(ERROR, "array must be one-dimensional, not %d dimensions", ARR_NDIM(qa)); if (ArrayGetNItems(ARR_NDIM(qa), ARR_DIMS(qa)) != 3) elog(ERROR, "array must have three elements"); if (ARR_ELEMTYPE(qa) != TSQUERYOID) elog(ERROR, "array must contain tsquery elements"); deconstruct_array(qa, TSQUERYOID, -1, false, 'i', &elemsp, NULL, &nelemsp); q = DatumGetTSQuery(elemsp[0]); if (q->size == 0) { pfree(elemsp); PG_RETURN_POINTER(acc); } if (!acc->size) { if (VARSIZE(acc) > HDRSIZETQ) { pfree(elemsp); PG_RETURN_POINTER(acc); } else acctree = QT2QTN(GETQUERY(q), GETOPERAND(q)); } else acctree = QT2QTN(GETQUERY(acc), GETOPERAND(acc)); QTNTernary(acctree); QTNSort(acctree); q = DatumGetTSQuery(elemsp[1]); if (q->size == 0) { pfree(elemsp); PG_RETURN_POINTER(acc); } qex = QT2QTN(GETQUERY(q), GETOPERAND(q)); QTNTernary(qex); QTNSort(qex); q = DatumGetTSQuery(elemsp[2]); if (q->size) subs = QT2QTN(GETQUERY(q), GETOPERAND(q)); acctree = findsubquery(acctree, qex, subs, &isfind); if (isfind || !acc->size) { /* pfree( acc ); do not pfree(p), because nodeAgg.c will */ if (acctree) { QTNBinary(acctree); oldcontext = MemoryContextSwitchTo(aggcontext); acc = QTN2QT(acctree); MemoryContextSwitchTo(oldcontext); } else { acc = (TSQuery) MemoryContextAlloc(aggcontext, HDRSIZETQ); SET_VARSIZE(acc, HDRSIZETQ); acc->size = 0; } } pfree(elemsp); QTNFree(qex); QTNFree(subs); QTNFree(acctree); PG_RETURN_TSQUERY(acc); }
Datum tsquery_rewrite_query(PG_FUNCTION_ARGS) { TSQuery query = PG_GETARG_TSQUERY_COPY(0); text *in = PG_GETARG_TEXT_P(1); TSQuery rewrited = query; MemoryContext outercontext = CurrentMemoryContext; MemoryContext oldcontext; QTNode *tree; char *buf; SPIPlanPtr plan; Portal portal; bool isnull; if (query->size == 0) { PG_FREE_IF_COPY(in, 1); PG_RETURN_POINTER(rewrited); } tree = QT2QTN(GETQUERY(query), GETOPERAND(query)); QTNTernary(tree); QTNSort(tree); buf = text_to_cstring(in); SPI_connect(); if ((plan = SPI_prepare(buf, 0, NULL)) == NULL) elog(ERROR, "SPI_prepare(\"%s\") failed", buf); if ((portal = SPI_cursor_open(NULL, plan, NULL, NULL, true)) == NULL) elog(ERROR, "SPI_cursor_open(\"%s\") failed", buf); SPI_cursor_fetch(portal, true, 100); if (SPI_tuptable == NULL || SPI_tuptable->tupdesc->natts != 2 || SPI_gettypeid(SPI_tuptable->tupdesc, 1) != TSQUERYOID || SPI_gettypeid(SPI_tuptable->tupdesc, 2) != TSQUERYOID) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("ts_rewrite query must return two tsquery columns"))); while (SPI_processed > 0 && tree) { uint64 i; for (i = 0; i < SPI_processed && tree; i++) { Datum qdata = SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 1, &isnull); Datum sdata; if (isnull) continue; sdata = SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 2, &isnull); if (!isnull) { TSQuery qtex = DatumGetTSQuery(qdata); TSQuery qtsubs = DatumGetTSQuery(sdata); QTNode *qex, *qsubs = NULL; if (qtex->size == 0) { if (qtex != (TSQuery) DatumGetPointer(qdata)) pfree(qtex); if (qtsubs != (TSQuery) DatumGetPointer(sdata)) pfree(qtsubs); continue; } qex = QT2QTN(GETQUERY(qtex), GETOPERAND(qtex)); QTNTernary(qex); QTNSort(qex); if (qtsubs->size) qsubs = QT2QTN(GETQUERY(qtsubs), GETOPERAND(qtsubs)); oldcontext = MemoryContextSwitchTo(outercontext); tree = findsubquery(tree, qex, qsubs, NULL); MemoryContextSwitchTo(oldcontext); QTNFree(qex); if (qtex != (TSQuery) DatumGetPointer(qdata)) pfree(qtex); QTNFree(qsubs); if (qtsubs != (TSQuery) DatumGetPointer(sdata)) pfree(qtsubs); if (tree) { /* ready the tree for another pass */ QTNClearFlags(tree, QTN_NOCHANGE); QTNSort(tree); } } } SPI_freetuptable(SPI_tuptable); SPI_cursor_fetch(portal, true, 100); } SPI_freetuptable(SPI_tuptable); SPI_cursor_close(portal); SPI_freeplan(plan); SPI_finish(); if (tree) { QTNBinary(tree); rewrited = QTN2QT(tree); QTNFree(tree); PG_FREE_IF_COPY(query, 0); } else { SET_VARSIZE(rewrited, HDRSIZETQ); rewrited->size = 0; } pfree(buf); PG_FREE_IF_COPY(in, 1); PG_RETURN_POINTER(rewrited); }
datum_t tsa_rewrite_accum(PG_FUNC_ARGS) { TSQuery acc; array_s *qa; TSQuery q; QTNode *qex = NULL, *subs = NULL, *acctree = NULL; bool isfind = false; datum_t *elemsp; int nelemsp; struct mctx * aggcontext; struct mctx * oldcontext; if (!AggCheckCallContext(fcinfo, &aggcontext)) elog(ERROR, "tsa_rewrite_accum called in non-aggregate context"); if (PG_ARG_ISNULL(0) || ARG_POINTER(0) == NULL) { acc = (TSQuery) mctx_alloc(aggcontext, HDRSIZETQ); VLA_SET_SZ_STND(acc, HDRSIZETQ); acc->size = 0; } else acc = ARG_TSQUERY(0); if (PG_ARG_ISNULL(1) || ARG_POINTER(1) == NULL) RET_TSQUERY(acc); else qa = ARG_ARRAY_P_COPY(1); if (ARR_NDIM(qa) != 1) elog(ERROR, "array must be one-dimensional, not %d dimensions", ARR_NDIM(qa)); if (ArrayGetNItems(ARR_NDIM(qa), ARR_DIMS(qa)) != 3) elog(ERROR, "array must have three elements"); if (ARR_ELEMTYPE(qa) != TSQUERYOID) elog(ERROR, "array must contain tsquery elements"); deconstruct_array(qa, TSQUERYOID, -1, false, 'i', &elemsp, NULL, &nelemsp); q = DatumGetTSQuery(elemsp[0]); if (q->size == 0) { pfree(elemsp); RET_POINTER(acc); } if (!acc->size) { if (VLA_SZ(acc) > HDRSIZETQ) { pfree(elemsp); RET_POINTER(acc); } else acctree = QT2QTN(GETQUERY(q), GETOPERAND(q)); } else acctree = QT2QTN(GETQUERY(acc), GETOPERAND(acc)); QTNTernary(acctree); QTNSort(acctree); q = DatumGetTSQuery(elemsp[1]); if (q->size == 0) { pfree(elemsp); RET_POINTER(acc); } qex = QT2QTN(GETQUERY(q), GETOPERAND(q)); QTNTernary(qex); QTNSort(qex); q = DatumGetTSQuery(elemsp[2]); if (q->size) subs = QT2QTN(GETQUERY(q), GETOPERAND(q)); acctree = findsubquery(acctree, qex, subs, &isfind); if (isfind || !acc->size) { /* pfree( acc ); do not pfree(p), because nodeAgg.c will */ if (acctree) { QTNBinary(acctree); oldcontext = mctx_switch(aggcontext); acc = QTN2QT(acctree); mctx_switch(oldcontext); } else { acc = (TSQuery) mctx_alloc(aggcontext, HDRSIZETQ); VLA_SET_SZ_STND(acc, HDRSIZETQ); acc->size = 0; } } pfree(elemsp); QTNFree(qex); QTNFree(subs); QTNFree(acctree); RET_TSQUERY(acc); }