1/*-------------------------------------------------------------------------
4 * Utilities for reconstructing tsquery
6 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
10 * src/backend/utils/adt/tsquery_rewrite.c
12 *-------------------------------------------------------------------------
25 * If "node" is equal to "ex", return a copy of "subs" instead.
26 * If "ex" matches a subset of node's children, return a modified version
27 * of "node" in which those children are replaced with a copy of "subs".
28 * Otherwise return "node" unmodified.
30 * The QTN_NOCHANGE bit is set in successfully modified nodes, so that
31 * we won't uselessly recurse into them.
32 * Also, set *isfind true if we make a replacement.
37 /* Can't match unless signature matches and node type matches. */
42 /* Ignore nodes marked NOCHANGE, too. */
48 /* Must be same operator. */
55 * Simple case: when same number of children, match if equal.
56 * (This is reliable when the children were sorted earlier.)
60 /* Match; delete node and return a copy of subs instead. */
75 * AND and OR are commutative/associative, so we should check if a
76 * subset of the children match. For example, if node is A|B|C,
77 * and ex is B|C, we have a match after we notionally convert node
78 * to A|(B|C). This does not work for NOT or PHRASE nodes, but we
79 * can't get here for those node types because they have a fixed
82 * Because we expect that the children are sorted, it suffices to
83 * make one pass through the two lists to find the matches.
90 /* Assert that the subset rule is OK */
94 /* matched[] will record which children of node matched */
98 while (i < node->nchild && j < ex->nchild)
111 /* node->child[i] has no match, ignore it */
116 /* ex->child[j] has no match; we can give up immediately */
121 if (nmatched == ex->
nchild)
123 /* collapse out the matched children of node */
133 /* and instead insert a copy of subs */
144 * At this point we might have a node with zero or one child,
145 * which should be simplified. But we leave it to our caller
146 * (dofindsubquery) to take care of that.
150 * Re-sort the node to put new child in the right place. This
151 * is a bit bogus, because it won't matter for findsubquery's
152 * remaining processing, and it's insufficient to prepare the
153 * tree for another search (we would need to re-flatten as
154 * well, and we don't want to do that because we'd lose the
155 * QTN_NOCHANGE marking on the new child). But it's needed to
156 * keep the results the same as the regression tests expect.
172 else if (
QTNEq(node, ex))
192 * Recursive guts of findsubquery(): attempt to replace "ex" with "subs"
193 * at the root node, and if we failed to do so, recursively match against
196 * Delete any void subtrees resulting from the replacement.
197 * In the following example '5' is replaced by empty operand:
208 /* since this function recurses, it could be driven to stack overflow. */
211 /* also, since it's a bit expensive, let's check for query cancel. */
214 /* match at the node itself */
217 /* unless we matched here, consider matches at child nodes */
225 * Any subtrees that are replaced by NULL must be dropped from the
228 for (
i = 0;
i <
root->nchild;
i++)
238 * If we have just zero or one remaining child node, simplify out this
241 if (
root->nchild == 0)
246 else if (
root->nchild == 1 &&
root->valnode->qoperator.oper !=
OP_NOT)
259 * Substitute "subs" for "ex" throughout the QTNode tree at root.
261 * If isfind isn't NULL, set *isfind to show whether we made any substitution.
263 * Both "root" and "ex" must have been through QTNTernary and QTNSort
264 * to ensure reliable matching.
269 bool DidFind =
false;
293 if (query->
size == 0)
320 (
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
321 errmsg(
"ts_rewrite query must return two tsquery columns")));
374 /* ready the tree for another pass */
420 if (query->
size == 0 || ex->
size == 0)
int errcode(int sqlerrcode)
int errmsg(const char *fmt,...)
#define ereport(elevel,...)
#define PG_FREE_IF_COPY(ptr, n)
#define PG_GETARG_TEXT_PP(n)
#define PG_RETURN_POINTER(x)
Assert(PointerIsAligned(start, uint64))
void pfree(void *pointer)
void * palloc0(Size size)
MemoryContext CurrentMemoryContext
#define CHECK_FOR_INTERRUPTS()
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
static Pointer DatumGetPointer(Datum X)
static int cmp(const chr *x, const chr *y, size_t len)
Oid SPI_gettypeid(TupleDesc tupdesc, int fnumber)
int SPI_freeplan(SPIPlanPtr plan)
SPITupleTable * SPI_tuptable
void SPI_cursor_fetch(Portal portal, bool forward, long count)
void SPI_freetuptable(SPITupleTable *tuptable)
Portal SPI_cursor_open(const char *name, SPIPlanPtr plan, Datum *Values, const char *Nulls, bool read_only)
SPIPlanPtr SPI_prepare(const char *src, int nargs, Oid *argtypes)
void SPI_cursor_close(Portal portal)
Datum SPI_getbinval(HeapTuple tuple, TupleDesc tupdesc, int fnumber, bool *isnull)
void check_stack_depth(void)
static TSQuery DatumGetTSQuery(Datum X)
#define PG_GETARG_TSQUERY(n)
#define PG_GETARG_TSQUERY_COPY(n)
Datum tsquery_rewrite_query(PG_FUNCTION_ARGS)
QTNode * findsubquery(QTNode *root, QTNode *ex, QTNode *subs, bool *isfind)
static QTNode * findeq(QTNode *node, QTNode *ex, QTNode *subs, bool *isfind)
Datum tsquery_rewrite(PG_FUNCTION_ARGS)
static QTNode * dofindsubquery(QTNode *root, QTNode *ex, QTNode *subs, bool *isfind)
void QTNClearFlags(QTNode *in, uint32 flags)
int QTNodeCompare(QTNode *an, QTNode *bn)
QTNode * QTNCopy(QTNode *in)
void QTNTernary(QTNode *in)
bool QTNEq(QTNode *a, QTNode *b)
QTNode * QT2QTN(QueryItem *in, char *operand)
TSQuery QTN2QT(QTNode *in)
void QTNBinary(QTNode *in)
static void SET_VARSIZE(void *PTR, Size len)
char * text_to_cstring(const text *t)