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