mirror of
				https://github.com/postgres/postgres.git
				synced 2025-11-03 09:13:20 +03:00 
			
		
		
		
	conversion of basic ASCII letters. Remove all uses of strcasecmp and strncasecmp in favor of new functions pg_strcasecmp and pg_strncasecmp; remove most but not all direct uses of toupper and tolower in favor of pg_toupper and pg_tolower. These functions use the same notions of case folding already developed for identifier case conversion. I left the straight locale-based folding in place for situations where we are just manipulating user data and not trying to match it to built-in strings --- for example, the SQL upper() function is still locale dependent. Perhaps this will prove not to be what's wanted, but at the moment we can initdb and pass regression tests in Turkish locale.
		
			
				
	
	
		
			358 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			358 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * op function for ltree and lquery
 | 
						|
 * Teodor Sigaev <teodor@stack.net>
 | 
						|
 */
 | 
						|
 | 
						|
#include "ltree.h"
 | 
						|
#include <ctype.h>
 | 
						|
#include "utils/array.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 *
 | 
						|
getlexem(char *start, char *end, int *len)
 | 
						|
{
 | 
						|
	char	   *ptr;
 | 
						|
 | 
						|
	while (start < end && *start == '_')
 | 
						|
		start++;
 | 
						|
 | 
						|
	ptr = start;
 | 
						|
	if (ptr == end)
 | 
						|
		return NULL;
 | 
						|
 | 
						|
	while (ptr < end && *ptr != '_')
 | 
						|
		ptr++;
 | 
						|
 | 
						|
	*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 = getlexem(qn, endq, &lenq)) != NULL)
 | 
						|
	{
 | 
						|
		tn = t->name;
 | 
						|
		isok = false;
 | 
						|
		while ((tn = getlexem(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;
 | 
						|
}
 | 
						|
 | 
						|
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) ? pg_strncasecmp : strncmp;
 | 
						|
 | 
						|
		if (curvar->flag & LVAR_SUBLEXEM)
 | 
						|
		{
 | 
						|
			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(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")));
 | 
						|
 | 
						|
	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)
 | 
						|
										));
 | 
						|
}
 |