Overview
citext (CREATE EXTENSION citext) keeps the original case but compares as if lower() were applied to both sides. Use it for emails, usernames, and slugs so Bob@x.com and bob@x.com collide under a plain unique constraint, with no lower(email) in queries and no functional index.
Rules
- Index it with an ordinary B-tree and query
WHERE email = $1. Equality,LIKE,ILIKE, andORDER BYare all case-insensitive. - Folding follows the database
LC_CTYPEand is not true Unicode case folding. The PostgreSQL docs recommend nondeterministic ICU collations instead; PostgreSQL 18 addedLIKEsupport for them, which was a common reason to pickcitext. - Do not use it where case is meaningful (tokens, code identifiers).
CREATE EXTENSION IF NOT EXISTS citext;
CREATE TABLE users (id bigint PRIMARY KEY GENERATED ALWAYS AS IDENTITY, email citext NOT NULL UNIQUE);
INSERT INTO users (email) VALUES ('Alice@Example.com');
INSERT INTO users (email) VALUES ('alice@example.com'); -- unique violationRelated
- btree-index: a plain B-tree serves citext lookups.
- secondary-index: the alternative lower() expression index.
- postgres: extension setup and schema conventions.
- postgres-indexes: index strategy.
- postgres-types: PostgreSQL types at a glance.