1 /*
2 * contrib/citext/citext.c
3 */
4 #include "postgres.h"
5
6 #include "catalog/pg_collation.h"
7 #include "common/hashfn.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 PG_FUNCTION_INFO_V1(citext_hash_extended);
157
158 Datum
citext_hash_extended(PG_FUNCTION_ARGS)159 citext_hash_extended(PG_FUNCTION_ARGS)
160 {
161 text *txt = PG_GETARG_TEXT_PP(0);
162 uint64 seed = PG_GETARG_INT64(1);
163 char *str;
164 Datum result;
165
166 str = str_tolower(VARDATA_ANY(txt), VARSIZE_ANY_EXHDR(txt), DEFAULT_COLLATION_OID);
167 result = hash_any_extended((unsigned char *) str, strlen(str), seed);
168 pfree(str);
169
170 /* Avoid leaking memory for toasted inputs */
171 PG_FREE_IF_COPY(txt, 0);
172
173 PG_RETURN_DATUM(result);
174 }
175
176 /*
177 * ==================
178 * OPERATOR FUNCTIONS
179 * ==================
180 */
181
182 PG_FUNCTION_INFO_V1(citext_eq);
183
184 Datum
citext_eq(PG_FUNCTION_ARGS)185 citext_eq(PG_FUNCTION_ARGS)
186 {
187 text *left = PG_GETARG_TEXT_PP(0);
188 text *right = PG_GETARG_TEXT_PP(1);
189 char *lcstr,
190 *rcstr;
191 bool result;
192
193 /* We can't compare lengths in advance of downcasing ... */
194
195 lcstr = str_tolower(VARDATA_ANY(left), VARSIZE_ANY_EXHDR(left), DEFAULT_COLLATION_OID);
196 rcstr = str_tolower(VARDATA_ANY(right), VARSIZE_ANY_EXHDR(right), DEFAULT_COLLATION_OID);
197
198 /*
199 * Since we only care about equality or not-equality, we can avoid all the
200 * expense of strcoll() here, and just do bitwise comparison.
201 */
202 result = (strcmp(lcstr, rcstr) == 0);
203
204 pfree(lcstr);
205 pfree(rcstr);
206 PG_FREE_IF_COPY(left, 0);
207 PG_FREE_IF_COPY(right, 1);
208
209 PG_RETURN_BOOL(result);
210 }
211
212 PG_FUNCTION_INFO_V1(citext_ne);
213
214 Datum
citext_ne(PG_FUNCTION_ARGS)215 citext_ne(PG_FUNCTION_ARGS)
216 {
217 text *left = PG_GETARG_TEXT_PP(0);
218 text *right = PG_GETARG_TEXT_PP(1);
219 char *lcstr,
220 *rcstr;
221 bool result;
222
223 /* We can't compare lengths in advance of downcasing ... */
224
225 lcstr = str_tolower(VARDATA_ANY(left), VARSIZE_ANY_EXHDR(left), DEFAULT_COLLATION_OID);
226 rcstr = str_tolower(VARDATA_ANY(right), VARSIZE_ANY_EXHDR(right), DEFAULT_COLLATION_OID);
227
228 /*
229 * Since we only care about equality or not-equality, we can avoid all the
230 * expense of strcoll() here, and just do bitwise comparison.
231 */
232 result = (strcmp(lcstr, rcstr) != 0);
233
234 pfree(lcstr);
235 pfree(rcstr);
236 PG_FREE_IF_COPY(left, 0);
237 PG_FREE_IF_COPY(right, 1);
238
239 PG_RETURN_BOOL(result);
240 }
241
242 PG_FUNCTION_INFO_V1(citext_lt);
243
244 Datum
citext_lt(PG_FUNCTION_ARGS)245 citext_lt(PG_FUNCTION_ARGS)
246 {
247 text *left = PG_GETARG_TEXT_PP(0);
248 text *right = PG_GETARG_TEXT_PP(1);
249 bool result;
250
251 result = citextcmp(left, right, PG_GET_COLLATION()) < 0;
252
253 PG_FREE_IF_COPY(left, 0);
254 PG_FREE_IF_COPY(right, 1);
255
256 PG_RETURN_BOOL(result);
257 }
258
259 PG_FUNCTION_INFO_V1(citext_le);
260
261 Datum
citext_le(PG_FUNCTION_ARGS)262 citext_le(PG_FUNCTION_ARGS)
263 {
264 text *left = PG_GETARG_TEXT_PP(0);
265 text *right = PG_GETARG_TEXT_PP(1);
266 bool result;
267
268 result = citextcmp(left, right, PG_GET_COLLATION()) <= 0;
269
270 PG_FREE_IF_COPY(left, 0);
271 PG_FREE_IF_COPY(right, 1);
272
273 PG_RETURN_BOOL(result);
274 }
275
276 PG_FUNCTION_INFO_V1(citext_gt);
277
278 Datum
citext_gt(PG_FUNCTION_ARGS)279 citext_gt(PG_FUNCTION_ARGS)
280 {
281 text *left = PG_GETARG_TEXT_PP(0);
282 text *right = PG_GETARG_TEXT_PP(1);
283 bool result;
284
285 result = citextcmp(left, right, PG_GET_COLLATION()) > 0;
286
287 PG_FREE_IF_COPY(left, 0);
288 PG_FREE_IF_COPY(right, 1);
289
290 PG_RETURN_BOOL(result);
291 }
292
293 PG_FUNCTION_INFO_V1(citext_ge);
294
295 Datum
citext_ge(PG_FUNCTION_ARGS)296 citext_ge(PG_FUNCTION_ARGS)
297 {
298 text *left = PG_GETARG_TEXT_PP(0);
299 text *right = PG_GETARG_TEXT_PP(1);
300 bool result;
301
302 result = citextcmp(left, right, PG_GET_COLLATION()) >= 0;
303
304 PG_FREE_IF_COPY(left, 0);
305 PG_FREE_IF_COPY(right, 1);
306
307 PG_RETURN_BOOL(result);
308 }
309
310 PG_FUNCTION_INFO_V1(citext_pattern_lt);
311
312 Datum
citext_pattern_lt(PG_FUNCTION_ARGS)313 citext_pattern_lt(PG_FUNCTION_ARGS)
314 {
315 text *left = PG_GETARG_TEXT_PP(0);
316 text *right = PG_GETARG_TEXT_PP(1);
317 bool result;
318
319 result = internal_citext_pattern_cmp(left, right, PG_GET_COLLATION()) < 0;
320
321 PG_FREE_IF_COPY(left, 0);
322 PG_FREE_IF_COPY(right, 1);
323
324 PG_RETURN_BOOL(result);
325 }
326
327 PG_FUNCTION_INFO_V1(citext_pattern_le);
328
329 Datum
citext_pattern_le(PG_FUNCTION_ARGS)330 citext_pattern_le(PG_FUNCTION_ARGS)
331 {
332 text *left = PG_GETARG_TEXT_PP(0);
333 text *right = PG_GETARG_TEXT_PP(1);
334 bool result;
335
336 result = internal_citext_pattern_cmp(left, right, PG_GET_COLLATION()) <= 0;
337
338 PG_FREE_IF_COPY(left, 0);
339 PG_FREE_IF_COPY(right, 1);
340
341 PG_RETURN_BOOL(result);
342 }
343
344 PG_FUNCTION_INFO_V1(citext_pattern_gt);
345
346 Datum
citext_pattern_gt(PG_FUNCTION_ARGS)347 citext_pattern_gt(PG_FUNCTION_ARGS)
348 {
349 text *left = PG_GETARG_TEXT_PP(0);
350 text *right = PG_GETARG_TEXT_PP(1);
351 bool result;
352
353 result = internal_citext_pattern_cmp(left, right, PG_GET_COLLATION()) > 0;
354
355 PG_FREE_IF_COPY(left, 0);
356 PG_FREE_IF_COPY(right, 1);
357
358 PG_RETURN_BOOL(result);
359 }
360
361 PG_FUNCTION_INFO_V1(citext_pattern_ge);
362
363 Datum
citext_pattern_ge(PG_FUNCTION_ARGS)364 citext_pattern_ge(PG_FUNCTION_ARGS)
365 {
366 text *left = PG_GETARG_TEXT_PP(0);
367 text *right = PG_GETARG_TEXT_PP(1);
368 bool result;
369
370 result = internal_citext_pattern_cmp(left, right, PG_GET_COLLATION()) >= 0;
371
372 PG_FREE_IF_COPY(left, 0);
373 PG_FREE_IF_COPY(right, 1);
374
375 PG_RETURN_BOOL(result);
376 }
377
378 /*
379 * ===================
380 * AGGREGATE FUNCTIONS
381 * ===================
382 */
383
384 PG_FUNCTION_INFO_V1(citext_smaller);
385
386 Datum
citext_smaller(PG_FUNCTION_ARGS)387 citext_smaller(PG_FUNCTION_ARGS)
388 {
389 text *left = PG_GETARG_TEXT_PP(0);
390 text *right = PG_GETARG_TEXT_PP(1);
391 text *result;
392
393 result = citextcmp(left, right, PG_GET_COLLATION()) < 0 ? left : right;
394 PG_RETURN_TEXT_P(result);
395 }
396
397 PG_FUNCTION_INFO_V1(citext_larger);
398
399 Datum
citext_larger(PG_FUNCTION_ARGS)400 citext_larger(PG_FUNCTION_ARGS)
401 {
402 text *left = PG_GETARG_TEXT_PP(0);
403 text *right = PG_GETARG_TEXT_PP(1);
404 text *result;
405
406 result = citextcmp(left, right, PG_GET_COLLATION()) > 0 ? left : right;
407 PG_RETURN_TEXT_P(result);
408 }
409