PostgreSQLCharacter Types

Character Types

PostgreSQL offers three ways to store text: CHAR(n) for fixed-length strings, VARCHAR(n) for variable-length strings with a maximum, and TEXT for text of any length. They look like a hierarchy of increasing flexibility, but PostgreSQL's implementation blurs the lines between them more than you might expect coming from other databases.

Type

Behavior

CHAR(n)

Fixed length. Shorter values are right-padded with spaces up to n.

VARCHAR(n)

Variable length, up to a maximum of n characters. No padding.

TEXT

Variable length, unlimited. No maximum enforced.

CHAR(n): fixed-length and padded

CHAR pads with spaces

SQL
CREATE TABLE codes (code CHAR(5));

INSERT INTO codes VALUES ('AB');

SELECT code, length(code), code = 'AB' AS looks_equal
FROM codes;
  code  | length | looks_equal
--------+--------+-------------
 AB     |      5 | t
Warning
`CHAR(n)` silently right-pads stored values with spaces up to the declared length. `length()` on a `CHAR(n)` column returns `n`, not the length of the text you inserted, and trailing spaces are stripped for comparison purposes but still physically stored. This surprises almost everyone the first time they hit it — for that reason, `CHAR(n)` is rarely the right choice outside of legacy schemas or genuinely fixed-width codes (like a two-letter country code).
VARCHAR(n): variable length with a cap

VARCHAR enforces a maximum

SQL
CREATE TABLE users (
    username VARCHAR(30) NOT NULL
);

INSERT INTO users VALUES ('short_name');      -- fine
INSERT INTO users VALUES (repeat('x', 31));   -- error: value too long for type character varying(30)

VARCHAR(n) stores exactly what you insert (no padding) but rejects any value longer than n. It's a good fit when you have a genuine business rule to enforce — a username limited to 30 characters, a two-letter state abbreviation.

TEXT: unbounded text

TEXT has no length limit

SQL
CREATE TABLE articles (
    title VARCHAR(200) NOT NULL,
    body  TEXT NOT NULL
);
Note
Unlike many other databases, PostgreSQL stores and processes `CHAR(n)`, `VARCHAR(n)`, and `TEXT` almost identically under the hood — all three use the same variable-length storage representation internally, and there is no meaningful performance penalty for using `TEXT` or an unconstrained `VARCHAR` instead of a tightly-sized `VARCHAR(n)`. (This is a notable difference from databases where `VARCHAR` carries real storage or performance overhead compared to fixed types.)
Tip
Because of that, the pragmatic default in PostgreSQL is to reach for `TEXT` (or `VARCHAR` with no length specified) unless you have an actual constraint you want the database to enforce — like a two-character country code or a username capped at 30 characters. Use `VARCHAR(n)` for validation, not for a performance benefit that doesn't exist in PostgreSQL.
  • CHAR(n) — fixed length, pads with spaces, and rarely the right choice.

  • VARCHAR(n) — variable length capped at n, useful when you want the database to enforce a length rule.

  • TEXT — variable length, unbounded, and the pragmatic default in PostgreSQL.

  • All three have essentially the same performance characteristics internally — choose based on constraints you actually need, not imagined storage cost.