1 /*
2 * contrib/citext/citext.c
3 */
4 #include "postgres.h"
5
6 #include "access/hash.h"
7 #include "catalog/pg_collation.h"
8 #include "utils/builtins.h"
9 #include "utils/formatting.h"
10 #include "utils/varlena.h"
11
12 PG_MODULE_MAGIC;
13
14 /*
15 * ====================
16 * FORWARD DECLARATIONS
17 * ====================
18 */
19
20 static int32 citextcmp(text *left, text *right, Oid collid);
21 static int32 internal_citext_pattern_cmp(text *left, text *right, Oid collid);
22
23 /*
24 * =================
25 * UTILITY FUNCTIONS
26 * =================
27 */
28
29 /*
30 * citextcmp()
31 * Internal comparison function for citext strings.
32 * Returns int32 negative, zero, or positive.
33 */
34 static int32
citextcmp(text * left,text * right,Oid collid)35 citextcmp(text *left, text *right, Oid collid)
36 {
37 char *lcstr,
38 *rcstr;
39 int32 result;
40
41 /*
42 * We must do our str_tolower calls with DEFAULT_COLLATION_OID, not the
43 * input collation as you might expect. This is so that the behavior of
44 * citext's equality and hashing functions is not collation-dependent. We
45 * should change this once the core infrastructure is able to cope with
46 * collation-dependent equality and hashing functions.
47 */
48
49 lcstr = str_tolower(VARDATA_ANY(left), VARSIZE_ANY_EXHDR(left), DEFAULT_COLLATION_OID);
50 rcstr = str_tolower(VARDATA_ANY(right), VARSIZE_ANY_EXHDR(right), DEFAULT_COLLATION_OID);
51
52 result = varstr_cmp(lcstr, strlen(lcstr),
53 rcstr, strlen(rcstr),
54 collid);
55
56 pfree(lcstr);
57 pfree(rcstr);
58
59 return result;
60 }
61
62 /*
63 * citext_pattern_cmp()
64 * Internal character-by-character comparison function for citext strings.
65 * Returns int32 negative, zero, or positive.
66 */
67 static int32
internal_citext_pattern_cmp(text * left,text * right,Oid collid)68 internal_citext_pattern_cmp(text *left, text *right, Oid collid)
69 {
70 char *lcstr,
71 *rcstr;
72 int llen,
73 rlen;
74 int32 result;
75
76 lcstr = str_tolower(VARDATA_ANY(left), VARSIZE_ANY_EXHDR(left), DEFAULT_COLLATION_OID);
77 rcstr = str_tolower(VARDATA_ANY(right), VARSIZE_ANY_EXHDR(right), DEFAULT_COLLATION_OID);
78
79 llen = strlen(lcstr);
80 rlen = strlen(rcstr);
81
82 result = memcmp((void *) lcstr, (void *) rcstr, Min(llen, rlen));
83 if (result == 0)
84 {
85 if (llen < rlen)
86 result = -1;
87 else if (llen > rlen)
88 result = 1;
89 }
90
91 pfree(lcstr);
92 pfree(rcstr);
93
94 return result;
95 }
96
97 /*
98 * ==================
99 * INDEXING FUNCTIONS
100 * ==================
101 */
102
103 PG_FUNCTION_INFO_V1(citext_cmp);
104
105 Datum
citext_cmp(PG_FUNCTION_ARGS)106 citext_cmp(PG_FUNCTION_ARGS)
107 {
108 text *left = PG_GETARG_TEXT_PP(0);
109 text *right = PG_GETARG_TEXT_PP(1);
110 int32 result;
111
112 result = citextcmp(left, right, PG_GET_COLLATION());
113
114 PG_FREE_IF_COPY(left, 0);
115 PG_FREE_IF_COPY(right, 1);
116
117 PG_RETURN_INT32(result);
118 }
119
120 PG_FUNCTION_INFO_V1(citext_pattern_cmp);
121
122 Datum
citext_pattern_cmp(PG_FUNCTION_ARGS)123 citext_pattern_cmp(PG_FUNCTION_ARGS)
124 {
125 text *left = PG_GETARG_TEXT_PP(0);
126 text *right = PG_GETARG_TEXT_PP(1);
127 int32 result;
128
129 result = internal_citext_pattern_cmp(left, right, PG_GET_COLLATION());
130
131 PG_FREE_IF_COPY(left, 0);
132 PG_FREE_IF_COPY(right, 1);
133
134 PG_RETURN_INT32(result);
135 }
136
137 PG_FUNCTION_INFO_V1(citext_hash);
138
139 Datum
citext_hash(PG_FUNCTION_ARGS)140 citext_hash(PG_FUNCTION_ARGS)
141 {
142 text *txt = PG_GETARG_TEXT_PP(0);
143 char *str;
144 Datum result;
145
146 str = str_tolower(VARDATA_ANY(txt), VARSIZE_ANY_EXHDR(txt), DEFAULT_COLLATION_OID);
147 result = hash_any((unsigned char *) str, strlen(str));
148 pfree(str);
149
150 /* Avoid leaking memory for toasted inputs */
151 PG_FREE_IF_COPY(txt, 0);
152
153 PG_RETURN_DATUM(result);
154 }
155
156 /*
157 * ==================
158 * OPERATOR FUNCTIONS
159 * ==================
160 */
161
162 PG_FUNCTION_INFO_V1(citext_eq);
163
164 Datum
citext_eq(PG_FUNCTION_ARGS)165 citext_eq(PG_FUNCTION_ARGS)
166 {
167 text *left = PG_GETARG_TEXT_PP(0);
168 text *right = PG_GETARG_TEXT_PP(1);
169 char *lcstr,
170 *rcstr;
171 bool result;
172
173 /* We can't compare lengths in advance of downcasing ... */
174
175 lcstr = str_tolower(VARDATA_ANY(left), VARSIZE_ANY_EXHDR(left), DEFAULT_COLLATION_OID);
176 rcstr = str_tolower(VARDATA_ANY(right), VARSIZE_ANY_EXHDR(right), DEFAULT_COLLATION_OID);
177
178 /*
179 * Since we only care about equality or not-equality, we can avoid all the
180 * expense of strcoll() here, and just do bitwise comparison.
181 */
182 result = (strcmp(lcstr, rcstr) == 0);
183
184 pfree(lcstr);
185 pfree(rcstr);
186 PG_FREE_IF_COPY(left, 0);
187 PG_FREE_IF_COPY(right, 1);
188
189 PG_RETURN_BOOL(result);
190 }
191
192 PG_FUNCTION_INFO_V1(citext_ne);
193
194 Datum
citext_ne(PG_FUNCTION_ARGS)195 citext_ne(PG_FUNCTION_ARGS)
196 {
197 text *left = PG_GETARG_TEXT_PP(0);
198 text *right = PG_GETARG_TEXT_PP(1);
199 char *lcstr,
200 *rcstr;
201 bool result;
202
203 /* We can't compare lengths in advance of downcasing ... */
204
205 lcstr = str_tolower(VARDATA_ANY(left), VARSIZE_ANY_EXHDR(left), DEFAULT_COLLATION_OID);
206 rcstr = str_tolower(VARDATA_ANY(right), VARSIZE_ANY_EXHDR(right), DEFAULT_COLLATION_OID);
207
208 /*
209 * Since we only care about equality or not-equality, we can avoid all the
210 * expense of strcoll() here, and just do bitwise comparison.
211 */
212 result = (strcmp(lcstr, rcstr) != 0);
213
214 pfree(lcstr);
215 pfree(rcstr);
216 PG_FREE_IF_COPY(left, 0);
217 PG_FREE_IF_COPY(right, 1);
218
219 PG_RETURN_BOOL(result);
220 }
221
222 PG_FUNCTION_INFO_V1(citext_lt);
223
224 Datum
citext_lt(PG_FUNCTION_ARGS)225 citext_lt(PG_FUNCTION_ARGS)
226 {
227 text *left = PG_GETARG_TEXT_PP(0);
228 text *right = PG_GETARG_TEXT_PP(1);
229 bool result;
230
231 result = citextcmp(left, right, PG_GET_COLLATION()) < 0;
232
233 PG_FREE_IF_COPY(left, 0);
234 PG_FREE_IF_COPY(right, 1);
235
236 PG_RETURN_BOOL(result);
237 }
238
239 PG_FUNCTION_INFO_V1(citext_le);
240
241 Datum
citext_le(PG_FUNCTION_ARGS)242 citext_le(PG_FUNCTION_ARGS)
243 {
244 text *left = PG_GETARG_TEXT_PP(0);
245 text *right = PG_GETARG_TEXT_PP(1);
246 bool result;
247
248 result = citextcmp(left, right, PG_GET_COLLATION()) <= 0;
249
250 PG_FREE_IF_COPY(left, 0);
251 PG_FREE_IF_COPY(right, 1);
252
253 PG_RETURN_BOOL(result);
254 }
255
256 PG_FUNCTION_INFO_V1(citext_gt);
257
258 Datum
citext_gt(PG_FUNCTION_ARGS)259 citext_gt(PG_FUNCTION_ARGS)
260 {
261 text *left = PG_GETARG_TEXT_PP(0);
262 text *right = PG_GETARG_TEXT_PP(1);
263 bool result;
264
265 result = citextcmp(left, right, PG_GET_COLLATION()) > 0;
266
267 PG_FREE_IF_COPY(left, 0);
268 PG_FREE_IF_COPY(right, 1);
269
270 PG_RETURN_BOOL(result);
271 }
272
273 PG_FUNCTION_INFO_V1(citext_ge);
274
275 Datum
citext_ge(PG_FUNCTION_ARGS)276 citext_ge(PG_FUNCTION_ARGS)
277 {
278 text *left = PG_GETARG_TEXT_PP(0);
279 text *right = PG_GETARG_TEXT_PP(1);
280 bool result;
281
282 result = citextcmp(left, right, PG_GET_COLLATION()) >= 0;
283
284 PG_FREE_IF_COPY(left, 0);
285 PG_FREE_IF_COPY(right, 1);
286
287 PG_RETURN_BOOL(result);
288 }
289
290 PG_FUNCTION_INFO_V1(citext_pattern_lt);
291
292 Datum
citext_pattern_lt(PG_FUNCTION_ARGS)293 citext_pattern_lt(PG_FUNCTION_ARGS)
294 {
295 text *left = PG_GETARG_TEXT_PP(0);
296 text *right = PG_GETARG_TEXT_PP(1);
297 bool result;
298
299 result = internal_citext_pattern_cmp(left, right, PG_GET_COLLATION()) < 0;
300
301 PG_FREE_IF_COPY(left, 0);
302 PG_FREE_IF_COPY(right, 1);
303
304 PG_RETURN_BOOL(result);
305 }
306
307 PG_FUNCTION_INFO_V1(citext_pattern_le);
308
309 Datum
citext_pattern_le(PG_FUNCTION_ARGS)310 citext_pattern_le(PG_FUNCTION_ARGS)
311 {
312 text *left = PG_GETARG_TEXT_PP(0);
313 text *right = PG_GETARG_TEXT_PP(1);
314 bool result;
315
316 result = internal_citext_pattern_cmp(left, right, PG_GET_COLLATION()) <= 0;
317
318 PG_FREE_IF_COPY(left, 0);
319 PG_FREE_IF_COPY(right, 1);
320
321 PG_RETURN_BOOL(result);
322 }
323
324 PG_FUNCTION_INFO_V1(citext_pattern_gt);
325
326 Datum
citext_pattern_gt(PG_FUNCTION_ARGS)327 citext_pattern_gt(PG_FUNCTION_ARGS)
328 {
329 text *left = PG_GETARG_TEXT_PP(0);
330 text *right = PG_GETARG_TEXT_PP(1);
331 bool result;
332
333 result = internal_citext_pattern_cmp(left, right, PG_GET_COLLATION()) > 0;
334
335 PG_FREE_IF_COPY(left, 0);
336 PG_FREE_IF_COPY(right, 1);
337
338 PG_RETURN_BOOL(result);
339 }
340
341 PG_FUNCTION_INFO_V1(citext_pattern_ge);
342
343 Datum
citext_pattern_ge(PG_FUNCTION_ARGS)344 citext_pattern_ge(PG_FUNCTION_ARGS)
345 {
346 text *left = PG_GETARG_TEXT_PP(0);
347 text *right = PG_GETARG_TEXT_PP(1);
348 bool result;
349
350 result = internal_citext_pattern_cmp(left, right, PG_GET_COLLATION()) >= 0;
351
352 PG_FREE_IF_COPY(left, 0);
353 PG_FREE_IF_COPY(right, 1);
354
355 PG_RETURN_BOOL(result);
356 }
357
358 /*
359 * ===================
360 * AGGREGATE FUNCTIONS
361 * ===================
362 */
363
364 PG_FUNCTION_INFO_V1(citext_smaller);
365
366 Datum
citext_smaller(PG_FUNCTION_ARGS)367 citext_smaller(PG_FUNCTION_ARGS)
368 {
369 text *left = PG_GETARG_TEXT_PP(0);
370 text *right = PG_GETARG_TEXT_PP(1);
371 text *result;
372
373 result = citextcmp(left, right, PG_GET_COLLATION()) < 0 ? left : right;
374 PG_RETURN_TEXT_P(result);
375 }
376
377 PG_FUNCTION_INFO_V1(citext_larger);
378
379 Datum
citext_larger(PG_FUNCTION_ARGS)380 citext_larger(PG_FUNCTION_ARGS)
381 {
382 text *left = PG_GETARG_TEXT_PP(0);
383 text *right = PG_GETARG_TEXT_PP(1);
384 text *result;
385
386 result = citextcmp(left, right, PG_GET_COLLATION()) > 0 ? left : right;
387 PG_RETURN_TEXT_P(result);
388 }
389