mirror of
				https://github.com/postgres/postgres.git
				synced 2025-11-03 09:13:20 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			542 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			542 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*-------------------------------------------------------------------------
 | 
						|
 *
 | 
						|
 * tsearch2.c
 | 
						|
 *		Backwards-compatibility package for old contrib/tsearch2 API
 | 
						|
 *
 | 
						|
 * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
 | 
						|
 *
 | 
						|
 *
 | 
						|
 * IDENTIFICATION
 | 
						|
 *	  contrib/tsearch2/tsearch2.c
 | 
						|
 *
 | 
						|
 *-------------------------------------------------------------------------
 | 
						|
 */
 | 
						|
#include "postgres.h"
 | 
						|
 | 
						|
#include "catalog/namespace.h"
 | 
						|
#include "catalog/pg_type.h"
 | 
						|
#include "commands/trigger.h"
 | 
						|
#include "tsearch/ts_utils.h"
 | 
						|
#include "utils/builtins.h"
 | 
						|
#include "utils/guc.h"
 | 
						|
#include "utils/syscache.h"
 | 
						|
 | 
						|
PG_MODULE_MAGIC;
 | 
						|
 | 
						|
static Oid	current_dictionary_oid = InvalidOid;
 | 
						|
static Oid	current_parser_oid = InvalidOid;
 | 
						|
 | 
						|
/* insert given value at argument position 0 */
 | 
						|
#define INSERT_ARGUMENT0(argument, isnull)				\
 | 
						|
	do {												\
 | 
						|
		int i;											\
 | 
						|
		for (i = fcinfo->nargs; i > 0; i--)				\
 | 
						|
		{												\
 | 
						|
			fcinfo->arg[i] = fcinfo->arg[i-1];			\
 | 
						|
			fcinfo->argnull[i] = fcinfo->argnull[i-1];	\
 | 
						|
		}												\
 | 
						|
		fcinfo->arg[0] = (argument);					\
 | 
						|
		fcinfo->argnull[0] = (isnull);					\
 | 
						|
		fcinfo->nargs++;								\
 | 
						|
	} while (0)
 | 
						|
 | 
						|
#define TextGetObjectId(infunction, text) \
 | 
						|
	DatumGetObjectId(DirectFunctionCall1(infunction, \
 | 
						|
					 CStringGetDatum(text_to_cstring(text))))
 | 
						|
 | 
						|
#define UNSUPPORTED_FUNCTION(name)						\
 | 
						|
	PG_FUNCTION_INFO_V1(name);							\
 | 
						|
	Datum												\
 | 
						|
	name(PG_FUNCTION_ARGS)								\
 | 
						|
	{													\
 | 
						|
		ereport(ERROR,									\
 | 
						|
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\
 | 
						|
				 errmsg("function %s is no longer supported", \
 | 
						|
						format_procedure(fcinfo->flinfo->fn_oid)), \
 | 
						|
				 errhint("Switch to new tsearch functionality."))); \
 | 
						|
		/* keep compiler quiet */						\
 | 
						|
		PG_RETURN_NULL();								\
 | 
						|
	}													\
 | 
						|
	extern int no_such_variable
 | 
						|
 | 
						|
static Oid	GetCurrentDict(void);
 | 
						|
static Oid	GetCurrentParser(void);
 | 
						|
 | 
						|
PG_FUNCTION_INFO_V1(tsa_lexize_byname);
 | 
						|
PG_FUNCTION_INFO_V1(tsa_lexize_bycurrent);
 | 
						|
PG_FUNCTION_INFO_V1(tsa_set_curdict);
 | 
						|
PG_FUNCTION_INFO_V1(tsa_set_curdict_byname);
 | 
						|
PG_FUNCTION_INFO_V1(tsa_token_type_current);
 | 
						|
PG_FUNCTION_INFO_V1(tsa_set_curprs);
 | 
						|
PG_FUNCTION_INFO_V1(tsa_set_curprs_byname);
 | 
						|
PG_FUNCTION_INFO_V1(tsa_parse_current);
 | 
						|
PG_FUNCTION_INFO_V1(tsa_set_curcfg);
 | 
						|
PG_FUNCTION_INFO_V1(tsa_set_curcfg_byname);
 | 
						|
PG_FUNCTION_INFO_V1(tsa_to_tsvector_name);
 | 
						|
PG_FUNCTION_INFO_V1(tsa_to_tsquery_name);
 | 
						|
PG_FUNCTION_INFO_V1(tsa_plainto_tsquery_name);
 | 
						|
PG_FUNCTION_INFO_V1(tsa_headline_byname);
 | 
						|
PG_FUNCTION_INFO_V1(tsa_ts_stat);
 | 
						|
PG_FUNCTION_INFO_V1(tsa_tsearch2);
 | 
						|
PG_FUNCTION_INFO_V1(tsa_rewrite_accum);
 | 
						|
PG_FUNCTION_INFO_V1(tsa_rewrite_finish);
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * List of unsupported functions
 | 
						|
 *
 | 
						|
 * The parser and dictionary functions are defined only so that the former
 | 
						|
 * contents of pg_ts_parser and pg_ts_dict can be loaded into the system,
 | 
						|
 * for ease of reference while creating the new tsearch configuration.
 | 
						|
 */
 | 
						|
 | 
						|
UNSUPPORTED_FUNCTION(tsa_dex_init);
 | 
						|
UNSUPPORTED_FUNCTION(tsa_dex_lexize);
 | 
						|
 | 
						|
UNSUPPORTED_FUNCTION(tsa_snb_en_init);
 | 
						|
UNSUPPORTED_FUNCTION(tsa_snb_lexize);
 | 
						|
UNSUPPORTED_FUNCTION(tsa_snb_ru_init_koi8);
 | 
						|
UNSUPPORTED_FUNCTION(tsa_snb_ru_init_utf8);
 | 
						|
UNSUPPORTED_FUNCTION(tsa_snb_ru_init);
 | 
						|
 | 
						|
UNSUPPORTED_FUNCTION(tsa_spell_init);
 | 
						|
UNSUPPORTED_FUNCTION(tsa_spell_lexize);
 | 
						|
 | 
						|
UNSUPPORTED_FUNCTION(tsa_syn_init);
 | 
						|
UNSUPPORTED_FUNCTION(tsa_syn_lexize);
 | 
						|
 | 
						|
UNSUPPORTED_FUNCTION(tsa_thesaurus_init);
 | 
						|
UNSUPPORTED_FUNCTION(tsa_thesaurus_lexize);
 | 
						|
 | 
						|
UNSUPPORTED_FUNCTION(tsa_prsd_start);
 | 
						|
UNSUPPORTED_FUNCTION(tsa_prsd_getlexeme);
 | 
						|
UNSUPPORTED_FUNCTION(tsa_prsd_end);
 | 
						|
UNSUPPORTED_FUNCTION(tsa_prsd_lextype);
 | 
						|
UNSUPPORTED_FUNCTION(tsa_prsd_headline);
 | 
						|
 | 
						|
UNSUPPORTED_FUNCTION(tsa_reset_tsearch);
 | 
						|
UNSUPPORTED_FUNCTION(tsa_get_covers);
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * list of redefined functions
 | 
						|
 */
 | 
						|
 | 
						|
/* lexize(text, text) */
 | 
						|
Datum
 | 
						|
tsa_lexize_byname(PG_FUNCTION_ARGS)
 | 
						|
{
 | 
						|
	text	   *dictname = PG_GETARG_TEXT_PP(0);
 | 
						|
	Datum		arg1 = PG_GETARG_DATUM(1);
 | 
						|
 | 
						|
	return DirectFunctionCall2(ts_lexize,
 | 
						|
				ObjectIdGetDatum(TextGetObjectId(regdictionaryin, dictname)),
 | 
						|
							   arg1);
 | 
						|
}
 | 
						|
 | 
						|
/* lexize(text) */
 | 
						|
Datum
 | 
						|
tsa_lexize_bycurrent(PG_FUNCTION_ARGS)
 | 
						|
{
 | 
						|
	Datum		arg0 = PG_GETARG_DATUM(0);
 | 
						|
	Oid			id = GetCurrentDict();
 | 
						|
 | 
						|
	return DirectFunctionCall2(ts_lexize,
 | 
						|
							   ObjectIdGetDatum(id),
 | 
						|
							   arg0);
 | 
						|
}
 | 
						|
 | 
						|
/* set_curdict(int) */
 | 
						|
Datum
 | 
						|
tsa_set_curdict(PG_FUNCTION_ARGS)
 | 
						|
{
 | 
						|
	Oid			dict_oid = PG_GETARG_OID(0);
 | 
						|
 | 
						|
	if (!SearchSysCacheExists(TSDICTOID,
 | 
						|
							  ObjectIdGetDatum(dict_oid),
 | 
						|
							  0, 0, 0))
 | 
						|
		elog(ERROR, "cache lookup failed for text search dictionary %u",
 | 
						|
			 dict_oid);
 | 
						|
 | 
						|
	current_dictionary_oid = dict_oid;
 | 
						|
 | 
						|
	PG_RETURN_VOID();
 | 
						|
}
 | 
						|
 | 
						|
/* set_curdict(text) */
 | 
						|
Datum
 | 
						|
tsa_set_curdict_byname(PG_FUNCTION_ARGS)
 | 
						|
{
 | 
						|
	text	   *name = PG_GETARG_TEXT_PP(0);
 | 
						|
	Oid			dict_oid;
 | 
						|
 | 
						|
	dict_oid = get_ts_dict_oid(stringToQualifiedNameList(text_to_cstring(name)), false);
 | 
						|
 | 
						|
	current_dictionary_oid = dict_oid;
 | 
						|
 | 
						|
	PG_RETURN_VOID();
 | 
						|
}
 | 
						|
 | 
						|
/* token_type() */
 | 
						|
Datum
 | 
						|
tsa_token_type_current(PG_FUNCTION_ARGS)
 | 
						|
{
 | 
						|
	INSERT_ARGUMENT0(ObjectIdGetDatum(GetCurrentParser()), false);
 | 
						|
	return ts_token_type_byid(fcinfo);
 | 
						|
}
 | 
						|
 | 
						|
/* set_curprs(int) */
 | 
						|
Datum
 | 
						|
tsa_set_curprs(PG_FUNCTION_ARGS)
 | 
						|
{
 | 
						|
	Oid			parser_oid = PG_GETARG_OID(0);
 | 
						|
 | 
						|
	if (!SearchSysCacheExists(TSPARSEROID,
 | 
						|
							  ObjectIdGetDatum(parser_oid),
 | 
						|
							  0, 0, 0))
 | 
						|
		elog(ERROR, "cache lookup failed for text search parser %u",
 | 
						|
			 parser_oid);
 | 
						|
 | 
						|
	current_parser_oid = parser_oid;
 | 
						|
 | 
						|
	PG_RETURN_VOID();
 | 
						|
}
 | 
						|
 | 
						|
/* set_curprs(text) */
 | 
						|
Datum
 | 
						|
tsa_set_curprs_byname(PG_FUNCTION_ARGS)
 | 
						|
{
 | 
						|
	text	   *name = PG_GETARG_TEXT_PP(0);
 | 
						|
	Oid			parser_oid;
 | 
						|
 | 
						|
	parser_oid = get_ts_parser_oid(stringToQualifiedNameList(text_to_cstring(name)), false);
 | 
						|
 | 
						|
	current_parser_oid = parser_oid;
 | 
						|
 | 
						|
	PG_RETURN_VOID();
 | 
						|
}
 | 
						|
 | 
						|
/* parse(text) */
 | 
						|
Datum
 | 
						|
tsa_parse_current(PG_FUNCTION_ARGS)
 | 
						|
{
 | 
						|
	INSERT_ARGUMENT0(ObjectIdGetDatum(GetCurrentParser()), false);
 | 
						|
	return ts_parse_byid(fcinfo);
 | 
						|
}
 | 
						|
 | 
						|
/* set_curcfg(int) */
 | 
						|
Datum
 | 
						|
tsa_set_curcfg(PG_FUNCTION_ARGS)
 | 
						|
{
 | 
						|
	Oid			arg0 = PG_GETARG_OID(0);
 | 
						|
	char	   *name;
 | 
						|
 | 
						|
	name = DatumGetCString(DirectFunctionCall1(regconfigout,
 | 
						|
											   ObjectIdGetDatum(arg0)));
 | 
						|
 | 
						|
	SetConfigOption("default_text_search_config", name,
 | 
						|
					PGC_USERSET, PGC_S_SESSION);
 | 
						|
 | 
						|
	PG_RETURN_VOID();
 | 
						|
}
 | 
						|
 | 
						|
/* set_curcfg(text) */
 | 
						|
Datum
 | 
						|
tsa_set_curcfg_byname(PG_FUNCTION_ARGS)
 | 
						|
{
 | 
						|
	text	   *arg0 = PG_GETARG_TEXT_PP(0);
 | 
						|
	char	   *name;
 | 
						|
 | 
						|
	name = text_to_cstring(arg0);
 | 
						|
 | 
						|
	SetConfigOption("default_text_search_config", name,
 | 
						|
					PGC_USERSET, PGC_S_SESSION);
 | 
						|
 | 
						|
	PG_RETURN_VOID();
 | 
						|
}
 | 
						|
 | 
						|
/* to_tsvector(text, text) */
 | 
						|
Datum
 | 
						|
tsa_to_tsvector_name(PG_FUNCTION_ARGS)
 | 
						|
{
 | 
						|
	text	   *cfgname = PG_GETARG_TEXT_PP(0);
 | 
						|
	Datum		arg1 = PG_GETARG_DATUM(1);
 | 
						|
	Oid			config_oid;
 | 
						|
 | 
						|
	config_oid = TextGetObjectId(regconfigin, cfgname);
 | 
						|
 | 
						|
	return DirectFunctionCall2(to_tsvector_byid,
 | 
						|
							   ObjectIdGetDatum(config_oid), arg1);
 | 
						|
}
 | 
						|
 | 
						|
/* to_tsquery(text, text) */
 | 
						|
Datum
 | 
						|
tsa_to_tsquery_name(PG_FUNCTION_ARGS)
 | 
						|
{
 | 
						|
	text	   *cfgname = PG_GETARG_TEXT_PP(0);
 | 
						|
	Datum		arg1 = PG_GETARG_DATUM(1);
 | 
						|
	Oid			config_oid;
 | 
						|
 | 
						|
	config_oid = TextGetObjectId(regconfigin, cfgname);
 | 
						|
 | 
						|
	return DirectFunctionCall2(to_tsquery_byid,
 | 
						|
							   ObjectIdGetDatum(config_oid), arg1);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* plainto_tsquery(text, text) */
 | 
						|
Datum
 | 
						|
tsa_plainto_tsquery_name(PG_FUNCTION_ARGS)
 | 
						|
{
 | 
						|
	text	   *cfgname = PG_GETARG_TEXT_PP(0);
 | 
						|
	Datum		arg1 = PG_GETARG_DATUM(1);
 | 
						|
	Oid			config_oid;
 | 
						|
 | 
						|
	config_oid = TextGetObjectId(regconfigin, cfgname);
 | 
						|
 | 
						|
	return DirectFunctionCall2(plainto_tsquery_byid,
 | 
						|
							   ObjectIdGetDatum(config_oid), arg1);
 | 
						|
}
 | 
						|
 | 
						|
/* headline(text, text, tsquery [,text]) */
 | 
						|
Datum
 | 
						|
tsa_headline_byname(PG_FUNCTION_ARGS)
 | 
						|
{
 | 
						|
	Datum		arg0 = PG_GETARG_DATUM(0);
 | 
						|
	Datum		arg1 = PG_GETARG_DATUM(1);
 | 
						|
	Datum		arg2 = PG_GETARG_DATUM(2);
 | 
						|
	Datum		result;
 | 
						|
	Oid			config_oid;
 | 
						|
 | 
						|
	/* first parameter has to be converted to oid */
 | 
						|
	config_oid = DatumGetObjectId(DirectFunctionCall1(regconfigin,
 | 
						|
								CStringGetDatum(TextDatumGetCString(arg0))));
 | 
						|
 | 
						|
	if (PG_NARGS() == 3)
 | 
						|
		result = DirectFunctionCall3(ts_headline_byid,
 | 
						|
								   ObjectIdGetDatum(config_oid), arg1, arg2);
 | 
						|
	else
 | 
						|
	{
 | 
						|
		Datum		arg3 = PG_GETARG_DATUM(3);
 | 
						|
 | 
						|
		result = DirectFunctionCall4(ts_headline_byid_opt,
 | 
						|
									 ObjectIdGetDatum(config_oid),
 | 
						|
									 arg1, arg2, arg3);
 | 
						|
	}
 | 
						|
 | 
						|
	return result;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * tsearch2 version of update trigger
 | 
						|
 *
 | 
						|
 * We pass this on to the core trigger after inserting the default text
 | 
						|
 * search configuration name as the second argument.  Note that this isn't
 | 
						|
 * a complete implementation of the original functionality; tsearch2 allowed
 | 
						|
 * transformation function names to be included in the list.  However, that
 | 
						|
 * is deliberately removed as being a security risk.
 | 
						|
 */
 | 
						|
Datum
 | 
						|
tsa_tsearch2(PG_FUNCTION_ARGS)
 | 
						|
{
 | 
						|
	TriggerData *trigdata;
 | 
						|
	Trigger    *trigger;
 | 
						|
	char	  **tgargs,
 | 
						|
			  **tgargs_old;
 | 
						|
	int			i;
 | 
						|
	Datum		res;
 | 
						|
 | 
						|
	/* Check call context */
 | 
						|
	if (!CALLED_AS_TRIGGER(fcinfo))		/* internal error */
 | 
						|
		elog(ERROR, "tsvector_update_trigger: not fired by trigger manager");
 | 
						|
 | 
						|
	trigdata = (TriggerData *) fcinfo->context;
 | 
						|
	trigger = trigdata->tg_trigger;
 | 
						|
 | 
						|
	if (trigger->tgnargs < 2)
 | 
						|
		elog(ERROR, "TSearch: format tsearch2(tsvector_field, text_field1,...)");
 | 
						|
 | 
						|
	/* create space for configuration name */
 | 
						|
	tgargs = (char **) palloc((trigger->tgnargs + 1) * sizeof(char *));
 | 
						|
	tgargs[0] = trigger->tgargs[0];
 | 
						|
	for (i = 1; i < trigger->tgnargs; i++)
 | 
						|
		tgargs[i + 1] = trigger->tgargs[i];
 | 
						|
 | 
						|
	tgargs[1] = pstrdup(GetConfigOptionByName("default_text_search_config",
 | 
						|
											  NULL, false));
 | 
						|
	tgargs_old = trigger->tgargs;
 | 
						|
	trigger->tgargs = tgargs;
 | 
						|
	trigger->tgnargs++;
 | 
						|
 | 
						|
	res = tsvector_update_trigger_byid(fcinfo);
 | 
						|
 | 
						|
	/* restore old trigger data */
 | 
						|
	trigger->tgargs = tgargs_old;
 | 
						|
	trigger->tgnargs--;
 | 
						|
 | 
						|
	pfree(tgargs[1]);
 | 
						|
	pfree(tgargs);
 | 
						|
 | 
						|
	return res;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
Datum
 | 
						|
tsa_rewrite_accum(PG_FUNCTION_ARGS)
 | 
						|
{
 | 
						|
	TSQuery		acc;
 | 
						|
	ArrayType  *qa;
 | 
						|
	TSQuery		q;
 | 
						|
	QTNode	   *qex = NULL,
 | 
						|
			   *subs = NULL,
 | 
						|
			   *acctree = NULL;
 | 
						|
	bool		isfind = false;
 | 
						|
	Datum	   *elemsp;
 | 
						|
	int			nelemsp;
 | 
						|
	MemoryContext aggcontext;
 | 
						|
	MemoryContext oldcontext;
 | 
						|
 | 
						|
	if (!AggCheckCallContext(fcinfo, &aggcontext))
 | 
						|
		elog(ERROR, "tsa_rewrite_accum called in non-aggregate context");
 | 
						|
 | 
						|
	if (PG_ARGISNULL(0) || PG_GETARG_POINTER(0) == NULL)
 | 
						|
	{
 | 
						|
		acc = (TSQuery) MemoryContextAlloc(aggcontext, HDRSIZETQ);
 | 
						|
		SET_VARSIZE(acc, HDRSIZETQ);
 | 
						|
		acc->size = 0;
 | 
						|
	}
 | 
						|
	else
 | 
						|
		acc = PG_GETARG_TSQUERY(0);
 | 
						|
 | 
						|
	if (PG_ARGISNULL(1) || PG_GETARG_POINTER(1) == NULL)
 | 
						|
		PG_RETURN_TSQUERY(acc);
 | 
						|
	else
 | 
						|
		qa = PG_GETARG_ARRAYTYPE_P_COPY(1);
 | 
						|
 | 
						|
	if (ARR_NDIM(qa) != 1)
 | 
						|
		elog(ERROR, "array must be one-dimensional, not %d dimensions",
 | 
						|
			 ARR_NDIM(qa));
 | 
						|
	if (ArrayGetNItems(ARR_NDIM(qa), ARR_DIMS(qa)) != 3)
 | 
						|
		elog(ERROR, "array must have three elements");
 | 
						|
	if (ARR_ELEMTYPE(qa) != TSQUERYOID)
 | 
						|
		elog(ERROR, "array must contain tsquery elements");
 | 
						|
 | 
						|
	deconstruct_array(qa, TSQUERYOID, -1, false, 'i', &elemsp, NULL, &nelemsp);
 | 
						|
 | 
						|
	q = DatumGetTSQuery(elemsp[0]);
 | 
						|
	if (q->size == 0)
 | 
						|
	{
 | 
						|
		pfree(elemsp);
 | 
						|
		PG_RETURN_POINTER(acc);
 | 
						|
	}
 | 
						|
 | 
						|
	if (!acc->size)
 | 
						|
	{
 | 
						|
		if (VARSIZE(acc) > HDRSIZETQ)
 | 
						|
		{
 | 
						|
			pfree(elemsp);
 | 
						|
			PG_RETURN_POINTER(acc);
 | 
						|
		}
 | 
						|
		else
 | 
						|
			acctree = QT2QTN(GETQUERY(q), GETOPERAND(q));
 | 
						|
	}
 | 
						|
	else
 | 
						|
		acctree = QT2QTN(GETQUERY(acc), GETOPERAND(acc));
 | 
						|
 | 
						|
	QTNTernary(acctree);
 | 
						|
	QTNSort(acctree);
 | 
						|
 | 
						|
	q = DatumGetTSQuery(elemsp[1]);
 | 
						|
	if (q->size == 0)
 | 
						|
	{
 | 
						|
		pfree(elemsp);
 | 
						|
		PG_RETURN_POINTER(acc);
 | 
						|
	}
 | 
						|
	qex = QT2QTN(GETQUERY(q), GETOPERAND(q));
 | 
						|
	QTNTernary(qex);
 | 
						|
	QTNSort(qex);
 | 
						|
 | 
						|
	q = DatumGetTSQuery(elemsp[2]);
 | 
						|
	if (q->size)
 | 
						|
		subs = QT2QTN(GETQUERY(q), GETOPERAND(q));
 | 
						|
 | 
						|
	acctree = findsubquery(acctree, qex, subs, &isfind);
 | 
						|
 | 
						|
	if (isfind || !acc->size)
 | 
						|
	{
 | 
						|
		/* pfree( acc ); do not pfree(p), because nodeAgg.c will */
 | 
						|
		if (acctree)
 | 
						|
		{
 | 
						|
			QTNBinary(acctree);
 | 
						|
			oldcontext = MemoryContextSwitchTo(aggcontext);
 | 
						|
			acc = QTN2QT(acctree);
 | 
						|
			MemoryContextSwitchTo(oldcontext);
 | 
						|
		}
 | 
						|
		else
 | 
						|
		{
 | 
						|
			acc = (TSQuery) MemoryContextAlloc(aggcontext, HDRSIZETQ);
 | 
						|
			SET_VARSIZE(acc, HDRSIZETQ);
 | 
						|
			acc->size = 0;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	pfree(elemsp);
 | 
						|
	QTNFree(qex);
 | 
						|
	QTNFree(subs);
 | 
						|
	QTNFree(acctree);
 | 
						|
 | 
						|
	PG_RETURN_TSQUERY(acc);
 | 
						|
}
 | 
						|
 | 
						|
Datum
 | 
						|
tsa_rewrite_finish(PG_FUNCTION_ARGS)
 | 
						|
{
 | 
						|
	TSQuery		acc = PG_GETARG_TSQUERY(0);
 | 
						|
	TSQuery		rewrited;
 | 
						|
 | 
						|
	if (acc == NULL || PG_ARGISNULL(0) || acc->size == 0)
 | 
						|
	{
 | 
						|
		rewrited = (TSQuery) palloc(HDRSIZETQ);
 | 
						|
		SET_VARSIZE(rewrited, HDRSIZETQ);
 | 
						|
		rewrited->size = 0;
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		rewrited = (TSQuery) palloc(VARSIZE(acc));
 | 
						|
		memcpy(rewrited, acc, VARSIZE(acc));
 | 
						|
		pfree(acc);
 | 
						|
	}
 | 
						|
 | 
						|
	PG_RETURN_POINTER(rewrited);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * Get Oid of current dictionary
 | 
						|
 */
 | 
						|
static Oid
 | 
						|
GetCurrentDict(void)
 | 
						|
{
 | 
						|
	if (current_dictionary_oid == InvalidOid)
 | 
						|
		ereport(ERROR,
 | 
						|
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 | 
						|
				 errmsg("no current dictionary"),
 | 
						|
				 errhint("Execute SELECT set_curdict(...).")));
 | 
						|
 | 
						|
	return current_dictionary_oid;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Get Oid of current parser
 | 
						|
 *
 | 
						|
 * Here, it seems reasonable to select the "default" parser if none has been
 | 
						|
 * set.
 | 
						|
 */
 | 
						|
static Oid
 | 
						|
GetCurrentParser(void)
 | 
						|
{
 | 
						|
	if (current_parser_oid == InvalidOid)
 | 
						|
		current_parser_oid = get_ts_parser_oid(stringToQualifiedNameList("pg_catalog.default"), false);
 | 
						|
	return current_parser_oid;
 | 
						|
}
 |