LCOV - code coverage report
Current view: top level - contrib/dict_xsyn - dict_xsyn.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 0.0 % 133 0
Test Date: 2026-01-26 10:56:24 Functions: 0.0 % 8 0
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            0 : 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            0 : PG_FUNCTION_INFO_V1(dxsyn_init);
      48            0 : PG_FUNCTION_INFO_V1(dxsyn_lexize);
      49              : 
      50              : static char *
      51            0 : find_word(char *in, char **end)
      52              : {
      53            0 :         char       *start;
      54              : 
      55            0 :         *end = NULL;
      56            0 :         while (*in && isspace((unsigned char) *in))
      57            0 :                 in += pg_mblen(in);
      58              : 
      59            0 :         if (!*in || *in == '#')
      60            0 :                 return NULL;
      61            0 :         start = in;
      62              : 
      63            0 :         while (*in && !isspace((unsigned char) *in))
      64            0 :                 in += pg_mblen(in);
      65              : 
      66            0 :         *end = in;
      67              : 
      68            0 :         return start;
      69            0 : }
      70              : 
      71              : static int
      72            0 : compare_syn(const void *a, const void *b)
      73              : {
      74            0 :         return strcmp(((const Syn *) a)->key, ((const Syn *) b)->key);
      75              : }
      76              : 
      77              : static void
      78            0 : read_dictionary(DictSyn *d, const char *filename)
      79              : {
      80            0 :         char       *real_filename = get_tsearch_config_filename(filename, "rules");
      81            0 :         tsearch_readline_state trst;
      82            0 :         char       *line;
      83            0 :         int                     cur = 0;
      84              : 
      85            0 :         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            0 :         while ((line = tsearch_readline(&trst)) != NULL)
      92              :         {
      93            0 :                 char       *value;
      94            0 :                 char       *key;
      95            0 :                 char       *pos;
      96            0 :                 char       *end;
      97              : 
      98            0 :                 if (*line == '\0')
      99            0 :                         continue;
     100              : 
     101            0 :                 value = str_tolower(line, strlen(line), DEFAULT_COLLATION_OID);
     102            0 :                 pfree(line);
     103              : 
     104            0 :                 pos = value;
     105            0 :                 while ((key = find_word(pos, &end)) != NULL)
     106              :                 {
     107              :                         /* Enlarge syn structure if full */
     108            0 :                         if (cur == d->len)
     109              :                         {
     110            0 :                                 d->len = (d->len > 0) ? 2 * d->len : 16;
     111            0 :                                 if (d->syn)
     112            0 :                                         d->syn = repalloc_array(d->syn, Syn, d->len);
     113              :                                 else
     114            0 :                                         d->syn = palloc_array(Syn, d->len);
     115            0 :                         }
     116              : 
     117              :                         /* Save first word only if we will match it */
     118            0 :                         if (pos != value || d->matchorig)
     119              :                         {
     120            0 :                                 d->syn[cur].key = pnstrdup(key, end - key);
     121            0 :                                 d->syn[cur].value = pstrdup(value);
     122              : 
     123            0 :                                 cur++;
     124            0 :                         }
     125              : 
     126            0 :                         pos = end;
     127              : 
     128              :                         /* Don't bother scanning synonyms if we will not match them */
     129            0 :                         if (!d->matchsynonyms)
     130            0 :                                 break;
     131              :                 }
     132              : 
     133            0 :                 pfree(value);
     134            0 :         }
     135              : 
     136            0 :         tsearch_readline_end(&trst);
     137              : 
     138            0 :         d->len = cur;
     139            0 :         if (cur > 1)
     140            0 :                 qsort(d->syn, d->len, sizeof(Syn), compare_syn);
     141              : 
     142            0 :         pfree(real_filename);
     143            0 : }
     144              : 
     145              : Datum
     146            0 : dxsyn_init(PG_FUNCTION_ARGS)
     147              : {
     148            0 :         List       *dictoptions = (List *) PG_GETARG_POINTER(0);
     149            0 :         DictSyn    *d;
     150            0 :         ListCell   *l;
     151            0 :         char       *filename = NULL;
     152              : 
     153            0 :         d = palloc0_object(DictSyn);
     154            0 :         d->len = 0;
     155            0 :         d->syn = NULL;
     156            0 :         d->matchorig = true;
     157            0 :         d->keeporig = true;
     158            0 :         d->matchsynonyms = false;
     159            0 :         d->keepsynonyms = true;
     160              : 
     161            0 :         foreach(l, dictoptions)
     162              :         {
     163            0 :                 DefElem    *defel = (DefElem *) lfirst(l);
     164              : 
     165            0 :                 if (strcmp(defel->defname, "matchorig") == 0)
     166              :                 {
     167            0 :                         d->matchorig = defGetBoolean(defel);
     168            0 :                 }
     169            0 :                 else if (strcmp(defel->defname, "keeporig") == 0)
     170              :                 {
     171            0 :                         d->keeporig = defGetBoolean(defel);
     172            0 :                 }
     173            0 :                 else if (strcmp(defel->defname, "matchsynonyms") == 0)
     174              :                 {
     175            0 :                         d->matchsynonyms = defGetBoolean(defel);
     176            0 :                 }
     177            0 :                 else if (strcmp(defel->defname, "keepsynonyms") == 0)
     178              :                 {
     179            0 :                         d->keepsynonyms = defGetBoolean(defel);
     180            0 :                 }
     181            0 :                 else if (strcmp(defel->defname, "rules") == 0)
     182              :                 {
     183              :                         /* we can't read the rules before parsing all options! */
     184            0 :                         filename = defGetString(defel);
     185            0 :                 }
     186              :                 else
     187              :                 {
     188            0 :                         ereport(ERROR,
     189              :                                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     190              :                                          errmsg("unrecognized xsyn parameter: \"%s\"",
     191              :                                                         defel->defname)));
     192              :                 }
     193            0 :         }
     194              : 
     195            0 :         if (filename)
     196            0 :                 read_dictionary(d, filename);
     197              : 
     198            0 :         PG_RETURN_POINTER(d);
     199            0 : }
     200              : 
     201              : Datum
     202            0 : dxsyn_lexize(PG_FUNCTION_ARGS)
     203              : {
     204            0 :         DictSyn    *d = (DictSyn *) PG_GETARG_POINTER(0);
     205            0 :         char       *in = (char *) PG_GETARG_POINTER(1);
     206            0 :         int                     length = PG_GETARG_INT32(2);
     207            0 :         Syn                     word;
     208            0 :         Syn                *found;
     209            0 :         TSLexeme   *res = NULL;
     210              : 
     211            0 :         if (!length || d->len == 0)
     212            0 :                 PG_RETURN_POINTER(NULL);
     213              : 
     214              :         /* Create search pattern */
     215              :         {
     216            0 :                 char       *temp = pnstrdup(in, length);
     217              : 
     218            0 :                 word.key = str_tolower(temp, length, DEFAULT_COLLATION_OID);
     219            0 :                 pfree(temp);
     220            0 :                 word.value = NULL;
     221            0 :         }
     222              : 
     223              :         /* Look for matching syn */
     224            0 :         found = (Syn *) bsearch(&word, d->syn, d->len, sizeof(Syn), compare_syn);
     225            0 :         pfree(word.key);
     226              : 
     227            0 :         if (!found)
     228            0 :                 PG_RETURN_POINTER(NULL);
     229              : 
     230              :         /* Parse string of synonyms and return array of words */
     231              :         {
     232            0 :                 char       *value = found->value;
     233            0 :                 char       *syn;
     234            0 :                 char       *pos;
     235            0 :                 char       *end;
     236            0 :                 int                     nsyns = 0;
     237              : 
     238            0 :                 res = palloc_object(TSLexeme);
     239              : 
     240            0 :                 pos = value;
     241            0 :                 while ((syn = find_word(pos, &end)) != NULL)
     242              :                 {
     243            0 :                         res = repalloc(res, sizeof(TSLexeme) * (nsyns + 2));
     244              : 
     245              :                         /* The first word is output only if keeporig=true */
     246            0 :                         if (pos != value || d->keeporig)
     247              :                         {
     248            0 :                                 res[nsyns].lexeme = pnstrdup(syn, end - syn);
     249            0 :                                 res[nsyns].nvariant = 0;
     250            0 :                                 res[nsyns].flags = 0;
     251            0 :                                 nsyns++;
     252            0 :                         }
     253              : 
     254            0 :                         pos = end;
     255              : 
     256              :                         /* Stop if we are not to output the synonyms */
     257            0 :                         if (!d->keepsynonyms)
     258            0 :                                 break;
     259              :                 }
     260            0 :                 res[nsyns].lexeme = NULL;
     261            0 :         }
     262              : 
     263            0 :         PG_RETURN_POINTER(res);
     264            0 : }
        

Generated by: LCOV version 2.3.2-1