mirror of https://github.com/postgres/postgres
parent
6fe8796341
commit
ab9907f5e5
@ -0,0 +1,17 @@ |
|||||||
|
# $PostgreSQL: pgsql/contrib/citext/Makefile,v 1.1 2008/07/29 18:31:20 tgl Exp $
|
||||||
|
|
||||||
|
MODULES = citext
|
||||||
|
DATA_built = citext.sql
|
||||||
|
DATA = uninstall_citext.sql
|
||||||
|
REGRESS = citext
|
||||||
|
|
||||||
|
ifdef USE_PGXS |
||||||
|
PG_CONFIG = pg_config
|
||||||
|
PGXS := $(shell $(PG_CONFIG) --pgxs)
|
||||||
|
include $(PGXS) |
||||||
|
else |
||||||
|
subdir = contrib/citext
|
||||||
|
top_builddir = ../..
|
||||||
|
include $(top_builddir)/src/Makefile.global |
||||||
|
include $(top_srcdir)/contrib/contrib-global.mk |
||||||
|
endif |
@ -0,0 +1,268 @@ |
|||||||
|
/*
|
||||||
|
* $PostgreSQL: pgsql/contrib/citext/citext.c,v 1.1 2008/07/29 18:31:20 tgl Exp $ |
||||||
|
*/ |
||||||
|
#include "postgres.h" |
||||||
|
|
||||||
|
#include "access/hash.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); |
||||||
|
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) |
||||||
|
{ |
||||||
|
char *lcstr, *rcstr; |
||||||
|
int32 result; |
||||||
|
|
||||||
|
lcstr = str_tolower(VARDATA_ANY(left), VARSIZE_ANY_EXHDR(left)); |
||||||
|
rcstr = str_tolower(VARDATA_ANY(right), VARSIZE_ANY_EXHDR(right)); |
||||||
|
|
||||||
|
result = varstr_cmp(lcstr, strlen(lcstr), |
||||||
|
rcstr, strlen(rcstr)); |
||||||
|
|
||||||
|
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_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)); |
||||||
|
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)); |
||||||
|
rcstr = str_tolower(VARDATA_ANY(right), VARSIZE_ANY_EXHDR(right)); |
||||||
|
|
||||||
|
/*
|
||||||
|
* 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)); |
||||||
|
rcstr = str_tolower(VARDATA_ANY(right), VARSIZE_ANY_EXHDR(right)); |
||||||
|
|
||||||
|
/*
|
||||||
|
* 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) < 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) <= 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) > 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) >= 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) < 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) > 0 ? left : right; |
||||||
|
PG_RETURN_TEXT_P(result); |
||||||
|
} |
@ -0,0 +1,450 @@ |
|||||||
|
/* $PostgreSQL: pgsql/contrib/citext/citext.sql.in,v 1.1 2008/07/29 18:31:20 tgl Exp $ */ |
||||||
|
|
||||||
|
-- Adjust this setting to control where the objects get created. |
||||||
|
SET search_path = public; |
||||||
|
|
||||||
|
-- |
||||||
|
-- PostgreSQL code for CITEXT. |
||||||
|
-- |
||||||
|
-- Most I/O functions, and a few others, piggyback on the "text" type |
||||||
|
-- functions via the implicit cast to text. |
||||||
|
-- |
||||||
|
|
||||||
|
-- |
||||||
|
-- Shell type to keep things a bit quieter. |
||||||
|
-- |
||||||
|
|
||||||
|
CREATE TYPE citext; |
||||||
|
|
||||||
|
-- |
||||||
|
-- Input and output functions. |
||||||
|
-- |
||||||
|
CREATE OR REPLACE FUNCTION citextin(cstring) |
||||||
|
RETURNS citext |
||||||
|
AS 'textin' |
||||||
|
LANGUAGE 'internal' IMMUTABLE STRICT; |
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION citextout(citext) |
||||||
|
RETURNS cstring |
||||||
|
AS 'textout' |
||||||
|
LANGUAGE 'internal' IMMUTABLE STRICT; |
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION citextrecv(internal) |
||||||
|
RETURNS citext |
||||||
|
AS 'textrecv' |
||||||
|
LANGUAGE 'internal' STABLE STRICT; |
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION citextsend(citext) |
||||||
|
RETURNS bytea |
||||||
|
AS 'textsend' |
||||||
|
LANGUAGE 'internal' STABLE STRICT; |
||||||
|
|
||||||
|
-- |
||||||
|
-- The type itself. |
||||||
|
-- |
||||||
|
|
||||||
|
CREATE TYPE citext ( |
||||||
|
INPUT = citextin, |
||||||
|
OUTPUT = citextout, |
||||||
|
RECEIVE = citextrecv, |
||||||
|
SEND = citextsend, |
||||||
|
INTERNALLENGTH = VARIABLE, |
||||||
|
STORAGE = extended |
||||||
|
); |
||||||
|
|
||||||
|
-- |
||||||
|
-- A single cast function, since bpchar needs to have its whitespace trimmed |
||||||
|
-- before it's cast to citext. |
||||||
|
-- |
||||||
|
CREATE OR REPLACE FUNCTION citext(bpchar) |
||||||
|
RETURNS citext |
||||||
|
AS 'rtrim1' |
||||||
|
LANGUAGE 'internal' IMMUTABLE STRICT; |
||||||
|
|
||||||
|
-- |
||||||
|
-- Implicit and assignment type casts. |
||||||
|
-- |
||||||
|
|
||||||
|
CREATE CAST (citext AS text) WITHOUT FUNCTION AS IMPLICIT; |
||||||
|
CREATE CAST (citext AS varchar) WITHOUT FUNCTION AS IMPLICIT; |
||||||
|
CREATE CAST (citext AS bpchar) WITHOUT FUNCTION AS ASSIGNMENT; |
||||||
|
CREATE CAST (text AS citext) WITHOUT FUNCTION AS ASSIGNMENT; |
||||||
|
CREATE CAST (varchar AS citext) WITHOUT FUNCTION AS ASSIGNMENT; |
||||||
|
CREATE CAST (bpchar AS citext) WITH FUNCTION citext(bpchar) AS ASSIGNMENT; |
||||||
|
|
||||||
|
-- |
||||||
|
-- Operator Functions. |
||||||
|
-- |
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION citext_eq( citext, citext ) |
||||||
|
RETURNS bool |
||||||
|
AS 'MODULE_PATHNAME' |
||||||
|
LANGUAGE C IMMUTABLE STRICT; |
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION citext_ne( citext, citext ) |
||||||
|
RETURNS bool |
||||||
|
AS 'MODULE_PATHNAME' |
||||||
|
LANGUAGE C IMMUTABLE STRICT; |
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION citext_lt( citext, citext ) |
||||||
|
RETURNS bool |
||||||
|
AS 'MODULE_PATHNAME' |
||||||
|
LANGUAGE C IMMUTABLE STRICT; |
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION citext_le( citext, citext ) |
||||||
|
RETURNS bool |
||||||
|
AS 'MODULE_PATHNAME' |
||||||
|
LANGUAGE C IMMUTABLE STRICT; |
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION citext_gt( citext, citext ) |
||||||
|
RETURNS bool |
||||||
|
AS 'MODULE_PATHNAME' |
||||||
|
LANGUAGE C IMMUTABLE STRICT; |
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION citext_ge( citext, citext ) |
||||||
|
RETURNS bool |
||||||
|
AS 'MODULE_PATHNAME' |
||||||
|
LANGUAGE C IMMUTABLE STRICT; |
||||||
|
|
||||||
|
-- We overload || just to preserve "citext-ness" of the result. |
||||||
|
CREATE OR REPLACE FUNCTION textcat(citext, citext) |
||||||
|
RETURNS citext |
||||||
|
AS 'textcat' |
||||||
|
LANGUAGE 'internal' IMMUTABLE STRICT; |
||||||
|
|
||||||
|
-- |
||||||
|
-- Operators. |
||||||
|
-- |
||||||
|
|
||||||
|
CREATE OPERATOR = ( |
||||||
|
LEFTARG = CITEXT, |
||||||
|
RIGHTARG = CITEXT, |
||||||
|
COMMUTATOR = =, |
||||||
|
NEGATOR = <>, |
||||||
|
PROCEDURE = citext_eq, |
||||||
|
RESTRICT = eqsel, |
||||||
|
JOIN = eqjoinsel, |
||||||
|
HASHES, |
||||||
|
MERGES |
||||||
|
); |
||||||
|
|
||||||
|
CREATE OPERATOR <> ( |
||||||
|
LEFTARG = CITEXT, |
||||||
|
RIGHTARG = CITEXT, |
||||||
|
NEGATOR = =, |
||||||
|
COMMUTATOR = <>, |
||||||
|
PROCEDURE = citext_ne, |
||||||
|
RESTRICT = neqsel, |
||||||
|
JOIN = neqjoinsel |
||||||
|
); |
||||||
|
|
||||||
|
CREATE OPERATOR < ( |
||||||
|
LEFTARG = CITEXT, |
||||||
|
RIGHTARG = CITEXT, |
||||||
|
NEGATOR = >=, |
||||||
|
COMMUTATOR = >, |
||||||
|
PROCEDURE = citext_lt, |
||||||
|
RESTRICT = scalarltsel, |
||||||
|
JOIN = scalarltjoinsel |
||||||
|
); |
||||||
|
|
||||||
|
CREATE OPERATOR <= ( |
||||||
|
LEFTARG = CITEXT, |
||||||
|
RIGHTARG = CITEXT, |
||||||
|
NEGATOR = >, |
||||||
|
COMMUTATOR = >=, |
||||||
|
PROCEDURE = citext_le, |
||||||
|
RESTRICT = scalarltsel, |
||||||
|
JOIN = scalarltjoinsel |
||||||
|
); |
||||||
|
|
||||||
|
CREATE OPERATOR >= ( |
||||||
|
LEFTARG = CITEXT, |
||||||
|
RIGHTARG = CITEXT, |
||||||
|
NEGATOR = <, |
||||||
|
COMMUTATOR = <=, |
||||||
|
PROCEDURE = citext_ge, |
||||||
|
RESTRICT = scalargtsel, |
||||||
|
JOIN = scalargtjoinsel |
||||||
|
); |
||||||
|
|
||||||
|
CREATE OPERATOR > ( |
||||||
|
LEFTARG = CITEXT, |
||||||
|
RIGHTARG = CITEXT, |
||||||
|
NEGATOR = <=, |
||||||
|
COMMUTATOR = <, |
||||||
|
PROCEDURE = citext_gt, |
||||||
|
RESTRICT = scalargtsel, |
||||||
|
JOIN = scalargtjoinsel |
||||||
|
); |
||||||
|
|
||||||
|
CREATE OPERATOR || ( |
||||||
|
LEFTARG = CITEXT, |
||||||
|
RIGHTARG = CITEXT, |
||||||
|
PROCEDURE = textcat |
||||||
|
); |
||||||
|
|
||||||
|
-- |
||||||
|
-- Support functions for indexing. |
||||||
|
-- |
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION citext_cmp(citext, citext) |
||||||
|
RETURNS int4 |
||||||
|
AS 'MODULE_PATHNAME' |
||||||
|
LANGUAGE C STRICT IMMUTABLE; |
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION citext_hash(citext) |
||||||
|
RETURNS int4 |
||||||
|
AS 'MODULE_PATHNAME' |
||||||
|
LANGUAGE C STRICT IMMUTABLE; |
||||||
|
|
||||||
|
-- |
||||||
|
-- The btree indexing operator class. |
||||||
|
-- |
||||||
|
|
||||||
|
CREATE OPERATOR CLASS citext_ops |
||||||
|
DEFAULT FOR TYPE CITEXT USING btree AS |
||||||
|
OPERATOR 1 < (citext, citext), |
||||||
|
OPERATOR 2 <= (citext, citext), |
||||||
|
OPERATOR 3 = (citext, citext), |
||||||
|
OPERATOR 4 >= (citext, citext), |
||||||
|
OPERATOR 5 > (citext, citext), |
||||||
|
FUNCTION 1 citext_cmp(citext, citext); |
||||||
|
|
||||||
|
-- |
||||||
|
-- The hash indexing operator class. |
||||||
|
-- |
||||||
|
|
||||||
|
CREATE OPERATOR CLASS citext_ops |
||||||
|
DEFAULT FOR TYPE citext USING hash AS |
||||||
|
OPERATOR 1 = (citext, citext), |
||||||
|
FUNCTION 1 citext_hash(citext); |
||||||
|
|
||||||
|
-- |
||||||
|
-- Aggregates. |
||||||
|
-- |
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION citext_smaller(citext, citext) |
||||||
|
RETURNS citext |
||||||
|
AS 'MODULE_PATHNAME' |
||||||
|
LANGUAGE 'C' IMMUTABLE STRICT; |
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION citext_larger(citext, citext) |
||||||
|
RETURNS citext |
||||||
|
AS 'MODULE_PATHNAME' |
||||||
|
LANGUAGE 'C' IMMUTABLE STRICT; |
||||||
|
|
||||||
|
CREATE AGGREGATE min(citext) ( |
||||||
|
SFUNC = citext_smaller, |
||||||
|
STYPE = citext, |
||||||
|
SORTOP = < |
||||||
|
); |
||||||
|
|
||||||
|
CREATE AGGREGATE max(citext) ( |
||||||
|
SFUNC = citext_larger, |
||||||
|
STYPE = citext, |
||||||
|
SORTOP = > |
||||||
|
); |
||||||
|
|
||||||
|
-- |
||||||
|
-- Miscellaneous functions |
||||||
|
-- These exist to preserve the "citext-ness" of the input. |
||||||
|
-- |
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION lower(citext) |
||||||
|
RETURNS citext AS 'lower' |
||||||
|
LANGUAGE 'internal' IMMUTABLE STRICT; |
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION upper(citext) |
||||||
|
RETURNS citext AS 'upper' |
||||||
|
LANGUAGE 'internal' IMMUTABLE STRICT; |
||||||
|
|
||||||
|
-- needed to avoid "function is not unique" errors |
||||||
|
-- XXX find a better way to deal with this... |
||||||
|
CREATE FUNCTION quote_literal(citext) |
||||||
|
RETURNS text AS 'quote_literal' |
||||||
|
LANGUAGE 'internal' IMMUTABLE STRICT; |
||||||
|
|
||||||
|
-- |
||||||
|
-- CITEXT pattern matching. |
||||||
|
-- |
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION texticlike(citext, citext) |
||||||
|
RETURNS bool AS 'texticlike' |
||||||
|
LANGUAGE 'internal' IMMUTABLE STRICT; |
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION texticnlike(citext, citext) |
||||||
|
RETURNS bool AS 'texticnlike' |
||||||
|
LANGUAGE 'internal' IMMUTABLE STRICT; |
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION texticregexeq(citext, citext) |
||||||
|
RETURNS bool AS 'texticregexeq' |
||||||
|
LANGUAGE 'internal' IMMUTABLE STRICT; |
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION texticregexne(citext, citext) |
||||||
|
RETURNS bool AS 'texticregexne' |
||||||
|
LANGUAGE 'internal' IMMUTABLE STRICT; |
||||||
|
|
||||||
|
CREATE OPERATOR ~ ( |
||||||
|
PROCEDURE = texticregexeq, |
||||||
|
LEFTARG = citext, |
||||||
|
RIGHTARG = citext, |
||||||
|
NEGATOR = !~, |
||||||
|
RESTRICT = icregexeqsel, |
||||||
|
JOIN = icregexeqjoinsel |
||||||
|
); |
||||||
|
|
||||||
|
CREATE OPERATOR ~* ( |
||||||
|
PROCEDURE = texticregexeq, |
||||||
|
LEFTARG = citext, |
||||||
|
RIGHTARG = citext, |
||||||
|
NEGATOR = !~*, |
||||||
|
RESTRICT = icregexeqsel, |
||||||
|
JOIN = icregexeqjoinsel |
||||||
|
); |
||||||
|
|
||||||
|
CREATE OPERATOR !~ ( |
||||||
|
PROCEDURE = texticregexne, |
||||||
|
LEFTARG = citext, |
||||||
|
RIGHTARG = citext, |
||||||
|
NEGATOR = ~, |
||||||
|
RESTRICT = icregexnesel, |
||||||
|
JOIN = icregexnejoinsel |
||||||
|
); |
||||||
|
|
||||||
|
CREATE OPERATOR !~* ( |
||||||
|
PROCEDURE = texticregexne, |
||||||
|
LEFTARG = citext, |
||||||
|
RIGHTARG = citext, |
||||||
|
NEGATOR = ~*, |
||||||
|
RESTRICT = icregexnesel, |
||||||
|
JOIN = icregexnejoinsel |
||||||
|
); |
||||||
|
|
||||||
|
CREATE OPERATOR ~~ ( |
||||||
|
PROCEDURE = texticlike, |
||||||
|
LEFTARG = citext, |
||||||
|
RIGHTARG = citext, |
||||||
|
NEGATOR = !~~, |
||||||
|
RESTRICT = iclikesel, |
||||||
|
JOIN = iclikejoinsel |
||||||
|
); |
||||||
|
|
||||||
|
CREATE OPERATOR ~~* ( |
||||||
|
PROCEDURE = texticlike, |
||||||
|
LEFTARG = citext, |
||||||
|
RIGHTARG = citext, |
||||||
|
NEGATOR = !~~*, |
||||||
|
RESTRICT = iclikesel, |
||||||
|
JOIN = iclikejoinsel |
||||||
|
); |
||||||
|
|
||||||
|
CREATE OPERATOR !~~ ( |
||||||
|
PROCEDURE = texticnlike, |
||||||
|
LEFTARG = citext, |
||||||
|
RIGHTARG = citext, |
||||||
|
NEGATOR = ~~, |
||||||
|
RESTRICT = icnlikesel, |
||||||
|
JOIN = icnlikejoinsel |
||||||
|
); |
||||||
|
|
||||||
|
CREATE OPERATOR !~~* ( |
||||||
|
PROCEDURE = texticnlike, |
||||||
|
LEFTARG = citext, |
||||||
|
RIGHTARG = citext, |
||||||
|
NEGATOR = ~~*, |
||||||
|
RESTRICT = icnlikesel, |
||||||
|
JOIN = icnlikejoinsel |
||||||
|
); |
||||||
|
|
||||||
|
-- |
||||||
|
-- Matching citext to text. |
||||||
|
-- |
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION texticlike(citext, text) |
||||||
|
RETURNS bool AS 'texticlike' |
||||||
|
LANGUAGE 'internal' IMMUTABLE STRICT; |
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION texticnlike(citext, text) |
||||||
|
RETURNS bool AS 'texticnlike' |
||||||
|
LANGUAGE 'internal' IMMUTABLE STRICT; |
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION texticregexeq(citext, text) |
||||||
|
RETURNS bool AS 'texticregexeq' |
||||||
|
LANGUAGE 'internal' IMMUTABLE STRICT; |
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION texticregexne(citext, text) |
||||||
|
RETURNS bool AS 'texticregexne' |
||||||
|
LANGUAGE 'internal' IMMUTABLE STRICT; |
||||||
|
|
||||||
|
CREATE OPERATOR ~ ( |
||||||
|
PROCEDURE = texticregexeq, |
||||||
|
LEFTARG = citext, |
||||||
|
RIGHTARG = text, |
||||||
|
NEGATOR = !~, |
||||||
|
RESTRICT = icregexeqsel, |
||||||
|
JOIN = icregexeqjoinsel |
||||||
|
); |
||||||
|
|
||||||
|
CREATE OPERATOR ~* ( |
||||||
|
PROCEDURE = texticregexeq, |
||||||
|
LEFTARG = citext, |
||||||
|
RIGHTARG = text, |
||||||
|
NEGATOR = !~*, |
||||||
|
RESTRICT = icregexeqsel, |
||||||
|
JOIN = icregexeqjoinsel |
||||||
|
); |
||||||
|
|
||||||
|
CREATE OPERATOR !~ ( |
||||||
|
PROCEDURE = texticregexne, |
||||||
|
LEFTARG = citext, |
||||||
|
RIGHTARG = text, |
||||||
|
NEGATOR = ~, |
||||||
|
RESTRICT = icregexnesel, |
||||||
|
JOIN = icregexnejoinsel |
||||||
|
); |
||||||
|
|
||||||
|
CREATE OPERATOR !~* ( |
||||||
|
PROCEDURE = texticregexne, |
||||||
|
LEFTARG = citext, |
||||||
|
RIGHTARG = text, |
||||||
|
NEGATOR = ~*, |
||||||
|
RESTRICT = icregexnesel, |
||||||
|
JOIN = icregexnejoinsel |
||||||
|
); |
||||||
|
|
||||||
|
CREATE OPERATOR ~~ ( |
||||||
|
PROCEDURE = texticlike, |
||||||
|
LEFTARG = citext, |
||||||
|
RIGHTARG = text, |
||||||
|
NEGATOR = !~~, |
||||||
|
RESTRICT = iclikesel, |
||||||
|
JOIN = iclikejoinsel |
||||||
|
); |
||||||
|
|
||||||
|
CREATE OPERATOR ~~* ( |
||||||
|
PROCEDURE = texticlike, |
||||||
|
LEFTARG = citext, |
||||||
|
RIGHTARG = text, |
||||||
|
NEGATOR = !~~*, |
||||||
|
RESTRICT = iclikesel, |
||||||
|
JOIN = iclikejoinsel |
||||||
|
); |
||||||
|
|
||||||
|
CREATE OPERATOR !~~ ( |
||||||
|
PROCEDURE = texticnlike, |
||||||
|
LEFTARG = citext, |
||||||
|
RIGHTARG = text, |
||||||
|
NEGATOR = ~~, |
||||||
|
RESTRICT = icnlikesel, |
||||||
|
JOIN = icnlikejoinsel |
||||||
|
); |
||||||
|
|
||||||
|
CREATE OPERATOR !~~* ( |
||||||
|
PROCEDURE = texticnlike, |
||||||
|
LEFTARG = citext, |
||||||
|
RIGHTARG = text, |
||||||
|
NEGATOR = ~~*, |
||||||
|
RESTRICT = icnlikesel, |
||||||
|
JOIN = icnlikejoinsel |
||||||
|
); |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,69 @@ |
|||||||
|
/* $PostgreSQL: pgsql/contrib/citext/uninstall_citext.sql,v 1.1 2008/07/29 18:31:20 tgl Exp $ */ |
||||||
|
|
||||||
|
-- Adjust this setting to control where the objects get dropped. |
||||||
|
SET search_path = public; |
||||||
|
|
||||||
|
DROP OPERATOR CLASS citext_ops USING btree CASCADE; |
||||||
|
DROP OPERATOR CLASS citext_ops USING hash CASCADE; |
||||||
|
|
||||||
|
DROP AGGREGATE min(citext); |
||||||
|
DROP AGGREGATE max(citext); |
||||||
|
|
||||||
|
DROP OPERATOR = (citext, citext); |
||||||
|
DROP OPERATOR <> (citext, citext); |
||||||
|
DROP OPERATOR < (citext, citext); |
||||||
|
DROP OPERATOR <= (citext, citext); |
||||||
|
DROP OPERATOR >= (citext, citext); |
||||||
|
DROP OPERATOR > (citext, citext); |
||||||
|
DROP OPERATOR || (citext, citext); |
||||||
|
|
||||||
|
DROP OPERATOR ~ (citext, citext); |
||||||
|
DROP OPERATOR ~* (citext, citext); |
||||||
|
DROP OPERATOR !~ (citext, citext); |
||||||
|
DROP OPERATOR !~* (citext, citext); |
||||||
|
DROP OPERATOR ~~ (citext, citext); |
||||||
|
DROP OPERATOR ~~* (citext, citext); |
||||||
|
DROP OPERATOR !~~ (citext, citext); |
||||||
|
DROP OPERATOR !~~* (citext, citext); |
||||||
|
|
||||||
|
DROP OPERATOR ~ (citext, text); |
||||||
|
DROP OPERATOR ~* (citext, text); |
||||||
|
DROP OPERATOR !~ (citext, text); |
||||||
|
DROP OPERATOR !~* (citext, text); |
||||||
|
DROP OPERATOR ~~ (citext, text); |
||||||
|
DROP OPERATOR ~~* (citext, text); |
||||||
|
DROP OPERATOR !~~ (citext, text); |
||||||
|
DROP OPERATOR !~~* (citext, text); |
||||||
|
|
||||||
|
DROP CAST (citext AS text); |
||||||
|
DROP CAST (citext AS varchar); |
||||||
|
DROP CAST (citext AS bpchar); |
||||||
|
DROP CAST (text AS citext); |
||||||
|
DROP CAST (varchar AS citext); |
||||||
|
DROP CAST (bpchar AS citext); |
||||||
|
|
||||||
|
DROP FUNCTION citext(bpchar); |
||||||
|
DROP FUNCTION citext_eq(citext, citext); |
||||||
|
DROP FUNCTION citext_ne(citext, citext); |
||||||
|
DROP FUNCTION citext_lt(citext, citext); |
||||||
|
DROP FUNCTION citext_le(citext, citext); |
||||||
|
DROP FUNCTION citext_gt(citext, citext); |
||||||
|
DROP FUNCTION citext_ge(citext, citext); |
||||||
|
DROP FUNCTION textcat(citext, citext); |
||||||
|
DROP FUNCTION citext_cmp(citext, citext); |
||||||
|
DROP FUNCTION citext_hash(citext); |
||||||
|
DROP FUNCTION citext_smaller(citext, citext); |
||||||
|
DROP FUNCTION citext_larger(citext, citext); |
||||||
|
DROP FUNCTION lower(citext); |
||||||
|
DROP FUNCTION upper(citext); |
||||||
|
DROP FUNCTION quote_literal(citext); |
||||||
|
DROP FUNCTION texticlike(citext, citext); |
||||||
|
DROP FUNCTION texticnlike(citext, citext); |
||||||
|
DROP FUNCTION texticregexeq(citext, citext); |
||||||
|
DROP FUNCTION texticregexne(citext, citext); |
||||||
|
DROP FUNCTION texticlike(citext, text); |
||||||
|
DROP FUNCTION texticnlike(citext, text); |
||||||
|
DROP FUNCTION texticregexeq(citext, text); |
||||||
|
DROP FUNCTION texticregexne(citext, text); |
||||||
|
|
||||||
|
DROP TYPE citext CASCADE; |
@ -0,0 +1,222 @@ |
|||||||
|
<!-- $PostgreSQL: pgsql/doc/src/sgml/citext.sgml,v 1.1 2008/07/29 18:31:20 tgl Exp $ --> |
||||||
|
|
||||||
|
<sect1 id="citext"> |
||||||
|
<title>citext</title> |
||||||
|
|
||||||
|
<indexterm zone="citext"> |
||||||
|
<primary>citext</primary> |
||||||
|
</indexterm> |
||||||
|
|
||||||
|
<para> |
||||||
|
The <filename>citext</> module provides a case-insensitive |
||||||
|
character string type, <type>citext</>. Essentially, it internally calls |
||||||
|
<function>lower</> when comparing values. Otherwise, it behaves almost |
||||||
|
exactly like <type>text</>. |
||||||
|
</para> |
||||||
|
|
||||||
|
<sect2> |
||||||
|
<title>Rationale</title> |
||||||
|
|
||||||
|
<para> |
||||||
|
The standard approach to doing case-insensitive matches |
||||||
|
in <productname>PostgreSQL</> has been to use the <function>lower</> |
||||||
|
function when comparing values, for example |
||||||
|
|
||||||
|
<programlisting> |
||||||
|
SELECT * FROM tab WHERE lower(col) = LOWER(?); |
||||||
|
</programlisting> |
||||||
|
</para> |
||||||
|
|
||||||
|
<para> |
||||||
|
This works reasonably well, but has a number of drawbacks: |
||||||
|
</para> |
||||||
|
|
||||||
|
<itemizedlist> |
||||||
|
<listitem> |
||||||
|
<para> |
||||||
|
It makes your SQL statements verbose, and you always have to remember to |
||||||
|
use <function>lower</> on both the column and the query value. |
||||||
|
</para> |
||||||
|
</listitem> |
||||||
|
<listitem> |
||||||
|
<para> |
||||||
|
It won't use an index, unless you create a functional index using |
||||||
|
<function>lower</>. |
||||||
|
</para> |
||||||
|
</listitem> |
||||||
|
<listitem> |
||||||
|
<para> |
||||||
|
If you declare a column as <literal>UNIQUE</> or <literal>PRIMARY |
||||||
|
KEY</>, the implicitly generated index is case-sensitive. So it's |
||||||
|
useless for case-insensitive searches, and it won't enforce |
||||||
|
uniqueness case-insensitively. |
||||||
|
</para> |
||||||
|
</listitem> |
||||||
|
</itemizedlist> |
||||||
|
|
||||||
|
<para> |
||||||
|
The <type>citext</> data type allows you to eliminate calls |
||||||
|
to <function>lower</> in SQL queries, and allows a primary key to |
||||||
|
be case-insensitive. <type>citext</> is locale-aware, just |
||||||
|
like <type>text</>, which means that the comparison of uppercase and |
||||||
|
lowercase characters is dependent on the rules of |
||||||
|
the <literal>LC_CTYPE</> locale setting. Again, this behavior is |
||||||
|
identical to the use of <function>lower</> in queries. But because it's |
||||||
|
done transparently by the datatype, you don't have to remember to do |
||||||
|
anything special in your queries. |
||||||
|
</para> |
||||||
|
|
||||||
|
</sect2> |
||||||
|
|
||||||
|
<sect2> |
||||||
|
<title>How to Use It</title> |
||||||
|
|
||||||
|
<para> |
||||||
|
Here's a simple example of usage: |
||||||
|
|
||||||
|
<programlisting> |
||||||
|
CREATE TABLE users ( |
||||||
|
nick CITEXT PRIMARY KEY, |
||||||
|
pass TEXT NOT NULL |
||||||
|
); |
||||||
|
|
||||||
|
INSERT INTO users VALUES ( 'larry', md5(random()::text) ); |
||||||
|
INSERT INTO users VALUES ( 'Tom', md5(random()::text) ); |
||||||
|
INSERT INTO users VALUES ( 'Damian', md5(random()::text) ); |
||||||
|
INSERT INTO users VALUES ( 'NEAL', md5(random()::text) ); |
||||||
|
INSERT INTO users VALUES ( 'Bjørn', md5(random()::text) ); |
||||||
|
|
||||||
|
SELECT * FROM users WHERE nick = 'Larry'; |
||||||
|
</programlisting> |
||||||
|
|
||||||
|
The <command>SELECT</> statement will return one tuple, even though |
||||||
|
the <structfield>nick</> column was set to <quote>larry</> and the query |
||||||
|
was for <quote>Larry</>. |
||||||
|
</para> |
||||||
|
</sect2> |
||||||
|
|
||||||
|
<sect2> |
||||||
|
<title>Limitations</title> |
||||||
|
|
||||||
|
<itemizedlist> |
||||||
|
<listitem> |
||||||
|
<para> |
||||||
|
<type>citext</>'s behavior depends on |
||||||
|
the <literal>LC_CTYPE</> setting of your database. How it compares |
||||||
|
values is therefore determined when |
||||||
|
<application>initdb</> is run to create the cluster. It is not truly |
||||||
|
case-insensitive in the terms defined by the Unicode standard. |
||||||
|
Effectively, what this means is that, as long as you're happy with your |
||||||
|
collation, you should be happy with <type>citext</>'s comparisons. But |
||||||
|
if you have data in different languages stored in your database, users |
||||||
|
of one language may find their query results are not as expected if the |
||||||
|
collation is for another language. |
||||||
|
</para> |
||||||
|
</listitem> |
||||||
|
|
||||||
|
<listitem> |
||||||
|
<para> |
||||||
|
The pattern-matching comparison operators, such as <literal>LIKE</>, |
||||||
|
<literal>~</>, <literal>~~</>, <literal>!~</>, <literal>!~~</>, etc., |
||||||
|
have been overloaded to make case-insensitive comparisons when their |
||||||
|
left-hand argument is of type <type>citext</>. However, related |
||||||
|
functions have not been changed, including: |
||||||
|
</para> |
||||||
|
|
||||||
|
<itemizedlist> |
||||||
|
<listitem> |
||||||
|
<para> |
||||||
|
<function>regexp_replace()</> |
||||||
|
</para> |
||||||
|
</listitem> |
||||||
|
<listitem> |
||||||
|
<para> |
||||||
|
<function>regexp_split_to_array()</> |
||||||
|
</para> |
||||||
|
</listitem> |
||||||
|
<listitem> |
||||||
|
<para> |
||||||
|
<function>regexp_split_to_table()</> |
||||||
|
</para> |
||||||
|
</listitem> |
||||||
|
<listitem> |
||||||
|
<para> |
||||||
|
<function>replace()</> |
||||||
|
</para> |
||||||
|
</listitem> |
||||||
|
<listitem> |
||||||
|
<para> |
||||||
|
<function>split_part()</> |
||||||
|
</para> |
||||||
|
</listitem> |
||||||
|
<listitem> |
||||||
|
<para> |
||||||
|
<function>strpos()</> |
||||||
|
</para> |
||||||
|
</listitem> |
||||||
|
<listitem> |
||||||
|
<para> |
||||||
|
<function>translate()</> |
||||||
|
</para> |
||||||
|
</listitem> |
||||||
|
</itemizedlist> |
||||||
|
|
||||||
|
<para> |
||||||
|
Of course, for the regular expression functions, you can specify |
||||||
|
case-insensitive comparisons in their <parameter>flags</> arguments, but |
||||||
|
the same cannot be done for the the non-regexp functions. |
||||||
|
</para> |
||||||
|
</listitem> |
||||||
|
|
||||||
|
<listitem> |
||||||
|
<para> |
||||||
|
<type>citext</> is not as efficient as <type>text</> because the |
||||||
|
operator functions and the btree comparison functions must make copies |
||||||
|
of the data and convert it to lower case for comparisons. It is, |
||||||
|
however, slightly more efficient than using <function>lower</> to get |
||||||
|
case-insensitive matching. |
||||||
|
</para> |
||||||
|
</listitem> |
||||||
|
|
||||||
|
<listitem> |
||||||
|
<para> |
||||||
|
<productname>PostgreSQL</> supports casting between <type>text</> |
||||||
|
and any other type (even non-string types) by using the other type's |
||||||
|
I/O functions. This doesn't work with <type>citext</>. However, |
||||||
|
you can cast via I/O functions in two steps, for example |
||||||
|
<literal><replaceable>somevalue</>::text::citext</literal> or |
||||||
|
<literal><replaceable>citextvalue</>::text::<replaceable>sometype</></literal>. |
||||||
|
</para> |
||||||
|
</listitem> |
||||||
|
|
||||||
|
<listitem> |
||||||
|
<para> |
||||||
|
<type>citext</> doesn't help much if you need data to compare |
||||||
|
case-sensitively in some contexts and case-insensitively in other |
||||||
|
contexts. The standard answer is to use the <type>text</> type and |
||||||
|
manually use the <function>lower</> function when you need to compare |
||||||
|
case-insensitively; this works all right if case-insensitive comparison |
||||||
|
is needed only infrequently. If you need case-insensitive most of |
||||||
|
the time and case-sensitive infrequently, consider storing the data |
||||||
|
as <type>citext</> and explicitly casting the column to <type>text</> |
||||||
|
when you want case-sensitive comparison. In either situation, you |
||||||
|
will need two indexes if you want both types of searches to be fast. |
||||||
|
</para> |
||||||
|
</listitem> |
||||||
|
</itemizedlist> |
||||||
|
</sect2> |
||||||
|
|
||||||
|
<sect2> |
||||||
|
<title>Author</title> |
||||||
|
|
||||||
|
<para> |
||||||
|
David E. Wheeler <email>david@kineticode.com</email> |
||||||
|
</para> |
||||||
|
|
||||||
|
<para> |
||||||
|
Inspired by the original <type>citext</> module by Donald Fraser. |
||||||
|
</para> |
||||||
|
|
||||||
|
</sect2> |
||||||
|
|
||||||
|
</sect1> |
Loading…
Reference in new issue