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