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 128 : if (dictloaded)
51 0 : ereport(ERROR,
52 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
53 : errmsg("multiple DictFile parameters")));
54 128 : NIImportDictionary(&(d->obj),
55 128 : get_tsearch_config_filename(defGetString(defel),
56 : "dict"));
57 128 : dictloaded = true;
58 : }
59 134 : else if (strcmp(defel->defname, "afffile") == 0)
60 : {
61 128 : if (affloaded)
62 0 : ereport(ERROR,
63 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
64 : errmsg("multiple AffFile parameters")));
65 128 : NIImportAffixes(&(d->obj),
66 128 : get_tsearch_config_filename(defGetString(defel),
67 : "affix"));
68 128 : affloaded = true;
69 : }
70 6 : else if (strcmp(defel->defname, "stopwords") == 0)
71 : {
72 0 : if (stoploaded)
73 0 : ereport(ERROR,
74 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
75 : errmsg("multiple StopWords parameters")));
76 0 : readstoplist(defGetString(defel), &(d->stoplist), str_tolower);
77 0 : stoploaded = true;
78 : }
79 : else
80 : {
81 6 : ereport(ERROR,
82 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
83 : errmsg("unrecognized Ispell parameter: \"%s\"",
84 : defel->defname)));
85 : }
86 : }
87 :
88 128 : if (affloaded && dictloaded)
89 : {
90 128 : NISortDictionary(&(d->obj));
91 110 : NISortAffixes(&(d->obj));
92 : }
93 0 : else if (!affloaded)
94 : {
95 0 : ereport(ERROR,
96 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
97 : errmsg("missing AffFile parameter")));
98 : }
99 : else
100 : {
101 0 : ereport(ERROR,
102 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
103 : errmsg("missing DictFile parameter")));
104 : }
105 :
106 110 : NIFinishBuild(&(d->obj));
107 :
108 110 : PG_RETURN_POINTER(d);
109 : }
110 :
111 : Datum
112 750 : dispell_lexize(PG_FUNCTION_ARGS)
113 : {
114 750 : DictISpell *d = (DictISpell *) PG_GETARG_POINTER(0);
115 750 : char *in = (char *) PG_GETARG_POINTER(1);
116 750 : int32 len = PG_GETARG_INT32(2);
117 : char *txt;
118 : TSLexeme *res;
119 : TSLexeme *ptr,
120 : *cptr;
121 :
122 750 : if (len <= 0)
123 0 : PG_RETURN_POINTER(NULL);
124 :
125 750 : txt = str_tolower(in, len, DEFAULT_COLLATION_OID);
126 750 : res = NINormalizeWord(&(d->obj), txt);
127 :
128 750 : if (res == NULL)
129 144 : PG_RETURN_POINTER(NULL);
130 :
131 606 : cptr = res;
132 1920 : for (ptr = cptr; ptr->lexeme; ptr++)
133 : {
134 1314 : if (searchstoplist(&(d->stoplist), ptr->lexeme))
135 : {
136 0 : pfree(ptr->lexeme);
137 0 : ptr->lexeme = NULL;
138 : }
139 : else
140 : {
141 1314 : if (cptr != ptr)
142 0 : memcpy(cptr, ptr, sizeof(TSLexeme));
143 1314 : cptr++;
144 : }
145 : }
146 606 : cptr->lexeme = NULL;
147 :
148 606 : PG_RETURN_POINTER(res);
149 : }
|