mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-08-23 03:00:41 +02:00
This is an ugly hack to get around the fact that significant parts of the core backend assume they don't need to worry about passing collation to equality and hashing functions. That's true for the core string datatypes, but citext should ideally have equality behavior that depends on the specified collation's LC_CTYPE. However, there's no chance of fixing the core before 9.2, so we'll have to live with this compromise arrangement for now. Per bug #6053 from Regina Obe. The code changes in this commit should be reverted in full once the core code is up to speed, but be careful about reverting the docs changes: I fixed a number of obsolete statements while at it.
280 lines
5.9 KiB
C
280 lines
5.9 KiB
C
/*
|
|
* contrib/citext/citext.c
|
|
*/
|
|
#include "postgres.h"
|
|
|
|
#include "access/hash.h"
|
|
#include "catalog/pg_collation.h"
|
|
#include "fmgr.h"
|
|
#include "utils/builtins.h"
|
|
#include "utils/formatting.h"
|
|
|
|
#ifdef PG_MODULE_MAGIC
|
|
PG_MODULE_MAGIC;
|
|
#endif
|
|
|
|
/*
|
|
* ====================
|
|
* FORWARD DECLARATIONS
|
|
* ====================
|
|
*/
|
|
|
|
static int32 citextcmp(text *left, text *right, Oid collid);
|
|
extern Datum citext_cmp(PG_FUNCTION_ARGS);
|
|
extern Datum citext_hash(PG_FUNCTION_ARGS);
|
|
extern Datum citext_eq(PG_FUNCTION_ARGS);
|
|
extern Datum citext_ne(PG_FUNCTION_ARGS);
|
|
extern Datum citext_gt(PG_FUNCTION_ARGS);
|
|
extern Datum citext_ge(PG_FUNCTION_ARGS);
|
|
extern Datum citext_lt(PG_FUNCTION_ARGS);
|
|
extern Datum citext_le(PG_FUNCTION_ARGS);
|
|
extern Datum citext_smaller(PG_FUNCTION_ARGS);
|
|
extern Datum citext_larger(PG_FUNCTION_ARGS);
|
|
|
|
/*
|
|
* =================
|
|
* UTILITY FUNCTIONS
|
|
* =================
|
|
*/
|
|
|
|
/*
|
|
* citextcmp()
|
|
* Internal comparison function for citext strings.
|
|
* Returns int32 negative, zero, or positive.
|
|
*/
|
|
static int32
|
|
citextcmp(text *left, text *right, Oid collid)
|
|
{
|
|
char *lcstr,
|
|
*rcstr;
|
|
int32 result;
|
|
|
|
/*
|
|
* We must do our str_tolower calls with DEFAULT_COLLATION_OID, not the
|
|
* input collation as you might expect. This is so that the behavior of
|
|
* citext's equality and hashing functions is not collation-dependent. We
|
|
* should change this once the core infrastructure is able to cope with
|
|
* collation-dependent equality and hashing functions.
|
|
*/
|
|
|
|
lcstr = str_tolower(VARDATA_ANY(left), VARSIZE_ANY_EXHDR(left), DEFAULT_COLLATION_OID);
|
|
rcstr = str_tolower(VARDATA_ANY(right), VARSIZE_ANY_EXHDR(right), DEFAULT_COLLATION_OID);
|
|
|
|
result = varstr_cmp(lcstr, strlen(lcstr),
|
|
rcstr, strlen(rcstr),
|
|
collid);
|
|
|
|
pfree(lcstr);
|
|
pfree(rcstr);
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* ==================
|
|
* INDEXING FUNCTIONS
|
|
* ==================
|
|
*/
|
|
|
|
PG_FUNCTION_INFO_V1(citext_cmp);
|
|
|
|
Datum
|
|
citext_cmp(PG_FUNCTION_ARGS)
|
|
{
|
|
text *left = PG_GETARG_TEXT_PP(0);
|
|
text *right = PG_GETARG_TEXT_PP(1);
|
|
int32 result;
|
|
|
|
result = citextcmp(left, right, PG_GET_COLLATION());
|
|
|
|
PG_FREE_IF_COPY(left, 0);
|
|
PG_FREE_IF_COPY(right, 1);
|
|
|
|
PG_RETURN_INT32(result);
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(citext_hash);
|
|
|
|
Datum
|
|
citext_hash(PG_FUNCTION_ARGS)
|
|
{
|
|
text *txt = PG_GETARG_TEXT_PP(0);
|
|
char *str;
|
|
Datum result;
|
|
|
|
str = str_tolower(VARDATA_ANY(txt), VARSIZE_ANY_EXHDR(txt), DEFAULT_COLLATION_OID);
|
|
result = hash_any((unsigned char *) str, strlen(str));
|
|
pfree(str);
|
|
|
|
/* Avoid leaking memory for toasted inputs */
|
|
PG_FREE_IF_COPY(txt, 0);
|
|
|
|
PG_RETURN_DATUM(result);
|
|
}
|
|
|
|
/*
|
|
* ==================
|
|
* OPERATOR FUNCTIONS
|
|
* ==================
|
|
*/
|
|
|
|
PG_FUNCTION_INFO_V1(citext_eq);
|
|
|
|
Datum
|
|
citext_eq(PG_FUNCTION_ARGS)
|
|
{
|
|
text *left = PG_GETARG_TEXT_PP(0);
|
|
text *right = PG_GETARG_TEXT_PP(1);
|
|
char *lcstr,
|
|
*rcstr;
|
|
bool result;
|
|
|
|
/* We can't compare lengths in advance of downcasing ... */
|
|
|
|
lcstr = str_tolower(VARDATA_ANY(left), VARSIZE_ANY_EXHDR(left), DEFAULT_COLLATION_OID);
|
|
rcstr = str_tolower(VARDATA_ANY(right), VARSIZE_ANY_EXHDR(right), DEFAULT_COLLATION_OID);
|
|
|
|
/*
|
|
* Since we only care about equality or not-equality, we can avoid all the
|
|
* expense of strcoll() here, and just do bitwise comparison.
|
|
*/
|
|
result = (strcmp(lcstr, rcstr) == 0);
|
|
|
|
pfree(lcstr);
|
|
pfree(rcstr);
|
|
PG_FREE_IF_COPY(left, 0);
|
|
PG_FREE_IF_COPY(right, 1);
|
|
|
|
PG_RETURN_BOOL(result);
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(citext_ne);
|
|
|
|
Datum
|
|
citext_ne(PG_FUNCTION_ARGS)
|
|
{
|
|
text *left = PG_GETARG_TEXT_PP(0);
|
|
text *right = PG_GETARG_TEXT_PP(1);
|
|
char *lcstr,
|
|
*rcstr;
|
|
bool result;
|
|
|
|
/* We can't compare lengths in advance of downcasing ... */
|
|
|
|
lcstr = str_tolower(VARDATA_ANY(left), VARSIZE_ANY_EXHDR(left), DEFAULT_COLLATION_OID);
|
|
rcstr = str_tolower(VARDATA_ANY(right), VARSIZE_ANY_EXHDR(right), DEFAULT_COLLATION_OID);
|
|
|
|
/*
|
|
* Since we only care about equality or not-equality, we can avoid all the
|
|
* expense of strcoll() here, and just do bitwise comparison.
|
|
*/
|
|
result = (strcmp(lcstr, rcstr) != 0);
|
|
|
|
pfree(lcstr);
|
|
pfree(rcstr);
|
|
PG_FREE_IF_COPY(left, 0);
|
|
PG_FREE_IF_COPY(right, 1);
|
|
|
|
PG_RETURN_BOOL(result);
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(citext_lt);
|
|
|
|
Datum
|
|
citext_lt(PG_FUNCTION_ARGS)
|
|
{
|
|
text *left = PG_GETARG_TEXT_PP(0);
|
|
text *right = PG_GETARG_TEXT_PP(1);
|
|
bool result;
|
|
|
|
result = citextcmp(left, right, PG_GET_COLLATION()) < 0;
|
|
|
|
PG_FREE_IF_COPY(left, 0);
|
|
PG_FREE_IF_COPY(right, 1);
|
|
|
|
PG_RETURN_BOOL(result);
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(citext_le);
|
|
|
|
Datum
|
|
citext_le(PG_FUNCTION_ARGS)
|
|
{
|
|
text *left = PG_GETARG_TEXT_PP(0);
|
|
text *right = PG_GETARG_TEXT_PP(1);
|
|
bool result;
|
|
|
|
result = citextcmp(left, right, PG_GET_COLLATION()) <= 0;
|
|
|
|
PG_FREE_IF_COPY(left, 0);
|
|
PG_FREE_IF_COPY(right, 1);
|
|
|
|
PG_RETURN_BOOL(result);
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(citext_gt);
|
|
|
|
Datum
|
|
citext_gt(PG_FUNCTION_ARGS)
|
|
{
|
|
text *left = PG_GETARG_TEXT_PP(0);
|
|
text *right = PG_GETARG_TEXT_PP(1);
|
|
bool result;
|
|
|
|
result = citextcmp(left, right, PG_GET_COLLATION()) > 0;
|
|
|
|
PG_FREE_IF_COPY(left, 0);
|
|
PG_FREE_IF_COPY(right, 1);
|
|
|
|
PG_RETURN_BOOL(result);
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(citext_ge);
|
|
|
|
Datum
|
|
citext_ge(PG_FUNCTION_ARGS)
|
|
{
|
|
text *left = PG_GETARG_TEXT_PP(0);
|
|
text *right = PG_GETARG_TEXT_PP(1);
|
|
bool result;
|
|
|
|
result = citextcmp(left, right, PG_GET_COLLATION()) >= 0;
|
|
|
|
PG_FREE_IF_COPY(left, 0);
|
|
PG_FREE_IF_COPY(right, 1);
|
|
|
|
PG_RETURN_BOOL(result);
|
|
}
|
|
|
|
/*
|
|
* ===================
|
|
* AGGREGATE FUNCTIONS
|
|
* ===================
|
|
*/
|
|
|
|
PG_FUNCTION_INFO_V1(citext_smaller);
|
|
|
|
Datum
|
|
citext_smaller(PG_FUNCTION_ARGS)
|
|
{
|
|
text *left = PG_GETARG_TEXT_PP(0);
|
|
text *right = PG_GETARG_TEXT_PP(1);
|
|
text *result;
|
|
|
|
result = citextcmp(left, right, PG_GET_COLLATION()) < 0 ? left : right;
|
|
PG_RETURN_TEXT_P(result);
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(citext_larger);
|
|
|
|
Datum
|
|
citext_larger(PG_FUNCTION_ARGS)
|
|
{
|
|
text *left = PG_GETARG_TEXT_PP(0);
|
|
text *right = PG_GETARG_TEXT_PP(1);
|
|
text *result;
|
|
|
|
result = citextcmp(left, right, PG_GET_COLLATION()) > 0 ? left : right;
|
|
PG_RETURN_TEXT_P(result);
|
|
}
|