CHAR, VARCHAR, and TEXT in PostgreSQL

Understand the differences between PostgreSQL CHAR, VARCHAR, and TEXT types: storage, length enforcement, blank-padding, and when to choose each for your schema.

5 min read · Back to overview

Quick Answer

PostgreSQL offers three character types: CHAR(n) for fixed-length blank-padded strings, VARCHAR(n) for variable-length strings with an enforced maximum, and TEXT for unlimited variable-length strings. All three have identical performance; the choice depends on whether you need length enforcement.

Spin up a Postgres database in 20 seconds with Vela.

Try Vela Sandbox

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
TypeStorageLength enforcementBlank-padding
CHAR(n)Variable (padded)Truncates excess spaces, errors on excess non-spacesYes, pads to n
VARCHAR(n)VariableError if input exceeds nNo
TEXT / VARCHARVariableNoneNo

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 TEXT for 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 use TEXT.
  • Add CHECK (char_length(col) > 0) when you want to reject empty strings, since NOT NULL alone allows empty strings.

Continue in PostgreSQL Data Types: NUMERIC.

Related in this section: Boolean · NUMERIC · DOUBLE PRECISION

Frequently Asked Questions

What is the difference between CHAR, VARCHAR, and TEXT in PostgreSQL?
CHAR(n) stores fixed-length strings padded with spaces to length n. VARCHAR(n) stores variable-length strings and raises an error when the input exceeds n characters. TEXT stores variable-length strings with no length limit. All three share the same internal storage mechanism in PostgreSQL.
Does changing a VARCHAR length limit lock the table?
Increasing the length limit (ALTER TABLE t ALTER COLUMN c TYPE VARCHAR(200)) takes only a brief metadata lock when going from a smaller to a larger limit. Decreasing it requires a full table rewrite and an ACCESS EXCLUSIVE lock that blocks reads and writes for the duration.
What happens to dependent views when I change a VARCHAR column length?
Increasing a VARCHAR length limit does not affect dependent views. Changing the base type (e.g. from VARCHAR to TEXT) may invalidate views that cast the column; PostgreSQL will raise an error and you must recreate those views after the type change.
Can I use IF EXISTS when altering character column types?
Use ALTER TABLE t ALTER COLUMN col_name TYPE new_type to change the type. There is no direct IF EXISTS for this operation, but you can check pg_attribute before running the statement to guard against missing columns.
What is the safest way to change a VARCHAR(n) column to TEXT in production?
For most cases, ALTER TABLE t ALTER COLUMN c TYPE TEXT runs in place without a table rewrite. Test on a Vela branch first to confirm behavior, then apply to production during a low-traffic window since a brief ACCESS EXCLUSIVE lock is still acquired.