Line data Source code
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 134 : dispell_init(PG_FUNCTION_ARGS)
32 : {
33 134 : List *dictoptions = (List *) PG_GETARG_POINTER(0);
34 : DictISpell *d;
35 134 : bool affloaded = false,
36 134 : dictloaded = false,
37 134 : stoploaded = false;
38 : ListCell *l;
39 :
40 134 : d = (DictISpell *) palloc0(sizeof(DictISpell));
41 :
42 134 : NIStartBuild(&(d->obj));
43 :
44 390 : foreach(l, dictoptions)
45 : {
46 262 : DefElem *defel = (DefElem *) lfirst(l);
47 :
48 262 : if (strcmp(defel->defname, "dictfile") == 0)
49 : {
50 : char *filename;
51 :
52 128 : if (dictloaded)
53 0 : ereport(ERROR,
54 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
55 : errmsg("multiple DictFile parameters")));
56 128 : filename = get_tsearch_config_filename(defGetString(defel),
57 : "dict");
58 128 : NIImportDictionary(&(d->obj), filename);
59 128 : pfree(filename);
60 128 : dictloaded = true;
61 : }
62 134 : else if (strcmp(defel->defname, "afffile") == 0)
63 : {
64 : char *filename;
65 :
66 128 : if (affloaded)
67 0 : ereport(ERROR,
68 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
69 : errmsg("multiple AffFile parameters")));
70 128 : filename = get_tsearch_config_filename(defGetString(defel),
71 : "affix");
72 128 : NIImportAffixes(&(d->obj), filename);
73 128 : pfree(filename);
74 128 : affloaded = true;
75 : }
76 6 : 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 6 : ereport(ERROR,
88 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
89 : errmsg("unrecognized Ispell parameter: \"%s\"",
90 : defel->defname)));
91 : }
92 : }
93 :
94 128 : if (affloaded && dictloaded)
95 : {
96 128 : NISortDictionary(&(d->obj));
97 110 : 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 110 : NIFinishBuild(&(d->obj));
113 :
114 110 : PG_RETURN_POINTER(d);
115 : }
116 :
117 : Datum
118 750 : dispell_lexize(PG_FUNCTION_ARGS)
119 : {
120 750 : DictISpell *d = (DictISpell *) PG_GETARG_POINTER(0);
121 750 : char *in = (char *) PG_GETARG_POINTER(1);
122 750 : int32 len = PG_GETARG_INT32(2);
123 : char *txt;
124 : TSLexeme *res;
125 : TSLexeme *ptr,
126 : *cptr;
127 :
128 750 : if (len <= 0)
129 0 : PG_RETURN_POINTER(NULL);
130 :
131 750 : txt = str_tolower(in, len, DEFAULT_COLLATION_OID);
132 750 : res = NINormalizeWord(&(d->obj), txt);
133 :
134 750 : if (res == NULL)
135 144 : PG_RETURN_POINTER(NULL);
136 :
137 606 : cptr = res;
138 1920 : for (ptr = cptr; ptr->lexeme; ptr++)
139 : {
140 1314 : if (searchstoplist(&(d->stoplist), ptr->lexeme))
141 : {
142 0 : pfree(ptr->lexeme);
143 0 : ptr->lexeme = NULL;
144 : }
145 : else
146 : {
147 1314 : if (cptr != ptr)
148 0 : memcpy(cptr, ptr, sizeof(TSLexeme));
149 1314 : cptr++;
150 : }
151 : }
152 606 : cptr->lexeme = NULL;
153 :
154 606 : PG_RETURN_POINTER(res);
155 : }
|