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

Generated by: LCOV version 1.14