| 1 | CREATE TABLE events ( |
| 2 | id BIGSERIAL PRIMARY KEY, |
| 3 | source_id TEXT NOT NULL REFERENCES sources(id) ON DELETE RESTRICT, |
| 4 | source_event_id TEXT NOT NULL, |
| 5 | title TEXT NOT NULL CHECK (length(title) BETWEEN 1 AND 512), |
| 6 | subtitle TEXT, |
| 7 | category TEXT NOT NULL CHECK (category IN ('concert','sports','festival','theater','other')), |
| 8 | sport TEXT CHECK (sport IS NULL OR sport IN ('nfl','nba','nhl','mlb','tennis','f1','soccer','golf','boxing','other')), |
| 9 | starts_at TIMESTAMPTZ, |
| 10 | venue_name TEXT, |
| 11 | city TEXT, |
| 12 | country CHAR(2), |
| 13 | latitude DOUBLE PRECISION CHECK (latitude IS NULL OR (latitude BETWEEN -90 AND 90)), |
| 14 | longitude DOUBLE PRECISION CHECK (longitude IS NULL OR (longitude BETWEEN -180 AND 180)), |
| 15 | image_url TEXT, |
| 16 | on_sale_at TIMESTAMPTZ, |
| 17 | presale_on_sale_at TIMESTAMPTZ, |
| 18 | status TEXT NOT NULL CHECK (status IN ('announced','presale','onsale','soldout','cancelled')), |
| 19 | price_min INTEGER CHECK (price_min IS NULL OR price_min >= 0), |
| 20 | price_max INTEGER CHECK (price_max IS NULL OR price_max >= 0), |
| 21 | currency CHAR(3), |
| 22 | inventory_remaining INTEGER CHECK (inventory_remaining IS NULL OR inventory_remaining >= 0), |
| 23 | buy_url TEXT NOT NULL, |
| 24 | raw JSONB, |
| 25 | content_hash CHAR(64) NOT NULL, |
| 26 | first_seen_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), |
| 27 | updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), |
| 28 | UNIQUE (source_id, source_event_id), |
| 29 | CONSTRAINT price_range CHECK (price_min IS NULL OR price_max IS NULL OR price_min <= price_max) |
| 30 | ); |
| 31 | |
| 32 | CREATE INDEX idx_events_status ON events(status); |
| 33 | CREATE INDEX idx_events_on_sale ON events(on_sale_at) WHERE on_sale_at IS NOT NULL; |
| 34 | CREATE INDEX idx_events_category ON events(category); |
| 35 | CREATE INDEX idx_events_sport ON events(sport) WHERE sport IS NOT NULL; |
| 36 | CREATE INDEX idx_events_starts_at ON events(starts_at) WHERE starts_at IS NOT NULL; |
| 37 | CREATE INDEX idx_events_source_status ON events(source_id, status); |
| 38 | CREATE INDEX idx_events_updated_at ON events(updated_at DESC); |
| 39 | CREATE INDEX idx_events_title_trgm ON events USING gin (title gin_trgm_ops); |
| 40 | CREATE INDEX idx_events_starts_at_source ON events(starts_at, source_id); |
| 41 | |
| 42 | CREATE TRIGGER events_set_updated_at |
| 43 | BEFORE UPDATE ON events |
| 44 | FOR EACH ROW EXECUTE FUNCTION set_updated_at(); |