LCOV - code coverage report
Current view: top level - contrib/dict_xsyn - dict_xsyn.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 96.1 % 102 98
Test Date: 2026-07-25 22:15:46 Functions: 100.0 % 8 8
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 84.3 % 70 59

             Branch data     Line data    Source code
       1                 :             : /*-------------------------------------------------------------------------
       2                 :             :  *
       3                 :             :  * dict_xsyn.c
       4                 :             :  *    Extended synonym dictionary
       5                 :             :  *
       6                 :             :  * Copyright (c) 2007-2026, PostgreSQL Global Development Group
       7                 :             :  *
       8                 :             :  * IDENTIFICATION
       9                 :             :  *    contrib/dict_xsyn/dict_xsyn.c
      10                 :             :  *
      11                 :             :  *-------------------------------------------------------------------------
      12                 :             :  */
      13                 :             : #include "postgres.h"
      14                 :             : 
      15                 :             : #include <ctype.h>
      16                 :             : 
      17                 :             : #include "catalog/pg_collation_d.h"
      18                 :             : #include "commands/defrem.h"
      19                 :             : #include "tsearch/ts_locale.h"
      20                 :             : #include "tsearch/ts_public.h"
      21                 :             : #include "utils/formatting.h"
      22                 :             : 
      23                 :           1 : PG_MODULE_MAGIC_EXT(
      24                 :             :                     .name = "dict_xsyn",
      25                 :             :                     .version = PG_VERSION
      26                 :             : );
      27                 :             : 
      28                 :             : typedef struct
      29                 :             : {
      30                 :             :     char       *key;            /* Word */
      31                 :             :     char       *value;          /* Unparsed list of synonyms, including the
      32                 :             :                                  * word itself */
      33                 :             : } Syn;
      34                 :             : 
      35                 :             : typedef struct
      36                 :             : {
      37                 :             :     int         len;
      38                 :             :     Syn        *syn;
      39                 :             : 
      40                 :             :     bool        matchorig;
      41                 :             :     bool        keeporig;
      42                 :             :     bool        matchsynonyms;
      43                 :             :     bool        keepsynonyms;
      44                 :             : } DictSyn;
      45                 :             : 
      46                 :             : 
      47                 :           2 : PG_FUNCTION_INFO_V1(dxsyn_init);
      48                 :           2 : PG_FUNCTION_INFO_V1(dxsyn_lexize);
      49                 :             : 
      50                 :             : static char *
      51                 :         149 : find_word(char *in, char **end)
      52                 :             : {
      53                 :             :     char       *start;
      54                 :             : 
      55                 :         149 :     *end = NULL;
      56   [ +  +  +  + ]:         205 :     while (*in && isspace((unsigned char) *in))
      57                 :          56 :         in += pg_mblen_cstr(in);
      58                 :             : 
      59   [ +  +  +  + ]:         149 :     if (!*in || *in == '#')
      60                 :          84 :         return NULL;
      61                 :          65 :     start = in;
      62                 :             : 
      63   [ +  -  +  + ]:         412 :     while (*in && !isspace((unsigned char) *in))
      64                 :         347 :         in += pg_mblen_cstr(in);
      65                 :             : 
      66                 :          65 :     *end = in;
      67                 :             : 
      68                 :          65 :     return start;
      69                 :             : }
      70                 :             : 
      71                 :             : static int
      72                 :          74 : compare_syn(const void *a, const void *b)
      73                 :             : {
      74                 :          74 :     return strcmp(((const Syn *) a)->key, ((const Syn *) b)->key);
      75                 :             : }
      76                 :             : 
      77                 :             : static void
      78                 :          14 : read_dictionary(DictSyn *d, const char *filename)
      79                 :             : {
      80                 :          14 :     char       *real_filename = get_tsearch_config_filename(filename, "rules");
      81                 :             :     tsearch_readline_state trst;
      82                 :             :     char       *line;
      83                 :          14 :     int         cur = 0;
      84                 :             : 
      85         [ -  + ]:          14 :     if (!tsearch_readline_begin(&trst, real_filename))
      86         [ #  # ]:           0 :         ereport(ERROR,
      87                 :             :                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
      88                 :             :                  errmsg("could not open synonym file \"%s\": %m",
      89                 :             :                         real_filename)));
      90                 :             : 
      91         [ +  + ]:          98 :     while ((line = tsearch_readline(&trst)) != NULL)
      92                 :             :     {
      93                 :             :         char       *value;
      94                 :             :         char       *key;
      95                 :             :         char       *pos;
      96                 :             :         char       *end;
      97                 :             : 
      98         [ -  + ]:          84 :         if (*line == '\0')
      99                 :           0 :             continue;
     100                 :             : 
     101                 :          84 :         value = str_tolower(line, strlen(line), DEFAULT_COLLATION_OID);
     102                 :          84 :         pfree(line);
     103                 :             : 
     104                 :          84 :         pos = value;
     105         [ +  + ]:         116 :         while ((key = find_word(pos, &end)) != NULL)
     106                 :             :         {
     107                 :             :             /* Enlarge syn structure if full */
     108         [ +  + ]:          38 :             if (cur == d->len)
     109                 :             :             {
     110         [ -  + ]:          14 :                 d->len = (d->len > 0) ? 2 * d->len : 16;
     111         [ -  + ]:          14 :                 if (d->syn)
     112                 :           0 :                     d->syn = repalloc_array(d->syn, Syn, d->len);
     113                 :             :                 else
     114                 :          14 :                     d->syn = palloc_array(Syn, d->len);
     115                 :             :             }
     116                 :             : 
     117                 :             :             /* Save first word only if we will match it */
     118   [ +  +  +  + ]:          38 :             if (pos != value || d->matchorig)
     119                 :             :             {
     120                 :          34 :                 d->syn[cur].key = pnstrdup(key, end - key);
     121                 :          34 :                 d->syn[cur].value = pstrdup(value);
     122                 :             : 
     123                 :          34 :                 cur++;
     124                 :             :             }
     125                 :             : 
     126                 :          38 :             pos = end;
     127                 :             : 
     128                 :             :             /* Don't bother scanning synonyms if we will not match them */
     129         [ +  + ]:          38 :             if (!d->matchsynonyms)
     130                 :           6 :                 break;
     131                 :             :         }
     132                 :             : 
     133                 :          84 :         pfree(value);
     134                 :             :     }
     135                 :             : 
     136                 :          14 :     tsearch_readline_end(&trst);
     137                 :             : 
     138                 :          14 :     d->len = cur;
     139         [ +  + ]:          14 :     if (cur > 1)
     140                 :           8 :         qsort(d->syn, d->len, sizeof(Syn), compare_syn);
     141                 :             : 
     142                 :          14 :     pfree(real_filename);
     143                 :          14 : }
     144                 :             : 
     145                 :             : Datum
     146                 :          15 : dxsyn_init(PG_FUNCTION_ARGS)
     147                 :             : {
     148                 :          15 :     List       *dictoptions = (List *) PG_GETARG_POINTER(0);
     149                 :             :     DictSyn    *d;
     150                 :             :     ListCell   *l;
     151                 :          15 :     char       *filename = NULL;
     152                 :             : 
     153                 :          15 :     d = palloc0_object(DictSyn);
     154                 :          15 :     d->len = 0;
     155                 :          15 :     d->syn = NULL;
     156                 :          15 :     d->matchorig = true;
     157                 :          15 :     d->keeporig = true;
     158                 :          15 :     d->matchsynonyms = false;
     159                 :          15 :     d->keepsynonyms = true;
     160                 :             : 
     161   [ +  +  +  +  :          85 :     foreach(l, dictoptions)
                   +  + ]
     162                 :             :     {
     163                 :          70 :         DefElem    *defel = (DefElem *) lfirst(l);
     164                 :             : 
     165         [ +  + ]:          70 :         if (strcmp(defel->defname, "matchorig") == 0)
     166                 :             :         {
     167                 :          14 :             d->matchorig = defGetBoolean(defel);
     168                 :             :         }
     169         [ +  + ]:          56 :         else if (strcmp(defel->defname, "keeporig") == 0)
     170                 :             :         {
     171                 :          14 :             d->keeporig = defGetBoolean(defel);
     172                 :             :         }
     173         [ +  + ]:          42 :         else if (strcmp(defel->defname, "matchsynonyms") == 0)
     174                 :             :         {
     175                 :          14 :             d->matchsynonyms = defGetBoolean(defel);
     176                 :             :         }
     177         [ +  + ]:          28 :         else if (strcmp(defel->defname, "keepsynonyms") == 0)
     178                 :             :         {
     179                 :          14 :             d->keepsynonyms = defGetBoolean(defel);
     180                 :             :         }
     181         [ +  - ]:          14 :         else if (strcmp(defel->defname, "rules") == 0)
     182                 :             :         {
     183                 :             :             /* we can't read the rules before parsing all options! */
     184                 :          14 :             filename = defGetString(defel);
     185                 :             :         }
     186                 :             :         else
     187                 :             :         {
     188         [ #  # ]:           0 :             ereport(ERROR,
     189                 :             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     190                 :             :                      errmsg("unrecognized xsyn parameter: \"%s\"",
     191                 :             :                             defel->defname)));
     192                 :             :         }
     193                 :             :     }
     194                 :             : 
     195         [ +  + ]:          15 :     if (filename)
     196                 :          14 :         read_dictionary(d, filename);
     197                 :             : 
     198                 :          15 :     PG_RETURN_POINTER(d);
     199                 :             : }
     200                 :             : 
     201                 :             : Datum
     202                 :          21 : dxsyn_lexize(PG_FUNCTION_ARGS)
     203                 :             : {
     204                 :          21 :     DictSyn    *d = (DictSyn *) PG_GETARG_POINTER(0);
     205                 :          21 :     char       *in = (char *) PG_GETARG_POINTER(1);
     206                 :          21 :     int         length = PG_GETARG_INT32(2);
     207                 :             :     Syn         word;
     208                 :             :     Syn        *found;
     209                 :          21 :     TSLexeme   *res = NULL;
     210                 :             : 
     211   [ +  -  +  + ]:          21 :     if (!length || d->len == 0)
     212                 :           3 :         PG_RETURN_POINTER(NULL);
     213                 :             : 
     214                 :             :     /* Create search pattern */
     215                 :          18 :     word.key = str_tolower(in, length, DEFAULT_COLLATION_OID);
     216                 :          18 :     word.value = NULL;
     217                 :             : 
     218                 :             :     /* Look for matching syn */
     219                 :          18 :     found = (Syn *) bsearch(&word, d->syn, d->len, sizeof(Syn), compare_syn);
     220                 :          18 :     pfree(word.key);
     221                 :             : 
     222         [ +  + ]:          18 :     if (!found)
     223                 :           9 :         PG_RETURN_POINTER(NULL);
     224                 :             : 
     225                 :             :     /* Parse string of synonyms and return array of words */
     226                 :             :     {
     227                 :           9 :         char       *value = found->value;
     228                 :             :         char       *syn;
     229                 :             :         char       *pos;
     230                 :             :         char       *end;
     231                 :           9 :         int         nsyns = 0;
     232                 :             : 
     233                 :           9 :         res = palloc_object(TSLexeme);
     234                 :             : 
     235                 :           9 :         pos = value;
     236         [ +  + ]:          33 :         while ((syn = find_word(pos, &end)) != NULL)
     237                 :             :         {
     238                 :          27 :             res = repalloc(res, sizeof(TSLexeme) * (nsyns + 2));
     239                 :             : 
     240                 :             :             /* The first word is output only if keeporig=true */
     241   [ +  +  +  + ]:          27 :             if (pos != value || d->keeporig)
     242                 :             :             {
     243                 :          22 :                 res[nsyns].lexeme = pnstrdup(syn, end - syn);
     244                 :          22 :                 res[nsyns].nvariant = 0;
     245                 :          22 :                 res[nsyns].flags = 0;
     246                 :          22 :                 nsyns++;
     247                 :             :             }
     248                 :             : 
     249                 :          27 :             pos = end;
     250                 :             : 
     251                 :             :             /* Stop if we are not to output the synonyms */
     252         [ +  + ]:          27 :             if (!d->keepsynonyms)
     253                 :           3 :                 break;
     254                 :             :         }
     255                 :           9 :         res[nsyns].lexeme = NULL;
     256                 :             :     }
     257                 :             : 
     258                 :           9 :     PG_RETURN_POINTER(res);
     259                 :             : }
        

Generated by: LCOV version 2.0-1