PostgreSQL supports three primary character data types: CHAR(n), VARCHAR(n), and TEXT. Unlike many other databases, all three are stored using the same internal mechanism — the difference is entirely in length enforcement behavior and blank-padding semantics.
Syntax
column_name CHARACTER(n) -- fixed-length, blank-padded; alias: CHAR(n)
column_name CHARACTER VARYING(n) -- variable-length, max n chars; alias: VARCHAR(n)
column_name TEXT -- variable-length, unlimited
column_name VARCHAR -- no length limit; identical to TEXT
| Type | Storage | Length enforcement | Blank-padding |
|---|---|---|---|
CHAR(n) | Variable (padded) | Truncates excess spaces, errors on excess non-spaces | Yes, pads to n |
VARCHAR(n) | Variable | Error if input exceeds n | No |
TEXT / VARCHAR | Variable | None | No |
Practical Example
Create an orders table that uses all three character types for different business purposes:
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
order_ref CHAR(8) NOT NULL, -- fixed 8-char reference code
status VARCHAR(20) NOT NULL, -- bounded status label
notes TEXT, -- free-form customer notes
country_code CHAR(2) NOT NULL -- ISO 3166-1 alpha-2 country code
);
Insert valid rows:
INSERT INTO orders (order_ref, status, notes, country_code)
VALUES
('ORD-0001', 'pending', 'Please gift wrap.', 'US'),
('ORD-0002', 'shipped', NULL, 'DE'),
('ORD-0003', 'delivered', 'Left at front door.','GB');
Attempt to exceed the VARCHAR length limit:
INSERT INTO orders (order_ref, status, notes, country_code)
VALUES ('ORD-0004', 'awaiting_payment_confirmation', NULL, 'FR');
-- ERROR: value too long for type character varying(20)
Attempt to exceed the CHAR column size:
INSERT INTO orders (order_ref, status, notes, country_code)
VALUES ('ORD-00001-EXTRA', 'pending', NULL, 'AU');
-- ERROR: value too long for type character(8)
CHAR Blank-Padding Behavior
CHAR(n) pads shorter values with trailing spaces. When comparing two CHAR values, PostgreSQL ignores trailing spaces — which can cause subtle bugs:
SELECT 'US'::CHAR(5) = 'US '::CHAR(5); -- returns true (spaces ignored)
This behavior is one reason most developers avoid CHAR in favor of VARCHAR or TEXT.
Inspecting Character Column Definitions
Use \d+ in psql to verify column types and lengths:
\d+ orders
Or query information_schema:
SELECT column_name, data_type, character_maximum_length
FROM information_schema.columns
WHERE table_name = 'orders';
Testing with Vela
When you need to widen a VARCHAR(n) constraint or change a column to TEXT, test the ALTER TABLE migration on a database branch first. Create a branch from production, run the type change, and validate that existing application queries and ORM models behave correctly before applying to the live database.
Production Tips
- Prefer
TEXTfor general-purpose string columns — it imposes no length restriction and avoids future migration pain when business requirements change. - Use
VARCHAR(n)as a data integrity constraint when there is a real business rule behind the limit (country codes, status labels, fixed-width identifiers). - Avoid
CHAR(n)in new designs — the blank-padding behavior is rarely useful and causes subtle comparison bugs. - Do not use
VARCHAR(255)as a cargo-cult default — choose a meaningful limit or useTEXT. - Add
CHECK (char_length(col) > 0)when you want to reject empty strings, sinceNOT NULLalone allows empty strings.