Przejdź do głównej zawartości

Praca z bazą danych w Codex: schematy, migracje i zapytania, których działanie udowodnisz

Codex prowadzi pracę z bazą danych na stosie Drizzle i PostgreSQL jako potok: projektuje schemat, generuje migracje tylko „w przód” poleceniem drizzle-kit, pisze zapytania pod istniejące indeksy i udowadnia każdą zmianę na bazie wypełnionej danymi testowymi w worktree albo w środowisku chmurowym Codex. Ludzie przeglądają SQL migracji i dowody, a nie każdą wygenerowaną linię.

Product manager chce dashboardu raportowego, który agreguje zamówienia, przychód i zwroty dla każdego użytkownika w ujęciu dziennym, tygodniowym i miesięcznym. Tabela orders ma już dziesiątki milionów wierszy, obecne zapytania są wolne, a jeden blokujący CREATE INDEX w godzinach pracy położy checkout. Ta strona jest dla programisty, który prowadzi tę zmianę w Codex, i dla tech leada, który musi zdecydować, co znaczy „udowodnione jako bezpieczne”, zanim zmiana trafi na produkcję.

  • Blok AGENTS.md, który nie pozwala Codexowi edytować zastosowanych migracji ani dotykać niczego poza lokalną lub testową bazą.
  • Pięć promptów do skopiowania: projekt schematu, migracja, zapytania z testami, przebieg dowodowy w chmurze i cotygodniowy audyt zapytań.
  • Skrypt konfiguracyjny środowiska chmurowego Codex, który daje każdemu zadaniu własny PostgreSQL.
  • Konfigurację serwera MCP dla PostgreSQL w trybie tylko do odczytu, dzięki której Codex czyta prawdziwe plany zapytań.
  • Zestaw weryfikacji (lint migracji, migracja od zera, różnicowe testy zapytań, asercje na planach) i jasną granicę tego, co nadal czyta człowiek.

Która powierzchnia Codex pasuje do którego zadania bazodanowego?

Dział zatytułowany „Która powierzchnia Codex pasuje do którego zadania bazodanowego?”

Praca z bazą wyraźnie rozkłada się na powierzchnie Codex. Wybieraj według tego, do czego zadanie musi mieć dostęp.

ZadaniePowierzchniaDlaczego
Projekt schematu, iteracjeCLI albo rozszerzenie IDE, interaktywnieOdpowiadasz na pytania projektowe na bieżąco
Kod migracji i zapytańCLI z codex --worktreeGałąź i wygenerowany SQL nie mieszają się z głównym checkoutem; lokalna baza jest wspólna, więc każdemu worktree daj własną bazę (createdb app_<branch> i ustaw na nią DATABASE_URL)
Dowód na danych w skali produkcyjnejZadanie w chmurze Codex (codex cloud exec)Baza wypełniona danymi testowymi działa w kontenerze, a przebieg best-of-N (--attempts N) nie zajmuje twojej maszyny
Prawdziwe plany zapytańCLI plus serwer MCP dla PostgreSQL tylko do odczytuCodex czyta wynik EXPLAIN ze stagingu, zamiast zgadywać
Cotygodniowy audyt regresjiAutomatyzacja w aplikacji desktopowej albo codex exec z harmonogramuDziała bez ciebie i raportuje tylko to, co się zmieniło

Flagi CLI na tej stronie sprawdzono na codex 0.157.1 i drizzle-kit 0.31.11. Mechanikę worktree opisuje przewodnik po worktree w Codex, a przewodnik po bazach danych dla wszystkich narzędzi zestawia Codexa z Claude Code i Cursorem.

Codex czyta AGENTS.md, zanim zacznie jakąkolwiek pracę, więc reguły bazodanowe należą tam, a nie do każdego promptu. Dodaj ten blok w katalogu głównym repozytorium:

## Database rules
- Stack: PostgreSQL 16, Drizzle ORM. Schema in src/lib/db/schema.ts, migrations in drizzle/.
- Generate migrations with `npx drizzle-kit generate --name <what_changed>`. Never hand-edit
a migration that exists on main, and never edit drizzle/meta/ by hand.
- Only connect to DATABASE_URL from the environment. Never connect to a host containing
"prod" and never run a migration against anything but a local or test database.
- Indexes on existing tables use CREATE INDEX CONCURRENTLY IF NOT EXISTS in
scripts/concurrent/<index_name>.sql, never in drizzle/: drizzle-kit migrate runs every
pending migration in one transaction, so CONCURRENTLY cannot go through it. Apply the file
with `psql "$DATABASE_URL" -v ON_ERROR_STOP=1 -f <file>` after `npm run db:migrate`,
then check pg_index.indisvalid for the new index.
- Schema changes on existing tables follow expand/contract: add, backfill, switch reads,
then drop in a later release. Never rename or drop a column in the same release that stops using it.
- If `pg_isready` fails, start PostgreSQL with `service postgresql start`.
- Before you finish: `npm run db:migrate`, then any new scripts/concurrent/ file with psql,
`npm run db:lint`, `npm test -- tests/db`.

npm run db:lint uruchamia linter migracji opisany w części o weryfikacji. Przewodnik po optymalizacji AGENTS.md pokazuje, jak utrzymać plik na tyle krótki, żeby takie reguły były przestrzegane.

Zaprojektuj schemat, zanim powstanie jakakolwiek migracja

Dział zatytułowany „Zaprojektuj schemat, zanim powstanie jakakolwiek migracja”

Zacznij w CLI albo w rozszerzeniu IDE. Błędy w schemacie najdrożej się odkręca, więc pierwszy prompt zabrania generowania czegokolwiek, co dałoby się zastosować na bazie.

Zakwestionuj propozycję w tym samym wątku: co się dzieje z wierszem podsumowania, gdy zamówienie zostanie zwrócone miesiąc później, i czy (user_id, day) musi być unikalne. Odpowiedzi na takie pytania stają się później kryteriami akceptacji testów zapytań.

Uruchom sesję w worktree, żeby migracja trafiła na własną gałąź:

Okno terminala
# Terminal, katalog główny repozytorium
codex --worktree

Trzy fakty o drizzle-kit kształtują ten prompt. Generuje on migracje tylko „w przód”: lista poleceń w 0.31.11 (generate, migrate, push, check, up, drop i inne) nie zawiera polecenia down ani rollback, a drop usuwa plik migracji, zamiast cofać zmiany w bazie. Wygenerowany CREATE INDEX jest zwykłym, blokującym poleceniem. A drizzle-kit migrate uruchamia wszystkie oczekujące migracje w jednej transakcji. Plan wycofania i indeksy tworzone współbieżnie musisz więc zamówić sam, a indeksy współbieżne nie mogą trafić do drizzle/.

Indeks współbieżny leży poza drizzle/, bo spotykają się tu dwie reguły. PostgreSQL nie pozwala uruchomić CREATE INDEX CONCURRENTLY wewnątrz bloku transakcji, a drizzle-kit migrate przekazuje wszystkie oczekujące migracje do migratora PostgreSQL z drizzle-orm, który stosuje je w jednej transakcji (sprawdzone w drizzle-orm 0.45.3, 2026-09-26). Własna migracja Drizzle (drizzle-kit generate --custom) działa w tej samej transakcji, więc indeks potrzebuje osobnego, nietransakcyjnego kroku psql we wdrożeniu, po db:migrate. Nieudane budowanie współbieżne zostawia indeks INVALID, a IF NOT EXISTS po cichu go wtedy pomija; dlatego krok 4 sprawdza indisvalid, zamiast ufać czystemu kodowi wyjścia. Wzorce migracji baz danych opisują dokładnie expand/contract i backfille.

Pisz zapytania z testami, które naprawdę mogą wykryć błąd

Dział zatytułowany „Pisz zapytania z testami, które naprawdę mogą wykryć błąd”

Zapytanie, które przechodzi kontrolę typów i zwraca wiersze, niewiele dowodzi. Prompt poniżej żąda dwóch rodzajów dowodu: wyników porównanych z niezależną wyrocznią w SQL i planów sprawdzonych pod kątem indeksu, którego każde zapytanie powinno użyć.

To test różnicowy wyłapuje błędne odpowiedzi: wyrocznia czyta surowe zamówienia i nic nie wie o tabelach podsumowań, więc podwójnie policzony zwrot albo przesunięcie strefy czasowej wychodzi jako niezgodność. Testy planów na małym zestawie danych testowych bywają mylące, bo planer na małych tabelach woli skan sekwencyjny; dlatego prompt umieszcza je w osobnym pliku, który działa tylko z PLAN_TESTS=1. Lokalna bramka npm test -- tests/db je pomija, a opisany niżej przebieg dowodowy w chmurze włącza je na zbiorze danych w skali produkcyjnej.

Udowodnij zmianę na danych w skali produkcyjnej w zadaniu chmurowym Codex

Dział zatytułowany „Udowodnij zmianę na danych w skali produkcyjnej w zadaniu chmurowym Codex”

Baza na laptopie ma kilka tysięcy wierszy. Pytania, które się liczą (ile trwa budowa indeksu współbieżnego, czy planer wybierze go przy 50 milionach wierszy), wymagają większego zbioru danych. Środowisko chmurowe Codex daje każdemu zadaniu własny kontener, więc każde zadanie może mieć własny, jednorazowy PostgreSQL.

Wpisz skrypt konfiguracyjny środowiska w ustawieniach webowych Codex, w sekcji Environments, i nazwij środowisko db-proof: codex cloud exec --env przyjmuje nazwę środowiska albo jego ID (sprawdzone w codex 0.157.1), a polecenie dowodowe poniżej używa nazwy.

#!/usr/bin/env bash
# Skrypt konfiguracyjny środowiska chmurowego Codex
set -euo pipefail
apt-get update && apt-get install -y --no-install-recommends postgresql postgresql-contrib
npm ci
service postgresql start
su postgres -c "createuser --superuser root" # uwierzytelnianie peer dla użytkownika root kontenera, bez hasła
createdb app_test
# Sprawdź, że baza odpowiada. Migracje uruchamia zadanie, żeby mogło zmierzyć ich czas
pg_isready

Ustaw DATABASE_URL na postgresql:///app_test?host=/var/run/postgresql jako zmienną środowiskową na tej samej stronie ustawień, a nie jako sekret, żeby faza agenta mogła ją odczytać niezależnie od tego, jak ograniczone są sekrety (i tak nie zawiera hasła). Nie zakładaj, że serwer uruchomiony przez skrypt konfiguracyjny nadal działa, gdy startuje agent; reguła w AGENTS.md mówi agentowi, jak go uruchomić.

Wypchnij gałąź, a potem zleć zadanie dowodowe:

ID zadania znajdziesz poleceniem codex cloud list --env db-proof. Wynik sprawdzisz poleceniem codex cloud status TASK_ID, a jeśli zadanie coś zmieniło, także codex cloud diff TASK_ID. Raport to artefakt, który dołączasz do pull requesta. Wypełnienie bazy 50 milionami wierszy wydłuża każdy przebieg, więc trzymaj liczbę wierszy na poziomie, który ujawnia interesujący cię plan; przewodnik po środowiskach chmurowych opisuje cache i skrypty utrzymaniowe, które przyspieszają kolejne przebiegi.

Pozwól Codexowi czytać prawdziwe plany zapytań przez MCP

Dział zatytułowany „Pozwól Codexowi czytać prawdziwe plany zapytań przez MCP”

Dane testowe odpowiadają na pytanie „czy to bezpieczne”. Baza stagingowa z prawdziwym ruchem odpowiada na pytanie „które zapytania są dziś wolne”. Postgres MCP Pro (PyPI postgres-mcp; ostatnie wydanie na PyPI to 0.3.0 z 2025-05-16, sprawdzone 2026-09-26) udostępnia m.in. narzędzia explain_query, get_top_queries i analyze_query_indexes. Uruchamiaj go w trybie restricted, który ogranicza go do transakcji tylko do odczytu z limitem czasu wykonania, i łącz się rolą tylko do odczytu:

# ~/.codex/config.toml. env_vars przekazuje DATABASE_URI z twojej powłoki; nic tajnego tu nie trafia
[mcp_servers.postgres]
command = "uvx"
args = ["postgres-mcp", "--access-mode=restricted"]
env_vars = ["DATABASE_URI"]
enabled_tools = ["list_schemas", "get_object_details", "explain_query", "get_top_queries", "analyze_query_indexes"]

Wyeksportuj DATABASE_URI z connection stringiem roli tylko do odczytu na stagingu, zanim uruchomisz codex, i sprawdź wpis poleceniem codex mcp get postgres. Starszy pakiet @modelcontextprotocol/server-postgres jest na npm oznaczony jako wycofany; nie kopiuj fragmentów, które go używają. Przewodnik po serwerach MCP dla baz danych zawiera SQL roli tylko do odczytu i rozszerzenia potrzebne narzędziom do indeksów (pg_stat_statements, hypopg). Przewodnik omawia też alternatywy, takie jak Supabase i DBHub. Jeśli pakiet bez nowego wydania od maja 2025 roku jest dla twojego zespołu problemem, DBHub nadal dostaje nowe wydania. Konfiguracja MCP w Codex opisuje tryby zatwierdzania i trzymanie sekretów poza config.toml.

Gdy dashboard trafi na produkcję, regresje przychodzą razem ze zwykłą pracą nad funkcjami. Zaplanuj audyt jako automatyzację wątku w aplikacji desktopowej Codex, żeby każdy przebieg pamiętał poprzedni, z sandboxem tylko do odczytu i opisanym wyżej serwerem MCP. Przewodnik po automatyzacjach Codex opisuje konfigurację, sandbox i uruchamianie tego samego promptu przez codex exec na stale włączonym hoście.

Jak zweryfikować pracę Codexa na bazie danych bez czytania każdej linii?

Dział zatytułowany „Jak zweryfikować pracę Codexa na bazie danych bez czytania każdej linii?”

Zmiany w bazie to jedna z niewielu klas zmian, w których człowiek nadal czyta sam kod, więc celem jest zawężenie tej lektury do SQL migracji i jednej strony dowodów. Podejście „dowody zamiast diffów” wyjaśnia ten podział.

  1. Zlintuj migrację. Squawk (squawk-cli na npm) wyłapuje blokujące budowanie indeksów, niebezpieczne zmiany kolumn i inne zagrożenia związane z blokadami w migracjach PostgreSQL. Podepnij go jako db:lint, na przykład npx --package squawk-cli squawk drizzle/*.sql scripts/concurrent/*.sql, i uruchamiaj w CI przy każdym pull requeście, który dotyka drizzle/ albo scripts/concurrent/.

  2. Zastosuj migracje od zera, dwa razy. CI tworzy pustą bazę, uruchamia wszystkie migracje, uruchamia je ponownie (nic nie może się zastosować drugi raz), stosuje pliki z scripts/concurrent/ przez psql i wykonuje npx drizzle-kit check, żeby wyłapać niespójne metadane migracji.

  3. Uruchom testy różnicowe i testy planów. Testy zapytań porównują każdy wynik z Drizzle z niezależnym agregatem w SQL i sprawdzają zamierzony indeks. Niepowodzenie oznacza błędną odpowiedź albo utracony indeks, a nie kwestię stylu.

  4. Dołącz raport z przebiegu dowodowego w chmurze. Pull request zawiera czasy, sprawdzenie indisvalid i plany z przebiegu na danych w skali produkcyjnej. Bez raportu nie ma merge’a.

  5. Wskazana osoba czyta SQL i zatwierdza zmianę. Programista, który zlecił zadanie, czyta każdy plik .sql i notatkę o wycofaniu, które są krótkie w porównaniu z wygenerowanym kodem aplikacji. Przy zmianach w tabelach powyżej ustalonego w zespole progu wielkości analizę blokad zatwierdza druga osoba (tech lead albo DBA). @codex review na pull requeście to przydatne dodatkowe przejście, a nie zatwierdzenie.

Kod aplikacji wokół zapytań (typy, podpięcie w serwisach) przechodzi te same bramki co każda inna zmiana wprowadzona przez Codexa: kontrolę typów, lint, testy i review, opisane w przewodniku testowanie z Codex.

ObjawPrawdopodobna przyczynaJak z tego wyjść
Wdrożenie kończy się błędem „CREATE INDEX CONCURRENTLY cannot run inside a transaction block”drizzle-kit migrate stosuje wszystkie oczekujące migracje w jednej transakcjiUsuń polecenie CONCURRENTLY z drizzle/, uruchom je przez psql poza migratorem jako osobny krok wdrożenia, usuń pozostawiony indeks INVALID poleceniem DROP INDEX CONCURRENTLY i spróbuj ponownie
drizzle-kit check nie przechodzi albo migracja stosuje się drugi razCodex ręcznie edytował zastosowaną migrację albo drizzle/meta/Przywróć te pliki z main, zmień tylko schema.ts, wygeneruj ponownie; zachowaj regułę w AGENTS.md, która tego zabrania
Testy planów przechodzą lokalnie, a dashboard na produkcji jest wolnyDane testowe są małe albo mają równomierny rozkład, więc planer wybrał inaczejWypełnij bazę danymi o rozkładzie zbliżonym do produkcji, uruchamiaj ANALYZE i trzymaj asercje na planach w przebiegu dowodowym w chmurze
Codex „naprawia” każde wolne zapytanie nowym indeksemPrompt nagradzał wyłącznie szybkośćWymagaj, żeby każdy indeks wskazywał zapytanie, któremu służy, i plan przed zmianą i po niej, oraz pytaj, który istniejący indeks staje się zbędny
Sumy na dashboardzie nie zgadzają się dla użytkowników spoza UTCGranice dni liczone w UTCPrzekazuj strefę czasową użytkownika do każdego zapytania i trzymaj zamówienie z 23:30 UTC w danych testowych (fixture) testu różnicowego
Dwie sesje w worktree kończą się błędem „relation already exists” albo widzą nawzajem swoje wiersze testoweKażde worktree wskazuje na ten sam lokalny DATABASE_URL, więc równoległe migracje i dane testowe kolidująUtwórz osobną bazę dla każdego worktree (createdb app_<branch>) i ustaw DATABASE_URL w środowisku tego worktree
Zadanie w chmurze zgłasza „connection refused”PostgreSQL uruchomiony podczas konfiguracji nie działa w fazie agentaZachowaj w AGENTS.md regułę z pg_isready i service postgresql start oraz sprawdź, czy zmienna środowiskowa zawiera URL do gniazda
Serwer MCP nie startuje albo nie pokazuje narzędziBrak uvx, niewyeksportowane DATABASE_URI albo literówka w enabled_toolsUruchom bezpośrednio uvx postgres-mcp --help, wyeksportuj zmienną przed startem codex i odczytaj prawdziwe nazwy narzędzi przez /mcp verbose