LCOV - code coverage report
Current view: top level - contrib/dict_xsyn - dict_xsyn.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 96.2 % 104 100
Test Date: 2026-03-12 16:14:50 Functions: 100.0 % 8 8
Legend: Lines:     hit not hit

            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              :     {
     216           18 :         char       *temp = pnstrdup(in, length);
     217              : 
     218           18 :         word.key = str_tolower(temp, length, DEFAULT_COLLATION_OID);
     219           18 :         pfree(temp);
     220           18 :         word.value = NULL;
     221              :     }
     222              : 
     223              :     /* Look for matching syn */
     224           18 :     found = (Syn *) bsearch(&word, d->syn, d->len, sizeof(Syn), compare_syn);
     225           18 :     pfree(word.key);
     226              : 
     227           18 :     if (!found)
     228            9 :         PG_RETURN_POINTER(NULL);
     229              : 
     230              :     /* Parse string of synonyms and return array of words */
     231              :     {
     232            9 :         char       *value = found->value;
     233              :         char       *syn;
     234              :         char       *pos;
     235              :         char       *end;
     236            9 :         int         nsyns = 0;
     237              : 
     238            9 :         res = palloc_object(TSLexeme);
     239              : 
     240            9 :         pos = value;
     241           33 :         while ((syn = find_word(pos, &end)) != NULL)
     242              :         {
     243           27 :             res = repalloc(res, sizeof(TSLexeme) * (nsyns + 2));
     244              : 
     245              :             /* The first word is output only if keeporig=true */
     246           27 :             if (pos != value || d->keeporig)
     247              :             {
     248           22 :                 res[nsyns].lexeme = pnstrdup(syn, end - syn);
     249           22 :                 res[nsyns].nvariant = 0;
     250           22 :                 res[nsyns].flags = 0;
     251           22 :                 nsyns++;
     252              :             }
     253              : 
     254           27 :             pos = end;
     255              : 
     256              :             /* Stop if we are not to output the synonyms */
     257           27 :             if (!d->keepsynonyms)
     258            3 :                 break;
     259              :         }
     260            9 :         res[nsyns].lexeme = NULL;
     261              :     }
     262              : 
     263            9 :     PG_RETURN_POINTER(res);
     264              : }
        

Generated by: LCOV version 2.0-1