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