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
11 #ifdef PG_MODULE_MAGIC
12 PG_MODULE_MAGIC;
13 #endif
14
15 /*
16 * ====================
17 * FORWARD DECLARATIONS
18 * ====================
19 */
20
21 static int32 citextcmp(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 * ==================
64 * INDEXING FUNCTIONS
65 * ==================
66 */
67
68 PG_FUNCTION_INFO_V1(citext_cmp);
69
70 Datum
citext_cmp(PG_FUNCTION_ARGS)71 citext_cmp(PG_FUNCTION_ARGS)
72 {
73 text *left = PG_GETARG_TEXT_PP(0);
74 text *right = PG_GETARG_TEXT_PP(1);
75 int32 result;
76
77 result = citextcmp(left, right, PG_GET_COLLATION());
78
79 PG_FREE_IF_COPY(left, 0);
80 PG_FREE_IF_COPY(right, 1);
81
82 PG_RETURN_INT32(result);
83 }
84
85 PG_FUNCTION_INFO_V1(citext_hash);
86
87 Datum
citext_hash(PG_FUNCTION_ARGS)88 citext_hash(PG_FUNCTION_ARGS)
89 {
90 text *txt = PG_GETARG_TEXT_PP(0);
91 char *str;
92 Datum result;
93
94 str = str_tolower(VARDATA_ANY(txt), VARSIZE_ANY_EXHDR(txt), DEFAULT_COLLATION_OID);
95 result = hash_any((unsigned char *) str, strlen(str));
96 pfree(str);
97
98 /* Avoid leaking memory for toasted inputs */
99 PG_FREE_IF_COPY(txt, 0);
100
101 PG_RETURN_DATUM(result);
102 }
103
104 /*
105 * ==================
106 * OPERATOR FUNCTIONS
107 * ==================
108 */
109
110 PG_FUNCTION_INFO_V1(citext_eq);
111
112 Datum
citext_eq(PG_FUNCTION_ARGS)113 citext_eq(PG_FUNCTION_ARGS)
114 {
115 text *left = PG_GETARG_TEXT_PP(0);
116 text *right = PG_GETARG_TEXT_PP(1);
117 char *lcstr,
118 *rcstr;
119 bool result;
120
121 /* We can't compare lengths in advance of downcasing ... */
122
123 lcstr = str_tolower(VARDATA_ANY(left), VARSIZE_ANY_EXHDR(left), DEFAULT_COLLATION_OID);
124 rcstr = str_tolower(VARDATA_ANY(right), VARSIZE_ANY_EXHDR(right), DEFAULT_COLLATION_OID);
125
126 /*
127 * Since we only care about equality or not-equality, we can avoid all the
128 * expense of strcoll() here, and just do bitwise comparison.
129 */
130 result = (strcmp(lcstr, rcstr) == 0);
131
132 pfree(lcstr);
133 pfree(rcstr);
134 PG_FREE_IF_COPY(left, 0);
135 PG_FREE_IF_COPY(right, 1);
136
137 PG_RETURN_BOOL(result);
138 }
139
140 PG_FUNCTION_INFO_V1(citext_ne);
141
142 Datum
citext_ne(PG_FUNCTION_ARGS)143 citext_ne(PG_FUNCTION_ARGS)
144 {
145 text *left = PG_GETARG_TEXT_PP(0);
146 text *right = PG_GETARG_TEXT_PP(1);
147 char *lcstr,
148 *rcstr;
149 bool result;
150
151 /* We can't compare lengths in advance of downcasing ... */
152
153 lcstr = str_tolower(VARDATA_ANY(left), VARSIZE_ANY_EXHDR(left), DEFAULT_COLLATION_OID);
154 rcstr = str_tolower(VARDATA_ANY(right), VARSIZE_ANY_EXHDR(right), DEFAULT_COLLATION_OID);
155
156 /*
157 * Since we only care about equality or not-equality, we can avoid all the
158 * expense of strcoll() here, and just do bitwise comparison.
159 */
160 result = (strcmp(lcstr, rcstr) != 0);
161
162 pfree(lcstr);
163 pfree(rcstr);
164 PG_FREE_IF_COPY(left, 0);
165 PG_FREE_IF_COPY(right, 1);
166
167 PG_RETURN_BOOL(result);
168 }
169
170 PG_FUNCTION_INFO_V1(citext_lt);
171
172 Datum
citext_lt(PG_FUNCTION_ARGS)173 citext_lt(PG_FUNCTION_ARGS)
174 {
175 text *left = PG_GETARG_TEXT_PP(0);
176 text *right = PG_GETARG_TEXT_PP(1);
177 bool result;
178
179 result = citextcmp(left, right, PG_GET_COLLATION()) < 0;
180
181 PG_FREE_IF_COPY(left, 0);
182 PG_FREE_IF_COPY(right, 1);
183
184 PG_RETURN_BOOL(result);
185 }
186
187 PG_FUNCTION_INFO_V1(citext_le);
188
189 Datum
citext_le(PG_FUNCTION_ARGS)190 citext_le(PG_FUNCTION_ARGS)
191 {
192 text *left = PG_GETARG_TEXT_PP(0);
193 text *right = PG_GETARG_TEXT_PP(1);
194 bool result;
195
196 result = citextcmp(left, right, PG_GET_COLLATION()) <= 0;
197
198 PG_FREE_IF_COPY(left, 0);
199 PG_FREE_IF_COPY(right, 1);
200
201 PG_RETURN_BOOL(result);
202 }
203
204 PG_FUNCTION_INFO_V1(citext_gt);
205
206 Datum
citext_gt(PG_FUNCTION_ARGS)207 citext_gt(PG_FUNCTION_ARGS)
208 {
209 text *left = PG_GETARG_TEXT_PP(0);
210 text *right = PG_GETARG_TEXT_PP(1);
211 bool result;
212
213 result = citextcmp(left, right, PG_GET_COLLATION()) > 0;
214
215 PG_FREE_IF_COPY(left, 0);
216 PG_FREE_IF_COPY(right, 1);
217
218 PG_RETURN_BOOL(result);
219 }
220
221 PG_FUNCTION_INFO_V1(citext_ge);
222
223 Datum
citext_ge(PG_FUNCTION_ARGS)224 citext_ge(PG_FUNCTION_ARGS)
225 {
226 text *left = PG_GETARG_TEXT_PP(0);
227 text *right = PG_GETARG_TEXT_PP(1);
228 bool result;
229
230 result = citextcmp(left, right, PG_GET_COLLATION()) >= 0;
231
232 PG_FREE_IF_COPY(left, 0);
233 PG_FREE_IF_COPY(right, 1);
234
235 PG_RETURN_BOOL(result);
236 }
237
238 /*
239 * ===================
240 * AGGREGATE FUNCTIONS
241 * ===================
242 */
243
244 PG_FUNCTION_INFO_V1(citext_smaller);
245
246 Datum
citext_smaller(PG_FUNCTION_ARGS)247 citext_smaller(PG_FUNCTION_ARGS)
248 {
249 text *left = PG_GETARG_TEXT_PP(0);
250 text *right = PG_GETARG_TEXT_PP(1);
251 text *result;
252
253 result = citextcmp(left, right, PG_GET_COLLATION()) < 0 ? left : right;
254 PG_RETURN_TEXT_P(result);
255 }
256
257 PG_FUNCTION_INFO_V1(citext_larger);
258
259 Datum
citext_larger(PG_FUNCTION_ARGS)260 citext_larger(PG_FUNCTION_ARGS)
261 {
262 text *left = PG_GETARG_TEXT_PP(0);
263 text *right = PG_GETARG_TEXT_PP(1);
264 text *result;
265
266 result = citextcmp(left, right, PG_GET_COLLATION()) > 0 ? left : right;
267 PG_RETURN_TEXT_P(result);
268 }
269