Skip to content

Commit a6fa23b

Browse files
docs: add postgres case sensitivity (#891)
* add postgres case sensitivity * Update content/blog/postgres-case-sensitivity.md Co-authored-by: Copilot <[email protected]> --------- Co-authored-by: Copilot <[email protected]>
1 parent c99a590 commit a6fa23b

File tree

2 files changed

+113
-0
lines changed

2 files changed

+113
-0
lines changed
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
---
2+
title: 'Postgres Case Sensitivity Explained'
3+
author: Adela
4+
updated_at: 2025/09/19 18:00
5+
feature_image: /content/blog/postgres-case-sensitivity/cover.webp
6+
tags: Explanation
7+
description: 'An engineering perspective to evaluate Postgres case sensitivity'
8+
---
9+
10+
PostgreSQL's case sensitivity rules can surprise even experienced developers. Here's the concise, no-gotchas guide.
11+
12+
## 1. Identifiers (table/column names)
13+
14+
* **Unquoted identifiers are folded to lowercase.**
15+
16+
```sql
17+
CREATE TABLE Customer (ID int, UserName varchar(50));
18+
-- Actually created as: customer(id, username)
19+
20+
SELECT * FROM customer; -- ✅ works
21+
SELECT * FROM Customer; -- ✅ works (folded to lowercase)
22+
```
23+
24+
* **Quoted identifiers preserve case and must be referenced exactly.**
25+
26+
```sql
27+
CREATE TABLE "Customer" ("ID" int, "UserName" varchar(50));
28+
29+
SELECT * FROM "Customer"; -- ✅ works
30+
SELECT * FROM customer; -- ❌ ERROR: relation "customer" does not exist
31+
SELECT id FROM "Customer"; -- ❌ ERROR: column "id" does not exist
32+
```
33+
34+
**Best practice:** avoid quoted identifiers. Use lowercase `snake_case` (e.g., `order_items`, `created_at`).
35+
36+
## 2. Strings (data values)
37+
38+
* **String comparisons are case-sensitive by default.**
39+
40+
```sql
41+
SELECT 'abc' = 'ABC'; -- false
42+
INSERT INTO users (username) VALUES ('JohnDoe'), ('janedoe');
43+
SELECT * FROM users WHERE username = 'johndoe'; -- 0 rows
44+
```
45+
46+
* **Case-insensitive matching**
47+
48+
* Functions and operators:
49+
50+
```sql
51+
SELECT * FROM users WHERE lower(username) = 'johndoe'; -- ✅ finds 1 row
52+
SELECT * FROM users WHERE username ILIKE 'john%'; -- ✅ case-insensitive LIKE
53+
SELECT * FROM users WHERE username ~* '^john'; -- ✅ case-insensitive regex
54+
```
55+
56+
* `citext` extension:
57+
58+
```sql
59+
CREATE EXTENSION IF NOT EXISTS citext;
60+
CREATE TABLE users (id SERIAL, username CITEXT UNIQUE);
61+
SELECT * FROM users WHERE username = 'johndoe'; -- ✅ automatically case-insensitive
62+
```
63+
64+
* Nondeterministic collations (PostgreSQL 12+):
65+
66+
```sql
67+
CREATE COLLATION case_insensitive (
68+
provider = icu,
69+
locale = 'und-u-ks-level2',
70+
deterministic = false
71+
);
72+
CREATE TABLE users (username TEXT COLLATE "case_insensitive");
73+
```
74+
75+
## 3. Indexing for case-insensitive search
76+
77+
* Functional index to avoid full scans:
78+
79+
```sql
80+
CREATE INDEX users_name_lower_idx ON users (lower(name));
81+
-- Query must match the expression:
82+
SELECT * FROM users WHERE lower(name) = lower($1);
83+
```
84+
85+
* For prefix searches:
86+
87+
```sql
88+
CREATE INDEX users_name_lower_like_idx ON users (lower(name) text_pattern_ops);
89+
SELECT * FROM users WHERE lower(name) LIKE lower($1) || '%';
90+
```
91+
92+
## 4. ORMs & migrations: common pitfalls
93+
94+
* Some ORMs emit **quoted identifiers**, locking you into exact casing everywhere. Prefer ORM settings that generate unquoted, lowercase names.
95+
* Mixing quoted and unquoted names across migrations leads to "why can't it find my table?" bugs — standardize on lowercase, unquoted schema objects.
96+
97+
```sql
98+
-- Wrong: Mixing quoted and unquoted
99+
CREATE TABLE user_accounts (user_id SERIAL);
100+
ALTER TABLE "User_Accounts" ADD COLUMN email VARCHAR(100); -- ❌ Fails
101+
102+
-- Correct: Consistent unquoted
103+
ALTER TABLE user_accounts ADD COLUMN email VARCHAR(100); -- ✅ Works
104+
```
105+
106+
## Quick Rules of Thumb
107+
108+
1. **Schema:** lowercase + unquoted identifiers, always.
109+
2. **Search:** use `ILIKE`, `lower()` with functional indexes, or `citext`.
110+
3. **Avoid quoted names** unless you have a compelling reason.
111+
4. **ORMs:** configure to generate lowercase, unquoted schema objects.
112+
113+
That's it — you'll stay consistent, avoid case traps, and keep queries fast.
30.2 KB
Loading

0 commit comments

Comments
 (0)