Snowflake World Tour hits your city

See how leading teams deploy agents at scale. Find a stop near you. Register free.

Connecting to Postgres

psql is the interactive terminal that ships with every Postgres installation. It's the fastest way to run queries, explore your schema, and manage your database — and once you learn a few tricks, it becomes an incredibly productive environment.

There are two main ways to connect. The first is a connection URI, which packs everything into a single string:

psql postgresql://myuser:mypassword@localhost:5432/mydb

The second approach uses individual flags, which I find easier to read when I'm working with multiple servers:

psql -h localhost -p 5432 -U myuser -d mydb

Here's what each flag means:

FlagPurpose
-hHostname or IP address
-pPort (default is 5432)
-UUsername
-dDatabase name

Once connected, you'll see a prompt like this:

mydb=>

The => means you're connected as a regular user. If you see =#, you're connected as a superuser. You can check your current connection details at any time:

mydb=> \conninfo
You are connected to database "mydb" as user "myuser" via socket in "/tmp" at port "5432".

aside positive Snowflake Postgres supports standard psql connections — use your existing psql client with psql postgresql://user:pass@your-instance.snowflakecomputing.com:5432/dbname. All the meta-commands and settings in this guide work with Snowflake Postgres.

Meta-Commands and Query Execution

Meta-commands are the backslash commands that make psql so much more than a SQL prompt. They let you explore your database without writing information_schema queries by hand.

Exploring Your Database

Here are the commands I use every single day:

CommandWhat it shows
\lList all databases
\c dbnameConnect to a different database
\dtList tables in current schema
\dt+List tables with sizes
\d tablenameDescribe a table (columns, types, constraints)
\dnList schemas
\duList roles and their attributes
\diList indexes
\dfList functions

Let's look at a few of these in action:

mydb=> \dt
              List of relations
 Schema |     Name      | Type  |  Owner
--------+---------------+-------+---------
 public | customers     | table | myuser
 public | orders        | table | myuser
 public | products      | table | myuser
(3 rows)

The \d command is probably the one I use most — it tells you everything about a table's structure:

mydb=> \d customers
                                   Table "public.customers"
   Column   |          Type          | Collation | Nullable |                Default
------------+------------------------+-----------+----------+---------------------------------------
 id         | integer                |           | not null | nextval('customers_id_seq'::regclass)
 name       | character varying(100) |           | not null |
 email      | character varying(255) |           |          |
 created_at | timestamp with time zone |         | not null | now()
Indexes:
    "customers_pkey" PRIMARY KEY, btree (id)
    "customers_email_key" UNIQUE CONSTRAINT, btree (email)

Running Queries

Type any SQL statement followed by a semicolon:

mydb=> SELECT name, email FROM customers LIMIT 3;
    name     |        email
-------------+---------------------
 Alice Chen  | alice@example.com
 Bob Smith   | bob@example.com
 Carol Jones | carol@example.com
(3 rows)

For multiline queries, psql waits until it sees a semicolon — the prompt changes to show you're continuing a statement:

mydb=> SELECT name,
mydb->        count(*) AS order_count
mydb-> FROM customers c
mydb->   JOIN orders o ON c.id = o.customer_id
mydb-> GROUP BY name
mydb-> ORDER BY order_count DESC;

Expanded Display

Wide tables are hard to read in the terminal. Toggle expanded mode with \x:

mydb=> \x
Expanded display is on.
mydb=> SELECT * FROM customers WHERE id = 1;
-[ RECORD 1 ]----------------------------
id         | 1
name       | Alice Chen
email      | alice@example.com
created_at | 2024-11-15 09:23:14.521+00

I recommend \x auto — it uses expanded mode only when the output is too wide for your terminal.

Timing and Performance

Turn on query timing to see how long each statement takes:

mydb=> \timing
Timing is on.
mydb=> SELECT count(*) FROM orders;
 count
--------
 284710
(1 row)

Time: 42.138 ms

This is invaluable when you're testing index changes or comparing query plans.

Getting Help

Two help commands worth knowing: \h shows SQL syntax help, and \? lists all meta-commands:

mydb=> \h CREATE INDEX
Command:     CREATE INDEX
Description: define a new index
Syntax:
CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] name ] ON ...

Customizing Your psql Environment

This is where psql goes from good to great. I wanted to share a couple of my favorite things you can do with your environment — the settings that make psql feel like home.

Making NULLs Visible

By default, NULL values display as blank — indistinguishable from an empty string. This has tripped up every Postgres user at some point. Fix it:

mydb=> \pset null '(null)'
Null display is "(null)".
mydb=> SELECT name, phone FROM customers WHERE id IN (1, 2);
    name     |    phone
-------------+-------------
 Alice Chen  | 555-0142
 Bob Smith   | (null)
(2 rows)

Pretty Output with Unicode Borders

mydb=> \pset linestyle unicode
Line style is unicode.
mydb=> \pset border 2
Border style is 2.
mydb=> SELECT name, email FROM customers LIMIT 2;
┌─────────────┬─────────────────────┐
│    name     │        email        │
├─────────────┼─────────────────────┤
│ Alice Chen  │ alice@example.com   │
│ Bob Smith   │ bob@example.com     │
└─────────────┴─────────────────────┘

The .psqlrc File

Every setting you've been typing manually can go in ~/.psqlrc so it loads automatically. Here's a starter file that I recommend:

-- Make NULLs visible
\pset null '(null)'

-- Unicode borders
\pset linestyle unicode
\pset border 2

-- Show timing on every query
\timing

-- Use expanded mode when needed
\x auto

-- Quiet startup messages
\set QUIET 1

-- Custom prompt showing database and user
\set PROMPT1 '%n@%/%R%# '

-- Don't page short results
\pset pager off

\set QUIET 0

Save this to ~/.psqlrc and every new psql session starts with your preferred settings.

Seeing the SQL Behind Meta-Commands

One of my favorite tricks: \set ECHO_HIDDEN on shows you the actual SQL query that psql runs when you type a meta-command. This is a great way to learn:

mydb=> \set ECHO_HIDDEN on
mydb=> \dt
********* QUERY **********
SELECT n.nspname as "Schema",
  c.relname as "Name",
  CASE c.relkind WHEN 'r' THEN 'table' ...
FROM pg_catalog.pg_class c
     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind IN ('r','p','')
...
**************************

Saved Queries

You can save frequently-used queries as psql variables and run them with a colon:

-- Add to .psqlrc
\set active_queries 'SELECT pid, state, query FROM pg_stat_activity WHERE state != \'idle\';'

Then use it:

mydb=> :active_queries
 pid  | state  |                query
------+--------+-------------------------------------
 1234 | active | SELECT * FROM orders WHERE ...
(1 row)

History Search and Tab Completion

Two features that save me time every day:

  • Ctrl+R — Search your command history. Start typing any part of a previous command and it finds matches.
  • Tab completion — psql completes table names, column names, SQL keywords, and even meta-commands. Start typing and hit Tab.

Try it: type SELECT * FROM cust and press Tab — psql will complete it to customers.

Importing and Exporting Data

psql has built-in commands for moving data in and out of your database without external tools.

Running SQL Files

The \i command executes a SQL file:

mydb=> \i /path/to/schema.sql
CREATE TABLE
CREATE TABLE
CREATE INDEX

This is how I run migration scripts and seed data files during development.

Sending Output to a File

Use \o to redirect query output:

mydb=> \o /tmp/report.txt
mydb=> SELECT * FROM orders WHERE total > 1000;
mydb=> \o

The first \o starts capturing, and \o with no argument stops it.

Using an External Editor

Type \e to open your current query (or an empty buffer) in your $EDITOR. Write your SQL, save, and quit — psql runs it immediately. This is especially nice for complex queries where you want syntax highlighting and proper indentation.

COPY for Bulk Data

\copy is psql's client-side bulk import/export command:

-- Export to CSV
mydb=> \copy orders TO '/tmp/orders.csv' WITH CSV HEADER;
COPY 284710

-- Import from CSV
mydb=> \copy staging_orders FROM '/tmp/new_orders.csv' WITH CSV HEADER;
COPY 500

The \copy command runs on your local machine — it reads and writes files from your filesystem, not the server's. This is important when you're connected to a remote database.

Conclusion

psql is a deceptively powerful tool. The basics get you running queries in seconds, but the meta-commands, customization options, and .psqlrc file turn it into a full development environment. I encourage you to experiment with the settings here, build up your own .psqlrc, and explore — there's always another trick to discover.

Related Resources

Interactive playground to practice psql commands.


Complete reference for psql commands, variables, and shortcuts.


Connecting to Snowflake Postgres and essential commands.


Settings, presets, echo, and saved queries for power users.

Updated 2026-07-08

This content is provided as is, and is not maintained on an ongoing basis. It may be out of date with current Snowflake instances