468 lines
10 KiB
Markdown
468 lines
10 KiB
Markdown
# PostgreSQL psql CLI
|
|
|
|
Command-line interface for PostgreSQL: connection, meta-commands, scripting, and interactive usage.
|
|
|
|
## Connection
|
|
|
|
### Basic Connection
|
|
```bash
|
|
# Connect to database
|
|
psql -U username -d database -h hostname -p 5432
|
|
|
|
# Connect using URI
|
|
psql postgresql://username:password@hostname:5432/database
|
|
|
|
# Environment variables
|
|
export PGUSER=postgres
|
|
export PGPASSWORD=mypassword
|
|
export PGHOST=localhost
|
|
export PGPORT=5432
|
|
export PGDATABASE=mydb
|
|
psql
|
|
```
|
|
|
|
### Password File (~/.pgpass)
|
|
```bash
|
|
# Format: hostname:port:database:username:password
|
|
# chmod 600 ~/.pgpass
|
|
localhost:5432:mydb:postgres:mypassword
|
|
*.example.com:5432:*:appuser:apppass
|
|
```
|
|
|
|
### SSL Connection
|
|
```bash
|
|
# Require SSL
|
|
psql "host=hostname sslmode=require user=username dbname=database"
|
|
|
|
# Verify certificate
|
|
psql "host=hostname sslmode=verify-full \
|
|
sslcert=/path/to/client.crt \
|
|
sslkey=/path/to/client.key \
|
|
sslrootcert=/path/to/ca.crt"
|
|
```
|
|
|
|
## Essential Meta-Commands
|
|
|
|
### Database Navigation
|
|
```bash
|
|
\l or \list # List databases
|
|
\l+ # List with sizes
|
|
\c database # Connect to database
|
|
\c database username # Connect as user
|
|
\conninfo # Connection info
|
|
```
|
|
|
|
### Schema Inspection
|
|
```bash
|
|
\dn # List schemas
|
|
\dt # List tables
|
|
\dt+ # Tables with sizes
|
|
\dt *.* # All tables, all schemas
|
|
\di # List indexes
|
|
\dv # List views
|
|
\dm # List materialized views
|
|
\ds # List sequences
|
|
\df # List functions
|
|
```
|
|
|
|
### Object Description
|
|
```bash
|
|
\d tablename # Describe table
|
|
\d+ tablename # Detailed description
|
|
\d indexname # Describe index
|
|
\df functionname # Describe function
|
|
\du # List users/roles
|
|
\dp tablename # Show permissions
|
|
```
|
|
|
|
### Output Formatting
|
|
```bash
|
|
\x # Toggle expanded output
|
|
\x on # Enable expanded
|
|
\x off # Disable expanded
|
|
\a # Toggle aligned output
|
|
\t # Toggle tuples only
|
|
\H # HTML output
|
|
\pset format csv # CSV format
|
|
\pset null '[NULL]' # Show NULL values
|
|
```
|
|
|
|
### Execution Commands
|
|
```bash
|
|
\i filename.sql # Execute SQL file
|
|
\o output.txt # Redirect output to file
|
|
\o # Stop redirecting
|
|
\! command # Execute shell command
|
|
\timing # Toggle timing
|
|
\q # Quit
|
|
```
|
|
|
|
## psql Command-Line Options
|
|
|
|
```bash
|
|
# Connection
|
|
-h hostname # Host
|
|
-p port # Port (default 5432)
|
|
-U username # Username
|
|
-d database # Database
|
|
-W # Prompt for password
|
|
|
|
# Execution
|
|
-c "SQL" # Execute command and exit
|
|
-f file.sql # Execute file
|
|
--command="SQL" # Execute command
|
|
|
|
# Output
|
|
-t # Tuples only (no headers)
|
|
-A # Unaligned output
|
|
-F "," # Field separator
|
|
-o output.txt # Output to file
|
|
-q # Quiet mode
|
|
-x # Expanded output
|
|
|
|
# Script options
|
|
-1 # Execute as transaction
|
|
--on-error-stop # Stop on error
|
|
-v variable=value # Set variable
|
|
-L logfile.log # Log session
|
|
```
|
|
|
|
## Running SQL
|
|
|
|
### Interactive Queries
|
|
```sql
|
|
-- Simple query
|
|
SELECT * FROM users;
|
|
|
|
-- Multi-line (ends with semicolon)
|
|
SELECT id, name, email
|
|
FROM users
|
|
WHERE active = true;
|
|
|
|
-- Edit in editor
|
|
\e
|
|
|
|
-- Repeat last query
|
|
\g
|
|
|
|
-- Send to file
|
|
\g output.txt
|
|
```
|
|
|
|
### Variables
|
|
```bash
|
|
# Set variable
|
|
\set myvar 'value'
|
|
\set limit 10
|
|
|
|
# Use variable
|
|
SELECT * FROM users LIMIT :limit;
|
|
|
|
# String variable (quoted)
|
|
\set username 'alice'
|
|
SELECT * FROM users WHERE name = :'username';
|
|
|
|
# Show all variables
|
|
\set
|
|
|
|
# Unset variable
|
|
\unset myvar
|
|
```
|
|
|
|
### Scripts
|
|
```sql
|
|
-- script.sql
|
|
\set ON_ERROR_STOP on
|
|
|
|
BEGIN;
|
|
|
|
CREATE TABLE IF NOT EXISTS users (
|
|
id SERIAL PRIMARY KEY,
|
|
name TEXT NOT NULL,
|
|
email TEXT UNIQUE
|
|
);
|
|
|
|
INSERT INTO users (name, email) VALUES
|
|
('Alice', 'alice@example.com'),
|
|
('Bob', 'bob@example.com');
|
|
|
|
COMMIT;
|
|
|
|
\echo 'Script completed!'
|
|
```
|
|
|
|
```bash
|
|
# Execute script
|
|
psql -d mydb -f script.sql
|
|
|
|
# With error stopping
|
|
psql -d mydb -f script.sql --on-error-stop
|
|
|
|
# In single transaction
|
|
psql -d mydb -1 -f script.sql
|
|
```
|
|
|
|
## Data Import/Export
|
|
|
|
### COPY (Server-side)
|
|
```sql
|
|
-- Export to CSV
|
|
COPY users TO '/tmp/users.csv' WITH (FORMAT CSV, HEADER);
|
|
|
|
-- Import from CSV
|
|
COPY users FROM '/tmp/users.csv' WITH (FORMAT CSV, HEADER);
|
|
|
|
-- Query to file
|
|
COPY (SELECT * FROM users WHERE active = true)
|
|
TO '/tmp/active_users.csv' WITH (FORMAT CSV, HEADER);
|
|
```
|
|
|
|
### \copy (Client-side)
|
|
```bash
|
|
# Export (from psql)
|
|
\copy users TO 'users.csv' WITH (FORMAT CSV, HEADER)
|
|
|
|
# Export query results
|
|
\copy (SELECT * FROM users WHERE active = true) TO 'active.csv' CSV HEADER
|
|
|
|
# Import
|
|
\copy users FROM 'users.csv' WITH (FORMAT CSV, HEADER)
|
|
|
|
# To stdout
|
|
\copy users TO STDOUT CSV HEADER > users.csv
|
|
```
|
|
|
|
### pg_dump / pg_restore
|
|
```bash
|
|
# Dump database
|
|
pg_dump mydb > mydb.sql
|
|
pg_dump -d mydb -Fc > mydb.dump # Custom format
|
|
|
|
# Dump specific table
|
|
pg_dump -t users mydb > users.sql
|
|
|
|
# Schema only
|
|
pg_dump -s mydb > schema.sql
|
|
|
|
# Data only
|
|
pg_dump -a mydb > data.sql
|
|
|
|
# Restore
|
|
psql mydb < mydb.sql
|
|
pg_restore -d mydb mydb.dump
|
|
```
|
|
|
|
## Configuration
|
|
|
|
### ~/.psqlrc
|
|
```bash
|
|
# Auto-loaded on psql startup
|
|
\set QUIET ON
|
|
|
|
-- Prompt customization
|
|
\set PROMPT1 '%n@%m:%>/%/%R%# '
|
|
|
|
-- Output settings
|
|
\pset null '[NULL]'
|
|
\pset border 2
|
|
\pset linestyle unicode
|
|
\pset expanded auto
|
|
|
|
-- Timing
|
|
\timing ON
|
|
|
|
-- Pager
|
|
\pset pager always
|
|
|
|
-- History
|
|
\set HISTSIZE 10000
|
|
|
|
-- Custom shortcuts
|
|
\set active_users 'SELECT * FROM users WHERE status = ''active'';'
|
|
\set dbsize 'SELECT pg_size_pretty(pg_database_size(current_database()));'
|
|
|
|
\set QUIET OFF
|
|
```
|
|
|
|
### Useful Aliases
|
|
```bash
|
|
# Add to ~/.psqlrc
|
|
\set locks 'SELECT pid, usename, pg_blocking_pids(pid) as blocked_by, query FROM pg_stat_activity WHERE cardinality(pg_blocking_pids(pid)) > 0;'
|
|
|
|
\set activity 'SELECT pid, usename, state, query FROM pg_stat_activity WHERE state != ''idle'';'
|
|
|
|
\set table_sizes 'SELECT schemaname, tablename, pg_size_pretty(pg_total_relation_size(schemaname||''.''||tablename)) FROM pg_tables ORDER BY pg_total_relation_size(schemaname||''.''||tablename) DESC;'
|
|
|
|
\set index_usage 'SELECT schemaname, tablename, indexname, idx_scan FROM pg_stat_user_indexes ORDER BY idx_scan;'
|
|
|
|
# Usage: :locks, :activity, :table_sizes
|
|
```
|
|
|
|
## Transactions
|
|
|
|
```sql
|
|
-- Begin transaction
|
|
BEGIN;
|
|
|
|
-- Or
|
|
START TRANSACTION;
|
|
|
|
-- Savepoint
|
|
SAVEPOINT sp1;
|
|
|
|
-- Rollback to savepoint
|
|
ROLLBACK TO sp1;
|
|
|
|
-- Commit
|
|
COMMIT;
|
|
|
|
-- Rollback
|
|
ROLLBACK;
|
|
```
|
|
|
|
## Performance Analysis
|
|
|
|
### EXPLAIN
|
|
```sql
|
|
-- Show query plan
|
|
EXPLAIN SELECT * FROM users WHERE id = 1;
|
|
|
|
-- With execution
|
|
EXPLAIN ANALYZE SELECT * FROM users WHERE age > 18;
|
|
|
|
-- Verbose
|
|
EXPLAIN (ANALYZE, BUFFERS, VERBOSE)
|
|
SELECT * FROM users WHERE active = true;
|
|
```
|
|
|
|
### Current Activity
|
|
```sql
|
|
-- Active queries
|
|
SELECT pid, usename, state, query
|
|
FROM pg_stat_activity;
|
|
|
|
-- Long-running queries
|
|
SELECT pid, now() - query_start AS duration, query
|
|
FROM pg_stat_activity
|
|
WHERE state != 'idle'
|
|
ORDER BY duration DESC;
|
|
|
|
-- Blocking queries
|
|
SELECT blocked.pid, blocking.pid AS blocking_pid,
|
|
blocked.query AS blocked_query,
|
|
blocking.query AS blocking_query
|
|
FROM pg_stat_activity blocked
|
|
JOIN pg_stat_activity blocking
|
|
ON blocking.pid = ANY(pg_blocking_pids(blocked.pid));
|
|
```
|
|
|
|
### Statistics
|
|
```sql
|
|
-- Database size
|
|
SELECT pg_size_pretty(pg_database_size(current_database()));
|
|
|
|
-- Table sizes
|
|
SELECT schemaname, tablename,
|
|
pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) AS size
|
|
FROM pg_tables
|
|
ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC;
|
|
|
|
-- Index usage
|
|
SELECT schemaname, tablename, indexname, idx_scan
|
|
FROM pg_stat_user_indexes
|
|
ORDER BY idx_scan;
|
|
```
|
|
|
|
## User Management
|
|
|
|
```sql
|
|
-- Create user
|
|
CREATE USER appuser WITH PASSWORD 'secure_password';
|
|
|
|
-- Create superuser
|
|
CREATE USER admin WITH PASSWORD 'password' SUPERUSER;
|
|
|
|
-- Alter user
|
|
ALTER USER appuser WITH PASSWORD 'new_password';
|
|
|
|
-- Grant permissions
|
|
GRANT CONNECT ON DATABASE mydb TO appuser;
|
|
GRANT USAGE ON SCHEMA public TO appuser;
|
|
GRANT SELECT, INSERT, UPDATE, DELETE ON users TO appuser;
|
|
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO appuser;
|
|
|
|
-- Default privileges
|
|
ALTER DEFAULT PRIVILEGES IN SCHEMA public
|
|
GRANT SELECT ON TABLES TO appuser;
|
|
|
|
-- View permissions
|
|
\dp users
|
|
|
|
-- Drop user
|
|
DROP USER appuser;
|
|
```
|
|
|
|
## Backup Patterns
|
|
|
|
```bash
|
|
# Daily backup script
|
|
#!/bin/bash
|
|
DATE=$(date +%Y%m%d)
|
|
pg_dump -Fc mydb > /backups/mydb_$DATE.dump
|
|
|
|
# Restore latest
|
|
pg_restore -d mydb /backups/mydb_latest.dump
|
|
|
|
# Backup all databases
|
|
pg_dumpall > /backups/all_databases.sql
|
|
|
|
# Backup specific schema
|
|
pg_dump -n public mydb > public_schema.sql
|
|
```
|
|
|
|
## Troubleshooting
|
|
|
|
### Connection Issues
|
|
```bash
|
|
# Test connection
|
|
psql -h hostname -U username -d postgres -c "SELECT 1;"
|
|
|
|
# Check pg_hba.conf
|
|
# /var/lib/postgresql/data/pg_hba.conf
|
|
|
|
# Verbose connection
|
|
psql -h hostname -d mydb --echo-all
|
|
```
|
|
|
|
### Performance Issues
|
|
```sql
|
|
-- Enable slow query logging
|
|
ALTER DATABASE mydb SET log_min_duration_statement = 100;
|
|
|
|
-- 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;
|
|
|
|
-- Find slow queries
|
|
SELECT query, mean_exec_time, calls
|
|
FROM pg_stat_statements
|
|
ORDER BY mean_exec_time DESC
|
|
LIMIT 10;
|
|
```
|
|
|
|
## Best Practices
|
|
|
|
1. **Use .pgpass** for credential management
|
|
2. **Set ON_ERROR_STOP** in scripts
|
|
3. **Use transactions** for multi-statement changes
|
|
4. **Test with EXPLAIN** before running expensive queries
|
|
5. **Use \timing** to measure query performance
|
|
6. **Configure ~/.psqlrc** for productivity
|
|
7. **Use variables** for dynamic queries
|
|
8. **Log sessions** with -L for auditing
|
|
9. **Use \copy** instead of COPY for client operations
|
|
10. **Regular backups** with pg_dump
|