-- 001_init.sql — initial schema for x0gp catalog. -- -- Tables: -- tracks static track definitions (oval, figure-eight, etc.) -- track_waypoints racing line samples (1-to-many with tracks) -- track_tags many-to-many tags for tracks -- cars car definitions (chassis, motor, battery, etc.) -- -- TimescaleDB extension is enabled opportunistically — not required for the -- catalog itself; the telemetry hypertable will be added in a later phase -- (S2). We enable it here so that the extension is provisioned alongside -- the base schema. CREATE EXTENSION IF NOT EXISTS timescaledb; -- --------------------------------------------------------------------------- -- Tracks -- --------------------------------------------------------------------------- CREATE TABLE IF NOT EXISTS tracks ( id TEXT PRIMARY KEY, name TEXT NOT NULL, description TEXT NOT NULL DEFAULT '', author_id TEXT NOT NULL DEFAULT 'system', visibility TEXT NOT NULL DEFAULT 'system' CHECK (visibility IN ('system', 'public', 'private')), scale INT NOT NULL DEFAULT 27, length_m DOUBLE PRECISION NOT NULL DEFAULT 0, width_m DOUBLE PRECISION NOT NULL DEFAULT 0, lane_width_m DOUBLE PRECISION NOT NULL DEFAULT 0.20, surface TEXT NOT NULL DEFAULT 'carpet' CHECK (surface IN ('carpet', 'tile', 'wood', 'paper', 'mixed')), best_lap_ms BIGINT NOT NULL DEFAULT 0, best_lap_holder TEXT NOT NULL DEFAULT '', bounds_min_x DOUBLE PRECISION NOT NULL DEFAULT 0, bounds_min_y DOUBLE PRECISION NOT NULL DEFAULT 0, bounds_max_x DOUBLE PRECISION NOT NULL DEFAULT 0, bounds_max_y DOUBLE PRECISION NOT NULL DEFAULT 0, created_ms BIGINT NOT NULL, updated_ms BIGINT NOT NULL ); CREATE INDEX IF NOT EXISTS tracks_visibility_idx ON tracks (visibility); CREATE INDEX IF NOT EXISTS tracks_author_idx ON tracks (author_id); CREATE TABLE IF NOT EXISTS track_waypoints ( track_id TEXT NOT NULL REFERENCES tracks(id) ON DELETE CASCADE, seq INT NOT NULL, x DOUBLE PRECISION NOT NULL, y DOUBLE PRECISION NOT NULL, heading_rad DOUBLE PRECISION NOT NULL, speed_ms DOUBLE PRECISION NOT NULL, curvature DOUBLE PRECISION NOT NULL, PRIMARY KEY (track_id, seq) ); CREATE TABLE IF NOT EXISTS track_tags ( track_id TEXT NOT NULL REFERENCES tracks(id) ON DELETE CASCADE, tag TEXT NOT NULL, PRIMARY KEY (track_id, tag) ); CREATE INDEX IF NOT EXISTS track_tags_tag_idx ON track_tags (tag); -- --------------------------------------------------------------------------- -- Cars -- --------------------------------------------------------------------------- CREATE TABLE IF NOT EXISTS cars ( id TEXT PRIMARY KEY, name TEXT NOT NULL, owner_id TEXT NOT NULL DEFAULT 'system', visibility TEXT NOT NULL DEFAULT 'system' CHECK (visibility IN ('system', 'public', 'private')), scale INT NOT NULL DEFAULT 27, length_mm DOUBLE PRECISION NOT NULL, width_mm DOUBLE PRECISION NOT NULL, height_mm DOUBLE PRECISION NOT NULL DEFAULT 0, weight_g DOUBLE PRECISION NOT NULL DEFAULT 0, chassis_model TEXT NOT NULL DEFAULT '', chassis_material TEXT NOT NULL DEFAULT '', chassis_printed BOOLEAN NOT NULL DEFAULT FALSE, chassis_wheelbase_mm DOUBLE PRECISION NOT NULL DEFAULT 0, chassis_track_mm DOUBLE PRECISION NOT NULL DEFAULT 0, motor_kind TEXT NOT NULL DEFAULT 'brushed', motor_class TEXT NOT NULL DEFAULT '', motor_kv INT NOT NULL DEFAULT 0, motor_power_w DOUBLE PRECISION NOT NULL DEFAULT 0, battery_voltage_v DOUBLE PRECISION NOT NULL DEFAULT 0, battery_capacity_mah INT NOT NULL DEFAULT 0, battery_cells INT NOT NULL DEFAULT 0, battery_chemistry TEXT NOT NULL DEFAULT 'li-po', drive TEXT NOT NULL DEFAULT '2WD' CHECK (drive IN ('2WD', '4WD')), top_speed_ms DOUBLE PRECISION NOT NULL DEFAULT 0, color_hex TEXT NOT NULL DEFAULT '#ff6b1a', avatar_url TEXT NOT NULL DEFAULT '', active BOOLEAN NOT NULL DEFAULT TRUE, total_distance_m DOUBLE PRECISION NOT NULL DEFAULT 0, total_races INT NOT NULL DEFAULT 0, total_laps INT NOT NULL DEFAULT 0, best_lap_ms BIGINT NOT NULL DEFAULT 0, created_ms BIGINT NOT NULL, updated_ms BIGINT NOT NULL ); CREATE INDEX IF NOT EXISTS cars_visibility_idx ON cars (visibility); CREATE INDEX IF NOT EXISTS cars_owner_idx ON cars (owner_id);