Suppress complaints about leaks in TS dictionary loading.

Like the situation with function cache loading, text search
dictionary loading functions tend to leak some cruft into the
dictionary's long-lived cache context.  To judge by the examples in
the core regression tests, not very many bytes are at stake.
Moreover, I don't see a way to prevent such leaks without changing the
API for TS template initialization functions: right now they do not
have to worry about making sure that their results are long-lived.

Hence, I think we should install a suppression rule rather than trying
to fix this completely.  However, I did grab some low-hanging fruit:
several places were leaking the result of get_tsearch_config_filename.
This seems worth doing mostly because they are inconsistent with other
dictionaries that were freeing it already.

Author: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: Andres Freund <andres@anarazel.de>
Discussion: https://postgr.es/m/285483.1746756246@sss.pgh.pa.us
This commit is contained in:
Tom Lane 2025-08-02 19:43:53 -04:00
parent 2c7b4ad24d
commit 7f6ededa76
5 changed files with 32 additions and 10 deletions

View File

@ -47,24 +47,30 @@ dispell_init(PG_FUNCTION_ARGS)
if (strcmp(defel->defname, "dictfile") == 0)
{
char *filename;
if (dictloaded)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("multiple DictFile parameters")));
NIImportDictionary(&(d->obj),
get_tsearch_config_filename(defGetString(defel),
"dict"));
filename = get_tsearch_config_filename(defGetString(defel),
"dict");
NIImportDictionary(&(d->obj), filename);
pfree(filename);
dictloaded = true;
}
else if (strcmp(defel->defname, "afffile") == 0)
{
char *filename;
if (affloaded)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("multiple AffFile parameters")));
NIImportAffixes(&(d->obj),
get_tsearch_config_filename(defGetString(defel),
"affix"));
filename = get_tsearch_config_filename(defGetString(defel),
"affix");
NIImportAffixes(&(d->obj), filename);
pfree(filename);
affloaded = true;
}
else if (strcmp(defel->defname, "stopwords") == 0)

View File

@ -199,6 +199,7 @@ skipline:
}
tsearch_readline_end(&trst);
pfree(filename);
d->len = cur;
qsort(d->syn, d->len, sizeof(Syn), compareSyn);

View File

@ -167,17 +167,17 @@ addWrd(DictThesaurus *d, char *b, char *e, uint32 idsubst, uint16 nwrd, uint16 p
static void
thesaurusRead(const char *filename, DictThesaurus *d)
{
char *real_filename = get_tsearch_config_filename(filename, "ths");
tsearch_readline_state trst;
uint32 idsubst = 0;
bool useasis = false;
char *line;
filename = get_tsearch_config_filename(filename, "ths");
if (!tsearch_readline_begin(&trst, filename))
if (!tsearch_readline_begin(&trst, real_filename))
ereport(ERROR,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("could not open thesaurus file \"%s\": %m",
filename)));
real_filename)));
while ((line = tsearch_readline(&trst)) != NULL)
{
@ -297,6 +297,7 @@ thesaurusRead(const char *filename, DictThesaurus *d)
d->nsubst = idsubst;
tsearch_readline_end(&trst);
pfree(real_filename);
}
static TheLexeme *

View File

@ -321,7 +321,9 @@ lookup_ts_dictionary_cache(Oid dictId)
/*
* Init method runs in dictionary's private memory context, and we
* make sure the options are stored there too
* make sure the options are stored there too. This typically
* results in a small amount of memory leakage, but it's not worth
* complicating the API for tmplinit functions to avoid it.
*/
oldcontext = MemoryContextSwitchTo(entry->dictCtx);

View File

@ -215,3 +215,15 @@
...
fun:cached_function_compile
}
# Suppress complaints about stuff leaked during TS dictionary loading.
# Not very much is typically lost there, and preventing it would
# require a risky API change for TS tmplinit functions.
{
hide_ts_dictionary_leaks
Memcheck:Leak
match-leak-kinds: definite,possible,indirect
...
fun:lookup_ts_dictionary_cache
}