FluentDBFluentDB
PostgreSQL guides

List tables, databases, schemas and users in PostgreSQL

By Kevin Piacentini, Software Engineer & Founder of FluentDB·

PostgreSQL has no SHOW TABLES. That is a MySQL command, and the first thing most people try. Postgres does have a SHOW, but it prints configuration settings, so SHOW TABLES gives you a syntax error.

There are two correct answers, and picking the wrong one is what wastes the afternoon. In the psql terminal you use backslash commands like \dt. From application code those do not exist, and you query information_schema or pg_catalog instead. This page covers both, for tables, databases, schemas, users and columns.

The short answer

In psql:

bash
\dt              -- tables in the schemas on your search_path
\dt *.*          -- tables in every schema
\l               -- databases
\dn              -- schemas
\du              -- roles (users)
\d users         -- describe one table
\c other_db      -- switch database

From SQL, which works from any client or application:

sql
-- tables
SELECT table_schema, table_name
FROM information_schema.tables
WHERE table_type = 'BASE TABLE'
  AND table_schema NOT IN ('pg_catalog', 'information_schema')
ORDER BY table_schema, table_name;

Why backslash commands fail in your code

\dt is not SQL. It is a feature of the psql client, which expands it into a real query against the system catalogues before anything reaches the server. Send \dt from a driver and you get a syntax error, because the server has never heard of it.

text
# psql: works
mydb=# \dt
         List of relations
 Schema |  Name  | Type  | Owner
--------+--------+-------+--------
 public | orders | table | kevin
 public | users  | table | kevin

# From Python: fails
psycopg.errors.SyntaxError: syntax error at or near "\"
Useful trick: run psql with -E (or type \set ECHO_HIDDEN on) and it prints the actual SQL behind every backslash command. That is the fastest way to get a catalogue query you can paste into your application.

Listing tables

\dt lists tables, and by default only user-created ones: the documentation is explicit that system objects are hidden unless you supply a pattern or the S modifier. \dt+ adds persistence, size on disk and any description.

The catch is search_path. \dt shows tables in the schemas currently on your path, which is why a table you know exists can be missing from the list:

bash
\dt                  -- only schemas on search_path
\dt reporting.*      -- one specific schema
\dt *.*              -- every schema
SHOW search_path;    -- what is actually on the path

From SQL there are two catalogues you can use. The portable one:

sql
SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'public'
  AND table_type = 'BASE TABLE'
ORDER BY table_name;

And the Postgres-specific one, which is faster and gives you more:

sql
SELECT schemaname, tablename, tableowner
FROM pg_catalog.pg_tables
WHERE schemaname NOT IN ('pg_catalog', 'information_schema')
ORDER BY schemaname, tablename;

Use information_schema if the same query has to run against other databases too, since it is a SQL standard. Use pg_catalogwhen you want Postgres-only detail such as the owner or the table's physical size.

Listing databases

\l lists databases with their names, owners, encodings and access privileges. Sizes are not included by default; \l+ adds size, default tablespace and description, and the docs note that size is only available for databases you can actually connect to.

sql
-- the SQL equivalent
SELECT datname FROM pg_database WHERE datistemplate = false ORDER BY datname;

The datistemplate = false filter drops template0 and template1, which exist in every cluster and are rarely what you are looking for.

There is no USE database in Postgres. A connection belongs to exactly one database and cannot query across databases, so switching means opening a new connection. In psql that is \c other_db. This is the single biggest difference from MySQL and it surprises almost everyone once.

Listing schemas, and what a schema actually is

\dn lists schemas, user-created ones only unless you add S. From SQL:

sql
SELECT schema_name
FROM information_schema.schemata
WHERE schema_name NOT LIKE 'pg_%'
  AND schema_name <> 'information_schema'
ORDER BY schema_name;

A schema is a namespace insidea database. That is the distinction people search for: databases are isolated from one another and need separate connections, while schemas live together and one query can join across them. If you are coming from MySQL, a MySQL "database" behaves much more like a Postgres schema than like a Postgres database.

Which schema you land in is controlled by search_path:

sql
SHOW search_path;
SET search_path TO reporting, public;

Listing users and roles

\du lists roles, and again only user-created ones by default. Postgres merged users and groups into a single concept called a role, so what you are listing is roles; the ones that can log in are what you would normally call users.

sql
SELECT rolname, rolsuper, rolcanlogin, rolcreatedb
FROM pg_catalog.pg_roles
WHERE rolname NOT LIKE 'pg\_%'
ORDER BY rolname;

Filter on rolcanlogin = true if you want only the roles that are actually login accounts.

Describing a table

\d table_name is the equivalent of MySQL's DESCRIBE, and it shows more: every column with its type and any NOT NULL or default, plus the associated indexes, constraints, rules and triggers. \d+ adds column comments, the view definition if it is a view, and the access method.

sql
SELECT column_name, data_type, is_nullable, column_default
FROM information_schema.columns
WHERE table_schema = 'public'
  AND table_name = 'users'
ORDER BY ordinal_position;

ORDER BY ordinal_position matters. Without it the column order is not guaranteed, and a column list in the wrong order is confusing in exactly the situation where you are trying to understand an unfamiliar table.

A translation table

If you are arriving from MySQL:

MySQL commands and their psql and SQL equivalents in PostgreSQL
MySQLpsqlSQL, works anywhere
SHOW TABLES;\dtinformation_schema.tables
SHOW DATABASES;\lpg_database
SHOW COLUMNS FROM users;\d usersinformation_schema.columns
DESCRIBE users;\d usersinformation_schema.columns
SHOW INDEX FROM users;\dipg_indexes
SELECT ... mysql.user;\dupg_catalog.pg_roles
USE mydb;\c mydbReconnect. No SQL equivalent.
No equivalent\dninformation_schema.schemata

The same idea applies to the other engines, where the answers differ again: see SHOW TABLES in MySQL and listing tables in SQLite. And when one of these queries fails with a Postgres error, our Postgres error library explains the common ones.

Or just look at it

FluentDB shows every table, schema, column and constraint in a native Mac window, with no commands to remember. Ask it a question in plain English and it writes the SQL against your real schema.

Download FluentDB for Mac

Listing Postgres objects FAQ

Does PostgreSQL have SHOW TABLES?

No. SHOW TABLES is MySQL syntax. In psql the equivalent is \dt, and from SQL you query information_schema.tables or pg_catalog.pg_tables. Postgres does have a SHOW command, but it displays configuration parameters such as SHOW search_path, not tables.

Why does \dt fail in my application code?

Backslash commands are features of the psql client, not of PostgreSQL itself. psql translates them into real queries before sending them, so the server never sees \dt. From application code use SELECT ... FROM information_schema.tables instead, which is ordinary SQL.

How do I list tables in a specific schema?

In psql use \dt schema_name.* to list tables in one schema, or \dt *.* for every schema. From SQL, filter on table_schema: SELECT table_name FROM information_schema.tables WHERE table_schema = 'your_schema'.

Why does \dt say 'Did not find any relations' when the table exists?

Almost always a search_path issue. \dt only shows tables in the schemas on your current search_path. Run SHOW search_path to see it, then either use \dt schema_name.* or set the path with SET search_path TO your_schema, public.

What is the difference between a database and a schema in Postgres?

A database is a separate container that you connect to individually, and one connection cannot query across databases. A schema is a namespace inside a database, so a single connection can query across schemas freely. This is why Postgres has no USE command: switching database means reconnecting, which in psql is \c dbname.

How do I list users in PostgreSQL?

Use \du in psql, or query pg_catalog.pg_roles from SQL. Postgres unified users and groups into roles in version 8.1, so what you are listing is roles; a role that can log in is what most people mean by a user, shown by the rolcanlogin column.

How do I describe a table in PostgreSQL?

In psql, \d table_name shows the columns, types, indexes, constraints and triggers, and \d+ table_name adds column comments and storage details. From SQL, query information_schema.columns filtered by table_name.