A quick reference guide for common PostgreSQL and TimescaleDB operations here so I don’t need to ask ChatGPT every time.

Creating a New User

-- Create a new user with password
CREATE USER myuser WITH PASSWORD 'securepassword';

-- Create a user with specific attributes
CREATE USER myuser WITH 
  PASSWORD 'securepassword'
  LOGIN
  CREATEDB
  VALID UNTIL '2027-12-31';

-- Create a superuser (use with caution)
CREATE USER admin_user WITH PASSWORD 'adminpass' SUPERUSER;

Granting User Permissions

Database Level Permissions

-- Grant all privileges on a database
GRANT ALL PRIVILEGES ON DATABASE mydb TO myuser;

-- Grant connect privilege
GRANT CONNECT ON DATABASE mydb TO myuser;

-- Grant specific privileges
GRANT CREATE ON DATABASE mydb TO myuser;

Schema Level Permissions

-- Grant all privileges on schema
GRANT ALL ON SCHEMA public TO myuser;

-- Grant usage on schema
GRANT USAGE ON SCHEMA public TO myuser;

-- Grant create on schema
GRANT CREATE ON SCHEMA public TO myuser;

Table Level Permissions

-- Grant all privileges on all tables in schema
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO myuser;

-- Grant specific privileges
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO myuser;

-- Grant on a specific table
GRANT SELECT, INSERT ON mytable TO myuser;

-- Set default privileges for future tables
ALTER DEFAULT PRIVILEGES IN SCHEMA public
  GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO myuser;

Sequence Permissions

-- Grant usage on all sequences (needed for auto-increment columns)
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO myuser;

-- Set default privileges for future sequences
ALTER DEFAULT PRIVILEGES IN SCHEMA public
  GRANT USAGE, SELECT ON SEQUENCES TO myuser;

Function Permissions

-- Grant execute on all functions
GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA public TO myuser;

-- Set default privileges for future functions
ALTER DEFAULT PRIVILEGES IN SCHEMA public
  GRANT EXECUTE ON FUNCTIONS TO myuser;

TimescaleDB Scheduled Jobs

Creating a Scheduled Job

-- Create a procedure to be scheduled
CREATE OR REPLACE PROCEDURE user_defined_action(job_id INT, config JSONB)
LANGUAGE PLPGSQL AS
$$
BEGIN
  RAISE NOTICE 'Executing action % with config %', job_id, config;
  -- Your custom logic here
END;
$$;

-- Schedule the job to run every hour
SELECT add_job('user_defined_action', '1h');

-- Schedule with flexible timing (next run = last finish + interval)
SELECT add_job('user_defined_action', '1h', fixed_schedule => false);

Job with Specific Start Time

-- Run at midnight every Sunday
SELECT add_job(
  'user_defined_action',
  '1 week',
  initial_start => '2026-01-19 00:00:00+00'::timestamptz
);

-- Schedule with timezone for DST handling
SELECT add_job(
  'user_defined_action',
  '1 week',
  initial_start => '2026-01-19 00:00:00+00'::timestamptz,
  timezone => 'Australia/Melbourne'
);

Job with Configuration

-- Create a job with custom configuration
SELECT add_job(
  'user_defined_action',
  '30 minutes',
  config => '{"retention_days": 90, "table_name": "metrics"}'::jsonb
);

Managing Jobs

-- View all scheduled jobs
SELECT * FROM timescaledb_information.jobs;

-- Manually run a job
CALL run_job(1000);

-- Alter a job schedule
SELECT alter_job(1000, schedule_interval => '2h');

-- Disable a job
SELECT alter_job(1000, scheduled => false);

-- Enable a job
SELECT alter_job(1000, scheduled => true);

-- Delete a job
SELECT delete_job(1000);

Practical Example: Data Retention Job

-- Create a procedure to delete old data
CREATE OR REPLACE PROCEDURE cleanup_old_data(job_id INT, config JSONB)
LANGUAGE PLPGSQL AS
$$
DECLARE
  retention_days INT;
BEGIN
  retention_days := (config->>'retention_days')::INT;
  
  DELETE FROM metrics
  WHERE time < NOW() - INTERVAL '1 day' * retention_days;
  
  RAISE NOTICE 'Cleaned up data older than % days', retention_days;
END;
$$;

-- Schedule cleanup to run daily at 2 AM
SELECT add_job(
  'cleanup_old_data',
  '1 day',
  initial_start => '2026-01-17 02:00:00+11'::timestamptz,
  config => '{"retention_days": 90}'::jsonb,
  timezone => 'Australia/Melbourne'
);

Common Permission Check Queries

-- Check user privileges on database
SELECT datname, grantee, privilege_type
FROM information_schema.database_privileges
WHERE grantee = 'myuser';

-- Check table privileges
SELECT table_schema, table_name, privilege_type
FROM information_schema.table_privileges
WHERE grantee = 'myuser';

-- List all users and their roles
SELECT usename, usesuper, usecreatedb, usecreaterole
FROM pg_user;

-- Check current user
SELECT current_user;

References

It's Hot in Melbourne!

- 1 min read

Melbourne just experienced a scorching day with temperatures peaking at 43 degrees Celsius (109°F). The hot winds brought sweltering conditions across the city, wreaking havoc and reminding everyone why summer in Australia is no joke.

While many people rush indoors to escape the heat, I actually find myself enjoying these extreme weather days – especially when I’m out riding my bike. There’s something memorable about pushing through the warm air, feeling the hot breeze against your face, and experiencing the city in its most intense summer state.