]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Suppress complaints about leaks in TS dictionary loading.
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 2 Aug 2025 23:43:53 +0000 (19:43 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Sun, 3 Aug 2025 01:59:46 +0000 (21:59 -0400)
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

src/backend/tsearch/dict_ispell.c
src/backend/tsearch/dict_synonym.c
src/backend/tsearch/dict_thesaurus.c
src/backend/utils/cache/ts_cache.c
src/tools/valgrind.supp

index 63bd193a78a894b3bff1080e9147882236a31066..debfbf956cc1f86990981646b5770f017daba92e 100644 (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)
index 0da5a9d686802bf8b666de9ad82b632e23141dc5..c2773eb01adee8e5053b0a551f681de6e622bcbf 100644 (file)
@@ -199,6 +199,7 @@ skipline:
        }
 
        tsearch_readline_end(&trst);
+       pfree(filename);
 
        d->len = cur;
        qsort(d->syn, d->len, sizeof(Syn), compareSyn);
index 1bebe36a6910e9f84deb836f7a19bd49b948ae04..1e6bbde1ca7d8bbd34da635edf3187eb19e8388a 100644 (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 *
index 18cccd778fd8c93652d3ee7f715a28027c7da687..e8ae53238d07a3b7ece3625106ce27cf1a27857b 100644 (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);
 
index fad20c8f708049b5c302ca0bc806a3653deff810..3880007dfb3bbaf3d2c52e85370a586437091a01 100644 (file)
    ...
    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
+}