Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * dict_ispell.c
4 : * Ispell dictionary interface
5 : *
6 : * Portions Copyright (c) 1996-2026, 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 67 : dispell_init(PG_FUNCTION_ARGS)
32 : {
33 67 : List *dictoptions = (List *) PG_GETARG_POINTER(0);
34 : DictISpell *d;
35 67 : bool affloaded = false,
36 67 : dictloaded = false,
37 67 : stoploaded = false;
38 : ListCell *l;
39 :
40 67 : d = palloc0_object(DictISpell);
41 :
42 67 : NIStartBuild(&(d->obj));
43 :
44 195 : foreach(l, dictoptions)
45 : {
46 131 : DefElem *defel = (DefElem *) lfirst(l);
47 :
48 131 : if (strcmp(defel->defname, "dictfile") == 0)
49 : {
50 : char *filename;
51 :
52 64 : if (dictloaded)
53 0 : ereport(ERROR,
54 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
55 : errmsg("multiple DictFile parameters")));
56 64 : filename = get_tsearch_config_filename(defGetString(defel),
57 : "dict");
58 64 : NIImportDictionary(&(d->obj), filename);
59 64 : pfree(filename);
60 64 : dictloaded = true;
61 : }
62 67 : else if (strcmp(defel->defname, "afffile") == 0)
63 : {
64 : char *filename;
65 :
66 64 : if (affloaded)
67 0 : ereport(ERROR,
68 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
69 : errmsg("multiple AffFile parameters")));
70 64 : filename = get_tsearch_config_filename(defGetString(defel),
71 : "affix");
72 64 : NIImportAffixes(&(d->obj), filename);
73 64 : pfree(filename);
74 64 : affloaded = true;
75 : }
76 3 : else if (strcmp(defel->defname, "stopwords") == 0)
77 : {
78 0 : if (stoploaded)
79 0 : ereport(ERROR,
80 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
81 : errmsg("multiple StopWords parameters")));
82 0 : readstoplist(defGetString(defel), &(d->stoplist), str_tolower);
83 0 : stoploaded = true;
84 : }
85 : else
86 : {
87 3 : ereport(ERROR,
88 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
89 : errmsg("unrecognized Ispell parameter: \"%s\"",
90 : defel->defname)));
91 : }
92 : }
93 :
94 64 : if (affloaded && dictloaded)
95 : {
96 64 : NISortDictionary(&(d->obj));
97 55 : NISortAffixes(&(d->obj));
98 : }
99 0 : else if (!affloaded)
100 : {
101 0 : ereport(ERROR,
102 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
103 : errmsg("missing AffFile parameter")));
104 : }
105 : else
106 : {
107 0 : ereport(ERROR,
108 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
109 : errmsg("missing DictFile parameter")));
110 : }
111 :
112 55 : NIFinishBuild(&(d->obj));
113 :
114 55 : PG_RETURN_POINTER(d);
115 : }
116 :
117 : Datum
118 375 : dispell_lexize(PG_FUNCTION_ARGS)
119 : {
120 375 : DictISpell *d = (DictISpell *) PG_GETARG_POINTER(0);
121 375 : char *in = (char *) PG_GETARG_POINTER(1);
122 375 : int32 len = PG_GETARG_INT32(2);
123 : char *txt;
124 : TSLexeme *res;
125 : TSLexeme *ptr,
126 : *cptr;
127 :
128 375 : if (len <= 0)
129 0 : PG_RETURN_POINTER(NULL);
130 :
131 375 : txt = str_tolower(in, len, DEFAULT_COLLATION_OID);
132 375 : res = NINormalizeWord(&(d->obj), txt);
133 :
134 375 : if (res == NULL)
135 72 : PG_RETURN_POINTER(NULL);
136 :
137 303 : cptr = res;
138 960 : for (ptr = cptr; ptr->lexeme; ptr++)
139 : {
140 657 : if (searchstoplist(&(d->stoplist), ptr->lexeme))
141 : {
142 0 : pfree(ptr->lexeme);
143 0 : ptr->lexeme = NULL;
144 : }
145 : else
146 : {
147 657 : if (cptr != ptr)
148 0 : memcpy(cptr, ptr, sizeof(TSLexeme));
149 657 : cptr++;
150 : }
151 : }
152 303 : cptr->lexeme = NULL;
153 :
154 303 : PG_RETURN_POINTER(res);
155 : }
|