CREATE EXTENSION IF NOT EXISTS timescaledb CASCADE; CREATE TABLE IF NOT EXISTS forecast_cache ( lat NUMERIC(8, 4) NOT NULL, lon NUMERIC(8, 4) NOT NULL, data JSONB NOT NULL, fetched_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), PRIMARY KEY (lat, lon) ); CREATE TABLE IF NOT EXISTS observations_cache ( lat NUMERIC(8, 4) NOT NULL, lon NUMERIC(8, 4) NOT NULL, data JSONB NOT NULL, fetched_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), PRIMARY KEY (lat, lon) ); CREATE TABLE IF NOT EXISTS warnings_cache ( fetched_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), data JSONB NOT NULL ); CREATE TABLE IF NOT EXISTS geocode_cache ( query_hash VARCHAR(64) PRIMARY KEY, query_text TEXT NOT NULL, results JSONB NOT NULL, fetched_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS radar_file_meta ( id SERIAL PRIMARY KEY, product VARCHAR(16) NOT NULL, filename VARCHAR(255) NOT NULL UNIQUE, file_size BIGINT, modified_at TIMESTAMPTZ, render_status VARCHAR(24) NOT NULL DEFAULT 'pending', render_path TEXT, render_bounds JSONB, render_error TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_radar_file_meta_product ON radar_file_meta (product, modified_at DESC); CREATE INDEX IF NOT EXISTS idx_warnings_cache_fetched ON warnings_cache (fetched_at DESC); SELECT create_hypertable('warnings_cache', 'fetched_at', if_not_exists => TRUE, migrate_data => TRUE);