Coverage Report

Created: 2025-08-12 06:43

/src/postgres/src/backend/tsearch/dict_ispell.c
Line
Count
Source (jump to first uncovered line)
1
/*-------------------------------------------------------------------------
2
 *
3
 * dict_ispell.c
4
 *    Ispell dictionary interface
5
 *
6
 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
7
 *
8
 *
9
 * IDENTIFICATION
10
 *    src/backend/tsearch/dict_ispell.c
11
 *
12
 *-------------------------------------------------------------------------
13
 */
14
#include "postgres.h"
15
16
#include "catalog/pg_collation_d.h"
17
#include "commands/defrem.h"
18
#include "tsearch/dicts/spell.h"
19
#include "tsearch/ts_public.h"
20
#include "utils/fmgrprotos.h"
21
#include "utils/formatting.h"
22
23
24
typedef struct
25
{
26
  StopList  stoplist;
27
  IspellDict  obj;
28
} DictISpell;
29
30
Datum
31
dispell_init(PG_FUNCTION_ARGS)
32
0
{
33
0
  List     *dictoptions = (List *) PG_GETARG_POINTER(0);
34
0
  DictISpell *d;
35
0
  bool    affloaded = false,
36
0
        dictloaded = false,
37
0
        stoploaded = false;
38
0
  ListCell   *l;
39
40
0
  d = (DictISpell *) palloc0(sizeof(DictISpell));
41
42
0
  NIStartBuild(&(d->obj));
43
44
0
  foreach(l, dictoptions)
45
0
  {
46
0
    DefElem    *defel = (DefElem *) lfirst(l);
47
48
0
    if (strcmp(defel->defname, "dictfile") == 0)
49
0
    {
50
0
      char     *filename;
51
52
0
      if (dictloaded)
53
0
        ereport(ERROR,
54
0
            (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
55
0
             errmsg("multiple DictFile parameters")));
56
0
      filename = get_tsearch_config_filename(defGetString(defel),
57
0
                           "dict");
58
0
      NIImportDictionary(&(d->obj), filename);
59
0
      pfree(filename);
60
0
      dictloaded = true;
61
0
    }
62
0
    else if (strcmp(defel->defname, "afffile") == 0)
63
0
    {
64
0
      char     *filename;
65
66
0
      if (affloaded)
67
0
        ereport(ERROR,
68
0
            (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
69
0
             errmsg("multiple AffFile parameters")));
70
0
      filename = get_tsearch_config_filename(defGetString(defel),
71
0
                           "affix");
72
0
      NIImportAffixes(&(d->obj), filename);
73
0
      pfree(filename);
74
0
      affloaded = true;
75
0
    }
76
0
    else if (strcmp(defel->defname, "stopwords") == 0)
77
0
    {
78
0
      if (stoploaded)
79
0
        ereport(ERROR,
80
0
            (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
81
0
             errmsg("multiple StopWords parameters")));
82
0
      readstoplist(defGetString(defel), &(d->stoplist), str_tolower);
83
0
      stoploaded = true;
84
0
    }
85
0
    else
86
0
    {
87
0
      ereport(ERROR,
88
0
          (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
89
0
           errmsg("unrecognized Ispell parameter: \"%s\"",
90
0
              defel->defname)));
91
0
    }
92
0
  }
93
94
0
  if (affloaded && dictloaded)
95
0
  {
96
0
    NISortDictionary(&(d->obj));
97
0
    NISortAffixes(&(d->obj));
98
0
  }
99
0
  else if (!affloaded)
100
0
  {
101
0
    ereport(ERROR,
102
0
        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
103
0
         errmsg("missing AffFile parameter")));
104
0
  }
105
0
  else
106
0
  {
107
0
    ereport(ERROR,
108
0
        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
109
0
         errmsg("missing DictFile parameter")));
110
0
  }
111
112
0
  NIFinishBuild(&(d->obj));
113
114
0
  PG_RETURN_POINTER(d);
115
0
}
116
117
Datum
118
dispell_lexize(PG_FUNCTION_ARGS)
119
0
{
120
0
  DictISpell *d = (DictISpell *) PG_GETARG_POINTER(0);
121
0
  char     *in = (char *) PG_GETARG_POINTER(1);
122
0
  int32   len = PG_GETARG_INT32(2);
123
0
  char     *txt;
124
0
  TSLexeme   *res;
125
0
  TSLexeme   *ptr,
126
0
         *cptr;
127
128
0
  if (len <= 0)
129
0
    PG_RETURN_POINTER(NULL);
130
131
0
  txt = str_tolower(in, len, DEFAULT_COLLATION_OID);
132
0
  res = NINormalizeWord(&(d->obj), txt);
133
134
0
  if (res == NULL)
135
0
    PG_RETURN_POINTER(NULL);
136
137
0
  cptr = res;
138
0
  for (ptr = cptr; ptr->lexeme; ptr++)
139
0
  {
140
0
    if (searchstoplist(&(d->stoplist), ptr->lexeme))
141
0
    {
142
0
      pfree(ptr->lexeme);
143
0
      ptr->lexeme = NULL;
144
0
    }
145
0
    else
146
0
    {
147
0
      if (cptr != ptr)
148
0
        memcpy(cptr, ptr, sizeof(TSLexeme));
149
0
      cptr++;
150
0
    }
151
0
  }
152
0
  cptr->lexeme = NULL;
153
154
0
  PG_RETURN_POINTER(res);
155
0
}