aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/tsginidx.c
diff options
context:
space:
mode:
authorHeikki Linnakangas <heikki.linnakangas@iki.fi>2014-03-12 17:13:22 +0200
committerHeikki Linnakangas <heikki.linnakangas@iki.fi>2014-03-12 17:51:30 +0200
commitc5608ea26a1f51998ad3cf987c3f0bda643c87a8 (patch)
treeea7370dc9696b18454f7102b3c26208455355d3c /src/backend/utils/adt/tsginidx.c
parentfecfc2b913c4be5eeed24b32ef51a3239580bd1e (diff)
downloadpostgresql-c5608ea26a1f51998ad3cf987c3f0bda643c87a8.tar.gz
postgresql-c5608ea26a1f51998ad3cf987c3f0bda643c87a8.zip
Allow opclasses to provide tri-valued GIN consistent functions.
With the GIN "fast scan" feature, GIN can skip items without fetching all the keys for them, if it can prove that they don't match regardless of those keys. So far, it has done the proving by calling the boolean consistent function with all combinations of TRUE/FALSE for the unfetched keys, but since that's O(n^2), it becomes unfeasible with more than a few keys. We can avoid calling consistent with all the combinations, if we can tell the operator class implementation directly which keys are unknown. This commit includes a triConsistent function for the built-in array and tsvector opclasses. Alexander Korotkov, with some changes by me.
Diffstat (limited to 'src/backend/utils/adt/tsginidx.c')
-rw-r--r--src/backend/utils/adt/tsginidx.c104
1 files changed, 102 insertions, 2 deletions
diff --git a/src/backend/utils/adt/tsginidx.c b/src/backend/utils/adt/tsginidx.c
index 9f6e8e9fd57..47685e8b930 100644
--- a/src/backend/utils/adt/tsginidx.c
+++ b/src/backend/utils/adt/tsginidx.c
@@ -15,6 +15,7 @@
#include "access/gin.h"
#include "access/skey.h"
+#include "miscadmin.h"
#include "tsearch/ts_type.h"
#include "tsearch/ts_utils.h"
#include "utils/builtins.h"
@@ -172,12 +173,12 @@ gin_extract_tsquery(PG_FUNCTION_ARGS)
typedef struct
{
QueryItem *first_item;
- bool *check;
+ GinLogicValue *check;
int *map_item_operand;
bool *need_recheck;
} GinChkVal;
-static bool
+static GinLogicValue
checkcondition_gin(void *checkval, QueryOperand *val)
{
GinChkVal *gcv = (GinChkVal *) checkval;
@@ -194,6 +195,66 @@ checkcondition_gin(void *checkval, QueryOperand *val)
return gcv->check[j];
}
+/*
+ * Evaluate tsquery boolean expression using ternary logic.
+ *
+ * chkcond is a callback function used to evaluate each VAL node in the query.
+ * checkval can be used to pass information to the callback. TS_execute doesn't
+ * do anything with it.
+ */
+static GinLogicValue
+TS_execute_ternary(QueryItem *curitem, void *checkval,
+ GinLogicValue (*chkcond) (void *checkval, QueryOperand *val))
+{
+ GinLogicValue val1, val2, result;
+ /* 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:
+ result = TS_execute_ternary(curitem + 1, checkval, chkcond);
+ if (result == GIN_MAYBE)
+ return result;
+ return !result;
+
+ case OP_AND:
+ val1 = TS_execute_ternary(curitem + curitem->qoperator.left,
+ checkval, chkcond);
+ if (val1 == GIN_FALSE)
+ return GIN_FALSE;
+ val2 = TS_execute_ternary(curitem + 1, checkval, chkcond);
+ if (val2 == GIN_FALSE)
+ return GIN_FALSE;
+ if (val1 == GIN_TRUE && val2 == GIN_TRUE)
+ return GIN_TRUE;
+ else
+ return GIN_MAYBE;
+
+ case OP_OR:
+ val1 = TS_execute_ternary(curitem + curitem->qoperator.left,
+ checkval, chkcond);
+ if (val1 == GIN_TRUE)
+ return GIN_TRUE;
+ val2 = TS_execute_ternary(curitem + 1, checkval, chkcond);
+ if (val2 == GIN_TRUE)
+ return GIN_TRUE;
+ if (val1 == GIN_FALSE && val2 == GIN_FALSE)
+ return GIN_FALSE;
+ else
+ return GIN_MAYBE;
+
+ default:
+ elog(ERROR, "unrecognized operator: %d", curitem->qoperator.oper);
+ }
+
+ /* not reachable, but keep compiler quiet */
+ return false;
+}
+
Datum
gin_tsquery_consistent(PG_FUNCTION_ARGS)
{
@@ -233,6 +294,45 @@ gin_tsquery_consistent(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(res);
}
+Datum
+gin_tsquery_triconsistent(PG_FUNCTION_ARGS)
+{
+ GinLogicValue *check = (GinLogicValue *) PG_GETARG_POINTER(0);
+
+ /* StrategyNumber strategy = PG_GETARG_UINT16(1); */
+ TSQuery query = PG_GETARG_TSQUERY(2);
+
+ /* int32 nkeys = PG_GETARG_INT32(3); */
+ Pointer *extra_data = (Pointer *) PG_GETARG_POINTER(4);
+ GinLogicValue res = GIN_FALSE;
+ bool recheck;
+
+ /* The query requires recheck only if it involves weights */
+ if (query->size > 0)
+ {
+ QueryItem *item;
+ GinChkVal gcv;
+
+ /*
+ * check-parameter array has one entry for each value (operand) in the
+ * query.
+ */
+ gcv.first_item = item = GETQUERY(query);
+ gcv.check = check;
+ gcv.map_item_operand = (int *) (extra_data[0]);
+ gcv.need_recheck = &recheck;
+
+ res = TS_execute_ternary(GETQUERY(query),
+ &gcv,
+ checkcondition_gin);
+
+ if (res == GIN_TRUE && recheck)
+ res = GIN_MAYBE;
+ }
+
+ PG_RETURN_GIN_LOGIC_VALUE(res);
+}
+
/*
* Formerly, gin_extract_tsvector had only two arguments. Now it has three,
* but we still need a pg_proc entry with two args to support reloading