feat(server): initial implementation

This commit captures the full server code accumulated across
several development sessions. It includes the following layers,
applied in roughly this order during development:

1. Base infrastructure
   - cmd/poc-server HTTP+WebSocket server, /health, /stats
   - internal/transport wire types (Envelope + all message types)
   - internal/catalog, internal/realtime, internal/stats
   - internal/storage/postgres with migrations 001-004
   - .env, .env.example, docker-compose, Dockerfile, scripts/

2. Swagger documentation
   - go get github.com/swaggo/http-swagger
   - swag init produces docs/docs.go
   - /swagger/index.html UI, /swagger/doc.json spec
   - annotations on health/stats/ws and catalog handlers

3. Races API
   - internal/races/{store,service,keyset,types}.go
   - migration 005_races.sql: finished_races, race_plans, race_queue
   - GET /api/races with keyset pagination, status filter
   - GET /api/races/upcoming
   - POST /api/races/queue/join, GET, DELETE
   - POST/GET /api/races/plans, DELETE /{id}
   - lobby.RaceMeta simplification (no host_id/host_name)

4. Races seeder
   - internal/races/seed with deterministic generator
   - --seed-races / --reset CLI flags in main.go
   - 30 finished + 5 live + 5 plans + 4 queue entries

5. Drivers and clans
   - internal/drivers, internal/clans (CRUD, validation)
   - migration 008_drivers_clans.sql
   - /api/clans and /api/drivers endpoints
   - 3-letter uppercase nickname/tag validation
   - lobby.DriverMeta: Nickname, AvatarURL, ClanID, ClanTag

6. Podium
   - internal/transport.RacePodiumEntry
   - lobby.SetDriverProfile for in-memory metadata sync
   - migration 007_podium.sql (podium JSONB column)
   - seeder populates top-3 per finished race

7. Live races persistence
   - migration 009_live_persistence.sql: live_races,
     live_race_drivers, lobby_drivers
   - internal/races/live_store.go: LiveStore with write-side
     mirror for lobby.Service mutations
   - Service.collectLive and Upcoming read from Postgres
   - main.go RestoreFromDB rehydrates lobby on startup

8. Unify live + finished into one races table
   - migration 010_unify_races.sql: rename finished_races to
     races, expand status CHECK, merge live rows
   - PgStore now hosts both write paths; LiveStore is a
     thin facade implementing lobby.Persistence
   - seeder resetAll drops only finished/cancelled rows and
     race_plans / race_queue / lobby_drivers

Each layer is consistent on its own; cross-layer changes are
visible in the file history. A future refactor may split this
commit into the per-stage boundaries listed above.
This commit is contained in:
x0gp
2026-06-22 22:01:09 +04:00
commit 978d36c505
71 changed files with 23500 additions and 0 deletions
@@ -0,0 +1,116 @@
-- 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);
@@ -0,0 +1,561 @@
-- 002_seed.sql — initial system catalog (tracks + cars).
-- Generated from internal/catalog/seeddefs via scripts/genseed.
-- Idempotent: safe to re-apply (ON CONFLICT DO NOTHING).
INSERT INTO tracks (id, name, description, author_id, visibility, scale,
length_m, width_m, lane_width_m, surface, best_lap_ms, best_lap_holder,
bounds_min_x, bounds_min_y, bounds_max_x, bounds_max_y,
created_ms, updated_ms)
VALUES ('monaco', 'Monaco', 'Circuit de Monaco. Narrow street circuit, tight hairpins, no room for error.', 'system', 'system', 27,
3.5500000000000003, 1.1, 0.18, 'tile', 0, 'Verstappen',
0.4, 1.4, 3.95, 2.5,
1700000000000, 1700000000000)
ON CONFLICT (id) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('monaco', 0, 0.4, 2.2, 0, 3.2, 0)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('monaco', 1, 1.1, 2.2, 0, 3, 0)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('monaco', 2, 1.5, 2.05, -0.4, 2.4, 1.2)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('monaco', 3, 1.7, 1.85, -0.9, 2.2, 1.8)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('monaco', 4, 1.55, 1.55, -1.6, 2, 1.4)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('monaco', 5, 1.25, 1.4, -2.4, 2.1, 1.2)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('monaco', 6, 0.95, 1.55, 3.141592653589793, 1.9, 1.6)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('monaco', 7, 0.75, 1.85, 2, 1.8, 2.2)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('monaco', 8, 0.95, 2.15, 2.6, 1.8, 2.6)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('monaco', 9, 1.2, 2.4, -2.8, 2.1, 1.4)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('monaco', 10, 1.6, 2.5, -3, 2.4, 1)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('monaco', 11, 2.6, 2.5, 0, 3.2, 0)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('monaco', 12, 3.4, 2.5, 0, 3.2, 0)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('monaco', 13, 3.6, 2.25, -0.5, 2.6, 1.5)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('monaco', 14, 3.55, 1.95, 0.4, 2.6, 1.5)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('monaco', 15, 3.3, 1.75, 1, 2.3, 1.4)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('monaco', 16, 2.95, 1.55, 1.7, 2.1, 1.8)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('monaco', 17, 2.6, 1.65, 2.6, 2.1, 1.8)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('monaco', 18, 2.4, 1.9, -2.7, 2.3, 1.4)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('monaco', 19, 2.4, 2.2, -3, 2.2, 1.6)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('monaco', 20, 2.6, 2.4, 2.8, 2.5, 1)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('monaco', 21, 3.2, 2.4, 0, 3.2, 0)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('monaco', 22, 3.7, 2.4, 0, 3.2, 0)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('monaco', 23, 3.95, 2.3, -0.3, 2.8, 0.8)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_tags (track_id, tag) VALUES ('monaco', 'f1')
ON CONFLICT DO NOTHING;
INSERT INTO track_tags (track_id, tag) VALUES ('monaco', 'street')
ON CONFLICT DO NOTHING;
INSERT INTO track_tags (track_id, tag) VALUES ('monaco', 'tight')
ON CONFLICT DO NOTHING;
INSERT INTO track_tags (track_id, tag) VALUES ('monaco', 'slow')
ON CONFLICT DO NOTHING;
INSERT INTO track_tags (track_id, tag) VALUES ('monaco', 'monaco')
ON CONFLICT DO NOTHING;
INSERT INTO tracks (id, name, description, author_id, visibility, scale,
length_m, width_m, lane_width_m, surface, best_lap_ms, best_lap_holder,
bounds_min_x, bounds_min_y, bounds_max_x, bounds_max_y,
created_ms, updated_ms)
VALUES ('barcelona-catalunya', 'Barcelona-Catalunya', 'Circuit de Barcelona-Catalunya. Mix of high-speed corners and technical chicanes.', 'system', 'system', 27,
3.75, 2.3000000000000003, 0.18, 'tile', 0, 'Verstappen',
0.3, 0.4, 4.05, 2.7,
1700000000000, 1700000000000)
ON CONFLICT (id) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 0, 0.3, 0.4, 0, 4.5, 0)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 1, 1.4, 0.4, 0, 4.5, 0)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 2, 1.7, 0.65, 1.5707963267948966, 2.2, 1.6)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 3, 1.5, 0.95, 3.141592653589793, 2, 2)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 4, 1.2, 1.1, 3.4415926535897934, 2.6, 1)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 5, 0.95, 1, 4.0415926535897935, 2.6, 1)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 6, 0.8, 1.2, -1.5707963267948966, 2.4, 1.2)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 7, 1.05, 1.5, 0, 3.2, 0.7)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 8, 1.3, 1.65, 1.5707963267948966, 2.4, 1)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 9, 1.15, 1.85, 3.141592653589793, 2.4, 1)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 10, 1.4, 2, 2.641592653589793, 3, 0.7)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 11, 1.7, 2, 2.141592653589793, 3, 0.7)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 12, 1.95, 2.2, 1.5707963267948966, 2.8, 0.5)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 13, 1.65, 2.5, 0.3, 2.8, 0.5)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 14, 2.2, 2.7, 0.7, 3.2, 0.5)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 15, 2.8, 2.7, 1.1, 3.4, 0.5)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 16, 3.1, 2.5, 1.9707963267948967, 2.2, 1.4)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 17, 2.95, 2.2, 3.541592653589793, 2.2, 1.4)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 18, 3.3, 1.9, 0, 4, 0)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 19, 4, 1.9, 0, 4, 0)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 20, 4.05, 1.6, -1.5707963267948966, 2.4, 1.4)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 21, 3.8, 1.4, 3.141592653589793, 2.4, 1.4)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 22, 3.5, 1.5, 3.9269908169872414, 2.6, 1)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 23, 3.1, 1.25, -1.9707963267948967, 3, 0.7)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 24, 2.7, 1, -2.4707963267948965, 3, 0.7)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 25, 2.3, 0.7, 3.541592653589793, 3.4, 0.5)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 26, 1.6, 0.5, 3.3415926535897933, 3.6, 0.4)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 27, 0.9, 0.42, 0, 4, 0)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_tags (track_id, tag) VALUES ('barcelona-catalunya', 'f1')
ON CONFLICT DO NOTHING;
INSERT INTO track_tags (track_id, tag) VALUES ('barcelona-catalunya', 'balanced')
ON CONFLICT DO NOTHING;
INSERT INTO track_tags (track_id, tag) VALUES ('barcelona-catalunya', 'testing')
ON CONFLICT DO NOTHING;
INSERT INTO track_tags (track_id, tag) VALUES ('barcelona-catalunya', 'barcelona')
ON CONFLICT DO NOTHING;
INSERT INTO tracks (id, name, description, author_id, visibility, scale,
length_m, width_m, lane_width_m, surface, best_lap_ms, best_lap_holder,
bounds_min_x, bounds_min_y, bounds_max_x, bounds_max_y,
created_ms, updated_ms)
VALUES ('austria-redbull-ring', 'Austria (Red Bull Ring)', 'Red Bull Ring. Short, fast, three straights separated by hairpins.', 'system', 'system', 27,
3.5500000000000003, 2, 0.18, 'wood', 0, 'Verstappen',
0.3, 0.4, 3.85, 2.4,
1700000000000, 1700000000000)
ON CONFLICT (id) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('austria-redbull-ring', 0, 0.3, 0.4, 0, 4.8, 0)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('austria-redbull-ring', 1, 1.6, 0.4, 0, 4.8, 0)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('austria-redbull-ring', 2, 1.85, 0.65, 1.5707963267948966, 2, 1.8)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('austria-redbull-ring', 3, 1.6, 0.95, 3.141592653589793, 2, 1.8)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('austria-redbull-ring', 4, 1.3, 1, 3.641592653589793, 3, 0.7)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('austria-redbull-ring', 5, 1.05, 1.2, -1.5707963267948966, 2.6, 1)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('austria-redbull-ring', 6, 1.4, 1.4, 0, 3.2, 0.5)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('austria-redbull-ring', 7, 1.85, 1.4, 1.7707963267948965, 3.2, 0.5)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('austria-redbull-ring', 8, 2.1, 1.65, 3.141592653589793, 3, 0.7)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('austria-redbull-ring', 9, 2, 1.85, -1.5707963267948966, 3, 0.7)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('austria-redbull-ring', 10, 2.25, 2.05, 0, 2, 1.8)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('austria-redbull-ring', 11, 2.4, 2, 1.5707963267948966, 2, 1.8)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('austria-redbull-ring', 12, 2.4, 2.2, 3.141592653589793, 2, 1.8)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('austria-redbull-ring', 13, 2.2, 2.3, -1.5707963267948966, 2, 1.8)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('austria-redbull-ring', 14, 2.55, 2.4, -0.2, 3.4, 0.4)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('austria-redbull-ring', 15, 3, 2.4, 0, 4, 0)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('austria-redbull-ring', 16, 3.5, 2.4, 0, 4.5, 0)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('austria-redbull-ring', 17, 3.85, 2.15, 1.7707963267948965, 3.4, 0.6)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('austria-redbull-ring', 18, 3.6, 1.8, 3.141592653589793, 3, 0.8)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('austria-redbull-ring', 19, 3.25, 1.85, 3.741592653589793, 3, 0.8)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('austria-redbull-ring', 20, 3, 1.6, -1.9707963267948967, 3.2, 0.6)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('austria-redbull-ring', 21, 2.6, 1.4, -3.141592653589793, 3.4, 0.5)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('austria-redbull-ring', 22, 2, 1.2, 2.641592653589793, 3.6, 0.4)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('austria-redbull-ring', 23, 1.4, 0.8, 2.9415926535897934, 3.8, 0.3)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('austria-redbull-ring', 24, 0.8, 0.5, 0, 4.2, 0.2)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_tags (track_id, tag) VALUES ('austria-redbull-ring', 'f1')
ON CONFLICT DO NOTHING;
INSERT INTO track_tags (track_id, tag) VALUES ('austria-redbull-ring', 'short')
ON CONFLICT DO NOTHING;
INSERT INTO track_tags (track_id, tag) VALUES ('austria-redbull-ring', 'fast')
ON CONFLICT DO NOTHING;
INSERT INTO track_tags (track_id, tag) VALUES ('austria-redbull-ring', 'austria')
ON CONFLICT DO NOTHING;
INSERT INTO tracks (id, name, description, author_id, visibility, scale,
length_m, width_m, lane_width_m, surface, best_lap_ms, best_lap_holder,
bounds_min_x, bounds_min_y, bounds_max_x, bounds_max_y,
created_ms, updated_ms)
VALUES ('great-britain-silverstone', 'Great Britain (Silverstone)', 'Silverstone Circuit. Fast, flowing corners; home of British motorsport.', 'system', 'system', 27,
3.5500000000000003, 1.2499999999999998, 0.18, 'carpet', 0, 'Hamilton',
0.3, 1.8, 3.85, 3.05,
1700000000000, 1700000000000)
ON CONFLICT (id) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('great-britain-silverstone', 0, 0.3, 2.8, 0, 5, 0)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('great-britain-silverstone', 1, 2.4, 2.8, 0, 5, 0)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('great-britain-silverstone', 2, 2.8, 2.65, -1.5707963267948966, 3, 0.8)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('great-britain-silverstone', 3, 2.6, 2.3, -3.141592653589793, 3.4, 0.6)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('great-britain-silverstone', 4, 2.95, 2.05, -2.0707963267948966, 3.2, 0.7)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('great-britain-silverstone', 5, 3.3, 1.8, 0, 2.2, 1.6)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('great-britain-silverstone', 6, 3.6, 1.95, 1.5707963267948966, 2.2, 1.6)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('great-britain-silverstone', 7, 3.6, 2.3, 3.141592653589793, 3, 0.7)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('great-britain-silverstone', 8, 3.85, 2.55, 1.5707963267948966, 2.6, 1)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('great-britain-silverstone', 9, 3.55, 2.85, 0, 2.4, 1)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('great-britain-silverstone', 10, 3.25, 2.85, 1.5707963267948966, 2.4, 1)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('great-britain-silverstone', 11, 3.05, 2.55, -1.8707963267948966, 3.4, 0.5)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('great-britain-silverstone', 12, 2.85, 2.65, -3.141592653589793, 4, 0.3)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('great-britain-silverstone', 13, 1.8, 2.8, 0, 4.6, 0)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('great-britain-silverstone', 14, 1.2, 2.95, 1.7707963267948965, 3.8, 0.4)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('great-britain-silverstone', 15, 1, 2.65, 3.541592653589793, 3.6, 0.5)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('great-britain-silverstone', 16, 0.7, 2.4, -1.8707963267948966, 2.4, 1.2)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('great-britain-silverstone', 17, 0.5, 2.6, 0, 2.6, 0.9)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('great-britain-silverstone', 18, 0.7, 2.85, 1.5707963267948966, 3, 0.7)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('great-britain-silverstone', 19, 0.45, 3.05, 3.541592653589793, 2.6, 1)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('great-britain-silverstone', 20, 0.7, 2.5, -1.1707963267948966, 2.4, 1)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('great-britain-silverstone', 21, 0.5, 2.05, 3.541592653589793, 2.6, 0.8)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('great-britain-silverstone', 22, 0.3, 2.4, 1.5707963267948966, 2.4, 1)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_tags (track_id, tag) VALUES ('great-britain-silverstone', 'f1')
ON CONFLICT DO NOTHING;
INSERT INTO track_tags (track_id, tag) VALUES ('great-britain-silverstone', 'fast')
ON CONFLICT DO NOTHING;
INSERT INTO track_tags (track_id, tag) VALUES ('great-britain-silverstone', 'flowing')
ON CONFLICT DO NOTHING;
INSERT INTO track_tags (track_id, tag) VALUES ('great-britain-silverstone', 'silverstone')
ON CONFLICT DO NOTHING;
INSERT INTO tracks (id, name, description, author_id, visibility, scale,
length_m, width_m, lane_width_m, surface, best_lap_ms, best_lap_holder,
bounds_min_x, bounds_min_y, bounds_max_x, bounds_max_y,
created_ms, updated_ms)
VALUES ('belgium-spa', 'Belgium (Spa-Francorchamps)', 'Spa-Francorchamps. Long, fast, legendary Eau Rouge / Raidillon.', 'system', 'system', 27,
3.85, 2.25, 0.18, 'carpet', 0, 'Verstappen',
0.4, 0.5, 4.25, 2.75,
1700000000000, 1700000000000)
ON CONFLICT (id) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('belgium-spa', 0, 0.4, 0.5, 0, 4.8, 0)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('belgium-spa', 1, 1.6, 0.5, 0, 4.8, 0)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('belgium-spa', 2, 1.95, 0.6, 0.3, 4.2, 0.5)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('belgium-spa', 3, 2.2, 0.95, 0.9, 4, 0.6)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('belgium-spa', 4, 2.1, 1.3, 3.541592653589793, 4, 0.6)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('belgium-spa', 5, 2.7, 1.5, 0, 5, 0)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('belgium-spa', 6, 3.5, 1.5, 0, 5, 0)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('belgium-spa', 7, 3.85, 1.3, -1.5707963267948966, 2.4, 1.4)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('belgium-spa', 8, 3.6, 1.1, 3.4415926535897934, 2.6, 1.2)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('belgium-spa', 9, 3.3, 1.25, 4.0415926535897935, 2.6, 1.2)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('belgium-spa', 10, 3.5, 1.55, 1.5707963267948966, 3.6, 0.4)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('belgium-spa', 11, 3.25, 1.85, 0, 2.2, 1.6)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('belgium-spa', 12, 3.05, 1.7, -1.5707963267948966, 2.4, 1.4)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('belgium-spa', 13, 3.25, 1.5, 0, 3, 0.7)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('belgium-spa', 14, 3.7, 1.65, 1.8707963267948966, 3.4, 0.5)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('belgium-spa', 15, 4.1, 2, 2.1707963267948966, 4, 0.3)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('belgium-spa', 16, 4.25, 2.4, 3.541592653589793, 4.6, 0.3)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('belgium-spa', 17, 4, 2.7, -1.8707963267948966, 2.4, 1.4)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('belgium-spa', 18, 3.7, 2.55, 3.141592653589793, 2.4, 1.4)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('belgium-spa', 19, 3.55, 2.75, 3.8415926535897933, 2.4, 1.4)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('belgium-spa', 20, 3.05, 2.7, -1.5707963267948966, 2, 1.8)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('belgium-spa', 21, 2.85, 2.45, 0, 2, 1.8)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('belgium-spa', 22, 2, 2.2, 2.8415926535897933, 3.6, 0.3)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('belgium-spa', 23, 1.2, 1.6, 2.541592653589793, 4, 0.2)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('belgium-spa', 24, 0.6, 1.05, 2.8415926535897933, 4.4, 0.2)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_tags (track_id, tag) VALUES ('belgium-spa', 'f1')
ON CONFLICT DO NOTHING;
INSERT INTO track_tags (track_id, tag) VALUES ('belgium-spa', 'long')
ON CONFLICT DO NOTHING;
INSERT INTO track_tags (track_id, tag) VALUES ('belgium-spa', 'fast')
ON CONFLICT DO NOTHING;
INSERT INTO track_tags (track_id, tag) VALUES ('belgium-spa', 'spa')
ON CONFLICT DO NOTHING;
INSERT INTO cars (id, name, owner_id, visibility, scale,
length_mm, width_mm, height_mm, weight_g,
chassis_model, chassis_material, chassis_printed, chassis_wheelbase_mm, chassis_track_mm,
motor_kind, motor_class, motor_kv, motor_power_w,
battery_voltage_v, battery_capacity_mah, battery_cells, battery_chemistry,
drive, top_speed_ms, color_hex, avatar_url, active,
created_ms, updated_ms)
VALUES ('f1-2024-redbull-rb20', 'Oracle Red Bull Racing RB20', 'system', 'system', 24,
238, 83, 40, 33,
'RB20 carbon monocoque', 'carbon-fibre', FALSE, 150, 75,
'turbo-hybrid', 'F1 PU', 0, 746000,
0, 0, 0, 'li-ion MGU-K',
'2WD', 95, '#1e3a8a', '', TRUE,
1700000000000, 1700000000000)
ON CONFLICT (id) DO NOTHING;
INSERT INTO cars (id, name, owner_id, visibility, scale,
length_mm, width_mm, height_mm, weight_g,
chassis_model, chassis_material, chassis_printed, chassis_wheelbase_mm, chassis_track_mm,
motor_kind, motor_class, motor_kv, motor_power_w,
battery_voltage_v, battery_capacity_mah, battery_cells, battery_chemistry,
drive, top_speed_ms, color_hex, avatar_url, active,
created_ms, updated_ms)
VALUES ('f1-2024-ferrari-sf24', 'Scuderia Ferrari SF-24', 'system', 'system', 24,
238, 83, 40, 33,
'SF-24 carbon monocoque', 'carbon-fibre', FALSE, 150, 75,
'turbo-hybrid', 'F1 PU 066/12', 0, 746000,
0, 0, 0, 'li-ion MGU-K',
'2WD', 94, '#dc2626', '', TRUE,
1700000000000, 1700000000000)
ON CONFLICT (id) DO NOTHING;
INSERT INTO cars (id, name, owner_id, visibility, scale,
length_mm, width_mm, height_mm, weight_g,
chassis_model, chassis_material, chassis_printed, chassis_wheelbase_mm, chassis_track_mm,
motor_kind, motor_class, motor_kv, motor_power_w,
battery_voltage_v, battery_capacity_mah, battery_cells, battery_chemistry,
drive, top_speed_ms, color_hex, avatar_url, active,
created_ms, updated_ms)
VALUES ('f1-2024-mclaren-mcl38', 'McLaren MCL38', 'system', 'system', 24,
238, 83, 40, 33,
'MCL38 carbon monocoque', 'carbon-fibre', FALSE, 150, 75,
'turbo-hybrid', 'F1 PU Mercedes-derived', 0, 746000,
0, 0, 0, 'li-ion MGU-K',
'2WD', 94, '#ea580c', '', TRUE,
1700000000000, 1700000000000)
ON CONFLICT (id) DO NOTHING;
INSERT INTO cars (id, name, owner_id, visibility, scale,
length_mm, width_mm, height_mm, weight_g,
chassis_model, chassis_material, chassis_printed, chassis_wheelbase_mm, chassis_track_mm,
motor_kind, motor_class, motor_kv, motor_power_w,
battery_voltage_v, battery_capacity_mah, battery_cells, battery_chemistry,
drive, top_speed_ms, color_hex, avatar_url, active,
created_ms, updated_ms)
VALUES ('f1-2024-mercedes-w15', 'Mercedes-AMG W15', 'system', 'system', 24,
238, 83, 40, 33,
'W15 carbon monocoque', 'carbon-fibre', FALSE, 150, 75,
'turbo-hybrid', 'F1 PU M15', 0, 746000,
0, 0, 0, 'li-ion MGU-K',
'2WD', 94, '#06b6d4', '', TRUE,
1700000000000, 1700000000000)
ON CONFLICT (id) DO NOTHING;
INSERT INTO cars (id, name, owner_id, visibility, scale,
length_mm, width_mm, height_mm, weight_g,
chassis_model, chassis_material, chassis_printed, chassis_wheelbase_mm, chassis_track_mm,
motor_kind, motor_class, motor_kv, motor_power_w,
battery_voltage_v, battery_capacity_mah, battery_cells, battery_chemistry,
drive, top_speed_ms, color_hex, avatar_url, active,
created_ms, updated_ms)
VALUES ('f1-2024-astonmartin-amr24', 'Aston Martin Aramco AMR24', 'system', 'system', 24,
238, 83, 40, 33,
'AMR24 carbon monocoque', 'carbon-fibre', FALSE, 150, 75,
'turbo-hybrid', 'F1 PU Mercedes-derived', 0, 746000,
0, 0, 0, 'li-ion MGU-K',
'2WD', 94, '#16a34a', '', TRUE,
1700000000000, 1700000000000)
ON CONFLICT (id) DO NOTHING;
@@ -0,0 +1,588 @@
-- 003_f1_seed.sql — replace legacy PoC seed (oval, figure-8, sprint-straight
-- and 1/27 toy cars) with the F1 2024 catalog (5 tracks + 5 cars).
-- Idempotent: drops the old system rows before re-inserting.
DELETE FROM cars WHERE id IN ('compact-1-27-touring', 'compact-1-27-formula');
DELETE FROM tracks WHERE id IN ('oval-classic', 'figure-eight', 'sprint-straight');
-- 002_seed.sql — initial system catalog (tracks + cars).
-- Generated from internal/catalog/seeddefs via scripts/genseed.
-- Idempotent: safe to re-apply (ON CONFLICT DO NOTHING).
INSERT INTO tracks (id, name, description, author_id, visibility, scale,
length_m, width_m, lane_width_m, surface, best_lap_ms, best_lap_holder,
bounds_min_x, bounds_min_y, bounds_max_x, bounds_max_y,
created_ms, updated_ms)
VALUES ('monaco', 'Monaco', 'Circuit de Monaco. Narrow street circuit, tight hairpins, no room for error.', 'system', 'system', 27,
3.5500000000000003, 1.1, 0.18, 'tile', 0, 'Verstappen',
0.4, 1.4, 3.95, 2.5,
1700000000000, 1700000000000)
ON CONFLICT (id) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('monaco', 0, 0.4, 2.2, 0, 3.2, 0)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('monaco', 1, 1.1, 2.2, 0, 3, 0)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('monaco', 2, 1.5, 2.05, -0.4, 2.4, 1.2)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('monaco', 3, 1.7, 1.85, -0.9, 2.2, 1.8)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('monaco', 4, 1.55, 1.55, -1.6, 2, 1.4)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('monaco', 5, 1.25, 1.4, -2.4, 2.1, 1.2)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('monaco', 6, 0.95, 1.55, 3.141592653589793, 1.9, 1.6)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('monaco', 7, 0.75, 1.85, 2, 1.8, 2.2)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('monaco', 8, 0.95, 2.15, 2.6, 1.8, 2.6)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('monaco', 9, 1.2, 2.4, -2.8, 2.1, 1.4)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('monaco', 10, 1.6, 2.5, -3, 2.4, 1)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('monaco', 11, 2.6, 2.5, 0, 3.2, 0)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('monaco', 12, 3.4, 2.5, 0, 3.2, 0)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('monaco', 13, 3.6, 2.25, -0.5, 2.6, 1.5)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('monaco', 14, 3.55, 1.95, 0.4, 2.6, 1.5)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('monaco', 15, 3.3, 1.75, 1, 2.3, 1.4)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('monaco', 16, 2.95, 1.55, 1.7, 2.1, 1.8)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('monaco', 17, 2.6, 1.65, 2.6, 2.1, 1.8)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('monaco', 18, 2.4, 1.9, -2.7, 2.3, 1.4)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('monaco', 19, 2.4, 2.2, -3, 2.2, 1.6)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('monaco', 20, 2.6, 2.4, 2.8, 2.5, 1)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('monaco', 21, 3.2, 2.4, 0, 3.2, 0)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('monaco', 22, 3.7, 2.4, 0, 3.2, 0)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('monaco', 23, 3.95, 2.3, -0.3, 2.8, 0.8)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_tags (track_id, tag) VALUES ('monaco', 'f1')
ON CONFLICT DO NOTHING;
INSERT INTO track_tags (track_id, tag) VALUES ('monaco', 'street')
ON CONFLICT DO NOTHING;
INSERT INTO track_tags (track_id, tag) VALUES ('monaco', 'tight')
ON CONFLICT DO NOTHING;
INSERT INTO track_tags (track_id, tag) VALUES ('monaco', 'slow')
ON CONFLICT DO NOTHING;
INSERT INTO track_tags (track_id, tag) VALUES ('monaco', 'monaco')
ON CONFLICT DO NOTHING;
INSERT INTO tracks (id, name, description, author_id, visibility, scale,
length_m, width_m, lane_width_m, surface, best_lap_ms, best_lap_holder,
bounds_min_x, bounds_min_y, bounds_max_x, bounds_max_y,
created_ms, updated_ms)
VALUES ('barcelona-catalunya', 'Barcelona-Catalunya', 'Circuit de Barcelona-Catalunya. Mix of high-speed corners and technical chicanes.', 'system', 'system', 27,
3.75, 2.3000000000000003, 0.18, 'tile', 0, 'Verstappen',
0.3, 0.4, 4.05, 2.7,
1700000000000, 1700000000000)
ON CONFLICT (id) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 0, 0.3, 0.4, 0, 4.5, 0)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 1, 1.4, 0.4, 0, 4.5, 0)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 2, 1.7, 0.65, 1.5707963267948966, 2.2, 1.6)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 3, 1.5, 0.95, 3.141592653589793, 2, 2)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 4, 1.2, 1.1, 3.4415926535897934, 2.6, 1)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 5, 0.95, 1, 4.0415926535897935, 2.6, 1)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 6, 0.8, 1.2, -1.5707963267948966, 2.4, 1.2)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 7, 1.05, 1.5, 0, 3.2, 0.7)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 8, 1.3, 1.65, 1.5707963267948966, 2.4, 1)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 9, 1.15, 1.85, 3.141592653589793, 2.4, 1)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 10, 1.4, 2, 2.641592653589793, 3, 0.7)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 11, 1.7, 2, 2.141592653589793, 3, 0.7)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 12, 1.95, 2.2, 1.5707963267948966, 2.8, 0.5)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 13, 1.65, 2.5, 0.3, 2.8, 0.5)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 14, 2.2, 2.7, 0.7, 3.2, 0.5)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 15, 2.8, 2.7, 1.1, 3.4, 0.5)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 16, 3.1, 2.5, 1.9707963267948967, 2.2, 1.4)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 17, 2.95, 2.2, 3.541592653589793, 2.2, 1.4)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 18, 3.3, 1.9, 0, 4, 0)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 19, 4, 1.9, 0, 4, 0)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 20, 4.05, 1.6, -1.5707963267948966, 2.4, 1.4)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 21, 3.8, 1.4, 3.141592653589793, 2.4, 1.4)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 22, 3.5, 1.5, 3.9269908169872414, 2.6, 1)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 23, 3.1, 1.25, -1.9707963267948967, 3, 0.7)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 24, 2.7, 1, -2.4707963267948965, 3, 0.7)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 25, 2.3, 0.7, 3.541592653589793, 3.4, 0.5)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 26, 1.6, 0.5, 3.3415926535897933, 3.6, 0.4)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('barcelona-catalunya', 27, 0.9, 0.42, 0, 4, 0)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_tags (track_id, tag) VALUES ('barcelona-catalunya', 'f1')
ON CONFLICT DO NOTHING;
INSERT INTO track_tags (track_id, tag) VALUES ('barcelona-catalunya', 'balanced')
ON CONFLICT DO NOTHING;
INSERT INTO track_tags (track_id, tag) VALUES ('barcelona-catalunya', 'testing')
ON CONFLICT DO NOTHING;
INSERT INTO track_tags (track_id, tag) VALUES ('barcelona-catalunya', 'barcelona')
ON CONFLICT DO NOTHING;
INSERT INTO tracks (id, name, description, author_id, visibility, scale,
length_m, width_m, lane_width_m, surface, best_lap_ms, best_lap_holder,
bounds_min_x, bounds_min_y, bounds_max_x, bounds_max_y,
created_ms, updated_ms)
VALUES ('austria-redbull-ring', 'Austria (Red Bull Ring)', 'Red Bull Ring. Short, fast, three straights separated by hairpins.', 'system', 'system', 27,
3.5500000000000003, 2, 0.18, 'wood', 0, 'Verstappen',
0.3, 0.4, 3.85, 2.4,
1700000000000, 1700000000000)
ON CONFLICT (id) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('austria-redbull-ring', 0, 0.3, 0.4, 0, 4.8, 0)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('austria-redbull-ring', 1, 1.6, 0.4, 0, 4.8, 0)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('austria-redbull-ring', 2, 1.85, 0.65, 1.5707963267948966, 2, 1.8)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('austria-redbull-ring', 3, 1.6, 0.95, 3.141592653589793, 2, 1.8)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('austria-redbull-ring', 4, 1.3, 1, 3.641592653589793, 3, 0.7)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('austria-redbull-ring', 5, 1.05, 1.2, -1.5707963267948966, 2.6, 1)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('austria-redbull-ring', 6, 1.4, 1.4, 0, 3.2, 0.5)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('austria-redbull-ring', 7, 1.85, 1.4, 1.7707963267948965, 3.2, 0.5)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('austria-redbull-ring', 8, 2.1, 1.65, 3.141592653589793, 3, 0.7)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('austria-redbull-ring', 9, 2, 1.85, -1.5707963267948966, 3, 0.7)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('austria-redbull-ring', 10, 2.25, 2.05, 0, 2, 1.8)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('austria-redbull-ring', 11, 2.4, 2, 1.5707963267948966, 2, 1.8)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('austria-redbull-ring', 12, 2.4, 2.2, 3.141592653589793, 2, 1.8)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('austria-redbull-ring', 13, 2.2, 2.3, -1.5707963267948966, 2, 1.8)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('austria-redbull-ring', 14, 2.55, 2.4, -0.2, 3.4, 0.4)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('austria-redbull-ring', 15, 3, 2.4, 0, 4, 0)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('austria-redbull-ring', 16, 3.5, 2.4, 0, 4.5, 0)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('austria-redbull-ring', 17, 3.85, 2.15, 1.7707963267948965, 3.4, 0.6)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('austria-redbull-ring', 18, 3.6, 1.8, 3.141592653589793, 3, 0.8)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('austria-redbull-ring', 19, 3.25, 1.85, 3.741592653589793, 3, 0.8)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('austria-redbull-ring', 20, 3, 1.6, -1.9707963267948967, 3.2, 0.6)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('austria-redbull-ring', 21, 2.6, 1.4, -3.141592653589793, 3.4, 0.5)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('austria-redbull-ring', 22, 2, 1.2, 2.641592653589793, 3.6, 0.4)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('austria-redbull-ring', 23, 1.4, 0.8, 2.9415926535897934, 3.8, 0.3)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('austria-redbull-ring', 24, 0.8, 0.5, 0, 4.2, 0.2)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_tags (track_id, tag) VALUES ('austria-redbull-ring', 'f1')
ON CONFLICT DO NOTHING;
INSERT INTO track_tags (track_id, tag) VALUES ('austria-redbull-ring', 'short')
ON CONFLICT DO NOTHING;
INSERT INTO track_tags (track_id, tag) VALUES ('austria-redbull-ring', 'fast')
ON CONFLICT DO NOTHING;
INSERT INTO track_tags (track_id, tag) VALUES ('austria-redbull-ring', 'austria')
ON CONFLICT DO NOTHING;
INSERT INTO tracks (id, name, description, author_id, visibility, scale,
length_m, width_m, lane_width_m, surface, best_lap_ms, best_lap_holder,
bounds_min_x, bounds_min_y, bounds_max_x, bounds_max_y,
created_ms, updated_ms)
VALUES ('great-britain-silverstone', 'Great Britain (Silverstone)', 'Silverstone Circuit. Fast, flowing corners; home of British motorsport.', 'system', 'system', 27,
3.5500000000000003, 2.4, 0.18, 'carpet', 0, 'Hamilton',
0.3, 0.25, 3.85, 2.65,
1700000000000, 1700000000000)
ON CONFLICT (id) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('great-britain-silverstone', 0, 0.3, 0.3, 0, 5, 0)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('great-britain-silverstone', 1, 2.4, 0.3, 0, 5, 0)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('great-britain-silverstone', 2, 2.8, 0.45, 1.5707963267948966, 3, 0.8)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('great-britain-silverstone', 3, 2.6, 0.8, 3.141592653589793, 3.4, 0.6)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('great-britain-silverstone', 4, 2.95, 1.05, 2.0707963267948966, 3.2, 0.7)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('great-britain-silverstone', 5, 3.3, 1.3, 0, 2.2, 1.6)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('great-britain-silverstone', 6, 3.6, 1.15, -1.5707963267948966, 2.2, 1.6)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('great-britain-silverstone', 7, 3.6, 0.8, -3.141592653589793, 3, 0.7)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('great-britain-silverstone', 8, 3.85, 0.55, -1.5707963267948966, 2.6, 1)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('great-britain-silverstone', 9, 3.55, 0.25, 0, 2.4, 1)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('great-britain-silverstone', 10, 3.25, 0.25, 1.5707963267948966, 2.4, 1)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('great-britain-silverstone', 11, 3.05, 0.55, 1.8707963267948966, 3.4, 0.5)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('great-britain-silverstone', 12, 2.85, 0.45, 3.141592653589793, 4, 0.3)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('great-britain-silverstone', 13, 1.8, 0.3, 0, 4.6, 0)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('great-britain-silverstone', 14, 1.2, 0.45, -1.7707963267948965, 3.8, 0.4)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('great-britain-silverstone', 15, 1, 0.85, -3.541592653589793, 3.6, 0.5)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('great-britain-silverstone', 16, 0.7, 1.15, 1.8707963267948966, 2.4, 1.2)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('great-britain-silverstone', 17, 0.5, 0.9, 0, 2.6, 0.9)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('great-britain-silverstone', 18, 0.7, 0.65, -1.5707963267948966, 3, 0.7)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('great-britain-silverstone', 19, 0.45, 0.45, -3.541592653589793, 2.6, 1)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('great-britain-silverstone', 20, 0.75, 1, 1.1707963267948966, 2.4, 1)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('great-britain-silverstone', 21, 0.5, 1.5, -3.541592653589793, 2.6, 0.8)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('great-britain-silverstone', 22, 0.3, 1.15, -1.5707963267948966, 2.4, 1)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('great-britain-silverstone', 23, 0.7, 1.6, 0.7853981633974483, 3.4, 0.5)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('great-britain-silverstone', 24, 2, 1.95, 1.9707963267948967, 3, 0.7)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('great-britain-silverstone', 25, 3, 2.4, 3.3415926535897933, 3.4, 0.4)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('great-britain-silverstone', 26, 2.4, 2.65, -1.8707963267948966, 4, 0.3)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('great-britain-silverstone', 27, 1.4, 2.6, -3.141592653589793, 4.4, 0.2)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('great-britain-silverstone', 28, 0.5, 2.3, -2.8415926535897933, 4.8, 0.2)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('great-britain-silverstone', 29, 0.3, 1.7, -1.2707963267948965, 5, 0.1)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_tags (track_id, tag) VALUES ('great-britain-silverstone', 'f1')
ON CONFLICT DO NOTHING;
INSERT INTO track_tags (track_id, tag) VALUES ('great-britain-silverstone', 'fast')
ON CONFLICT DO NOTHING;
INSERT INTO track_tags (track_id, tag) VALUES ('great-britain-silverstone', 'flowing')
ON CONFLICT DO NOTHING;
INSERT INTO track_tags (track_id, tag) VALUES ('great-britain-silverstone', 'silverstone')
ON CONFLICT DO NOTHING;
INSERT INTO tracks (id, name, description, author_id, visibility, scale,
length_m, width_m, lane_width_m, surface, best_lap_ms, best_lap_holder,
bounds_min_x, bounds_min_y, bounds_max_x, bounds_max_y,
created_ms, updated_ms)
VALUES ('belgium-spa', 'Belgium (Spa-Francorchamps)', 'Spa-Francorchamps. Long, fast, legendary Eau Rouge / Raidillon.', 'system', 'system', 27,
3.85, 2.25, 0.18, 'carpet', 0, 'Verstappen',
0.4, 0.5, 4.25, 2.75,
1700000000000, 1700000000000)
ON CONFLICT (id) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('belgium-spa', 0, 0.4, 0.5, 0, 4.8, 0)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('belgium-spa', 1, 1.6, 0.5, 0, 4.8, 0)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('belgium-spa', 2, 1.95, 0.6, 0.3, 4.2, 0.5)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('belgium-spa', 3, 2.2, 0.95, 0.9, 4, 0.6)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('belgium-spa', 4, 2.1, 1.3, 3.541592653589793, 4, 0.6)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('belgium-spa', 5, 2.7, 1.5, 0, 5, 0)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('belgium-spa', 6, 3.5, 1.5, 0, 5, 0)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('belgium-spa', 7, 3.85, 1.3, -1.5707963267948966, 2.4, 1.4)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('belgium-spa', 8, 3.6, 1.1, 3.4415926535897934, 2.6, 1.2)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('belgium-spa', 9, 3.3, 1.25, 4.0415926535897935, 2.6, 1.2)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('belgium-spa', 10, 3.5, 1.55, 1.5707963267948966, 3.6, 0.4)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('belgium-spa', 11, 3.25, 1.85, 0, 2.2, 1.6)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('belgium-spa', 12, 3.05, 1.7, -1.5707963267948966, 2.4, 1.4)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('belgium-spa', 13, 3.25, 1.5, 0, 3, 0.7)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('belgium-spa', 14, 3.7, 1.65, 1.8707963267948966, 3.4, 0.5)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('belgium-spa', 15, 4.1, 2, 2.1707963267948966, 4, 0.3)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('belgium-spa', 16, 4.25, 2.4, 3.541592653589793, 4.6, 0.3)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('belgium-spa', 17, 4, 2.7, -1.8707963267948966, 2.4, 1.4)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('belgium-spa', 18, 3.7, 2.55, 3.141592653589793, 2.4, 1.4)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('belgium-spa', 19, 3.55, 2.75, 3.8415926535897933, 2.4, 1.4)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('belgium-spa', 20, 3.05, 2.7, -1.5707963267948966, 2, 1.8)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('belgium-spa', 21, 2.85, 2.45, 0, 2, 1.8)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('belgium-spa', 22, 2, 2.2, 2.8415926535897933, 3.6, 0.3)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('belgium-spa', 23, 1.2, 1.6, 2.541592653589793, 4, 0.2)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ('belgium-spa', 24, 0.6, 1.05, 2.8415926535897933, 4.4, 0.2)
ON CONFLICT (track_id, seq) DO NOTHING;
INSERT INTO track_tags (track_id, tag) VALUES ('belgium-spa', 'f1')
ON CONFLICT DO NOTHING;
INSERT INTO track_tags (track_id, tag) VALUES ('belgium-spa', 'long')
ON CONFLICT DO NOTHING;
INSERT INTO track_tags (track_id, tag) VALUES ('belgium-spa', 'fast')
ON CONFLICT DO NOTHING;
INSERT INTO track_tags (track_id, tag) VALUES ('belgium-spa', 'spa')
ON CONFLICT DO NOTHING;
INSERT INTO cars (id, name, owner_id, visibility, scale,
length_mm, width_mm, height_mm, weight_g,
chassis_model, chassis_material, chassis_printed, chassis_wheelbase_mm, chassis_track_mm,
motor_kind, motor_class, motor_kv, motor_power_w,
battery_voltage_v, battery_capacity_mah, battery_cells, battery_chemistry,
drive, top_speed_ms, color_hex, avatar_url, active,
created_ms, updated_ms)
VALUES ('f1-2024-redbull-rb20', 'Oracle Red Bull Racing RB20', 'system', 'system', 24,
238, 83, 40, 33,
'RB20 carbon monocoque', 'carbon-fibre', FALSE, 150, 75,
'turbo-hybrid', 'F1 PU', 0, 746000,
0, 0, 0, 'li-ion MGU-K',
'2WD', 95, '#1e3a8a', '', TRUE,
1700000000000, 1700000000000)
ON CONFLICT (id) DO NOTHING;
INSERT INTO cars (id, name, owner_id, visibility, scale,
length_mm, width_mm, height_mm, weight_g,
chassis_model, chassis_material, chassis_printed, chassis_wheelbase_mm, chassis_track_mm,
motor_kind, motor_class, motor_kv, motor_power_w,
battery_voltage_v, battery_capacity_mah, battery_cells, battery_chemistry,
drive, top_speed_ms, color_hex, avatar_url, active,
created_ms, updated_ms)
VALUES ('f1-2024-ferrari-sf24', 'Scuderia Ferrari SF-24', 'system', 'system', 24,
238, 83, 40, 33,
'SF-24 carbon monocoque', 'carbon-fibre', FALSE, 150, 75,
'turbo-hybrid', 'F1 PU 066/12', 0, 746000,
0, 0, 0, 'li-ion MGU-K',
'2WD', 94, '#dc2626', '', TRUE,
1700000000000, 1700000000000)
ON CONFLICT (id) DO NOTHING;
INSERT INTO cars (id, name, owner_id, visibility, scale,
length_mm, width_mm, height_mm, weight_g,
chassis_model, chassis_material, chassis_printed, chassis_wheelbase_mm, chassis_track_mm,
motor_kind, motor_class, motor_kv, motor_power_w,
battery_voltage_v, battery_capacity_mah, battery_cells, battery_chemistry,
drive, top_speed_ms, color_hex, avatar_url, active,
created_ms, updated_ms)
VALUES ('f1-2024-mclaren-mcl38', 'McLaren MCL38', 'system', 'system', 24,
238, 83, 40, 33,
'MCL38 carbon monocoque', 'carbon-fibre', FALSE, 150, 75,
'turbo-hybrid', 'F1 PU Mercedes-derived', 0, 746000,
0, 0, 0, 'li-ion MGU-K',
'2WD', 94, '#ea580c', '', TRUE,
1700000000000, 1700000000000)
ON CONFLICT (id) DO NOTHING;
INSERT INTO cars (id, name, owner_id, visibility, scale,
length_mm, width_mm, height_mm, weight_g,
chassis_model, chassis_material, chassis_printed, chassis_wheelbase_mm, chassis_track_mm,
motor_kind, motor_class, motor_kv, motor_power_w,
battery_voltage_v, battery_capacity_mah, battery_cells, battery_chemistry,
drive, top_speed_ms, color_hex, avatar_url, active,
created_ms, updated_ms)
VALUES ('f1-2024-mercedes-w15', 'Mercedes-AMG W15', 'system', 'system', 24,
238, 83, 40, 33,
'W15 carbon monocoque', 'carbon-fibre', FALSE, 150, 75,
'turbo-hybrid', 'F1 PU M15', 0, 746000,
0, 0, 0, 'li-ion MGU-K',
'2WD', 94, '#06b6d4', '', TRUE,
1700000000000, 1700000000000)
ON CONFLICT (id) DO NOTHING;
INSERT INTO cars (id, name, owner_id, visibility, scale,
length_mm, width_mm, height_mm, weight_g,
chassis_model, chassis_material, chassis_printed, chassis_wheelbase_mm, chassis_track_mm,
motor_kind, motor_class, motor_kv, motor_power_w,
battery_voltage_v, battery_capacity_mah, battery_cells, battery_chemistry,
drive, top_speed_ms, color_hex, avatar_url, active,
created_ms, updated_ms)
VALUES ('f1-2024-astonmartin-amr24', 'Aston Martin Aramco AMR24', 'system', 'system', 24,
238, 83, 40, 33,
'AMR24 carbon monocoque', 'carbon-fibre', FALSE, 150, 75,
'turbo-hybrid', 'F1 PU Mercedes-derived', 0, 746000,
0, 0, 0, 'li-ion MGU-K',
'2WD', 94, '#16a34a', '', TRUE,
1700000000000, 1700000000000)
ON CONFLICT (id) DO NOTHING;
@@ -0,0 +1,75 @@
-- 004_monaco_rescale.sql
-- Replace Monaco centerline (seq 0..23) inserted by 003_f1_seed.sql
-- with the new 51-waypoint CSV at D:/x0gp/tracks_waypoints_10x10.csv
-- Source CSV: 51 waypoints, scale_xy=0.016 (about 1/62.5), v_scale=sqrt(scale)=0.1265.
-- New footprint: 6.46 x 5.37 m, perimeter approx 21.87 m.
-- Idempotent: DELETE old waypoints first, UPDATE tracks metadata, INSERT new ones.
BEGIN;
DELETE FROM track_waypoints WHERE track_id = 'monaco';
UPDATE tracks SET
length_m = 21.87,
width_m = 6.46,
bounds_min_x = -3.231,
bounds_min_y = -2.684,
bounds_max_x = 3.231,
bounds_max_y = 2.684,
updated_ms = (EXTRACT(EPOCH FROM now()) * 1000)::bigint
WHERE id = 'monaco';
-- New centerline (51 waypoints, scale_xy=0.016, v=0.632 m/s)
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 0, -3.2064, -1.0454, 1.4710, 0.632, -0.1988);
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 1, -3.1387, -0.5857, 1.3926, 0.632, -0.1787);
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 2, -3.0418, -0.1312, 1.3043, 0.632, -0.2544);
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 3, -2.8946, 0.3084, 1.1557, 0.632, -0.4431);
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 4, -2.6693, 0.7141, 0.8629, 0.632, -1.0875);
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 5, -2.3517, 0.9426, 0.1919, 0.632, -1.2844);
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 6, -1.8935, 0.8648, -0.1182, 0.632, -0.2237);
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 7, -1.4302, 0.8332, -0.0660, 0.632, 0.0475);
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 8, -0.9667, 0.8035, -0.0743, 0.632, 0.0756);
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 9, -0.5052, 0.7644, 0.0045, 0.632, 0.0806);
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 10, -0.0425, 0.8077, 0.0000, 0.632, -0.0425);
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 11, 0.4197, 0.7644, -0.0351, 0.632, 0.3650);
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 12, 0.8809, 0.7753, 0.3862, 0.632, 1.3887);
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 13, 1.1985, 1.0811, 1.2440, 0.632, 1.4550);
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 14, 1.1372, 1.5315, 1.5878, 0.632, -0.2144);
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 15, 1.1839, 1.9468, 1.0284, 0.632, -1.2681);
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 16, 1.5551, 2.2249, 0.6162, 0.632, -0.4763);
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 17, 1.9414, 2.4832, 0.5226, 0.632, -0.1756);
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 18, 2.3526, 2.6842, 0.4546, 0.632, -0.1494);
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 19, 2.3526, 2.6842, -1.2476, 0.632, -0.0225);
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 20, 2.4622, 2.3570, -1.2545, 0.632, 0.1775);
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 21, 2.5976, 1.9356, -0.5751, 0.632, 34.1925);
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 22, 2.6154, 2.2577, 1.0415, 0.632, -0.8400);
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 23, 2.9239, 2.4933, -0.0053, 0.632, -3.1975);
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 24, 3.2311, 2.2544, -1.4159, 0.632, -2.7456);
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 25, 3.0259, 1.8399, -2.1127, 0.632, -0.6488);
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 26, 2.7546, 1.4629, -2.2528, 0.632, -0.2769);
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 27, 2.4413, 1.1199, -2.3708, 0.632, -0.3044);
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 28, 2.0895, 0.8170, -2.5300, 0.632, -0.3825);
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 29, 1.6852, 0.5897, -2.7255, 0.632, -0.4019);
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 30, 1.2447, 0.4437, -2.9031, 0.632, -0.3538);
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 31, 0.7862, 0.3711, -3.0547, 0.632, -0.3044);
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 32, 0.3218, 0.3632, 3.0973, 0.632, -0.0975);
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 33, -0.1396, 0.4121, 3.1387, 0.632, -0.0394);
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 34, -0.5818, 0.3659, 3.0594, 0.632, -0.2375);
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 35, -1.0290, 0.4854, 2.9319, 0.632, -0.0950);
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 36, -1.4880, 0.5588, 2.9664, 0.632, -0.2500);
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 37, -1.9441, 0.6474, -3.1052, 0.632, 0.8825);
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 38, -2.3631, 0.5269, -2.4971, 0.632, 1.3600);
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 39, -2.6181, 0.1409, -1.9547, 0.632, 0.9481);
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 40, -2.7014, -0.3108, -1.5706, 0.632, 0.3788);
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 41, -2.6179, -0.7633, -1.6025, 0.632, -0.3425);
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 42, -2.7299, -1.2092, -1.8788, 0.632, -0.0725);
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 43, -2.8930, -1.6280, -1.6659, 0.632, 0.7706);
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 44, -2.8134, -2.0840, -1.2045, 0.632, 0.2162);
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 45, -2.5685, -2.4739, -1.5682, 0.632, -2.4475);
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 46, -2.8118, -2.6842, 3.0638, 0.632, -4.0544);
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 47, -3.1281, -2.4303, 2.0706, 0.632, -1.2500);
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 48, -3.2004, -1.9728, 1.6822, 0.632, -0.4706);
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 49, -3.2311, -1.5093, 1.5774, 0.632, -0.2275);
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 50, -3.2064, -1.0454, 1.4710, 0.632, -0.1988);
COMMIT;
@@ -0,0 +1,90 @@
-- 005_races.sql — race plan, finished race archive, queue.
--
-- Storage split:
-- * Active races (lobby/countdown/racing) live in-memory in lobby.Service.
-- * Finished races are snapshotted into finished_races on SetRaceStatus(finished).
-- * Race plans describe recurring / one-off scheduled races; the scheduler
-- materializes them into lobby.RaceMeta as their start_at_ms approaches.
-- * Queue entries are per-driver per-race_id subscriptions to the next
-- upcoming races. Driver is auto-attached when a slot opens.
--
-- Keyset pagination:
-- * finished_races ordered by (finished_ms DESC, id DESC). Cursor encodes
-- (finished_ms, id); lookup uses (finished_ms, id) < (cursor_ms, cursor_id).
-- * race_plans ordered by (start_at_ms ASC, id ASC). Cursor is
-- (start_at_ms, id) with strict-less comparison.
CREATE TABLE IF NOT EXISTS finished_races (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
host_id TEXT NOT NULL,
host_name TEXT NOT NULL DEFAULT '',
track_id TEXT NOT NULL DEFAULT '',
max_cars INT NOT NULL,
laps INT NOT NULL,
time_limit_s INT NOT NULL,
driver_ids TEXT[] NOT NULL DEFAULT '{}',
status TEXT NOT NULL DEFAULT 'finished'
CHECK (status IN ('finished', 'cancelled')),
created_ms BIGINT NOT NULL,
started_ms BIGINT NOT NULL DEFAULT 0,
finished_ms BIGINT NOT NULL,
duration_ms BIGINT NOT NULL DEFAULT 0,
total_laps INT NOT NULL DEFAULT 0,
total_drivers INT NOT NULL DEFAULT 0,
winner_driver_id TEXT NOT NULL DEFAULT '',
winner_name TEXT NOT NULL DEFAULT '',
best_lap_ms BIGINT NOT NULL DEFAULT 0
);
CREATE INDEX IF NOT EXISTS finished_races_finished_ms_id_idx
ON finished_races (finished_ms DESC, id DESC);
CREATE INDEX IF NOT EXISTS finished_races_host_idx
ON finished_races (host_id);
CREATE INDEX IF NOT EXISTS finished_races_track_idx
ON finished_races (track_id);
CREATE INDEX IF NOT EXISTS finished_races_status_idx
ON finished_races (status);
-- ---------------------------------------------------------------------------
-- Race plans
-- ---------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS race_plans (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
track_id TEXT NOT NULL DEFAULT 'default',
max_cars INT NOT NULL,
laps INT NOT NULL DEFAULT 0,
time_limit_s INT NOT NULL DEFAULT 0,
host_id TEXT NOT NULL,
host_name TEXT NOT NULL DEFAULT '',
start_at_ms BIGINT NOT NULL,
interval_s INT NOT NULL DEFAULT 0, -- 0 = single-shot
count INT NOT NULL DEFAULT 0, -- 0 = repeat forever
enabled BOOLEAN NOT NULL DEFAULT TRUE,
created_ms BIGINT NOT NULL,
updated_ms BIGINT NOT NULL,
next_fire_ms BIGINT NOT NULL, -- last materialised fire time
fires_done INT NOT NULL DEFAULT 0
);
CREATE INDEX IF NOT EXISTS race_plans_start_idx
ON race_plans (start_at_ms ASC, id ASC);
CREATE INDEX IF NOT EXISTS race_plans_enabled_idx
ON race_plans (enabled, next_fire_ms);
-- ---------------------------------------------------------------------------
-- Queue
-- ---------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS race_queue (
driver_id TEXT NOT NULL,
race_id TEXT NOT NULL,
plan_id TEXT NOT NULL DEFAULT '',
enqueued_ms BIGINT NOT NULL,
PRIMARY KEY (driver_id, race_id)
);
CREATE INDEX IF NOT EXISTS race_queue_race_idx ON race_queue (race_id);
CREATE INDEX IF NOT EXISTS race_queue_driver_idx ON race_queue (driver_id, enqueued_ms);
@@ -0,0 +1,12 @@
-- 006_drop_host.sql — remove host_id and host_name from race tables.
--
-- Races no longer carry a host (driver) reference. A race is owned by a
-- physical track; the "host" concept is removed from the public API and
-- from lobby.RaceMeta. Drivers participate as a flat list of driver_ids.
ALTER TABLE finished_races DROP COLUMN IF EXISTS host_id;
ALTER TABLE finished_races DROP COLUMN IF EXISTS host_name;
DROP INDEX IF EXISTS finished_races_host_idx;
ALTER TABLE race_plans DROP COLUMN IF EXISTS host_id;
ALTER TABLE race_plans DROP COLUMN IF EXISTS host_name;
@@ -0,0 +1,16 @@
-- 007_podium.sql — add podium to finished_races.
--
-- Podium is the top-3 finishers of a completed race. Stored as JSONB
-- (a small ordered list, rarely queried as a relation). Shape per row:
-- { "position": 1, "driver_id": "driver-alice",
-- "name": "driver-alice", "total_time_ms": 123456 }
--
-- Ordering: position 1 first.
-- GIN index over the jsonb lets future UI filters (e.g. "races won by
-- driver X") use @> containment queries without a schema change.
ALTER TABLE finished_races
ADD COLUMN IF NOT EXISTS podium JSONB NOT NULL DEFAULT '[]'::jsonb;
CREATE INDEX IF NOT EXISTS finished_races_podium_gin
ON finished_races USING GIN (podium);
@@ -0,0 +1,37 @@
-- 008_drivers_clans.sql — drivers and clans.
--
-- A driver is a person who can join races. Each driver has a unique
-- 3-letter nickname (A-Z, uppercase, exactly 3 characters) and an
-- optional avatar URL. Drivers can belong to at most one clan.
--
-- A clan is a team that groups drivers. Each clan has a unique 3-letter
-- tag (same rules as the driver nickname) and an optional avatar URL.
--
-- Constraints:
-- * nickname is exactly 3 uppercase ASCII letters and unique.
-- * tag is exactly 3 uppercase ASCII letters and unique.
-- * driver.clan_id is a soft FK (nullable); ON DELETE SET NULL.
CREATE TABLE IF NOT EXISTS clans (
id TEXT PRIMARY KEY,
tag TEXT NOT NULL UNIQUE,
name TEXT NOT NULL,
avatar_url TEXT NOT NULL DEFAULT '',
created_ms BIGINT NOT NULL,
updated_ms BIGINT NOT NULL
);
CREATE INDEX IF NOT EXISTS clans_tag_idx ON clans (tag);
CREATE TABLE IF NOT EXISTS drivers (
id TEXT PRIMARY KEY,
nickname TEXT NOT NULL UNIQUE,
name TEXT NOT NULL,
avatar_url TEXT NOT NULL DEFAULT '',
clan_id TEXT REFERENCES clans(id) ON DELETE SET NULL,
created_ms BIGINT NOT NULL,
updated_ms BIGINT NOT NULL
);
CREATE INDEX IF NOT EXISTS drivers_nickname_idx ON drivers (nickname);
CREATE INDEX IF NOT EXISTS drivers_clan_idx ON drivers (clan_id);
@@ -0,0 +1,68 @@
-- 009_live_persistence.sql — persistence for live races and lobby drivers.
--
-- The active race surface (lobby | countdown | racing) is now mirrored
-- in Postgres. finished_races continues to be the canonical archive
-- (no rename yet — it stays in the same shape and the next migration
-- will unify the two under a single `races` table).
--
-- live_races
-- One row per active race. Status is one of
-- lobby | countdown | racing. The `podium` column is reserved for
-- future use (we may eventually move finished_podium into this
-- table as well).
--
-- live_race_drivers
-- Many-to-many between live_races and drivers. The order column is
-- the car's slot in the race (0..max_cars-1).
--
-- lobby_drivers
-- Presence of a driver in the lobby: connected / idle / racing /
-- offline and the current race if racing.
CREATE TABLE IF NOT EXISTS live_races (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
track_id TEXT NOT NULL,
max_cars INT NOT NULL,
laps INT NOT NULL,
time_limit_s INT NOT NULL,
status TEXT NOT NULL DEFAULT 'lobby'
CHECK (status IN ('lobby', 'countdown', 'racing', 'cancelled')),
created_ms BIGINT NOT NULL,
started_ms BIGINT NOT NULL DEFAULT 0,
finished_ms BIGINT NOT NULL DEFAULT 0,
updated_ms BIGINT NOT NULL
);
CREATE INDEX IF NOT EXISTS live_races_status_idx
ON live_races (status, updated_ms DESC);
CREATE INDEX IF NOT EXISTS live_races_track_idx
ON live_races (track_id);
CREATE TABLE IF NOT EXISTS live_race_drivers (
race_id TEXT NOT NULL REFERENCES live_races(id) ON DELETE CASCADE,
driver_id TEXT NOT NULL,
slot INT NOT NULL DEFAULT 0,
joined_ms BIGINT NOT NULL,
PRIMARY KEY (race_id, driver_id)
);
CREATE INDEX IF NOT EXISTS live_race_drivers_driver_idx
ON live_race_drivers (driver_id);
CREATE TABLE IF NOT EXISTS lobby_drivers (
driver_id TEXT PRIMARY KEY,
name TEXT NOT NULL DEFAULT '',
nickname TEXT NOT NULL DEFAULT '',
avatar_url TEXT NOT NULL DEFAULT '',
clan_id TEXT NOT NULL DEFAULT '',
clan_tag TEXT NOT NULL DEFAULT '',
status TEXT NOT NULL DEFAULT 'idle'
CHECK (status IN ('idle', 'racing', 'offline')),
current_race_id TEXT NOT NULL DEFAULT '',
connected_ms BIGINT NOT NULL,
last_seen_ms BIGINT NOT NULL
);
CREATE INDEX IF NOT EXISTS lobby_drivers_status_idx
ON lobby_drivers (status);
@@ -0,0 +1,65 @@
-- 010_unify_races.sql — unify finished_races and live_races into a single
-- `races` table with a 5-state status.
--
-- After this migration the `races` table holds both the live
-- surface (lobby | countdown | racing) and the finished archive
-- (finished | cancelled). live_race_drivers keeps the same shape
-- (one row per (race, driver)). The former `finished_races` columns
-- are kept verbatim; the former `live_races` columns are added
-- (started_ms is reused, created_ms was there too).
-- 1) Rename finished_races -> races.
ALTER TABLE finished_races RENAME TO races;
-- 2) Drop the old finished-only check and replace with a unified one.
ALTER TABLE races DROP CONSTRAINT IF EXISTS finished_races_status_check;
ALTER TABLE races ADD CONSTRAINT races_status_check
CHECK (status IN ('lobby', 'countdown', 'racing', 'finished', 'cancelled'));
-- 2a) Add the live-only columns to the (former) finished table.
ALTER TABLE races ADD COLUMN IF NOT EXISTS updated_ms BIGINT NOT NULL DEFAULT 0;
-- 3) Drop the old index that referenced the old table name and
-- recreate it. The new keyset index sorts on (status, finished_ms
-- DESC, id DESC) so the list view can read live+finished together
-- when needed.
DROP INDEX IF EXISTS finished_races_finished_ms_id_idx;
CREATE INDEX IF NOT EXISTS races_status_finished_idx
ON races (status, finished_ms DESC, id DESC);
DROP INDEX IF EXISTS finished_races_host_idx; -- host_id no longer exists, but the
-- index name may have survived. Defensive drop.
CREATE INDEX IF NOT EXISTS races_started_idx
ON races (status, started_ms DESC, id DESC);
-- 4) Move live rows from live_races into races BEFORE renaming
-- live_race_drivers, so the FK has somewhere to point.
INSERT INTO races
(id, name, track_id, max_cars, laps, time_limit_s, status,
created_ms, started_ms, finished_ms, updated_ms, podium)
SELECT
id, name, track_id, max_cars, laps, time_limit_s, status,
created_ms, started_ms, 0, updated_ms, '[]'::jsonb
FROM live_races
ON CONFLICT (id) DO UPDATE SET
name = EXCLUDED.name,
track_id = EXCLUDED.track_id,
max_cars = EXCLUDED.max_cars,
laps = EXCLUDED.laps,
time_limit_s = EXCLUDED.time_limit_s,
status = EXCLUDED.status,
created_ms = EXCLUDED.created_ms,
started_ms = EXCLUDED.started_ms,
updated_ms = EXCLUDED.updated_ms;
-- 5) live_race_drivers: rename to race_drivers and re-target the FK
-- at the unified races table.
ALTER TABLE live_race_drivers RENAME TO race_drivers;
ALTER TABLE race_drivers
DROP CONSTRAINT IF EXISTS live_race_drivers_race_id_fkey;
ALTER TABLE race_drivers
ADD CONSTRAINT race_drivers_race_id_fkey
FOREIGN KEY (race_id) REFERENCES races(id) ON DELETE CASCADE;
-- 6) Drop the now-redundant live tables.
DROP TABLE IF EXISTS live_race_drivers;
DROP TABLE IF EXISTS live_races;
+373
View File
@@ -0,0 +1,373 @@
package postgres
import (
"context"
"errors"
"fmt"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgconn"
"github.com/jackc/pgx/v5/pgxpool"
"github.com/x0gp/server/internal/catalog"
)
// PgStore implements catalog.Store backed by Postgres + TimescaleDB.
type PgStore struct {
pool *pgxpool.Pool
}
// NewPgStore wraps an open pool.
func NewPgStore(pool *pgxpool.Pool) *PgStore {
return &PgStore{pool: pool}
}
// Compile-time check.
var _ catalog.Store = (*PgStore)(nil)
// ---------------------------------------------------------------------------
// Load
// ---------------------------------------------------------------------------
// Load reads the full catalog into memory. Used at startup.
func (s *PgStore) Load(ctx context.Context) ([]catalog.TrackMeta, []catalog.CarMeta, error) {
tracks, err := s.loadTracks(ctx)
if err != nil {
return nil, nil, fmt.Errorf("load tracks: %w", err)
}
cars, err := s.loadCars(ctx)
if err != nil {
return nil, nil, fmt.Errorf("load cars: %w", err)
}
return tracks, cars, nil
}
func (s *PgStore) loadTracks(ctx context.Context) ([]catalog.TrackMeta, error) {
rows, err := s.pool.Query(ctx, `
SELECT id, name, description, author_id, visibility, scale,
length_m, width_m, lane_width_m, surface,
best_lap_ms, best_lap_holder,
bounds_min_x, bounds_min_y, bounds_max_x, bounds_max_y,
created_ms, updated_ms
FROM tracks
ORDER BY id`)
if err != nil {
return nil, err
}
defer rows.Close()
out := make([]catalog.TrackMeta, 0)
for rows.Next() {
var t catalog.TrackMeta
if err := rows.Scan(
&t.ID, &t.Name, &t.Description, &t.AuthorID, &t.Visibility, &t.Scale,
&t.LengthM, &t.WidthM, &t.LaneWidthM, &t.Surface,
&t.BestLapMs, &t.BestLapHolder,
&t.Bounds.MinX, &t.Bounds.MinY, &t.Bounds.MaxX, &t.Bounds.MaxY,
&t.CreatedMs, &t.UpdatedMs,
); err != nil {
return nil, err
}
// Derive bounds length/width if not stored explicitly.
t.Bounds.LengthM = t.Bounds.MaxX - t.Bounds.MinX
t.Bounds.WidthM = t.Bounds.MaxY - t.Bounds.MinY
out = append(out, t)
}
if err := rows.Err(); err != nil {
return nil, err
}
// Fetch waypoints and tags per track.
for i := range out {
wps, err := s.loadWaypoints(ctx, out[i].ID)
if err != nil {
return nil, fmt.Errorf("waypoints %s: %w", out[i].ID, err)
}
out[i].Centerline = wps
tags, err := s.loadTags(ctx, out[i].ID)
if err != nil {
return nil, fmt.Errorf("tags %s: %w", out[i].ID, err)
}
out[i].Tags = tags
}
return out, nil
}
func (s *PgStore) loadWaypoints(ctx context.Context, trackID string) ([]catalog.Waypoint, error) {
rows, err := s.pool.Query(ctx, `
SELECT x, y, heading_rad, speed_ms, curvature
FROM track_waypoints
WHERE track_id = $1
ORDER BY seq`, trackID)
if err != nil {
return nil, err
}
defer rows.Close()
out := make([]catalog.Waypoint, 0)
for rows.Next() {
var w catalog.Waypoint
if err := rows.Scan(&w.X, &w.Y, &w.HeadingRad, &w.SpeedMs, &w.Curvature); err != nil {
return nil, err
}
out = append(out, w)
}
return out, rows.Err()
}
func (s *PgStore) loadTags(ctx context.Context, trackID string) ([]string, error) {
rows, err := s.pool.Query(ctx,
`SELECT tag FROM track_tags WHERE track_id = $1 ORDER BY tag`, trackID)
if err != nil {
return nil, err
}
defer rows.Close()
out := make([]string, 0)
for rows.Next() {
var tag string
if err := rows.Scan(&tag); err != nil {
return nil, err
}
out = append(out, tag)
}
return out, rows.Err()
}
func (s *PgStore) loadCars(ctx context.Context) ([]catalog.CarMeta, error) {
rows, err := s.pool.Query(ctx, `
SELECT id, name, owner_id, visibility, scale,
length_mm, width_mm, height_mm, weight_g,
chassis_model, chassis_material, chassis_printed,
chassis_wheelbase_mm, chassis_track_mm,
motor_kind, motor_class, motor_kv, motor_power_w,
battery_voltage_v, battery_capacity_mah, battery_cells, battery_chemistry,
drive, top_speed_ms, color_hex, avatar_url, active,
total_distance_m, total_races, total_laps, best_lap_ms,
created_ms, updated_ms
FROM cars
ORDER BY id`)
if err != nil {
return nil, err
}
defer rows.Close()
out := make([]catalog.CarMeta, 0)
for rows.Next() {
var c catalog.CarMeta
if err := rows.Scan(
&c.ID, &c.Name, &c.OwnerID, &c.Visibility, &c.Scale,
&c.LengthMm, &c.WidthMm, &c.HeightMm, &c.WeightG,
&c.Chassis.Model, &c.Chassis.Material, &c.Chassis.Printed,
&c.Chassis.WheelbaseMm, &c.Chassis.TrackMm,
&c.Motor.Kind, &c.Motor.Class, &c.Motor.KV, &c.Motor.PowerW,
&c.Battery.VoltageV, &c.Battery.CapacityMah, &c.Battery.Cells, &c.Battery.Chemistry,
&c.Drive, &c.TopSpeedMs, &c.ColorHex, &c.AvatarURL, &c.Active,
&c.TotalDistanceM, &c.TotalRaces, &c.TotalLaps, &c.BestLapMs,
&c.CreatedMs, &c.UpdatedMs,
); err != nil {
return nil, err
}
out = append(out, c)
}
return out, rows.Err()
}
// ---------------------------------------------------------------------------
// Mutators
// ---------------------------------------------------------------------------
// UpsertTrack writes the track, its waypoints, and its tags in one transaction.
func (s *PgStore) UpsertTrack(ctx context.Context, t catalog.TrackMeta) error {
tx, err := s.pool.BeginTx(ctx, pgx.TxOptions{})
if err != nil {
return err
}
defer tx.Rollback(ctx)
if _, err := tx.Exec(ctx, `
INSERT INTO tracks (id, name, description, author_id, visibility, scale,
length_m, width_m, lane_width_m, surface,
best_lap_ms, best_lap_holder,
bounds_min_x, bounds_min_y, bounds_max_x, bounds_max_y,
created_ms, updated_ms)
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18)
ON CONFLICT (id) DO UPDATE SET
name = EXCLUDED.name,
description = EXCLUDED.description,
author_id = EXCLUDED.author_id,
visibility = EXCLUDED.visibility,
scale = EXCLUDED.scale,
length_m = EXCLUDED.length_m,
width_m = EXCLUDED.width_m,
lane_width_m = EXCLUDED.lane_width_m,
surface = EXCLUDED.surface,
best_lap_ms = EXCLUDED.best_lap_ms,
best_lap_holder = EXCLUDED.best_lap_holder,
bounds_min_x = EXCLUDED.bounds_min_x,
bounds_min_y = EXCLUDED.bounds_min_y,
bounds_max_x = EXCLUDED.bounds_max_x,
bounds_max_y = EXCLUDED.bounds_max_y,
updated_ms = EXCLUDED.updated_ms`,
t.ID, t.Name, t.Description, t.AuthorID, string(t.Visibility), t.Scale,
t.LengthM, t.WidthM, t.LaneWidthM, string(t.Surface),
t.BestLapMs, t.BestLapHolder,
t.Bounds.MinX, t.Bounds.MinY, t.Bounds.MaxX, t.Bounds.MaxY,
t.CreatedMs, t.UpdatedMs,
); err != nil {
return err
}
if _, err := tx.Exec(ctx, `DELETE FROM track_waypoints WHERE track_id = $1`, t.ID); err != nil {
return err
}
for i, w := range t.Centerline {
if _, err := tx.Exec(ctx, `
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature)
VALUES ($1, $2, $3, $4, $5, $6, $7)`,
t.ID, i, w.X, w.Y, w.HeadingRad, w.SpeedMs, w.Curvature,
); err != nil {
return err
}
}
if _, err := tx.Exec(ctx, `DELETE FROM track_tags WHERE track_id = $1`, t.ID); err != nil {
return err
}
for _, tag := range t.Tags {
if _, err := tx.Exec(ctx,
`INSERT INTO track_tags (track_id, tag) VALUES ($1, $2)`,
t.ID, tag,
); err != nil {
return err
}
}
return tx.Commit(ctx)
}
// DeleteTrack removes a track and all child rows (cascade on FK).
func (s *PgStore) DeleteTrack(ctx context.Context, id string) error {
_, err := s.pool.Exec(ctx, `DELETE FROM tracks WHERE id = $1`, id)
return err
}
// UpsertCar writes a car (insert or update).
func (s *PgStore) UpsertCar(ctx context.Context, c catalog.CarMeta) error {
_, err := s.pool.Exec(ctx, `
INSERT INTO cars (id, name, owner_id, visibility, scale,
length_mm, width_mm, height_mm, weight_g,
chassis_model, chassis_material, chassis_printed,
chassis_wheelbase_mm, chassis_track_mm,
motor_kind, motor_class, motor_kv, motor_power_w,
battery_voltage_v, battery_capacity_mah, battery_cells, battery_chemistry,
drive, top_speed_ms, color_hex, avatar_url, active,
total_distance_m, total_races, total_laps, best_lap_ms,
created_ms, updated_ms)
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22,$23,$24,$25,$26,$27,$28,$29,$30,$31,$32)
ON CONFLICT (id) DO UPDATE SET
name = EXCLUDED.name,
owner_id = EXCLUDED.owner_id,
visibility = EXCLUDED.visibility,
scale = EXCLUDED.scale,
length_mm = EXCLUDED.length_mm,
width_mm = EXCLUDED.width_mm,
height_mm = EXCLUDED.height_mm,
weight_g = EXCLUDED.weight_g,
chassis_model = EXCLUDED.chassis_model,
chassis_material = EXCLUDED.chassis_material,
chassis_printed = EXCLUDED.chassis_printed,
chassis_wheelbase_mm= EXCLUDED.chassis_wheelbase_mm,
chassis_track_mm = EXCLUDED.chassis_track_mm,
motor_kind = EXCLUDED.motor_kind,
motor_class = EXCLUDED.motor_class,
motor_kv = EXCLUDED.motor_kv,
motor_power_w = EXCLUDED.motor_power_w,
battery_voltage_v = EXCLUDED.battery_voltage_v,
battery_capacity_mah= EXCLUDED.battery_capacity_mah,
battery_cells = EXCLUDED.battery_cells,
battery_chemistry = EXCLUDED.battery_chemistry,
drive = EXCLUDED.drive,
top_speed_ms = EXCLUDED.top_speed_ms,
color_hex = EXCLUDED.color_hex,
avatar_url = EXCLUDED.avatar_url,
active = EXCLUDED.active,
total_distance_m = EXCLUDED.total_distance_m,
total_races = EXCLUDED.total_races,
total_laps = EXCLUDED.total_laps,
best_lap_ms = EXCLUDED.best_lap_ms,
updated_ms = EXCLUDED.updated_ms`,
c.ID, c.Name, c.OwnerID, string(c.Visibility), c.Scale,
c.LengthMm, c.WidthMm, c.HeightMm, c.WeightG,
c.Chassis.Model, c.Chassis.Material, c.Chassis.Printed,
c.Chassis.WheelbaseMm, c.Chassis.TrackMm,
c.Motor.Kind, c.Motor.Class, c.Motor.KV, c.Motor.PowerW,
c.Battery.VoltageV, c.Battery.CapacityMah, c.Battery.Cells, c.Battery.Chemistry,
string(c.Drive), c.TopSpeedMs, c.ColorHex, c.AvatarURL, c.Active,
c.TotalDistanceM, c.TotalRaces, c.TotalLaps, c.BestLapMs,
c.CreatedMs, c.UpdatedMs,
)
return err
}
// DeleteCar removes a car by id.
func (s *PgStore) DeleteCar(ctx context.Context, id string) error {
_, err := s.pool.Exec(ctx, `DELETE FROM cars WHERE id = $1`, id)
return err
}
// UpdateCarStats applies a partial mutation to the stats columns. We
// first read the row, run the callback, then write the mutated fields
// back. This mirrors the in-memory implementation used by tests.
func (s *PgStore) UpdateCarStats(ctx context.Context, id string, fn func(*catalog.CarMeta)) error {
tx, err := s.pool.BeginTx(ctx, pgx.TxOptions{})
if err != nil {
return err
}
defer tx.Rollback(ctx)
var c catalog.CarMeta
err = tx.QueryRow(ctx, `
SELECT id, name, owner_id, visibility, scale,
length_mm, width_mm, height_mm, weight_g,
chassis_model, chassis_material, chassis_printed,
chassis_wheelbase_mm, chassis_track_mm,
motor_kind, motor_class, motor_kv, motor_power_w,
battery_voltage_v, battery_capacity_mah, battery_cells, battery_chemistry,
drive, top_speed_ms, color_hex, avatar_url, active,
total_distance_m, total_races, total_laps, best_lap_ms,
created_ms, updated_ms
FROM cars WHERE id = $1 FOR UPDATE`, id,
).Scan(
&c.ID, &c.Name, &c.OwnerID, &c.Visibility, &c.Scale,
&c.LengthMm, &c.WidthMm, &c.HeightMm, &c.WeightG,
&c.Chassis.Model, &c.Chassis.Material, &c.Chassis.Printed,
&c.Chassis.WheelbaseMm, &c.Chassis.TrackMm,
&c.Motor.Kind, &c.Motor.Class, &c.Motor.KV, &c.Motor.PowerW,
&c.Battery.VoltageV, &c.Battery.CapacityMah, &c.Battery.Cells, &c.Battery.Chemistry,
&c.Drive, &c.TopSpeedMs, &c.ColorHex, &c.AvatarURL, &c.Active,
&c.TotalDistanceM, &c.TotalRaces, &c.TotalLaps, &c.BestLapMs,
&c.CreatedMs, &c.UpdatedMs,
)
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return catalog.ErrNotFound
}
var pgErr *pgconn.PgError
if errors.As(err, &pgErr) {
return err
}
return err
}
fn(&c)
if _, err := tx.Exec(ctx, `
UPDATE cars SET
total_distance_m = $2,
total_races = $3,
total_laps = $4,
best_lap_ms = $5,
updated_ms = $6
WHERE id = $1`,
c.ID, c.TotalDistanceM, c.TotalRaces, c.TotalLaps, c.BestLapMs, c.UpdatedMs,
); err != nil {
return err
}
return tx.Commit(ctx)
}
@@ -0,0 +1,226 @@
// Package postgres owns the persistence layer of the catalog.
//
// It exposes a *pgxpool.Pool plus a small set of repository helpers used by
// internal/catalog. Migrations are applied at startup from the SQL files in
// the migrations/ subdirectory.
package postgres
import (
"context"
"embed"
"fmt"
"sort"
"strings"
"time"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgxpool"
)
//go:embed migrations/*.sql
var migrationsFS embed.FS
// Config captures the connection parameters. URL takes precedence over the
// individual fields if non-empty.
type Config struct {
URL string
Host string
Port int
User string
Password string
Database string
SSLMode string
MaxConns int32
MinConns int32
ConnectTimeout time.Duration
}
// Open establishes a pgxpool, pings the database and returns it. The
// returned pool must be closed by the caller.
func Open(ctx context.Context, cfg Config) (*pgxpool.Pool, error) {
dsn, err := cfg.DSN()
if err != nil {
return nil, err
}
pcfg, err := pgxpool.ParseConfig(dsn)
if err != nil {
return nil, fmt.Errorf("parse dsn: %w", err)
}
if cfg.MaxConns > 0 {
pcfg.MaxConns = cfg.MaxConns
}
if cfg.MinConns > 0 {
pcfg.MinConns = cfg.MinConns
}
if cfg.ConnectTimeout > 0 {
pcfg.ConnConfig.ConnectTimeout = cfg.ConnectTimeout
}
pool, err := pgxpool.NewWithConfig(ctx, pcfg)
if err != nil {
return nil, fmt.Errorf("connect: %w", err)
}
pingCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
if err := pool.Ping(pingCtx); err != nil {
pool.Close()
return nil, fmt.Errorf("ping: %w", err)
}
return pool, nil
}
// DSN builds the postgres connection string.
func (c Config) DSN() (string, error) {
if c.URL != "" {
return c.URL, nil
}
if c.Host == "" {
return "", fmt.Errorf("postgres: host required")
}
port := c.Port
if port == 0 {
port = 5432
}
ssl := c.SSLMode
if ssl == "" {
ssl = "disable"
}
return fmt.Sprintf("postgres://%s:%s@%s:%d/%s?sslmode=%s",
c.User, c.Password, c.Host, port, c.Database, ssl), nil
}
// Migrate applies any pending SQL migrations from the embedded migrations/
// directory. Migrations are tracked in the schema_migrations table and run
// in lexical filename order. Each migration runs in its own transaction.
func Migrate(ctx context.Context, pool *pgxpool.Pool) error {
if _, err := pool.Exec(ctx, `
CREATE TABLE IF NOT EXISTS schema_migrations (
version TEXT PRIMARY KEY,
applied_at TIMESTAMPTZ NOT NULL DEFAULT now()
)`); err != nil {
return fmt.Errorf("create schema_migrations: %w", err)
}
entries, err := migrationsFS.ReadDir("migrations")
if err != nil {
return fmt.Errorf("read migrations: %w", err)
}
names := make([]string, 0, len(entries))
for _, e := range entries {
if !e.IsDir() && strings.HasSuffix(e.Name(), ".sql") {
names = append(names, e.Name())
}
}
sort.Strings(names)
applied := map[string]struct{}{}
rows, err := pool.Query(ctx, `SELECT version FROM schema_migrations`)
if err != nil {
return fmt.Errorf("list applied migrations: %w", err)
}
for rows.Next() {
var v string
if err := rows.Scan(&v); err != nil {
rows.Close()
return err
}
applied[v] = struct{}{}
}
rows.Close()
for _, name := range names {
if _, ok := applied[name]; ok {
continue
}
body, err := migrationsFS.ReadFile("migrations/" + name)
if err != nil {
return fmt.Errorf("read %s: %w", name, err)
}
if err := applyMigration(ctx, pool, name, string(body)); err != nil {
return fmt.Errorf("apply %s: %w", name, err)
}
}
return nil
}
func applyMigration(ctx context.Context, pool *pgxpool.Pool, name, body string) error {
tx, err := pool.BeginTx(ctx, pgx.TxOptions{})
if err != nil {
return err
}
defer tx.Rollback(ctx)
// pgx's simple-protocol Exec() rejects multi-statement strings. Split
// the body on ";\n" boundaries and run each statement individually.
for i, stmt := range splitStatements(body) {
if _, err := tx.Exec(ctx, stmt); err != nil {
preview := stmt
if len(preview) > 120 {
preview = preview[:120] + "..."
}
return fmt.Errorf("statement %d failed: %w; sql=%q", i+1, err, preview)
}
}
if _, err := tx.Exec(ctx,
`INSERT INTO schema_migrations (version) VALUES ($1)`, name); err != nil {
return err
}
return tx.Commit(ctx)
}
// splitStatements splits a SQL script on top-level ";" boundaries. It
// is deliberately simple: it ignores ";" inside string literals and
// comments, which is fine for our migration files (no literal ";"
// in any INSERT).
func splitStatements(body string) []string {
var (
out []string
buf strings.Builder
inStr bool
escNext bool
)
for i := 0; i < len(body); i++ {
c := body[i]
if escNext {
buf.WriteByte(c)
escNext = false
continue
}
if c == '\\' && inStr {
buf.WriteByte(c)
escNext = true
continue
}
if c == '\'' {
inStr = !inStr
buf.WriteByte(c)
continue
}
if c == '-' && !inStr && i+1 < len(body) && body[i+1] == '-' {
// line comment until \n
for i < len(body) && body[i] != '\n' {
buf.WriteByte(body[i])
i++
}
if i < len(body) {
buf.WriteByte('\n')
}
continue
}
if c == ';' && !inStr {
s := strings.TrimSpace(buf.String())
if s != "" {
out = append(out, s)
}
buf.Reset()
continue
}
buf.WriteByte(c)
}
if rest := strings.TrimSpace(buf.String()); rest != "" {
out = append(out, rest)
}
return out
}