postgresql/contrib/ltree/lquery_op.c

382 lines
7.1 KiB
C
Raw Normal View History

/*
2002-09-04 22:31:48 +02:00
* op function for ltree and lquery
* Teodor Sigaev <teodor@stack.net>
2010-09-20 22:08:53 +02:00
* contrib/ltree/lquery_op.c
*/
#include "postgres.h"
#include <ctype.h>
#include "catalog/pg_collation.h"
#include "utils/formatting.h"
#include "ltree.h"
PG_FUNCTION_INFO_V1(ltq_regex);
PG_FUNCTION_INFO_V1(ltq_rregex);
PG_FUNCTION_INFO_V1(lt_q_regex);
PG_FUNCTION_INFO_V1(lt_q_rregex);
#define NEXTVAL(x) ( (lquery*)( (char*)(x) + INTALIGN( VARSIZE(x) ) ) )
2002-09-04 22:31:48 +02:00
typedef struct
{
lquery_level *q;
int nq;
ltree_level *t;
int nt;
int posq;
int post;
} FieldNot;
static char *
getlexeme(char *start, char *end, int *len)
2002-09-04 22:31:48 +02:00
{
char *ptr;
int charlen;
while (start < end && (charlen = pg_mblen(start)) == 1 && t_iseq(start, '_'))
start += charlen;
ptr = start;
if (ptr >= end)
return NULL;
while (ptr < end && !((charlen = pg_mblen(ptr)) == 1 && t_iseq(ptr, '_')))
ptr += charlen;
*len = ptr - start;
return start;
}
bool
compare_subnode(ltree_level *t, char *qn, int len, int (*cmpptr) (const char *, const char *, size_t), bool anyend)
2002-09-04 22:31:48 +02:00
{
char *endt = t->name + t->len;
char *endq = qn + len;
char *tn;
int lent,
lenq;
bool isok;
while ((qn = getlexeme(qn, endq, &lenq)) != NULL)
2002-09-04 22:31:48 +02:00
{
tn = t->name;
isok = false;
while ((tn = getlexeme(tn, endt, &lent)) != NULL)
2002-09-04 22:31:48 +02:00
{
if (
(
2002-09-04 22:31:48 +02:00
lent == lenq ||
(lent > lenq && anyend)
) &&
(*cmpptr) (qn, tn, lenq) == 0)
{
isok = true;
break;
}
tn += lent;
}
2002-09-04 22:31:48 +02:00
if (!isok)
return false;
qn += lenq;
}
return true;
}
int
ltree_strncasecmp(const char *a, const char *b, size_t s)
{
char *al = str_tolower(a, s, DEFAULT_COLLATION_OID);
char *bl = str_tolower(b, s, DEFAULT_COLLATION_OID);
int res;
res = strncmp(al, bl, s);
pfree(al);
pfree(bl);
return res;
}
static bool
checkLevel(lquery_level *curq, ltree_level *curt)
2002-09-04 22:31:48 +02:00
{
int (*cmpptr) (const char *, const char *, size_t);
lquery_variant *curvar = LQL_FIRST(curq);
2002-09-04 22:31:48 +02:00
int i;
2002-09-04 22:31:48 +02:00
for (i = 0; i < curq->numvar; i++)
{
cmpptr = (curvar->flag & LVAR_INCASE) ? ltree_strncasecmp : strncmp;
2002-09-04 22:31:48 +02:00
if (curvar->flag & LVAR_SUBLEXEME)
2002-09-04 22:31:48 +02:00
{
if (compare_subnode(curt, curvar->name, curvar->len, cmpptr, (curvar->flag & LVAR_ANYEND)))
return true;
2002-09-04 22:31:48 +02:00
}
else if (
(
curvar->len == curt->len ||
2005-10-15 04:49:52 +02:00
(curt->len > curvar->len && (curvar->flag & LVAR_ANYEND))
2002-09-04 22:31:48 +02:00
) &&
(*cmpptr) (curvar->name, curt->name, curvar->len) == 0)
{
return true;
}
2002-09-04 22:31:48 +02:00
curvar = LVAR_NEXT(curvar);
}
return false;
}
/*
void
printFieldNot(FieldNot *fn ) {
while(fn->q) {
elog(NOTICE,"posQ:%d lenQ:%d posT:%d lenT:%d", fn->posq,fn->nq,fn->post,fn->nt);
fn++;
}
2002-09-04 22:31:48 +02:00
}
*/
2003-08-04 02:43:34 +02:00
static struct
{
bool muse;
uint32 high_pos;
2017-06-21 20:39:04 +02:00
} SomeStack =
2003-08-04 02:43:34 +02:00
{
false, 0,
};
static bool
checkCond(lquery_level *curq, int query_numlevel, ltree_level *curt, int tree_numlevel, FieldNot *ptr)
2002-09-04 22:31:48 +02:00
{
uint32 low_pos = 0,
high_pos = 0,
cur_tpos = 0;
int tlen = tree_numlevel,
qlen = query_numlevel;
int isok;
lquery_level *prevq = NULL;
ltree_level *prevt = NULL;
2003-08-04 02:43:34 +02:00
if (SomeStack.muse)
{
high_pos = SomeStack.high_pos;
qlen--;
prevq = curq;
curq = LQL_NEXT(curq);
SomeStack.muse = false;
}
2002-09-04 22:31:48 +02:00
while (tlen > 0 && qlen > 0)
{
if (curq->numvar)
{
prevt = curt;
2002-09-04 22:31:48 +02:00
while (cur_tpos < low_pos)
{
curt = LEVEL_NEXT(curt);
tlen--;
cur_tpos++;
2002-09-04 22:31:48 +02:00
if (tlen == 0)
return false;
2002-09-04 22:31:48 +02:00
if (ptr && ptr->q)
ptr->nt++;
}
2002-09-04 22:31:48 +02:00
if (ptr && curq->flag & LQL_NOT)
{
if (!(prevq && prevq->numvar == 0))
prevq = curq;
2002-09-04 22:31:48 +02:00
if (ptr->q == NULL)
{
ptr->t = prevt;
ptr->q = prevq;
2002-09-04 22:31:48 +02:00
ptr->nt = 1;
ptr->nq = 1 + ((prevq == curq) ? 0 : 1);
ptr->posq = query_numlevel - qlen - ((prevq == curq) ? 0 : 1);
ptr->post = cur_tpos;
2002-09-04 22:31:48 +02:00
}
else
{
ptr->nt++;
ptr->nq++;
}
2002-09-04 22:31:48 +02:00
if (qlen == 1 && ptr->q->numvar == 0)
ptr->nt = tree_numlevel - ptr->post;
curt = LEVEL_NEXT(curt);
tlen--;
cur_tpos++;
2002-09-04 22:31:48 +02:00
if (high_pos < cur_tpos)
high_pos++;
2002-09-04 22:31:48 +02:00
}
else
{
isok = false;
2002-09-04 22:31:48 +02:00
while (cur_tpos <= high_pos && tlen > 0 && !isok)
{
isok = checkLevel(curq, curt);
curt = LEVEL_NEXT(curt);
tlen--;
cur_tpos++;
2003-08-04 02:43:34 +02:00
if (isok && prevq && prevq->numvar == 0 && tlen > 0 && cur_tpos <= high_pos)
{
FieldNot tmpptr;
if (ptr)
memcpy(&tmpptr, ptr, sizeof(FieldNot));
SomeStack.high_pos = high_pos - cur_tpos;
SomeStack.muse = true;
2003-08-04 02:43:34 +02:00
if (checkCond(prevq, qlen + 1, curt, tlen, (ptr) ? &tmpptr : NULL))
return true;
}
2002-09-04 22:31:48 +02:00
if (!isok && ptr)
ptr->nt++;
}
2002-09-04 22:31:48 +02:00
if (!isok)
return false;
2002-09-04 22:31:48 +02:00
if (ptr && ptr->q)
{
if (checkCond(ptr->q, ptr->nq, ptr->t, ptr->nt, NULL))
return false;
ptr->q = NULL;
}
2002-09-04 22:31:48 +02:00
low_pos = cur_tpos;
high_pos = cur_tpos;
}
2002-09-04 22:31:48 +02:00
}
else
{
low_pos = cur_tpos + curq->low;
high_pos = cur_tpos + curq->high;
2002-09-04 22:31:48 +02:00
if (ptr && ptr->q)
{
ptr->nq++;
2002-09-04 22:31:48 +02:00
if (qlen == 1)
ptr->nt = tree_numlevel - ptr->post;
}
}
prevq = curq;
curq = LQL_NEXT(curq);
qlen--;
2002-09-04 22:31:48 +02:00
}
2002-09-04 22:31:48 +02:00
if (low_pos > tree_numlevel || tree_numlevel > high_pos)
return false;
2002-09-04 22:31:48 +02:00
while (qlen > 0)
{
if (curq->numvar)
{
if (!(curq->flag & LQL_NOT))
return false;
2002-09-04 22:31:48 +02:00
}
else
{
low_pos = cur_tpos + curq->low;
high_pos = cur_tpos + curq->high;
}
curq = LQL_NEXT(curq);
qlen--;
}
2002-09-04 22:31:48 +02:00
if (low_pos > tree_numlevel || tree_numlevel > high_pos)
return false;
2002-09-04 22:31:48 +02:00
if (ptr && ptr->q && checkCond(ptr->q, ptr->nq, ptr->t, ptr->nt, NULL))
return false;
2002-09-04 22:31:48 +02:00
return true;
}
Datum
2002-09-04 22:31:48 +02:00
ltq_regex(PG_FUNCTION_ARGS)
{
ltree *tree = PG_GETARG_LTREE_P(0);
lquery *query = PG_GETARG_LQUERY_P(1);
2002-09-04 22:31:48 +02:00
bool res = false;
2002-09-04 22:31:48 +02:00
if (query->flag & LQUERY_HASNOT)
{
FieldNot fn;
2002-09-04 22:31:48 +02:00
fn.q = NULL;
2002-09-04 22:31:48 +02:00
res = checkCond(LQUERY_FIRST(query), query->numlevel,
LTREE_FIRST(tree), tree->numlevel, &fn);
}
else
{
res = checkCond(LQUERY_FIRST(query), query->numlevel,
LTREE_FIRST(tree), tree->numlevel, NULL);
}
2002-09-04 22:31:48 +02:00
PG_FREE_IF_COPY(tree, 0);
PG_FREE_IF_COPY(query, 1);
PG_RETURN_BOOL(res);
}
2002-09-04 22:31:48 +02:00
Datum
ltq_rregex(PG_FUNCTION_ARGS)
{
PG_RETURN_DATUM(DirectFunctionCall2(ltq_regex,
PG_GETARG_DATUM(1),
PG_GETARG_DATUM(0)
));
}
Datum
lt_q_regex(PG_FUNCTION_ARGS)
{
ltree *tree = PG_GETARG_LTREE_P(0);
2003-08-04 02:43:34 +02:00
ArrayType *_query = PG_GETARG_ARRAYTYPE_P(1);
lquery *query = (lquery *) ARR_DATA_PTR(_query);
bool res = false;
int num = ArrayGetNItems(ARR_NDIM(_query), ARR_DIMS(_query));
if (ARR_NDIM(_query) > 1)
2003-08-04 02:43:34 +02:00
ereport(ERROR,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
errmsg("array must be one-dimensional")));
if (array_contains_nulls(_query))
ereport(ERROR,
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
errmsg("array must not contain nulls")));
2003-08-04 02:43:34 +02:00
while (num > 0)
{
if (DatumGetBool(DirectFunctionCall2(ltq_regex,
PointerGetDatum(tree), PointerGetDatum(query))))
2003-08-04 02:43:34 +02:00
{
res = true;
break;
}
num--;
query = NEXTVAL(query);
}
PG_FREE_IF_COPY(tree, 0);
PG_FREE_IF_COPY(_query, 1);
PG_RETURN_BOOL(res);
}
Datum
lt_q_rregex(PG_FUNCTION_ARGS)
{
PG_RETURN_DATUM(DirectFunctionCall2(lt_q_regex,
PG_GETARG_DATUM(1),
PG_GETARG_DATUM(0)
));
}