mirror of
				https://github.com/postgres/postgres.git
				synced 2025-10-31 10:30:33 +03:00 
			
		
		
		
	By project convention, these names should include "P" when dealing with a pointer type; that is, if the result of a GETARG macro is of type FOO *, it should be called PG_GETARG_FOO_P not just PG_GETARG_FOO. Some newer types such as JSONB and ranges had not followed the convention, and a number of contrib modules hadn't gotten that memo either. Rename the offending macros to improve consistency. In passing, fix a few places that thought PG_DETOAST_DATUM() returns a Datum; it does not, it returns "struct varlena *". Applying DatumGetPointer to that happens not to cause any bad effects today, but it's formally wrong. Also, adjust an ltree macro that was designed without any thought for what pgindent would do with it. This is all cosmetic and shouldn't have any impact on generated code. Mark Dilger, some further tweaks by me Discussion: https://postgr.es/m/EA5676F4-766F-4F38-8348-ECC7DB427C6A@gmail.com
		
			
				
	
	
		
			382 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			382 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * op function for ltree and lquery
 | |
|  * Teodor Sigaev <teodor@stack.net>
 | |
|  * 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) ) ) )
 | |
| 
 | |
| 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;
 | |
| 	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;
 | |
| 	int			lent,
 | |
| 				lenq;
 | |
| 	bool		isok;
 | |
| 
 | |
| 	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;
 | |
| }
 | |
| 
 | |
| /*
 | |
| 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++;
 | |
| 	}
 | |
| }
 | |
| */
 | |
| 
 | |
| 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_P(0);
 | |
| 	lquery	   *query = PG_GETARG_LQUERY_P(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_P(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)
 | |
| 										));
 | |
| }
 |