summaryrefslogtreecommitdiffstats
path: root/src/backend/utils/adt/tsquery_util.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:15:05 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:15:05 +0000
commit46651ce6fe013220ed397add242004d764fc0153 (patch)
tree6e5299f990f88e60174a1d3ae6e48eedd2688b2b /src/backend/utils/adt/tsquery_util.c
parentInitial commit. (diff)
downloadpostgresql-14-upstream.tar.xz
postgresql-14-upstream.zip
Adding upstream version 14.5.upstream/14.5upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/backend/utils/adt/tsquery_util.c')
-rw-r--r--src/backend/utils/adt/tsquery_util.c447
1 files changed, 447 insertions, 0 deletions
diff --git a/src/backend/utils/adt/tsquery_util.c b/src/backend/utils/adt/tsquery_util.c
new file mode 100644
index 0000000..7f93642
--- /dev/null
+++ b/src/backend/utils/adt/tsquery_util.c
@@ -0,0 +1,447 @@
+/*-------------------------------------------------------------------------
+ *
+ * tsquery_util.c
+ * Utilities for tsquery datatype
+ *
+ * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/utils/adt/tsquery_util.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "miscadmin.h"
+#include "tsearch/ts_utils.h"
+
+/*
+ * Build QTNode tree for a tsquery given in QueryItem array format.
+ */
+QTNode *
+QT2QTN(QueryItem *in, char *operand)
+{
+ QTNode *node = (QTNode *) palloc0(sizeof(QTNode));
+
+ /* since this function recurses, it could be driven to stack overflow. */
+ check_stack_depth();
+
+ node->valnode = in;
+
+ if (in->type == QI_OPR)
+ {
+ node->child = (QTNode **) palloc0(sizeof(QTNode *) * 2);
+ node->child[0] = QT2QTN(in + 1, operand);
+ node->sign = node->child[0]->sign;
+ if (in->qoperator.oper == OP_NOT)
+ node->nchild = 1;
+ else
+ {
+ node->nchild = 2;
+ node->child[1] = QT2QTN(in + in->qoperator.left, operand);
+ node->sign |= node->child[1]->sign;
+ }
+ }
+ else if (operand)
+ {
+ node->word = operand + in->qoperand.distance;
+ node->sign = ((uint32) 1) << (((unsigned int) in->qoperand.valcrc) % 32);
+ }
+
+ return node;
+}
+
+/*
+ * Free a QTNode tree.
+ *
+ * Referenced "word" and "valnode" items are freed if marked as transient
+ * by flags.
+ */
+void
+QTNFree(QTNode *in)
+{
+ if (!in)
+ return;
+
+ /* since this function recurses, it could be driven to stack overflow. */
+ check_stack_depth();
+
+ if (in->valnode->type == QI_VAL && in->word && (in->flags & QTN_WORDFREE) != 0)
+ pfree(in->word);
+
+ if (in->valnode->type == QI_OPR)
+ {
+ int i;
+
+ for (i = 0; i < in->nchild; i++)
+ QTNFree(in->child[i]);
+ }
+ if (in->child)
+ pfree(in->child);
+
+ if (in->flags & QTN_NEEDFREE)
+ pfree(in->valnode);
+
+ pfree(in);
+}
+
+/*
+ * Sort comparator for QTNodes.
+ *
+ * The sort order is somewhat arbitrary.
+ */
+int
+QTNodeCompare(QTNode *an, QTNode *bn)
+{
+ /* since this function recurses, it could be driven to stack overflow. */
+ check_stack_depth();
+
+ if (an->valnode->type != bn->valnode->type)
+ return (an->valnode->type > bn->valnode->type) ? -1 : 1;
+
+ if (an->valnode->type == QI_OPR)
+ {
+ QueryOperator *ao = &an->valnode->qoperator;
+ QueryOperator *bo = &bn->valnode->qoperator;
+
+ if (ao->oper != bo->oper)
+ return (ao->oper > bo->oper) ? -1 : 1;
+
+ if (an->nchild != bn->nchild)
+ return (an->nchild > bn->nchild) ? -1 : 1;
+
+ {
+ int i,
+ res;
+
+ for (i = 0; i < an->nchild; i++)
+ if ((res = QTNodeCompare(an->child[i], bn->child[i])) != 0)
+ return res;
+ }
+
+ if (ao->oper == OP_PHRASE && ao->distance != bo->distance)
+ return (ao->distance > bo->distance) ? -1 : 1;
+
+ return 0;
+ }
+ else if (an->valnode->type == QI_VAL)
+ {
+ QueryOperand *ao = &an->valnode->qoperand;
+ QueryOperand *bo = &bn->valnode->qoperand;
+
+ if (ao->valcrc != bo->valcrc)
+ {
+ return (ao->valcrc > bo->valcrc) ? -1 : 1;
+ }
+
+ return tsCompareString(an->word, ao->length, bn->word, bo->length, false);
+ }
+ else
+ {
+ elog(ERROR, "unrecognized QueryItem type: %d", an->valnode->type);
+ return 0; /* keep compiler quiet */
+ }
+}
+
+/*
+ * qsort comparator for QTNode pointers.
+ */
+static int
+cmpQTN(const void *a, const void *b)
+{
+ return QTNodeCompare(*(QTNode *const *) a, *(QTNode *const *) b);
+}
+
+/*
+ * Canonicalize a QTNode tree by sorting the children of AND/OR nodes
+ * into an arbitrary but well-defined order.
+ */
+void
+QTNSort(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++)
+ QTNSort(in->child[i]);
+ if (in->nchild > 1 && in->valnode->qoperator.oper != OP_PHRASE)
+ qsort((void *) in->child, in->nchild, sizeof(QTNode *), cmpQTN);
+}
+
+/*
+ * Are two QTNode trees equal according to QTNodeCompare?
+ */
+bool
+QTNEq(QTNode *a, QTNode *b)
+{
+ uint32 sign = a->sign & b->sign;
+
+ if (!(sign == a->sign && sign == b->sign))
+ return false;
+
+ return (QTNodeCompare(a, b) == 0) ? true : false;
+}
+
+/*
+ * 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);
+ }
+ }
+}
+
+/*
+ * Convert a tree to binary tree by inserting intermediate nodes.
+ * (Opposite of QTNTernary)
+ */
+void
+QTNBinary(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++)
+ QTNBinary(in->child[i]);
+
+ while (in->nchild > 2)
+ {
+ QTNode *nn = (QTNode *) palloc0(sizeof(QTNode));
+
+ nn->valnode = (QueryItem *) palloc0(sizeof(QueryItem));
+ nn->child = (QTNode **) palloc0(sizeof(QTNode *) * 2);
+
+ nn->nchild = 2;
+ nn->flags = QTN_NEEDFREE;
+
+ nn->child[0] = in->child[0];
+ nn->child[1] = in->child[1];
+ nn->sign = nn->child[0]->sign | nn->child[1]->sign;
+
+ nn->valnode->type = in->valnode->type;
+ nn->valnode->qoperator.oper = in->valnode->qoperator.oper;
+
+ in->child[0] = nn;
+ in->child[1] = in->child[in->nchild - 1];
+ in->nchild--;
+ }
+}
+
+/*
+ * Count the total length of operand strings in tree (including '\0'-
+ * terminators) and the total number of nodes.
+ * Caller must initialize *sumlen and *nnode to zeroes.
+ */
+static void
+cntsize(QTNode *in, int *sumlen, int *nnode)
+{
+ /* since this function recurses, it could be driven to stack overflow. */
+ check_stack_depth();
+
+ *nnode += 1;
+ if (in->valnode->type == QI_OPR)
+ {
+ int i;
+
+ for (i = 0; i < in->nchild; i++)
+ cntsize(in->child[i], sumlen, nnode);
+ }
+ else
+ {
+ *sumlen += in->valnode->qoperand.length + 1;
+ }
+}
+
+typedef struct
+{
+ QueryItem *curitem;
+ char *operand;
+ char *curoperand;
+} QTN2QTState;
+
+/*
+ * Recursively convert a QTNode tree into flat tsquery format.
+ * Caller must have allocated arrays of the correct size.
+ */
+static void
+fillQT(QTN2QTState *state, QTNode *in)
+{
+ /* since this function recurses, it could be driven to stack overflow. */
+ check_stack_depth();
+
+ if (in->valnode->type == QI_VAL)
+ {
+ memcpy(state->curitem, in->valnode, sizeof(QueryOperand));
+
+ memcpy(state->curoperand, in->word, in->valnode->qoperand.length);
+ state->curitem->qoperand.distance = state->curoperand - state->operand;
+ state->curoperand[in->valnode->qoperand.length] = '\0';
+ state->curoperand += in->valnode->qoperand.length + 1;
+ state->curitem++;
+ }
+ else
+ {
+ QueryItem *curitem = state->curitem;
+
+ Assert(in->valnode->type == QI_OPR);
+
+ memcpy(state->curitem, in->valnode, sizeof(QueryOperator));
+
+ Assert(in->nchild <= 2);
+ state->curitem++;
+
+ fillQT(state, in->child[0]);
+
+ if (in->nchild == 2)
+ {
+ curitem->qoperator.left = state->curitem - curitem;
+ fillQT(state, in->child[1]);
+ }
+ }
+}
+
+/*
+ * Build flat tsquery from a QTNode tree.
+ */
+TSQuery
+QTN2QT(QTNode *in)
+{
+ TSQuery out;
+ int len;
+ int sumlen = 0,
+ nnode = 0;
+ QTN2QTState state;
+
+ cntsize(in, &sumlen, &nnode);
+
+ if (TSQUERY_TOO_BIG(nnode, sumlen))
+ ereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("tsquery is too large")));
+ len = COMPUTESIZE(nnode, sumlen);
+
+ out = (TSQuery) palloc0(len);
+ SET_VARSIZE(out, len);
+ out->size = nnode;
+
+ state.curitem = GETQUERY(out);
+ state.operand = state.curoperand = GETOPERAND(out);
+
+ fillQT(&state, in);
+ return out;
+}
+
+/*
+ * Copy a QTNode tree.
+ *
+ * Modifiable copies of the words and valnodes are made, too.
+ */
+QTNode *
+QTNCopy(QTNode *in)
+{
+ QTNode *out;
+
+ /* since this function recurses, it could be driven to stack overflow. */
+ check_stack_depth();
+
+ out = (QTNode *) palloc(sizeof(QTNode));
+
+ *out = *in;
+ out->valnode = (QueryItem *) palloc(sizeof(QueryItem));
+ *(out->valnode) = *(in->valnode);
+ out->flags |= QTN_NEEDFREE;
+
+ if (in->valnode->type == QI_VAL)
+ {
+ out->word = palloc(in->valnode->qoperand.length + 1);
+ memcpy(out->word, in->word, in->valnode->qoperand.length);
+ out->word[in->valnode->qoperand.length] = '\0';
+ out->flags |= QTN_WORDFREE;
+ }
+ else
+ {
+ int i;
+
+ out->child = (QTNode **) palloc(sizeof(QTNode *) * in->nchild);
+
+ for (i = 0; i < in->nchild; i++)
+ out->child[i] = QTNCopy(in->child[i]);
+ }
+
+ return out;
+}
+
+/*
+ * Clear the specified flag bit(s) in all nodes of a QTNode tree.
+ */
+void
+QTNClearFlags(QTNode *in, uint32 flags)
+{
+ /* since this function recurses, it could be driven to stack overflow. */
+ check_stack_depth();
+
+ in->flags &= ~flags;
+
+ if (in->valnode->type != QI_VAL)
+ {
+ int i;
+
+ for (i = 0; i < in->nchild; i++)
+ QTNClearFlags(in->child[i], flags);
+ }
+}