openGauss-server/contrib/ltree/lquery_op.cpp

310 lines
8.4 KiB
C++

/*
* op function for ltree and lquery
* Teodor Sigaev <teodor@stack.net>
* contrib/ltree/lquery_op.c
*/
#include "postgres.h"
#include "knl/knl_variable.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);
extern "C" Datum ltq_regex(PG_FUNCTION_ARGS);
extern "C" Datum ltq_rregex(PG_FUNCTION_ARGS);
extern "C" Datum lt_q_regex(PG_FUNCTION_ARGS);
extern "C" Datum lt_q_rregex(PG_FUNCTION_ARGS);
#define NEXTVAL(x) ((lquery*)((char*)(x) + INTALIGN(VARSIZE(x))))
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)
{
char* ptr = NULL;
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)
{
char* endt = t->name + t->len;
char* endq = qn + len;
char* tn = NULL;
int lent, lenq;
bool isok = false;
while ((qn = getlexeme(qn, endq, &lenq)) != NULL) {
tn = t->name;
isok = false;
while ((tn = getlexeme(tn, endt, &lent)) != NULL) {
if ((lent == lenq || (lent > lenq && anyend)) && (*cmpptr)(qn, tn, lenq) == 0) {
isok = true;
break;
}
tn += lent;
}
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)
{
int (*cmpptr)(const char*, const char*, size_t);
lquery_variant* curvar = LQL_FIRST(curq);
int i;
for (i = 0; i < curq->numvar; i++) {
cmpptr = (curvar->flag & LVAR_INCASE) ? ltree_strncasecmp : strncmp;
if (curvar->flag & LVAR_SUBLEXEME) {
if (compare_subnode(curt, curvar->name, curvar->len, cmpptr, (curvar->flag & LVAR_ANYEND)))
return true;
} else if ((curvar->len == curt->len || (curt->len > curvar->len && (curvar->flag & LVAR_ANYEND))) &&
(*cmpptr)(curvar->name, curt->name, curvar->len) == 0) {
return true;
}
curvar = LVAR_NEXT(curvar);
}
return false;
}
static struct {
bool muse;
uint32 high_pos;
} SomeStack =
{
false,
0,
};
static bool checkCond(lquery_level* curq, int query_numlevel, ltree_level* curt, int tree_numlevel, FieldNot* ptr)
{
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;
if (SomeStack.muse) {
high_pos = SomeStack.high_pos;
qlen--;
prevq = curq;
curq = LQL_NEXT(curq);
SomeStack.muse = false;
}
while (tlen > 0 && qlen > 0) {
if (curq->numvar) {
prevt = curt;
while (cur_tpos < low_pos) {
curt = LEVEL_NEXT(curt);
tlen--;
cur_tpos++;
if (tlen == 0)
return false;
if (ptr && ptr->q)
ptr->nt++;
}
if (ptr && curq->flag & LQL_NOT) {
if (!(prevq && prevq->numvar == 0))
prevq = curq;
if (ptr->q == NULL) {
ptr->t = prevt;
ptr->q = prevq;
ptr->nt = 1;
ptr->nq = 1 + ((prevq == curq) ? 0 : 1);
ptr->posq = query_numlevel - qlen - ((prevq == curq) ? 0 : 1);
ptr->post = cur_tpos;
} else {
ptr->nt++;
ptr->nq++;
}
if (qlen == 1 && ptr->q->numvar == 0)
ptr->nt = tree_numlevel - ptr->post;
curt = LEVEL_NEXT(curt);
tlen--;
cur_tpos++;
if (high_pos < cur_tpos)
high_pos++;
} else {
isok = false;
while (cur_tpos <= high_pos && tlen > 0 && !isok) {
isok = checkLevel(curq, curt);
curt = LEVEL_NEXT(curt);
tlen--;
cur_tpos++;
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;
if (checkCond(prevq, qlen + 1, curt, tlen, (ptr) ? &tmpptr : NULL))
return true;
}
if (!isok && ptr)
ptr->nt++;
}
if (!isok)
return false;
if (ptr && ptr->q) {
if (checkCond(ptr->q, ptr->nq, ptr->t, ptr->nt, NULL))
return false;
ptr->q = NULL;
}
low_pos = cur_tpos;
high_pos = cur_tpos;
}
} else {
low_pos = cur_tpos + curq->low;
high_pos = cur_tpos + curq->high;
if (ptr && ptr->q) {
ptr->nq++;
if (qlen == 1)
ptr->nt = tree_numlevel - ptr->post;
}
}
prevq = curq;
curq = LQL_NEXT(curq);
qlen--;
}
if (low_pos > tree_numlevel || tree_numlevel > high_pos)
return false;
while (qlen > 0) {
if (curq->numvar) {
if (!(curq->flag & LQL_NOT))
return false;
} else {
low_pos = cur_tpos + curq->low;
high_pos = cur_tpos + curq->high;
}
curq = LQL_NEXT(curq);
qlen--;
}
if (low_pos > tree_numlevel || tree_numlevel > high_pos)
return false;
if (ptr && ptr->q && checkCond(ptr->q, ptr->nq, ptr->t, ptr->nt, NULL))
return false;
return true;
}
Datum ltq_regex(PG_FUNCTION_ARGS)
{
ltree* tree = PG_GETARG_LTREE(0);
lquery* query = PG_GETARG_LQUERY(1);
bool res = false;
if (query->flag & LQUERY_HASNOT) {
FieldNot fn;
fn.q = NULL;
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);
}
PG_FREE_IF_COPY(tree, 0);
PG_FREE_IF_COPY(query, 1);
PG_RETURN_BOOL(res);
}
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(0);
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)
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")));
while (num > 0) {
if (DatumGetBool(DirectFunctionCall2(ltq_regex, PointerGetDatum(tree), PointerGetDatum(query)))) {
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)));
}