Document WCX project split analysis
This commit is contained in:
462
docs/database-schema-gap-analysis.md
Normal file
462
docs/database-schema-gap-analysis.md
Normal file
@ -0,0 +1,462 @@
|
||||
# Gap-analys för reproducerbart facitdatabasschema
|
||||
|
||||
## 1. Omfattning och metod
|
||||
|
||||
Analysen är avgränsad till om facitdatabasens struktur kan återskapas från
|
||||
versionshanterade filer. Följande har inspekterats:
|
||||
|
||||
- `docs/project-split-analysis.md` och
|
||||
`docs/project-split-analysis-summary.md`,
|
||||
- `scripts/schema.sql`,
|
||||
- databasberoendena i `import_site.py`, `migrate_csv.py`, `check_ocr.py`,
|
||||
`process_pending_ocr.py`, `match_filenames.py` och
|
||||
`test_duration_matching.py`,
|
||||
- `sqlite_schema`, `PRAGMA table_xinfo`, `PRAGMA foreign_key_list`,
|
||||
`PRAGMA index_list` och `PRAGMA index_xinfo` i
|
||||
`/storage/disk1/WCX/database/wcx.db`, öppnad med `sqlite3 -readonly`, och
|
||||
- resultatet av att läsa `scripts/schema.sql` i en separat SQLite-databas i
|
||||
minnet.
|
||||
|
||||
**Verifierat faktum:** Produktionsdatabasen öppnades endast med
|
||||
`sqlite3 -readonly`. Ingen kontroll eller verifiering skrev till den.
|
||||
|
||||
**Verifierat faktum:** `PRAGMA integrity_check` gav `ok` och
|
||||
`PRAGMA foreign_key_check` gav inga rader. Både `PRAGMA user_version` och
|
||||
`PRAGMA application_id` är `0`.
|
||||
|
||||
**Antagande:** Schemat som finns i den inspekterade `wcx.db` är den avsedda
|
||||
produktionsmodellen. Analysen kan verifiera faktisk struktur och kodberoenden,
|
||||
men inte om varje historiskt schemaingrepp var avsiktligt.
|
||||
|
||||
## 2. Fullständigt faktiskt produktionsschema
|
||||
|
||||
### 2.1 Tabeller
|
||||
|
||||
**Verifierat faktum:** Följande användardefinierade tabeller finns i
|
||||
produktionen. SQL-definitionerna nedan är hämtade från `sqlite_schema`. Den
|
||||
kompakta placeringen av OCR-kolumnerna på samma rad som `modified_at` är bara
|
||||
hur SQLite lagrade den stegvis ändrade tabellens SQL-text; den ändrar inte
|
||||
kolumnernas semantik.
|
||||
|
||||
```sql
|
||||
CREATE TABLE movie (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
aka TEXT,
|
||||
nationality TEXT,
|
||||
age INTEGER,
|
||||
shoot_location TEXT,
|
||||
shoot_date TEXT,
|
||||
duration_seconds INTEGER,
|
||||
description TEXT,
|
||||
rating REAL,
|
||||
web_url TEXT,
|
||||
thumbnail TEXT,
|
||||
published TEXT,
|
||||
updated TEXT,
|
||||
|
||||
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
modified_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
ocr_status TEXT NOT NULL DEFAULT 'pending',
|
||||
ocr_raw_text TEXT,
|
||||
ocr_error TEXT,
|
||||
ocr_processed_at TEXT,
|
||||
|
||||
CHECK (age IS NULL OR age >= 0),
|
||||
CHECK (rating IS NULL OR rating BETWEEN 0 AND 10),
|
||||
CHECK (duration_seconds IS NULL OR duration_seconds >= 0)
|
||||
);
|
||||
|
||||
CREATE TABLE movie_history (
|
||||
history_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
movie_id TEXT NOT NULL,
|
||||
|
||||
name TEXT NOT NULL,
|
||||
aka TEXT,
|
||||
nationality TEXT,
|
||||
age INTEGER,
|
||||
shoot_location TEXT,
|
||||
shoot_date TEXT,
|
||||
duration_seconds INTEGER,
|
||||
description TEXT,
|
||||
rating REAL,
|
||||
web_url TEXT,
|
||||
thumbnail TEXT,
|
||||
published TEXT,
|
||||
updated TEXT,
|
||||
|
||||
created_at TEXT,
|
||||
modified_at TEXT,
|
||||
|
||||
ocr_status TEXT,
|
||||
ocr_raw_text TEXT,
|
||||
ocr_error TEXT,
|
||||
ocr_processed_at TEXT,
|
||||
|
||||
archived_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
change_source TEXT NOT NULL DEFAULT 'site_import',
|
||||
change_summary TEXT NOT NULL,
|
||||
|
||||
FOREIGN KEY (movie_id) REFERENCES movie(id)
|
||||
);
|
||||
|
||||
CREATE TABLE movie_name_alias (
|
||||
alias_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
movie_id TEXT NOT NULL,
|
||||
alias TEXT NOT NULL,
|
||||
normalized_alias TEXT NOT NULL,
|
||||
source TEXT NOT NULL DEFAULT 'manual',
|
||||
|
||||
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
FOREIGN KEY (movie_id) REFERENCES movie(id),
|
||||
|
||||
UNIQUE (movie_id, normalized_alias)
|
||||
);
|
||||
```
|
||||
|
||||
**Verifierat faktum:** SQLite har dessutom skapat den interna tabellen
|
||||
`sqlite_sequence(name, seq)` eftersom `movie_history.history_id` och
|
||||
`movie_name_alias.alias_id` använder `AUTOINCREMENT`. Den ska inte deklareras
|
||||
manuellt i projektets schema.
|
||||
|
||||
### 2.2 Index
|
||||
|
||||
**Verifierat faktum:** Produktionen innehåller följande explicita index:
|
||||
|
||||
```sql
|
||||
CREATE INDEX idx_movie_history_movie_id
|
||||
ON movie_history(movie_id);
|
||||
|
||||
CREATE INDEX idx_movie_history_archived_at
|
||||
ON movie_history(archived_at);
|
||||
|
||||
CREATE INDEX idx_movie_name_alias_movie_id
|
||||
ON movie_name_alias(movie_id);
|
||||
|
||||
CREATE INDEX idx_movie_name_alias_normalized
|
||||
ON movie_name_alias(normalized_alias);
|
||||
```
|
||||
|
||||
**Verifierat faktum:** SQLite har också skapat två unika automatiska index:
|
||||
|
||||
| Index | Ursprung | Nyckelkolumner | Partiellt |
|
||||
| --- | --- | --- | --- |
|
||||
| `sqlite_autoindex_movie_1` | `PRIMARY KEY` | `movie.id` | nej |
|
||||
| `sqlite_autoindex_movie_name_alias_1` | `UNIQUE` | `movie_id`, `normalized_alias` | nej |
|
||||
|
||||
Alla sex index använder stigande ordning och `BINARY`-kollation. De fyra
|
||||
explicita indexen är icke-unika och icke-partiella.
|
||||
|
||||
### 2.3 Constraints
|
||||
|
||||
**Verifierat faktum:** Produktionsschemat deklarerar:
|
||||
|
||||
- primärnyckel på `movie.id`,
|
||||
- primärnyckel med `AUTOINCREMENT` på `movie_history.history_id`,
|
||||
- primärnyckel med `AUTOINCREMENT` på `movie_name_alias.alias_id`,
|
||||
- `NOT NULL` enligt SQL-definitionerna ovan,
|
||||
- standardvärdena `CURRENT_TIMESTAMP`, `'pending'`, `'site_import'` och
|
||||
`'manual'` enligt SQL-definitionerna ovan,
|
||||
- `CHECK` för icke-negativ `age` och `duration_seconds` samt `rating` i
|
||||
intervallet 0–10,
|
||||
- främmande nyckel från `movie_history.movie_id` till `movie.id`, med
|
||||
`NO ACTION` för både update och delete,
|
||||
- främmande nyckel från `movie_name_alias.movie_id` till `movie.id`, med
|
||||
`NO ACTION` för både update och delete, och
|
||||
- unikhet för kombinationen
|
||||
`movie_name_alias(movie_id, normalized_alias)`.
|
||||
|
||||
**Verifierat faktum:** Det finns ingen `CHECK`-constraint som begränsar
|
||||
`ocr_status` till de statusvärden som koden använder (`pending`, `completed`,
|
||||
`manual_review` och `failed`).
|
||||
|
||||
**Verifierat faktum:** Främmande nycklar är del av schemat, men SQLite kräver
|
||||
att `PRAGMA foreign_keys = ON` aktiveras per anslutning för enforcement.
|
||||
`import_site.py` aktiverar detta. De övriga inspekterade Python-skripten gör
|
||||
det inte. Detta är ett runtime-beteende, inte en strukturell skillnad mellan
|
||||
produktionsschemat och Git-schemat.
|
||||
|
||||
### 2.4 Triggers och views
|
||||
|
||||
**Verifierat faktum:** Produktionsdatabasen innehåller inga triggers och inga
|
||||
views. Historisering görs uttryckligen av `import_site.py`, inte av en trigger.
|
||||
|
||||
## 3. Schema som kan återskapas från Git i dag
|
||||
|
||||
**Verifierat faktum:** När `scripts/schema.sql` läses i en tom SQLite-databas
|
||||
skapas `movie`, `movie_history`, `movie_name_alias`, den interna
|
||||
`sqlite_sequence`, samma fyra explicita index och samma två automatiska index
|
||||
som i produktionen. Inga triggers eller views skapas.
|
||||
|
||||
Det återskapade Git-schemats `movie` är:
|
||||
|
||||
```sql
|
||||
CREATE TABLE IF NOT EXISTS movie (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
aka TEXT,
|
||||
nationality TEXT,
|
||||
age INTEGER,
|
||||
shoot_location TEXT,
|
||||
shoot_date TEXT,
|
||||
duration_seconds INTEGER,
|
||||
description TEXT,
|
||||
rating REAL,
|
||||
web_url TEXT,
|
||||
thumbnail TEXT,
|
||||
published TEXT,
|
||||
updated TEXT,
|
||||
|
||||
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
modified_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CHECK (age IS NULL OR age >= 0),
|
||||
CHECK (rating IS NULL OR rating BETWEEN 0 AND 10),
|
||||
CHECK (duration_seconds IS NULL OR duration_seconds >= 0)
|
||||
);
|
||||
```
|
||||
|
||||
Git-schemats `movie_history` och `movie_name_alias` är semantiskt identiska
|
||||
med definitionerna i avsnitt 2.1. De omfattar redan samtliga fyra
|
||||
OCR-kolumner i `movie_history`. Git skapar även exakt de fyra explicita index
|
||||
som visas i avsnitt 2.2.
|
||||
|
||||
**Verifierat faktum:** `IF NOT EXISTS` i Git-filen men inte i den lagrade
|
||||
produktionsdefinitionen är ingen strukturell skillnad i den skapade databasen.
|
||||
|
||||
**Verifierat faktum:** Git innehåller ingen versionsmarkör eller
|
||||
migrationshistorik. En nyskapad databas får `user_version = 0`, precis som den
|
||||
inspekterade produktionsdatabasen.
|
||||
|
||||
## 4. Exakt jämförelse
|
||||
|
||||
### 4.1 Skillnader
|
||||
|
||||
**Verifierat faktum:** Den fullständiga strukturella skillnaden är fyra
|
||||
kolumner som finns i produktionens `movie` men saknas i Git-schemats `movie`:
|
||||
|
||||
| Position i produktion | Kolumn | Typ | `NOT NULL` | Standardvärde | Saknas från Git |
|
||||
| ---: | --- | --- | --- | --- | --- |
|
||||
| 16 | `ocr_status` | `TEXT` | ja | `'pending'` | ja |
|
||||
| 17 | `ocr_raw_text` | `TEXT` | nej | inget | ja |
|
||||
| 18 | `ocr_error` | `TEXT` | nej | inget | ja |
|
||||
| 19 | `ocr_processed_at` | `TEXT` | nej | inget | ja |
|
||||
|
||||
Konsekvenserna är konkreta:
|
||||
|
||||
- nya `movie`-rader från `import_site.py` förlitar sig på standardvärdet
|
||||
`'pending'`,
|
||||
- OCR-kön kan inte läsa `ocr_status` eller `ocr_error`,
|
||||
- OCR-resultat kan inte lagras, och
|
||||
- `import_site.py` kan inte läsa en komplett rad för arkivering till
|
||||
`movie_history`.
|
||||
|
||||
### 4.2 Element som inte skiljer sig
|
||||
|
||||
**Verifierat faktum:** Följande matchar exakt semantiskt mellan produktion och
|
||||
en tom databas skapad från Git:
|
||||
|
||||
- tabellerna `movie_history` och `movie_name_alias`, inklusive samtliga
|
||||
kolumner, ordning, typer, nullbarhet och standardvärden,
|
||||
- alla `movie`-kolumner som föregår OCR-kolumnerna,
|
||||
- samtliga tre `CHECK`-constraints på `movie`,
|
||||
- båda främmande nycklarna och deras `NO ACTION`-beteende,
|
||||
- unikhetskravet på `(movie_id, normalized_alias)`,
|
||||
- samtliga fyra explicita index,
|
||||
- båda automatiska unika indexen,
|
||||
- avsaknaden av triggers, och
|
||||
- avsaknaden av views.
|
||||
|
||||
**Verifierat faktum:** Inga tabeller, index, constraints, triggers eller views
|
||||
finns endast i Git-schemat. Den interna tabellen `sqlite_sequence` uppstår i
|
||||
båda fallen och är inte ett gap.
|
||||
|
||||
## 5. Skriptberoenden per schemaelement
|
||||
|
||||
### 5.1 `movie`
|
||||
|
||||
| Skript | Lästa eller skrivna element | Förutsättning |
|
||||
| --- | --- | --- |
|
||||
| `import_site.py` | Alla 20 produktionskolumner läses för befintliga poster; webbägda fält och `modified_at` uppdateras; nya poster infogas. | Kräver de fyra OCR-kolumnerna för `SELECT` och historisering. Nya poster förlitar sig på `ocr_status DEFAULT 'pending'`. Förlitar sig även på `movie_history`, dess OCR-kolumner och obligatoriska `change_summary`. Aktiverar främmande nycklar. |
|
||||
| `migrate_csv.py` | Läser `id`; infogar eller uppdaterar alla metadatafält samt `ocr_status`; uppdaterar `modified_at`. | Kräver `ocr_status`. Anger status explicit och förlitar sig därför inte på dess default i sina egna inserts. Skriptet aktiverar inte främmande nycklar. |
|
||||
| `check_ocr.py` | Läser `name`, `thumbnail`; skriver `nationality`, `shoot_location`, `shoot_date`, samtliga fyra OCR-kolumner och `modified_at`. | Kräver alla fyra OCR-kolumnerna. Använder statusvärdena `completed`, `manual_review` och `failed`, men schemat validerar inte statusdomänen. |
|
||||
| `process_pending_ocr.py` | Läser `id`, `name`, `published`, `ocr_status`, `ocr_error`. | Kräver `ocr_status` och `ocr_error`; förutsätter att nya obehandlade poster får status `pending`. |
|
||||
| `match_filenames.py` | Läser `id`, `name`, `duration_seconds`. | Fungerar mot både produktions- och Git-schemat. |
|
||||
| `test_duration_matching.py` | Läser `id`, `name`, `duration_seconds`. | Fungerar mot både produktions- och Git-schemat. |
|
||||
|
||||
### 5.2 `movie_history`
|
||||
|
||||
| Skript | Beroende |
|
||||
| --- | --- |
|
||||
| `import_site.py` | Infogar den föregående kompletta `movie`-raden, inklusive samtliga OCR-fält, samt `change_source` och `change_summary`. `archived_at` lämnas till standardvärdet. |
|
||||
| `match_filenames.py` | Läser `movie_id`, `duration_seconds` och `archived_at`. |
|
||||
| `test_duration_matching.py` | Läser `movie_id` och `duration_seconds`. |
|
||||
|
||||
**Verifierat faktum:** Inget skript förutsätter en historiseringstrigger;
|
||||
`import_site.py` gör historik-insert och film-update i samma anslutningskontext.
|
||||
|
||||
### 5.3 `movie_name_alias`
|
||||
|
||||
| Skript | Beroende |
|
||||
| --- | --- |
|
||||
| `match_filenames.py` | Läser `movie_id`, `alias`, `normalized_alias` och `source`. |
|
||||
|
||||
**Verifierat faktum:** Inget inspekterat skript skriver alias. Constraints och
|
||||
index på aliastabellen finns ändå likadant i produktion och Git.
|
||||
|
||||
## 6. Minsta säkra åtgärd
|
||||
|
||||
**Rekommendation:** Gör en enda avgränsad schemaändring: lägg till de fyra
|
||||
verifierade produktionskolumnerna i `movie` i `scripts/schema.sql`, direkt
|
||||
efter `modified_at`, med exakt följande definitioner:
|
||||
|
||||
```sql
|
||||
ocr_status TEXT NOT NULL DEFAULT 'pending',
|
||||
ocr_raw_text TEXT,
|
||||
ocr_error TEXT,
|
||||
ocr_processed_at TEXT,
|
||||
```
|
||||
|
||||
Detta är den minsta åtgärden eftersom alla andra tabeller, index och
|
||||
constraints redan reproduceras. Lägg inte samtidigt till triggers, views,
|
||||
nya index, status-`CHECK`, ändrad främmande-nyckelpolicy eller andra
|
||||
modellförbättringar; sådana ändringar skulle gå utöver att återskapa dagens
|
||||
produktion.
|
||||
|
||||
**Rekommendation:** Behandla den korrigerade `schema.sql` som ett komplett
|
||||
grundschema för helt nya databaser. Verifiera det först mot en ny temporär
|
||||
databas. Kör inte den kompletta filen som en påstådd reparationsmigration mot
|
||||
produktion: `CREATE TABLE IF NOT EXISTS` kompletterar inte kolumner i en redan
|
||||
existerande tabell.
|
||||
|
||||
**Antagande:** Produktionsdatabasen behöver ingen strukturändring för detta
|
||||
gap, eftersom den redan har exakt de fyra föreslagna kolumnerna. Den behöver
|
||||
endast adopteras av en framtida migrationsmodell efter separat kontroll.
|
||||
|
||||
## 7. Enkel migrationsmodell för SQLite
|
||||
|
||||
**Rekommendation:** Inför en liten, versionshanterad modell med två delar:
|
||||
|
||||
1. `schema.sql` är aktuell baseline för nya, tomma databaser.
|
||||
2. En katalog med ordnade migrationer, exempelvis
|
||||
`migrations/0001_add_movie_ocr_columns.sql`, beskriver övergångar för äldre
|
||||
databaser.
|
||||
|
||||
Använd en liten Python-runner baserad på standardbibliotekets `sqlite3` och en
|
||||
tabell som exempelvis:
|
||||
|
||||
```sql
|
||||
CREATE TABLE schema_migration (
|
||||
version INTEGER PRIMARY KEY,
|
||||
name TEXT NOT NULL UNIQUE,
|
||||
applied_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
```
|
||||
|
||||
Runnern bör:
|
||||
|
||||
- öppna en explicit vald databas och aldrig ha produktionssökvägen som dold
|
||||
teststandard,
|
||||
- aktivera `PRAGMA foreign_keys = ON`,
|
||||
- kontrollera befintliga kolumner med `PRAGMA table_xinfo` före migration,
|
||||
- köra varje ej registrerad migration atomärt i en transaktion,
|
||||
- registrera versionen först när hela migrationen lyckats,
|
||||
- avbryta om databasen har en okänd eller partiell struktur, och
|
||||
- köra `PRAGMA foreign_key_check` efteråt.
|
||||
|
||||
**Rekommendation:** Eftersom SQLite saknar portabelt
|
||||
`ALTER TABLE ... ADD COLUMN IF NOT EXISTS` ska migration `0001` inte bara
|
||||
exekveras blint. Runnern ska skilja mellan tre tillstånd:
|
||||
|
||||
- inga OCR-kolumner finns: lägg till alla fyra och registrera migrationen,
|
||||
- alla fyra finns med exakt rätt typ, nullbarhet och default: registrera
|
||||
migrationen som adopterad utan att ändra tabellen, och
|
||||
- endast några finns eller definitionerna avviker: avbryt och kräv manuell
|
||||
analys.
|
||||
|
||||
Exempel på de faktiska övergångssatserna för ett äldre Git-skapat schema är:
|
||||
|
||||
```sql
|
||||
ALTER TABLE movie
|
||||
ADD COLUMN ocr_status TEXT NOT NULL DEFAULT 'pending';
|
||||
ALTER TABLE movie
|
||||
ADD COLUMN ocr_raw_text TEXT;
|
||||
ALTER TABLE movie
|
||||
ADD COLUMN ocr_error TEXT;
|
||||
ALTER TABLE movie
|
||||
ADD COLUMN ocr_processed_at TEXT;
|
||||
```
|
||||
|
||||
**Rekommendation:** Använd antingen `schema_migration` eller konsekvent
|
||||
`PRAGMA user_version` som versionskälla, inte två oberoende sanningar. Tabellen
|
||||
är enklare att granska eftersom den även lagrar migrationsnamn och tidpunkt.
|
||||
Nuvarande `user_version = 0` visar att ingen versionsmodell används i dag.
|
||||
|
||||
## 8. Verifieringsplan utan produktionsskrivningar
|
||||
|
||||
Alla steg nedan ska använda en ny katalog skapad med `mktemp -d` och en ny
|
||||
databasfil där. Ingen testparameter får peka på
|
||||
`/storage/disk1/WCX/database/wcx.db`.
|
||||
|
||||
1. **Skydda referensen.** Registrera produktionsdatabasens filstorlek, mtime
|
||||
och kryptografiska hash före testet. Använd därefter endast
|
||||
`sqlite3 -readonly` om referensmetadata behöver läsas igen.
|
||||
2. **Skapa baseline.** Skapa `${TEMP_DIR}/wcx.db` från den korrigerade
|
||||
`schema.sql`.
|
||||
3. **Jämför struktur maskinellt.** Jämför tabeller, kolumner, ordning, typer,
|
||||
nullbarhet, defaults, primärnyckelflaggor, främmande nycklar, explicita och
|
||||
automatiska index, indexkolumner, triggers och views mot den dokumenterade
|
||||
produktionsmodellen. Ignorera endast harmlösa skillnader i SQL-format och
|
||||
`IF NOT EXISTS`.
|
||||
4. **Kontrollera constraints.** Verifiera att ogiltig negativ `age` och
|
||||
`duration_seconds`, rating utanför 0–10, dubblett av
|
||||
`(movie_id, normalized_alias)` samt främmande nyckelbrott med
|
||||
`PRAGMA foreign_keys = ON` nekas i temporärdatabasen.
|
||||
5. **Kontrollera defaults.** Infoga en minimal `movie(id, name)` och verifiera
|
||||
att `created_at` och `modified_at` sätts samt att `ocr_status = 'pending'`
|
||||
och övriga OCR-fält är `NULL`.
|
||||
6. **Testa import.** Kör `import_site.py` med explicit `--input` till en liten
|
||||
temporär JSON-fil och explicit `--database` till temporärdatabasen. Kör en
|
||||
andra import med nyare uppdateringsdatum och verifiera att
|
||||
`movie_history` får hela den föregående raden inklusive OCR-fälten.
|
||||
7. **Testa CSV-operationer.** Kör först `migrate_csv.py --dry-run`. Om apply
|
||||
behöver provas, använd endast explicit `--database` till temporärdatabasen
|
||||
och en isolerad test-CSV.
|
||||
8. **Testa OCR:s databasberoenden utan API-anrop.** Anropa de rena
|
||||
databasfunktionerna i `check_ocr.py` mot temporärdatabasen för statusfallen
|
||||
failed, manual review och completed. Verifiera alla skrivna metadata- och
|
||||
OCR-kolumner. Testa `process_pending_ocr.py`-frågorna separat så att
|
||||
`pending` kan väljas och `ocr_error` läsas. Gör inget Vision API-anrop.
|
||||
9. **Testa konsumentfrågorna.** Kör matchningskodens databasladdning och
|
||||
längddiagnostik mot temporärdatabasen efter att film, alias och historik har
|
||||
lagts in.
|
||||
10. **Testa migrationsvägen separat.** Skapa ytterligare en ny temporär
|
||||
databas från dagens ofullständiga Git-schema, kör migration `0001`, och
|
||||
upprepa strukturjämförelsen. Testa även adoption mot en ny databas som
|
||||
redan har alla fyra korrekta kolumner samt avbrott mot ett medvetet
|
||||
partiellt schema.
|
||||
11. **Kör integritetskontroller.** Kräv `PRAGMA integrity_check = 'ok'` och
|
||||
tomt resultat från `PRAGMA foreign_key_check` i varje temporärdatabas.
|
||||
12. **Bekräfta produktionsskyddet.** Kontrollera att produktionens storlek,
|
||||
mtime och hash är identiska med värdena från steg 1. Radera därefter endast
|
||||
den explicit skapade temporärkatalogen.
|
||||
|
||||
**Rekommendation:** Godkänn reproducerbarheten först när både baseline-vägen
|
||||
och migrationsvägen ger samma semantiska schema som avsnitt 2 och samtliga
|
||||
databasberoende skript klarar sina isolerade operationer mot temporärdata.
|
||||
|
||||
## 9. Slutsats
|
||||
|
||||
**Verifierat faktum:** Facitdatabasen är nästan, men inte helt,
|
||||
reproducerbar från Git. Gapet består exakt av fyra OCR-kolumner i `movie`.
|
||||
Inga tabeller, index, constraints, triggers eller views saknas utöver dessa
|
||||
kolumndefinitioner, och inga sådana objekt skiljer sig i övrigt.
|
||||
|
||||
**Rekommendation:** Komplettera baselineschemat med de fyra exakta
|
||||
produktionsdefinitionerna och inför en liten, transaktionell migrationsrunner
|
||||
som kan migrera äldre Git-skapade databaser och adoptera en redan korrekt
|
||||
produktion utan att ändra den. All utveckling och verifiering ska ske mot nya
|
||||
temporära databaser.
|
||||
Reference in New Issue
Block a user