postgresql/contrib/ltree/ltxtquery_op.c
Jeff Davis c8308a984d Fix more multibyte issues in ltree.
Commit 84d5efa7e3 missed some multibyte issues caused by short-circuit
logic in the callers. The callers assumed that if the predicate string
is longer than the label string, then it couldn't possibly be a match,
but it can be when using case-insensitive matching (LVAR_INCASE) if
casefolding changes the byte length.

Fix by refactoring to get rid of the short-circuit logic as well as
the function pointer, and consolidate the logic in a replacement
function ltree_label_match().

Discussion: https://postgr.es/m/02c6ef6cf56a5013ede61ad03c7a26affd27d449.camel@j-davis.com
Backpatch-through: 14
2026-02-26 12:23:22 -08:00

110 lines
2.3 KiB
C

/*
* 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;
bool prefix = (val->flag & LVAR_ANYEND);
bool ci = (val->flag & LVAR_INCASE);
while (tlen > 0)
{
if (val->flag & LVAR_SUBLEXEME)
{
if (compare_subnode(level, op, val->length, prefix, ci))
return true;
}
else if (ltree_label_match(op, val->length, level->name, level->len,
prefix, ci))
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)
));
}