Part 1: psql - PostgreSQL Interactive Terminal Guide

Table of Contents

Overview

psql is PostgreSQL’s interactive terminal interface, providing command-line access to PostgreSQL databases with advanced features for both interactive and scripted use.

Key Features

  • Interactive SQL execution
  • Meta-commands
  • Script execution
  • Output formatting
  • Command history
  • Tab completion
  • Variable support
  • Conditional execution

Connection & Authentication

Basic Connection

# Connect to database
psql -d database_name
 
# Connect with user
psql -U username -d database_name
 
# Connect to specific host/port
psql -h hostname -p 5432 -d database_name
 
# Connect with URL
psql postgresql://username:password@hostname:5432/database_name

Connection Options

# Environment variables
export PGHOST=hostname
export PGPORT=5432
export PGDATABASE=dbname
export PGUSER=username
export PGPASSWORD=password
 
# Service file (~/.pg_service.conf)
[service_name]
host=hostname
port=5432
dbname=database_name
user=username

SSL Connections

# Connect with SSL
psql "sslmode=verify-full sslcert=client-cert.pem \
      sslkey=client-key.pem sslrootcert=root.crt \
      host=hostname dbname=database_name"

Basic Commands

Meta-Commands

-- List databases
\l
\l+  -- with additional information
 
-- Connect to database
\c database_name
 
-- List tables
\dt
\dt+  -- with additional information
 
-- List schemas
\dn
\dn+
 
-- Describe table
\d table_name
\d+ table_name  -- with additional information

Help Commands

-- General help
\?
 
-- SQL command help
\h
 
-- Specific command help
\h SELECT
 
-- Command history
\s
 
-- Edit command in editor
\e
 
-- Edit function in editor
\ef function_name

Object Information

-- List roles
\du
 
-- List tablespaces
\db
 
-- List functions
\df
 
-- List views
\dv
 
-- List sequences
\ds
 
-- List indexes
\di
 
-- Show table access privileges
\z table_name

System Information

-- Show configuration
\show
 
-- Show client encoding
\encoding
 
-- Show search path
\echo :search_path
 
-- Show version
SELECT version();

Query Execution

Basic Execution

-- Execute query
SELECT * FROM table_name;
 
-- Execute from file
\i script.sql
 
-- Execute command and save results
\o output.txt
SELECT * FROM table_name;
\o
 
-- Timing of queries
\timing
SELECT count(*) FROM large_table;

Transaction Control

-- Begin transaction
\set AUTOCOMMIT off
BEGIN;
 
-- Commit transaction
COMMIT;
 
-- Rollback transaction
ROLLBACK;
 
-- Set savepoint
SAVEPOINT my_savepoint;
ROLLBACK TO my_savepoint;

Output Formatting

Display Options

-- Set output format
\x auto  -- Extended display mode
\x on    -- Always extended
\x off   -- Never extended
 
-- Set field separator
\pset fieldsep ','
 
-- Set null display
\pset null '[NULL]'
 
-- Set line style
\pset linestyle ascii
\pset linestyle unicode

Format Control

-- Border styles
\pset border 2
 
-- Toggle headers
\pset headers off
 
-- Toggle footer
\pset footer off
 
-- Set format
\pset format unaligned
\pset format aligned
\pset format wrapped
\pset format html
\pset format latex

[Continue to Part 2…]

Part 2: psql Advanced Guide

Advanced Features

Variables and Interpolation

-- Set variables
\set variable_name 'value'
 
-- Use variables
SELECT :variable_name;
 
-- Conditional execution
\if :{?variable_name}
    \echo "Variable is set"
\else
    \echo "Variable is not set"
\endif

Custom Prompts

-- Set prompt
\set PROMPT1 '%[%033[1;33;40m%]%n@%/%R%[%033[0m%]%# '
\set PROMPT2 '[more] %R > '
 
-- Include timing in prompt
\set PROMPT1 '%[%033[1;33;40m%]%n@%/%R%[%033[0m%]%# [%x] '

Scripting Features

-- Error handling
\set ON_ERROR_STOP on
 
-- Echo commands
\set ECHO all
 
-- Quiet mode
\set QUIET on
 
-- Verbosity
\set VERBOSITY verbose

Scripting & Automation

Backup and Restore

# Backup database
pg_dump dbname > backup.sql
 
# Restore database
psql dbname < backup.sql
 
# Custom format backup
pg_dump -Fc dbname > backup.dump
 
# Restore custom format
pg_restore -d dbname backup.dump

Maintenance Scripts

#!/bin/bash
# Database maintenance script
 
DBNAME="mydb"
LOGFILE="maintenance.log"
 
psql -d "$DBNAME" << EOF >> "$LOGFILE" 2>&1
-- Vacuum analyze
VACUUM ANALYZE;
 
-- Update statistics
ANALYZE;
 
-- Reindex
REINDEX DATABASE "$DBNAME";
EOF

Monitoring Scripts

#!/bin/bash
# Database monitoring script
 
DBNAME="mydb"
EMAIL="[email protected]"
 
# Check long running queries
psql -d "$DBNAME" -X -A -t << EOF | mail -s "Long Running Queries" "$EMAIL"
SELECT pid, 
       now() - pg_stat_activity.query_start AS duration,
       query
FROM pg_stat_activity
WHERE state != 'idle'
  AND now() - pg_stat_activity.query_start > interval '5 minutes';
EOF

Best Practices

Performance Monitoring

-- Check table sizes
SELECT relname as table_name,
       pg_size_pretty(pg_total_relation_size(relid)) as total_size
FROM pg_catalog.pg_statio_user_tables
ORDER BY pg_total_relation_size(relid) DESC;
 
-- Check index usage
SELECT schemaname, tablename, indexname, idx_scan, idx_tup_read
FROM pg_stat_user_indexes
ORDER BY idx_scan DESC;
 
-- Check cache hit ratio
SELECT 
    sum(heap_blks_read) as heap_read,
    sum(heap_blks_hit)  as heap_hit,
    sum(heap_blks_hit) / (sum(heap_blks_hit) + sum(heap_blks_read)) as ratio
FROM pg_statio_user_tables;

Security Best Practices

-- Check user privileges
SELECT grantee, privilege_type 
FROM information_schema.role_table_grants 
WHERE table_name='sensitive_table';
 
-- Audit connections
SELECT datname, usename, client_addr, backend_start
FROM pg_stat_activity;
 
-- Check ssl status
SHOW ssl;
SELECT * FROM pg_stat_ssl;

Example Scripts

Database Health Check

#!/bin/bash
# Comprehensive database health check
 
DBNAME="production_db"
REPORT="health_check_$(date +%Y%m%d).txt"
 
psql -d "$DBNAME" << EOF > "$REPORT"
\qecho '=== Database Size ==='
SELECT pg_size_pretty(pg_database_size(current_database()));
 
\qecho '\n=== Table Sizes ==='
SELECT relname as "Table",
       pg_size_pretty(pg_total_relation_size(relid)) as "Total Size",
       pg_size_pretty(pg_relation_size(relid)) as "Data Size",
       pg_size_pretty(pg_total_relation_size(relid) - pg_relation_size(relid)) 
         as "Index Size"
FROM pg_catalog.pg_statio_user_tables
ORDER BY pg_total_relation_size(relid) DESC
LIMIT 10;
 
\qecho '\n=== Cache Hit Ratio ==='
SELECT 
    relname as "Table",
    heap_blks_read as heap_read,
    heap_blks_hit as heap_hit,
    CASE WHEN heap_blks_hit + heap_blks_read = 0 
         THEN 0 
         ELSE round(100.0 * heap_blks_hit / (heap_blks_hit + heap_blks_read), 2) 
    END as hit_ratio
FROM pg_statio_user_tables
ORDER BY heap_blks_hit + heap_blks_read DESC
LIMIT 10;
 
\qecho '\n=== Index Usage ==='
SELECT 
    schemaname as "Schema",
    tablename as "Table",
    indexname as "Index",
    idx_scan as "Index Scans",
    idx_tup_read as "Tuples Read",
    idx_tup_fetch as "Tuples Fetched"
FROM pg_stat_user_indexes
ORDER BY idx_scan DESC
LIMIT 10;
 
\qecho '\n=== Bloat Estimation ==='
WITH constants AS (
  SELECT current_setting('block_size')::numeric AS bs,
         23 AS hdr,
         8 AS ma
),
bloat_info AS (
  SELECT
    schemaname, tablename, reltuples::numeric as tups,
    relpages::numeric as pages, relam, bs,
    ceil((cc.reltuples*((datahdr+ma-
      (CASE WHEN datahdr%ma=0 THEN ma ELSE datahdr%ma END))+nullhdr2+4))/(bs-20::float)) 
      AS otta
  FROM pg_class cc
  JOIN pg_namespace nn ON cc.relnamespace = nn.oid
  JOIN constants ON true
  LEFT JOIN pg_statistic ON relid=cc.oid
)
SELECT
  schemaname as "Schema",
  tablename as "Table",
  ROUND(((pages/otta::numeric)*100 - 100)::numeric, 2) as "Bloat%",
  pg_size_pretty((bs*(pages-otta))::bigint) as "Wasted Size"
FROM bloat_info
WHERE pages > otta
ORDER BY ((pages/otta::numeric)*100 - 100) DESC
LIMIT 10;
EOF
 
echo "Health check report generated: $REPORT"

Automated Backup Script

#!/bin/bash
# Automated backup script with retention
 
BACKUP_DIR="/backup/postgres"
DBNAME="production_db"
RETENTION_DAYS=7
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="$BACKUP_DIR/$DBNAME_$DATE.custom"
LOG_FILE="$BACKUP_DIR/backup.log"
 
log() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOG_FILE"
}
 
# Create backup directory if it doesn't exist
mkdir -p "$BACKUP_DIR"
 
# Perform backup
log "Starting backup of $DBNAME"
pg_dump -Fc "$DBNAME" > "$BACKUP_FILE"
 
if [ $? -eq 0 ]; then
    log "Backup completed successfully: $BACKUP_FILE"
    
    # Compress backup
    gzip "$BACKUP_FILE"
    log "Backup compressed"
    
    # Remove old backups
    find "$BACKUP_DIR" -name "*.custom.gz" -mtime +$RETENTION_DAYS -delete
    log "Old backups removed"
else
    log "Backup failed!"
    exit 1
fi
 
# Check backup size
BACKUP_SIZE=$(du -h "$BACKUP_FILE.gz" | cut -f1)
log "Backup size: $BACKUP_SIZE"
 
# Verify backup
log "Verifying backup"
pg_restore -l "$BACKUP_FILE.gz" >/dev/null 2>&1
if [ $? -eq 0 ]; then
    log "Backup verification successful"
else
    log "Backup verification failed!"
    exit 1
fi

Remember:

  • Use appropriate connection security
  • Maintain good password practices
  • Regular backups
  • Monitor performance
  • Document commands and scripts
  • Use version control for scripts
  • Test in development first

For detailed information, consult the PostgreSQL documentation and psql manual (man psql).