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 0 : 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 0 : citextcmp(text *left, text *right, Oid collid)
40 : {
41 0 : char *lcstr,
42 : *rcstr;
43 0 : 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 0 : lcstr = str_tolower(VARDATA_ANY(left), VARSIZE_ANY_EXHDR(left), DEFAULT_COLLATION_OID);
54 0 : rcstr = str_tolower(VARDATA_ANY(right), VARSIZE_ANY_EXHDR(right), DEFAULT_COLLATION_OID);
55 :
56 0 : result = varstr_cmp(lcstr, strlen(lcstr),
57 0 : rcstr, strlen(rcstr),
58 0 : collid);
59 :
60 0 : pfree(lcstr);
61 0 : pfree(rcstr);
62 :
63 0 : return result;
64 0 : }
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 0 : internal_citext_pattern_cmp(text *left, text *right, Oid collid)
73 : {
74 0 : char *lcstr,
75 : *rcstr;
76 0 : int llen,
77 : rlen;
78 0 : int32 result;
79 :
80 0 : lcstr = str_tolower(VARDATA_ANY(left), VARSIZE_ANY_EXHDR(left), DEFAULT_COLLATION_OID);
81 0 : rcstr = str_tolower(VARDATA_ANY(right), VARSIZE_ANY_EXHDR(right), DEFAULT_COLLATION_OID);
82 :
83 0 : llen = strlen(lcstr);
84 0 : rlen = strlen(rcstr);
85 :
86 0 : result = memcmp(lcstr, rcstr, Min(llen, rlen));
87 0 : if (result == 0)
88 : {
89 0 : if (llen < rlen)
90 0 : result = -1;
91 0 : else if (llen > rlen)
92 0 : result = 1;
93 0 : }
94 :
95 0 : pfree(lcstr);
96 0 : pfree(rcstr);
97 :
98 0 : return result;
99 0 : }
100 :
101 : /*
102 : * ==================
103 : * INDEXING FUNCTIONS
104 : * ==================
105 : */
106 :
107 0 : PG_FUNCTION_INFO_V1(citext_cmp);
108 :
109 : Datum
110 0 : citext_cmp(PG_FUNCTION_ARGS)
111 : {
112 0 : text *left = PG_GETARG_TEXT_PP(0);
113 0 : text *right = PG_GETARG_TEXT_PP(1);
114 0 : int32 result;
115 :
116 0 : result = citextcmp(left, right, PG_GET_COLLATION());
117 :
118 0 : PG_FREE_IF_COPY(left, 0);
119 0 : PG_FREE_IF_COPY(right, 1);
120 :
121 0 : PG_RETURN_INT32(result);
122 0 : }
123 :
124 0 : PG_FUNCTION_INFO_V1(citext_pattern_cmp);
125 :
126 : Datum
127 0 : citext_pattern_cmp(PG_FUNCTION_ARGS)
128 : {
129 0 : text *left = PG_GETARG_TEXT_PP(0);
130 0 : text *right = PG_GETARG_TEXT_PP(1);
131 0 : int32 result;
132 :
133 0 : result = internal_citext_pattern_cmp(left, right, PG_GET_COLLATION());
134 :
135 0 : PG_FREE_IF_COPY(left, 0);
136 0 : PG_FREE_IF_COPY(right, 1);
137 :
138 0 : PG_RETURN_INT32(result);
139 0 : }
140 :
141 0 : PG_FUNCTION_INFO_V1(citext_hash);
142 :
143 : Datum
144 0 : citext_hash(PG_FUNCTION_ARGS)
145 : {
146 0 : text *txt = PG_GETARG_TEXT_PP(0);
147 0 : char *str;
148 0 : Datum result;
149 :
150 0 : str = str_tolower(VARDATA_ANY(txt), VARSIZE_ANY_EXHDR(txt), DEFAULT_COLLATION_OID);
151 0 : result = hash_any((unsigned char *) str, strlen(str));
152 0 : pfree(str);
153 :
154 : /* Avoid leaking memory for toasted inputs */
155 0 : PG_FREE_IF_COPY(txt, 0);
156 :
157 0 : PG_RETURN_DATUM(result);
158 0 : }
159 :
160 0 : PG_FUNCTION_INFO_V1(citext_hash_extended);
161 :
162 : Datum
163 0 : citext_hash_extended(PG_FUNCTION_ARGS)
164 : {
165 0 : text *txt = PG_GETARG_TEXT_PP(0);
166 0 : uint64 seed = PG_GETARG_INT64(1);
167 0 : char *str;
168 0 : Datum result;
169 :
170 0 : str = str_tolower(VARDATA_ANY(txt), VARSIZE_ANY_EXHDR(txt), DEFAULT_COLLATION_OID);
171 0 : result = hash_any_extended((unsigned char *) str, strlen(str), seed);
172 0 : pfree(str);
173 :
174 : /* Avoid leaking memory for toasted inputs */
175 0 : PG_FREE_IF_COPY(txt, 0);
176 :
177 0 : PG_RETURN_DATUM(result);
178 0 : }
179 :
180 : /*
181 : * ==================
182 : * OPERATOR FUNCTIONS
183 : * ==================
184 : */
185 :
186 0 : PG_FUNCTION_INFO_V1(citext_eq);
187 :
188 : Datum
189 0 : citext_eq(PG_FUNCTION_ARGS)
190 : {
191 0 : text *left = PG_GETARG_TEXT_PP(0);
192 0 : text *right = PG_GETARG_TEXT_PP(1);
193 0 : char *lcstr,
194 : *rcstr;
195 0 : bool result;
196 :
197 : /* We can't compare lengths in advance of downcasing ... */
198 :
199 0 : lcstr = str_tolower(VARDATA_ANY(left), VARSIZE_ANY_EXHDR(left), DEFAULT_COLLATION_OID);
200 0 : 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 0 : result = (strcmp(lcstr, rcstr) == 0);
207 :
208 0 : pfree(lcstr);
209 0 : pfree(rcstr);
210 0 : PG_FREE_IF_COPY(left, 0);
211 0 : PG_FREE_IF_COPY(right, 1);
212 :
213 0 : PG_RETURN_BOOL(result);
214 0 : }
215 :
216 0 : PG_FUNCTION_INFO_V1(citext_ne);
217 :
218 : Datum
219 0 : citext_ne(PG_FUNCTION_ARGS)
220 : {
221 0 : text *left = PG_GETARG_TEXT_PP(0);
222 0 : text *right = PG_GETARG_TEXT_PP(1);
223 0 : char *lcstr,
224 : *rcstr;
225 0 : bool result;
226 :
227 : /* We can't compare lengths in advance of downcasing ... */
228 :
229 0 : lcstr = str_tolower(VARDATA_ANY(left), VARSIZE_ANY_EXHDR(left), DEFAULT_COLLATION_OID);
230 0 : 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 0 : result = (strcmp(lcstr, rcstr) != 0);
237 :
238 0 : pfree(lcstr);
239 0 : pfree(rcstr);
240 0 : PG_FREE_IF_COPY(left, 0);
241 0 : PG_FREE_IF_COPY(right, 1);
242 :
243 0 : PG_RETURN_BOOL(result);
244 0 : }
245 :
246 0 : PG_FUNCTION_INFO_V1(citext_lt);
247 :
248 : Datum
249 0 : citext_lt(PG_FUNCTION_ARGS)
250 : {
251 0 : text *left = PG_GETARG_TEXT_PP(0);
252 0 : text *right = PG_GETARG_TEXT_PP(1);
253 0 : bool result;
254 :
255 0 : result = citextcmp(left, right, PG_GET_COLLATION()) < 0;
256 :
257 0 : PG_FREE_IF_COPY(left, 0);
258 0 : PG_FREE_IF_COPY(right, 1);
259 :
260 0 : PG_RETURN_BOOL(result);
261 0 : }
262 :
263 0 : PG_FUNCTION_INFO_V1(citext_le);
264 :
265 : Datum
266 0 : citext_le(PG_FUNCTION_ARGS)
267 : {
268 0 : text *left = PG_GETARG_TEXT_PP(0);
269 0 : text *right = PG_GETARG_TEXT_PP(1);
270 0 : bool result;
271 :
272 0 : result = citextcmp(left, right, PG_GET_COLLATION()) <= 0;
273 :
274 0 : PG_FREE_IF_COPY(left, 0);
275 0 : PG_FREE_IF_COPY(right, 1);
276 :
277 0 : PG_RETURN_BOOL(result);
278 0 : }
279 :
280 0 : PG_FUNCTION_INFO_V1(citext_gt);
281 :
282 : Datum
283 0 : citext_gt(PG_FUNCTION_ARGS)
284 : {
285 0 : text *left = PG_GETARG_TEXT_PP(0);
286 0 : text *right = PG_GETARG_TEXT_PP(1);
287 0 : bool result;
288 :
289 0 : result = citextcmp(left, right, PG_GET_COLLATION()) > 0;
290 :
291 0 : PG_FREE_IF_COPY(left, 0);
292 0 : PG_FREE_IF_COPY(right, 1);
293 :
294 0 : PG_RETURN_BOOL(result);
295 0 : }
296 :
297 0 : PG_FUNCTION_INFO_V1(citext_ge);
298 :
299 : Datum
300 0 : citext_ge(PG_FUNCTION_ARGS)
301 : {
302 0 : text *left = PG_GETARG_TEXT_PP(0);
303 0 : text *right = PG_GETARG_TEXT_PP(1);
304 0 : bool result;
305 :
306 0 : result = citextcmp(left, right, PG_GET_COLLATION()) >= 0;
307 :
308 0 : PG_FREE_IF_COPY(left, 0);
309 0 : PG_FREE_IF_COPY(right, 1);
310 :
311 0 : PG_RETURN_BOOL(result);
312 0 : }
313 :
314 0 : PG_FUNCTION_INFO_V1(citext_pattern_lt);
315 :
316 : Datum
317 0 : citext_pattern_lt(PG_FUNCTION_ARGS)
318 : {
319 0 : text *left = PG_GETARG_TEXT_PP(0);
320 0 : text *right = PG_GETARG_TEXT_PP(1);
321 0 : bool result;
322 :
323 0 : result = internal_citext_pattern_cmp(left, right, PG_GET_COLLATION()) < 0;
324 :
325 0 : PG_FREE_IF_COPY(left, 0);
326 0 : PG_FREE_IF_COPY(right, 1);
327 :
328 0 : PG_RETURN_BOOL(result);
329 0 : }
330 :
331 0 : PG_FUNCTION_INFO_V1(citext_pattern_le);
332 :
333 : Datum
334 0 : citext_pattern_le(PG_FUNCTION_ARGS)
335 : {
336 0 : text *left = PG_GETARG_TEXT_PP(0);
337 0 : text *right = PG_GETARG_TEXT_PP(1);
338 0 : bool result;
339 :
340 0 : result = internal_citext_pattern_cmp(left, right, PG_GET_COLLATION()) <= 0;
341 :
342 0 : PG_FREE_IF_COPY(left, 0);
343 0 : PG_FREE_IF_COPY(right, 1);
344 :
345 0 : PG_RETURN_BOOL(result);
346 0 : }
347 :
348 0 : PG_FUNCTION_INFO_V1(citext_pattern_gt);
349 :
350 : Datum
351 0 : citext_pattern_gt(PG_FUNCTION_ARGS)
352 : {
353 0 : text *left = PG_GETARG_TEXT_PP(0);
354 0 : text *right = PG_GETARG_TEXT_PP(1);
355 0 : bool result;
356 :
357 0 : result = internal_citext_pattern_cmp(left, right, PG_GET_COLLATION()) > 0;
358 :
359 0 : PG_FREE_IF_COPY(left, 0);
360 0 : PG_FREE_IF_COPY(right, 1);
361 :
362 0 : PG_RETURN_BOOL(result);
363 0 : }
364 :
365 0 : PG_FUNCTION_INFO_V1(citext_pattern_ge);
366 :
367 : Datum
368 0 : citext_pattern_ge(PG_FUNCTION_ARGS)
369 : {
370 0 : text *left = PG_GETARG_TEXT_PP(0);
371 0 : text *right = PG_GETARG_TEXT_PP(1);
372 0 : bool result;
373 :
374 0 : result = internal_citext_pattern_cmp(left, right, PG_GET_COLLATION()) >= 0;
375 :
376 0 : PG_FREE_IF_COPY(left, 0);
377 0 : PG_FREE_IF_COPY(right, 1);
378 :
379 0 : PG_RETURN_BOOL(result);
380 0 : }
381 :
382 : /*
383 : * ===================
384 : * AGGREGATE FUNCTIONS
385 : * ===================
386 : */
387 :
388 0 : PG_FUNCTION_INFO_V1(citext_smaller);
389 :
390 : Datum
391 0 : citext_smaller(PG_FUNCTION_ARGS)
392 : {
393 0 : text *left = PG_GETARG_TEXT_PP(0);
394 0 : text *right = PG_GETARG_TEXT_PP(1);
395 0 : text *result;
396 :
397 0 : result = citextcmp(left, right, PG_GET_COLLATION()) < 0 ? left : right;
398 0 : PG_RETURN_TEXT_P(result);
399 0 : }
400 :
401 0 : PG_FUNCTION_INFO_V1(citext_larger);
402 :
403 : Datum
404 0 : citext_larger(PG_FUNCTION_ARGS)
405 : {
406 0 : text *left = PG_GETARG_TEXT_PP(0);
407 0 : text *right = PG_GETARG_TEXT_PP(1);
408 0 : text *result;
409 :
410 0 : result = citextcmp(left, right, PG_GET_COLLATION()) > 0 ? left : right;
411 0 : PG_RETURN_TEXT_P(result);
412 0 : }
|