diff options
Diffstat (limited to 'contrib/ltree/ltxtquery_op.c')
-rw-r--r-- | contrib/ltree/ltxtquery_op.c | 111 |
1 files changed, 111 insertions, 0 deletions
diff --git a/contrib/ltree/ltxtquery_op.c b/contrib/ltree/ltxtquery_op.c new file mode 100644 index 0000000..002102c --- /dev/null +++ b/contrib/ltree/ltxtquery_op.c @@ -0,0 +1,111 @@ +/* + * txtquery operations with ltree + * Teodor Sigaev <teodor@stack.net> + * contrib/ltree/ltxtquery_op.c + */ +#include "postgres.h" + +#include <ctype.h> + +#include "ltree.h" +#include "miscadmin.h" + +PG_FUNCTION_INFO_V1(ltxtq_exec); +PG_FUNCTION_INFO_V1(ltxtq_rexec); + +/* + * check for boolean condition + */ +bool +ltree_execute(ITEM *curitem, void *checkval, bool calcnot, bool (*chkcond) (void *checkval, ITEM *val)) +{ + /* since this function recurses, it could be driven to stack overflow */ + check_stack_depth(); + + if (curitem->type == VAL) + return (*chkcond) (checkval, curitem); + else if (curitem->val == (int32) '!') + { + return calcnot ? + ((ltree_execute(curitem + 1, checkval, calcnot, chkcond)) ? false : true) + : true; + } + else if (curitem->val == (int32) '&') + { + if (ltree_execute(curitem + curitem->left, checkval, calcnot, chkcond)) + return ltree_execute(curitem + 1, checkval, calcnot, chkcond); + else + return false; + } + else + { /* |-operator */ + if (ltree_execute(curitem + curitem->left, checkval, calcnot, chkcond)) + return true; + else + return ltree_execute(curitem + 1, checkval, calcnot, chkcond); + } +} + +typedef struct +{ + ltree *node; + char *operand; +} CHKVAL; + +static bool +checkcondition_str(void *checkval, ITEM *val) +{ + ltree_level *level = LTREE_FIRST(((CHKVAL *) checkval)->node); + int tlen = ((CHKVAL *) checkval)->node->numlevel; + char *op = ((CHKVAL *) checkval)->operand + val->distance; + int (*cmpptr) (const char *, const char *, size_t); + + cmpptr = (val->flag & LVAR_INCASE) ? ltree_strncasecmp : strncmp; + while (tlen > 0) + { + if (val->flag & LVAR_SUBLEXEME) + { + if (compare_subnode(level, op, val->length, cmpptr, (val->flag & LVAR_ANYEND))) + return true; + } + else if ((val->length == level->len || + (level->len > val->length && (val->flag & LVAR_ANYEND))) && + (*cmpptr) (op, level->name, val->length) == 0) + return true; + + tlen--; + level = LEVEL_NEXT(level); + } + + return false; +} + +Datum +ltxtq_exec(PG_FUNCTION_ARGS) +{ + ltree *val = PG_GETARG_LTREE_P(0); + ltxtquery *query = PG_GETARG_LTXTQUERY_P(1); + CHKVAL chkval; + bool result; + + chkval.node = val; + chkval.operand = GETOPERAND(query); + + result = ltree_execute(GETQUERY(query), + &chkval, + true, + checkcondition_str); + + PG_FREE_IF_COPY(val, 0); + PG_FREE_IF_COPY(query, 1); + PG_RETURN_BOOL(result); +} + +Datum +ltxtq_rexec(PG_FUNCTION_ARGS) +{ + PG_RETURN_DATUM(DirectFunctionCall2(ltxtq_exec, + PG_GETARG_DATUM(1), + PG_GETARG_DATUM(0) + )); +} |