feat: consolidate database management work
Add catalog-owned logical relationships and runtime snapshots, extend the database-management UI and validation coverage, and document the updated operational workflow. Keep active sensitive-generation status in a tooltip and indicator, and update the layout E2E to follow the history action in its new database-scoped location.
This commit is contained in:
@@ -0,0 +1,299 @@
|
||||
# Visualizzatore ERD nel browser: proposta solo open source
|
||||
|
||||
Data dell'audit: 31 agosto 2026.
|
||||
|
||||
## Decisione
|
||||
|
||||
Per ThothII sceglierei **AntV X6 + ELK.js** come fondazione del visualizzatore ERD.
|
||||
|
||||
- **AntV X6** è MIT, non ha una distinta edizione Professional e include nel progetto OSS
|
||||
le funzioni che servono davvero a un diagramma complesso: nodi e porte personalizzabili,
|
||||
pan/zoom, selezione, minimappa, history, clipboard, tastiera, routing, virtual rendering ed
|
||||
export SVG/PNG/JPEG.
|
||||
- **ELK.js** è il motore di layout automatico, EPL-2.0 e senza tier commerciale. Il layout
|
||||
layered gestisce porte, vincoli sull'ordine delle porte, archi multipli, self-loop e routing
|
||||
ortogonale: proprietà importanti per foreign key composite e schemi affollati.
|
||||
- La combinazione non dipende da esempi o componenti a pagamento. Il lavoro applicativo che
|
||||
resta da fare — semantica ER, livelli di dettaglio, filtri e adattatore X6/ELK — appartiene
|
||||
davvero al dominio di ThothII e non è una funzione nascosta dietro un piano Pro.
|
||||
|
||||
La seconda scelta è **React Flow + ELK.js**. È probabilmente l'integrazione più naturale con
|
||||
l'attuale frontend React e ha una migliore storia documentata per accessibilità. Il runtime è
|
||||
MIT e non viene tecnicamente depotenziato, ma diversi pattern avanzati già implementati
|
||||
(auto-layout, edge routing, undo/redo, copy/paste, expand/collapse) sono pubblicati come esempi
|
||||
con licenza React Flow Pro. Non è quindi la scelta più lineare se il criterio prioritario è
|
||||
evitare anche una dipendenza progettuale da materiale Professional.
|
||||
|
||||
## Cosa significa «open source senza limitazioni Professional»
|
||||
|
||||
L'audit distingue tre casi:
|
||||
|
||||
1. **OSS completo**: licenza OSI e nessun tier commerciale che trattenga funzioni essenziali.
|
||||
2. **OSS con caveat**: il motore è aperto, ma esistono esempi premium, API instabili o limiti
|
||||
architetturali rilevanti. Può essere usato, purché il limite sia accettato esplicitamente.
|
||||
3. **Escluso**: licenza proprietaria oppure core aperto con funzioni necessarie al viewer
|
||||
complesso riservate alla versione commerciale.
|
||||
|
||||
La valutazione riguarda sia la licenza sia ciò che si può effettivamente costruire senza
|
||||
acquistare un prodotto complementare. Le note sulle licenze sono tecniche, non consulenza legale.
|
||||
|
||||
## Matrice di selezione
|
||||
|
||||
| Soluzione | Licenza OSS | Tier Pro rilevante | Adeguatezza a ERD complessi | Verdetto |
|
||||
|---|---|---|---|---|
|
||||
| **AntV X6 + ELK.js** | MIT + EPL-2.0 | Nessuno individuato | Porte per colonna, minimappa, routing, export, virtual rendering e layout avanzato | **Consigliata** |
|
||||
| **React Flow + ELK.js** | MIT + EPL-2.0 | Esempi/pattern avanzati Pro, non runtime separato | Ottima UX React, nodi HTML, minimappa e accessibilità; export SVG non nativo | **Valida con caveat** |
|
||||
| **maxGraph** | Apache-2.0 | Nessuno | Molto completo, SVG, porte, folding e layout; API imperativa e integrazione React costosa | **Valida con caveat** |
|
||||
| **Cytoscape.js + ELK** | MIT | Nessuno | Molto scalabile come grafo, ma meno adatto a tabelle ricche e porte per campo | **Alternativa di nicchia** |
|
||||
| **Liam ERD/CLI** | Apache-2.0 | Nessuno | Viewer ERD già rifinito e self-hostable; il package embeddable è interno e instabile | **Reference o app separata** |
|
||||
| **Graphviz + Viz.js** | EPL-2.0 + MIT | Nessuno | Layout ed SVG eccellenti; non offre da solo un explorer applicativo | **Renderer di export** |
|
||||
| **Mermaid.js + Panzoom** | MIT + MIT/ISC | Mermaid Chart è separato | Facile, ma insufficiente per interazioni e modelli ER ricchi | **Solo preview semplice** |
|
||||
| **Sprotty/GLSP** | EPL-2.0 | Nessuno | Potente e completo, ma è un framework di modeling più pesante del necessario | **Non prioritario** |
|
||||
| **JointJS** | Core MPL-2.0 | Molte funzioni utili sono in JointJS+ | Lo split commerciale intercetta proprio le necessità del viewer | **Esclusa** |
|
||||
| **GoJS** | Proprietaria | Licenza di deployment | Completa tecnicamente, ma non open source | **Esclusa** |
|
||||
| **yFiles** | Proprietaria | Licenza commerciale | Completa tecnicamente, ma non open source | **Esclusa** |
|
||||
|
||||
## 1. Scelta principale: AntV X6 + ELK.js
|
||||
|
||||
### Funzioni disponibili nell'open source
|
||||
|
||||
X6 offre un canvas diagrammatico basato su SVG con supporto anche a nodi HTML/React. Il core
|
||||
espone porte, archi personalizzabili, self-loop e multiedge. I plugin distribuiti dal progetto
|
||||
comprendono Scroller, MiniMap, Selection, History, Clipboard, Keyboard, DnD, Stencil, Snapline,
|
||||
Transform ed Export. La documentazione copre inoltre router `orth`, `manhattan` ed `er`, zoom e
|
||||
panning. Non è emersa un'edizione X6 Pro che renda a pagamento queste capacità.
|
||||
|
||||
Fonti primarie: [repository e licenza X6](https://github.com/antvis/X6),
|
||||
[porte](https://x6.antv.antgroup.com/en/tutorial/basic/port),
|
||||
[router](https://x6.antv.antgroup.com/en/api/registry/router),
|
||||
[minimappa](https://x6.antv.antgroup.com/en/tutorial/plugins/minimap),
|
||||
[scroller](https://x6.antv.antgroup.com/en/tutorial/plugins/scroller) ed
|
||||
[export](https://x6.antv.antgroup.com/en/tutorial/plugins/export).
|
||||
|
||||
ELK non è un renderer: calcola la geometria del grafo. L'algoritmo layered supporta porte e
|
||||
relativi vincoli, archi ortogonali, self-loop e archi multipli. Va eseguito in un Web Worker per
|
||||
non bloccare l'interfaccia sui modelli grandi. L'adattatore dovrà passare a ELK le porte delle
|
||||
colonne e usare anche le `edge sections` e i bend point restituiti, non soltanto le coordinate
|
||||
dei nodi.
|
||||
|
||||
Fonti primarie: [ELK.js e licenza](https://github.com/kieler/elkjs) e
|
||||
[ELK layered](https://eclipse.dev/elk/reference/algorithms/org-eclipse-elk-layered.html).
|
||||
|
||||
### Limiti reali
|
||||
|
||||
- X6 è più imperativo di React Flow: conviene incapsularlo in un singolo componente React con
|
||||
un adapter stabile, evitando di spargere istanze e listener nel resto dell'applicazione.
|
||||
- L'accessibilità per screen reader non è documentata al livello di React Flow; tab order,
|
||||
focus, descrizioni ARIA e navigazione da tastiera richiedono test e lavoro applicativo.
|
||||
- Un nodo React/HTML può introdurre `foreignObject` nell'SVG. Per un export portabile e
|
||||
stampabile è meglio produrre un SVG separato dallo stesso modello e dalla geometria ELK.
|
||||
|
||||
Questi sono costi tecnici, non limitazioni commerciali.
|
||||
|
||||
## 2. Seconda scelta: React Flow + ELK.js
|
||||
|
||||
`@xyflow/react` è MIT. Custom nodes React, handle multipli, pan/zoom, fit view, Controls,
|
||||
MiniMap, selezione e funzioni di accessibilità sono nel runtime OSS. React Flow Pro vende
|
||||
supporto, template ed esempi con relativo codice sorgente; non esiste una libreria runtime Pro
|
||||
che sblocchi il canvas.
|
||||
|
||||
Il caveat è comunque concreto: auto-layout, edge routing, undo/redo, copy/paste ed
|
||||
expand/collapse compaiono nel catalogo degli esempi Pro e quel codice usa la **xyflow Pro
|
||||
License**, non la MIT del core. Le stesse funzioni possono essere sviluppate sopra le API OSS,
|
||||
ma non si può considerare tutto il materiale ufficiale liberamente riutilizzabile.
|
||||
|
||||
Fonti primarie: [package React Flow](https://github.com/xyflow/xyflow/blob/main/packages/react/package.json),
|
||||
[funzioni del core](https://reactflow.dev/index),
|
||||
[catalogo degli esempi Pro](https://reactflow.dev/examples/pro-examples),
|
||||
[auto-layout](https://reactflow.dev/examples/layout/auto-layout) e
|
||||
[termini Pro](https://reactflow.dev/pro).
|
||||
|
||||
È una buona scelta se si privilegiano velocità d'integrazione React, componenti HTML e
|
||||
accessibilità. Non è la prima scelta di questo audit perché l'utente ha chiesto espressamente
|
||||
di minimizzare la distanza fra ciò che è open source e l'offerta Professional.
|
||||
|
||||
Altro limite: React Flow combina nodi DOM e archi SVG. L'esempio ufficiale di download usa
|
||||
`html-to-image`, quindi il diagramma interattivo non si traduce automaticamente in un SVG puro.
|
||||
|
||||
## 3. Altre soluzioni interamente open source
|
||||
|
||||
### maxGraph
|
||||
|
||||
maxGraph, successore TypeScript di mxGraph, è Apache-2.0 e non ha un piano Pro. Offre rendering
|
||||
SVG, connection constraints/porte, loop e multigraph, layout, routing, outline, grouping e
|
||||
folding. Può quindi sostenere un editor ERD ricco.
|
||||
|
||||
Lo terrei come terza scelta: l'API è imperativa, non esiste un wrapper React ufficiale, la
|
||||
virtualizzazione non è documentata e gran parte dell'accessibilità va costruita. Il progetto è
|
||||
ancora pre-1.0. Il costo di integrazione e manutenzione sarebbe maggiore di X6.
|
||||
|
||||
Fonti: [licenza e repository](https://github.com/maxGraph/maxGraph),
|
||||
[guida Vite/TypeScript](https://maxgraph.github.io/maxGraph/docs/getting-started/) e
|
||||
[gestione della complessità](https://maxgraph.github.io/maxGraph/docs/usage/group-and-complexity-management/).
|
||||
|
||||
### Cytoscape.js
|
||||
|
||||
Cytoscape.js e molte estensioni sono MIT, senza tier professionale. È ottimo per grandi grafi,
|
||||
filtri, selezioni e algoritmi di rete. Il rendering Canvas è però meno naturale per schede-tabella
|
||||
ricche, testo selezionabile e handle collegati alle singole colonne. Inoltre l'adapter
|
||||
`cytoscape.js-elk` non passa le porte a ELK né usa le route degli archi: non risolve da solo il
|
||||
problema delle foreign key per colonna.
|
||||
|
||||
È appropriato per una vista di dipendenze ad alto livello, non come renderer ERD principale.
|
||||
L'estensione `cytoscape-svg` è GPL-3.0; in un prodotto che non vuole assorbire quel vincolo è
|
||||
preferibile una pipeline Graphviz/Viz.js o un generatore SVG proprio.
|
||||
|
||||
Fonti: [Cytoscape.js](https://github.com/cytoscape/cytoscape.js),
|
||||
[adapter ELK](https://github.com/cytoscape/cytoscape.js-elk) e
|
||||
[cytoscape-svg](https://github.com/kinimesi/cytoscape-svg).
|
||||
|
||||
### Liam ERD
|
||||
|
||||
Liam ERD è Apache-2.0, self-hostable e già orientato al problema: pan/zoom, ricerca, filtro,
|
||||
command palette e modalità `TABLE_NAME`, `KEY_ONLY`, `ALL_FIELDS`. La documentazione dichiara
|
||||
supporto a schemi con oltre cento tabelle. La CLI può generare un'app Vite statica.
|
||||
|
||||
Non userei però direttamente `@liam-hq/erd-core` dentro ThothII: il maintainer lo definisce una
|
||||
dipendenza interna, ne sconsiglia l'uso diretto e avverte che l'API può cambiare; il package è
|
||||
ancora 0.x. Inoltre occorre verificare la compatibilità con la versione React di ThothII e la
|
||||
fedeltà delle foreign key composite. Liam è quindi un ottimo benchmark UX, una CLI per una vista
|
||||
separata o una base da forkare consapevolmente, non l'interfaccia stabile su cui fondare il
|
||||
prodotto.
|
||||
|
||||
Fonti: [repository Liam](https://github.com/liam-hq/liam),
|
||||
[README di erd-core](https://github.com/liam-hq/liam/blob/main/frontend/packages/erd-core/README.md),
|
||||
[funzioni UI](https://liambx.com/docs/ui-features) e [CLI](https://liambx.com/docs/cli).
|
||||
|
||||
### Sprotty/GLSP
|
||||
|
||||
Sprotty e Eclipse GLSP sono EPL-2.0 e offrono un'infrastruttura seria per diagrammi SVG,
|
||||
layout e protocolli client/server. Sono pensati per strumenti di modeling estensibili, con
|
||||
dependency injection e un'architettura più ampia di un viewer. Restano una soluzione OSS valida,
|
||||
ma sproporzionata per questa esigenza salvo che ThothII evolva in un vero editor di modelli.
|
||||
|
||||
Fonti: [Sprotty](https://github.com/eclipse-sprotty/sprotty) ed
|
||||
[Eclipse GLSP](https://eclipse.dev/glsp/documentation/overview/).
|
||||
|
||||
## 4. Renderer complementari, non fondazioni del viewer
|
||||
|
||||
### Graphviz e Viz.js
|
||||
|
||||
Graphviz è EPL-2.0; `@viz-js/viz`, il port WebAssembly utilizzabile nel browser, è MIT. Graphviz
|
||||
produce SVG di alta qualità, supporta label HTML-like e porte e rimane molto utile per export,
|
||||
stampa o una vista read-only. Non fornisce però da solo ricerca, filtri, livelli di dettaglio,
|
||||
editing o gestione dello stato applicativo.
|
||||
|
||||
Lo userei come renderer di export alternativo, non come UI primaria. Se il layout a schermo
|
||||
deve corrispondere esattamente all'export, è preferibile generare l'SVG direttamente dalla
|
||||
geometria ELK invece di mantenere due motori di layout indipendenti.
|
||||
|
||||
Fonti: [licenza Graphviz](https://graphviz.org/license/),
|
||||
[formati SVG](https://graphviz.org/docs/outputs/svg/) e
|
||||
[Viz.js](https://github.com/mdaines/viz-js).
|
||||
|
||||
### Mermaid.js e librerie di pan/zoom
|
||||
|
||||
Mermaid.js è MIT e non ha feature del renderer open source bloccate. **Mermaid Chart** è invece
|
||||
un servizio commerciale distinto e il suo piano gratuito ha limiti: non va confuso con la
|
||||
libreria self-hosted.
|
||||
|
||||
Si può aggiungere navigazione a un SVG Mermaid con `@panzoom/panzoom` (MIT) o `d3-zoom` (ISC),
|
||||
entrambi senza tier Pro. Questo migliora l'esperienza corrente, ma non supera i limiti strutturali
|
||||
del diagramma ER Mermaid: interazioni a livello di tabella, controllo ridotto delle porte e del
|
||||
routing, assenza di semantic zoom e difficoltà crescente su schemi molto grandi.
|
||||
|
||||
Mermaid resta adatto a preview e documentazione, non al viewer finale.
|
||||
|
||||
Fonti: [Mermaid.js](https://github.com/mermaid-js/mermaid),
|
||||
[distinzione da Mermaid Chart](https://mermaid.ai/open-source/ecosystem/mermaid-chart.html),
|
||||
[Panzoom](https://github.com/timmywil/panzoom) e [d3-zoom](https://github.com/d3/d3-zoom).
|
||||
|
||||
### Python
|
||||
|
||||
SchemaSpy ed ERAlchemy sono open source e possono generare documentazione o grafi attraverso
|
||||
Graphviz, ma non sostituiscono il componente interattivo React. Python è utile lato backend per
|
||||
normalizzare metadati o produrre artefatti batch; pan/zoom, selezione, ricerca e dettagli restano
|
||||
responsabilità del browser. Aggiungere un servizio Python solo per disegnare l'ERD non porta un
|
||||
vantaggio architetturale a ThothII.
|
||||
|
||||
## 5. Soluzioni escluse
|
||||
|
||||
### JointJS / JointJS+
|
||||
|
||||
Il core JointJS è MPL-2.0, ma JointJS+ è commerciale e comprende proprio molte funzioni che
|
||||
servirebbero qui: PaperScroller, Navigator/minimappa, selection, clipboard, keyboard, undo/redo,
|
||||
toolbar, export e layout aggiuntivi. Sarebbe tecnicamente possibile ricostruirle sul core, ma la
|
||||
distanza fra OSS e Professional è troppo grande rispetto al criterio richiesto.
|
||||
|
||||
Fonti: [licenza](https://www.jointjs.com/license),
|
||||
[confronto delle funzioni](https://www.jointjs.com/features) e
|
||||
[prezzi](https://www.jointjs.com/pricing).
|
||||
|
||||
### GoJS e yFiles
|
||||
|
||||
Sono prodotti completi e maturi, ma non open source. GoJS richiede una licenza per il deployment
|
||||
e mostra una filigrana senza chiave; yFiles usa licenze proprietarie, con evaluation temporanea e
|
||||
licenza necessaria per la distribuzione. Non soddisfano il requisito, quindi non entrano nella
|
||||
shortlist.
|
||||
|
||||
Fonti: [licensing GoJS](https://gojs.net/latest/intro/deployment.html) e
|
||||
[licensing yFiles](https://docs.yworks.com/yfiles-html/dguide/deployment/licensing.html).
|
||||
|
||||
## 6. Architettura proposta per ThothII
|
||||
|
||||
Il frontend usa React 18 e oggi `SchemaLinkingViewer.tsx` genera Mermaid con un limite di 45
|
||||
elementi. La resa corrente perde informazione: accorpa più foreign key fra la stessa coppia di
|
||||
tabelle, omette i self-reference e usa cardinalità generiche. Il catalogo espone già tabelle,
|
||||
colonne, chiavi primarie e relazioni con coppie ordinate di colonne, quindi può alimentare un
|
||||
modello più fedele.
|
||||
|
||||
Propongo questa separazione:
|
||||
|
||||
1. **Modello renderer-neutral**: `TableNode`, `ColumnPort` e `RelationEdge` con nome del
|
||||
constraint e lista ordinata delle coppie sorgente/destinazione. Non appiattire le FK composite.
|
||||
2. **Snapshot API**: un endpoint coerente, per esempio
|
||||
`GET /catalog/databases/:databaseId/schema-diagram`, che restituisca tabelle, colonne,
|
||||
relazioni e versione del catalogo in una sola lettura, evitando la richiesta colonne N+1.
|
||||
3. **Layout worker**: ELK.js in Web Worker, con porte per colonna, port constraints, route degli
|
||||
archi e cache per versione del database e filtri correnti.
|
||||
4. **Renderer interattivo**: X6 incapsulato in `DatabaseRelationshipDiagram.tsx`, affiancato
|
||||
alla vista tabellare con un toggle List/Diagram e con riuso del drawer dei dettagli.
|
||||
5. **Export**: generatore SVG separato basato sullo stesso modello e sulla geometria ELK;
|
||||
Graphviz/Viz.js può essere un fallback per layout alternativi o documentazione batch.
|
||||
|
||||
Il catalogo non registra oggi tutti i vincoli UNIQUE né il tipo MATCH delle FK. Senza questi
|
||||
dati non sempre è possibile distinguere correttamente 1:1 da 1:N. Il renderer non deve inventare
|
||||
la cardinalità: deve mostrare una notazione neutra finché il metadato necessario non viene
|
||||
raccolto.
|
||||
|
||||
### Funzioni necessarie per schemi complessi
|
||||
|
||||
- semantic zoom: solo nomi tabella, poi PK/FK, infine tutti i campi;
|
||||
- ricerca e filtri per schema, tabella, colonna e tipo di relazione;
|
||||
- modalità focus con vicinato a uno o due hop;
|
||||
- evidenziazione della relazione e delle due colonne al passaggio/selezione;
|
||||
- minimappa, fit selection, navigazione da tastiera e pannello dettagli;
|
||||
- distinzione visiva di PK, FK, nullable, composite key, self-loop e relazioni parallele;
|
||||
- layout automatico ricalcolabile, ma posizioni manuali persistibili;
|
||||
- rendering progressivo o virtuale e fallback tabellare sempre disponibile;
|
||||
- export dell'intero schema e dell'area filtrata in SVG/PNG.
|
||||
|
||||
## 7. Proof of concept consigliato
|
||||
|
||||
Un POC breve dovrebbe usare **X6 + ELK.js** su tre dataset sintetici: circa 30, 150 e 500
|
||||
tabelle, includendo FK composite, più FK tra la stessa coppia, self-loop, tabelle isolate e hub
|
||||
ad alto grado.
|
||||
|
||||
I criteri di accettazione dovrebbero misurare:
|
||||
|
||||
- tempo di layout nel worker e tempo al primo frame interattivo;
|
||||
- fluidità di pan/zoom e uso memoria sul caso da 500 tabelle;
|
||||
- correttezza di porte, archi paralleli, self-loop e FK composite;
|
||||
- leggibilità nelle tre soglie di semantic zoom;
|
||||
- navigazione completa da tastiera e comportamento con screen reader;
|
||||
- qualità e portabilità dell'SVG esportato;
|
||||
- assenza di codice, esempi o componenti soggetti a licenza commerciale.
|
||||
|
||||
Se X6 non raggiunge il livello di accessibilità richiesto senza un costo eccessivo, il confronto
|
||||
finale va fatto con React Flow + lo stesso adapter ELK e lo stesso modello dati. In questo modo
|
||||
si cambia renderer senza rifare API, semantica delle relazioni o pipeline di export.
|
||||
@@ -0,0 +1,246 @@
|
||||
# Gestione delle relationship: da ThothAI a ThothII
|
||||
|
||||
**Stato:** analisi e direzione funzionale/UX confermate nel *grill with docs*; non costituisce ancora un piano di implementazione.
|
||||
**Revisione esaminata:** commit ThothII `f586152636b1fd653b0bc1d40b54be7f89bbd2bb`. I sorgenti legacy di ThothAI citati sotto sono versionati nello stesso repository, sotto `Thoth/ThothAI/`.
|
||||
**Ambito:** import delle foreign key fisiche, inferenza di relationship logiche, modifica umana, pubblicazione verso il workflow NL→SQL e principali gap tra i due sistemi.
|
||||
|
||||
## Sintesi fattuale
|
||||
|
||||
1. In ThothAI le foreign key dichiarate nel database venivano importate e una procedura separata proponeva relationship basate sul nome dei campi e su una verifica dei valori. Entrambi i percorsi scrivevano però nello stesso modello `Relationship` (`Thoth/ThothAI/backend/thoth_core/dbmanagement.py:484-640`; `Thoth/ThothAI/backend/thoth_core/admin_models/admin_sqldb.py:571-826`; `Thoth/ThothAI/backend/thoth_core/models.py:488-530`).
|
||||
2. Il ricordo di una relationship inferita persistita come `generated` non trova riscontro nel modello esaminato: `Relationship` contiene solo quattro foreign key verso tabelle e colonne, senza provenienza, stato, confidenza o flag `generated`. La procedura di inferenza calcola localmente pattern e tasso di validazione, ma non li salva (`Thoth/ThothAI/backend/thoth_core/models.py:488-530`; `Thoth/ThothAI/backend/thoth_core/admin_models/admin_sqldb.py:697-801`).
|
||||
3. L'amministratore Django di ThothAI permetteva CRUD manuale sul medesimo insieme di relationship e verificava che gli endpoint appartenessero allo stesso database e alle tabelle selezionate; non distingueva visivamente o semanticamente relationship fisiche, inferite e manuali (`Thoth/ThothAI/backend/thoth_core/admin_models/admin_relationship.py:19-313`).
|
||||
4. ThothII oggi separa già i due concetti: il catalogo backend conserva solo le foreign key fisiche dichiarate, mentre il core/harness possiede annotazioni di foreign key logiche curate e un comando che suggerisce candidate per nome, primary key e SQL osservato (`CONTEXT.md:291-300`; `docs/adr/0006-separate-physical-and-logical-relationships.md:3-8`; `harness/tht/cli/schema_cmd.py:203-299`).
|
||||
5. I due mondi ThothII non sono ancora integrati: i record di Database Management non modificano il workflow NL→SQL e l'integrazione catalogo→core/schema-linking è ancora un gate di design esplicitamente differito (`PROJECT_STATE.md:116-124`; `PROJECT_STATE.md:161-165`).
|
||||
|
||||
## Evidenze ThothAI
|
||||
|
||||
### Foreign key ufficiali
|
||||
|
||||
Il percorso di import legge le foreign key dal database, limita l'import alle tabelle già presenti nel catalogo, crea le colonne mancanti, crea o riusa un record `Relationship` e aggiorna anche le stringhe denormalizzate `pk_field`/`fk_field` sulle colonne (`Thoth/ThothAI/backend/thoth_core/dbmanagement.py:484-640`, in particolare `:501-513`, `:526-591` e `:606-607`).
|
||||
|
||||
**Fatto:** una foreign key fisica diventa quindi un record applicativo, non rimane soltanto un fatto letto al momento dal database.
|
||||
|
||||
### Relationship inferite
|
||||
|
||||
La routine legacy dichiara sei famiglie di confronto tra il nome della colonna candidata e quello della primary key: corrispondenza esatta, snake case, kebab case, camel case, concatenazione e solo nome tabella (`Thoth/ThothAI/backend/thoth_core/admin_models/admin_sqldb.py:571-689`).
|
||||
|
||||
Per una candidata, la routine:
|
||||
|
||||
- legge fino a 20 valori distinti e non nulli dalla colonna candidata;
|
||||
- verifica ogni valore contro la primary key bersaglio;
|
||||
- accetta la candidata quando almeno il 70% dei valori esaminati trova riscontro;
|
||||
- crea o recupera un normale `Relationship` e aggiorna i campi denormalizzati delle tabelle (`Thoth/ThothAI/backend/thoth_core/admin_models/admin_sqldb.py:697-801`).
|
||||
|
||||
**Correzione documentale:** un commento parla di campionamento casuale, ma la query mostrata non contiene un ordinamento casuale; il comportamento verificabile dal codice è “fino a 20 valori distinti e non nulli”, non un campione statisticamente casuale (`Thoth/ThothAI/backend/thoth_core/admin_models/admin_sqldb.py:697-716`).
|
||||
|
||||
**Rischio legacy:** nomi di tabelle e colonne sono interpolati direttamente in SQL in questo percorso. Portare la logica letteralmente in ThothII riprodurrebbe un problema di quoting/sicurezza e richiederebbe inoltre una policy esplicita per l'accesso ai valori del DWH (`Thoth/ThothAI/backend/thoth_core/admin_models/admin_sqldb.py:709-750`).
|
||||
|
||||
### Un solo modello per tre origini
|
||||
|
||||
Il modello `Relationship` legacy contiene soltanto `source_table`, `target_table`, `source_column` e `target_column`, più metodi di rappresentazione/aggiornamento. Non contiene campi per origine, algoritmo, evidenza, confidenza, approvazione o disabilitazione, né un vincolo di unicità dichiarato nel modello (`Thoth/ThothAI/backend/thoth_core/models.py:488-530`).
|
||||
|
||||
L'admin consente aggiunta, modifica e cancellazione ordinarie e valida la coerenza tra database, tabelle e colonne (`Thoth/ThothAI/backend/thoth_core/admin_models/admin_relationship.py:30-38`, `:125-157`, `:218-245`).
|
||||
|
||||
**Conclusione fattuale:** relationship importate, inferite e create manualmente convergono nello stesso tipo persistito. Dal record finale non è possibile ricostruirne con certezza l'origine. Il termine `generated`, se usato nell'interfaccia o nel linguaggio operativo dell'epoca, non era una qualificazione persistita dal modello esaminato.
|
||||
|
||||
### Uso nella comprensione dello schema
|
||||
|
||||
La generazione M-Schema conserva colonne PK/FK e ricostruisce la sezione `【Foreign keys】` analizzando le stringhe denormalizzate `fk_field` (`Thoth/ThothAI/frontend/sql_generator/helpers/main_helpers/main_generate_mschema.py:25-61`, `:94-101`, `:160-187`).
|
||||
|
||||
**Fatto:** le relationship curate nel catalogo legacy influenzavano la rappresentazione dello schema consumata dal generatore SQL, anche se tramite una proiezione denormalizzata.
|
||||
|
||||
## Stato attuale di ThothII
|
||||
|
||||
### Catalogo backend: relationship fisiche
|
||||
|
||||
Il modello di dominio corrente distingue esplicitamente:
|
||||
|
||||
- **Physical Relationship:** vincolo dichiarato nel database;
|
||||
- **Catalog Relationship:** copia persistita di quel fatto fisico, non creabile o modificabile manualmente ma eliminabile per cleanup;
|
||||
- **Logical Relationship:** relazione semantica curata o inferita, con ownership e lifecycle separati (`CONTEXT.md:291-300`; `docs/adr/0006-separate-physical-and-logical-relationships.md:3-8`).
|
||||
|
||||
La migrazione del catalogo crea `catalog_relationships` e le coppie ordinate in `catalog_relationship_columns`. L'identità della relazione fisica è `(source_table_id, constraint_name)`; non esistono campi di origine, stato o confidenza (`backend/src/catalog/migrations/003_catalog_schema_sync.ts:42-74`). Il tipo TypeScript è solo strutturale e le descrizioni generate riguardano esclusivamente tabelle o colonne (`backend/src/catalog/types.ts:123-152`).
|
||||
|
||||
L'introspezione PostgreSQL legge soltanto `pg_constraint` con `contype = 'f'` e mantiene l'ordine delle coppie per le chiavi composite (`backend/src/catalog/schema-introspector.ts:110-171`; `backend/src/catalog/schema-introspector.ts:360-416`; `docs/contracts/catalog-schema-snapshot.md:40-53`). La sincronizzazione autoritativa crea, aggiorna o elimina le copie fisiche in base allo snapshot (`backend/src/catalog/repository.ts:1165-1218`; `backend/src/catalog/repository.ts:1316-1393`).
|
||||
|
||||
Le API e la UI espongono lettura, sync e cleanup in massa, non CRUD logico per singola relationship (`backend/src/routes/catalog-schema.ts:25-37`; `backend/src/routes/catalog-schema.ts:134-150`; `frontend/src/api/catalog-databases.ts:452-489`; `frontend/src/shell/database-management/DatabaseRelationships.tsx:114-123`; `frontend/src/shell/database-management/DatabaseRelationships.tsx:160-225`).
|
||||
|
||||
**Effetto rilevante:** il cleanup è intenzionalmente reversibile tramite una sync successiva e può lasciare temporaneamente un catalogo incompleto (`docs/adr/0008-allow-manual-catalog-metadata-cleanup.md:7-16`). Un test di integrazione ammette anche una relationship rimasta senza coppie di colonne dopo la cancellazione delle colonne catalogate (`backend/test/catalog-repository.integration.test.ts:194-264`, in particolare `:246-252`). Un futuro consumer non può quindi assumere che ogni stato intermedio del catalogo sia pubblicabile così com'è.
|
||||
|
||||
### Core/harness: relationship logiche e suggerimenti
|
||||
|
||||
Il modello M-Schema del core possiede già `TableAnnotation.foreign_keys`, descritte come foreign key logiche curate e unite alle foreign key fisiche (`harness/tht/mschema/models.py:35-39`; `harness/tht/mschema/models.py:76-84`). Il renderer fonde i due insiemi e li presenta insieme nella sezione `【Foreign keys】` (`harness/tht/mschema/render.py:9-20`; `harness/tht/mschema/render.py:46-80`).
|
||||
|
||||
Il comando `schema suggest-fks` costruisce candidate da:
|
||||
|
||||
- uguaglianze trovate in SQL, quando esattamente un lato è una primary key;
|
||||
- convenzione speciale `*time_key → dim_time.<PK singola>`;
|
||||
- stesso nome tra colonna e primary key a proprietario univoco;
|
||||
- assunzioni esplicite per disambiguare;
|
||||
- esclusione di nomi PK generici come `id`, `key` e `code` e dei proprietari ambigui (`harness/tht/cli/schema_cmd.py:203-299`; `harness/tht/mschema/fkmine.py:1-58`).
|
||||
|
||||
Il comando può produrre un documento candidato e, con `--write`, aggiungere annotazioni mancanti in modo idempotente; il messaggio stesso chiede revisione manuale (`harness/tht/cli/schema_cmd.py:406-429`; `harness/tht/cli/schema_cmd.py:519-540`; `harness/tests/test_schema_fk_annotations.py:131-146`; `harness/tests/test_schema_fk_annotations.py:454-470`).
|
||||
|
||||
**Fatto:** ThothII non parte da zero sull'inferenza. Possiede già un motore più conservativo, basato su PK univoche e SQL osservato, ma non conserva per ogni relazione origine, evidenza, frequenza o confidenza. Il miner conta le occorrenze internamente, ma il modello candidato non promuove quel conteggio a lifecycle persistito (`harness/tht/mschema/fkmine.py:1-58`; `harness/tht/mschema/models.py:35-39`).
|
||||
|
||||
**Rischi strutturali già visibili:**
|
||||
|
||||
- `ForeignKey` ammette liste di colonne, ma non valida che source e target abbiano la stessa cardinalità; il renderer usa `zip`, quindi una relazione malformata può essere troncata silenziosamente (`harness/tht/mschema/models.py:35-39`; `harness/tht/mschema/render.py:76-78`).
|
||||
- il merge evita duplicati rispetto alle FK fisiche iniziali, ma non aggiorna l'insieme `seen` dopo aver aggiunto un'annotazione; due annotazioni logiche uguali possono sopravvivere al merge (`harness/tht/mschema/render.py:9-20`).
|
||||
- il catalogo fisico supporta coppie composite ordinate, mentre le euristiche correnti e legacy sono sostanzialmente unary. La semantica delle candidate composite resta da decidere (`backend/src/catalog/migrations/003_catalog_schema_sync.ts:56-74`; `harness/tht/cli/schema_cmd.py:203-299`).
|
||||
|
||||
### Revisione e pubblicazione correnti
|
||||
|
||||
Le annotazioni canoniche del workspace sono un blob Git. Il flusso pubblico produce candidate, verifica le annotazioni e richiede un'accettazione umana esplicita dopo commit/push/pull; l'accettazione registra digest del candidato e delle annotazioni, revisione e blob (`docs/contracts/workspace-preprocessing-cli.md:89-106`; `backend/src/workspaces/preprocessing-service.ts:202-297`). Il preprocessing successivo procede solo se il digest accettato coincide con le annotazioni correnti (`backend/src/workspaces/preprocessing-service.ts:335-388`).
|
||||
|
||||
**Limite fattuale:** il record di review conserva digest, revisione e blob, ma non attore, motivazione o decisioni per singola candidata (`backend/src/workspaces/preprocessing-state.ts:67-73`).
|
||||
|
||||
Il runtime usa le annotazioni quando renderizza lo schema, ma la ricerca vettoriale indicizza record di tabelle e colonne senza contenuto esplicito delle relationship (`harness/tht/vectorstore/records.py:106-138`; `harness/tht/cli/search_cmd.py:233-273`). Inoltre la vista colonne usata in F4 continua a leggere i commenti fisici, non le annotazioni (`harness/tht/cli/schema_cmd.py:592-621`).
|
||||
|
||||
La review dei join durante una sessione è distinta dalla curatela globale: il reviewer conferma l'insieme dei join oppure chiede una revisione completa; non modifica la mappa canonica delle relationship (`harness/.pi/skills/tht-sessione/SKILL.md:301-341`; `frontend/src/widgets/JoinReviewWidget.tsx:5-84`). Il modello di sessione registra join come due stringhe e una decisione opzionale, senza ID stabile della relationship o coppie ordinate strutturate (`harness/tht/session/models.py:147-170`).
|
||||
|
||||
## Delta e rischi da sottoporre al grill
|
||||
|
||||
| Tema | Fatto documentato | Delta/rischio aperto |
|
||||
|---|---|---|
|
||||
| Origine | ThothAI perdeva l'origine; ThothII separa fisico e logico a livello concettuale | Decidere quale provenienza debba essere persistita per manuale, euristica, SQL osservato e import fisico |
|
||||
| `generated` | Non era un flag del modello ThothAI; in ThothII “Generated Description” è già un termine del catalogo (`CONTEXT.md:302-311`) | Usare `generated` anche per relationship potrebbe creare ambiguità terminologica |
|
||||
| Cancellazione utente | In ThothAI era CRUD sul record unico; in ThothII il cleanup fisico viene ricostruito dalla sync | “Eliminare” può significare cancellare una relazione logica, sopprimere un fatto fisico per il core oppure pulire temporaneamente la copia catalogata: sono operazioni diverse |
|
||||
| Inferenza | ThothAI usava sei pattern e valori DWH; ThothII usa PK univoche, nomi e SQL osservato | Stabilire se sostituire, integrare o non portare il campionamento dei valori; servono policy di dati sensibili, query read-only, quoting e limiti |
|
||||
| Approvazione | ThothII dispone di review Git/digest dell'intero artefatto | Mancano decisioni per candidata, motivazione, attore, sticky rejection, versione algoritmo e gestione dello stale |
|
||||
| Compositi | Il catalogo fisico conserva coppie ordinate; le annotazioni accettano liste | Mancano invarianti forti e una strategia di inferenza/review per join compositi |
|
||||
| Pubblicazione | Catalogo management e runtime core sono oggi separati | Va stabilito se pubblicare tutto, una selezione esplicita o una revisione immutabile; il catalogo può essere incompleto durante cleanup/sync |
|
||||
| UI e ownership | UI catalogo fisico read-only; curatela logica via CLI/Git; review join per sessione separata | Va scelto chi cura la mappa e in quale superficie, senza confondere amministrazione globale e correzione della singola sessione |
|
||||
| Retrieval | Le relationship entrano nel render M-Schema ma non nei record vettoriali | Va deciso se e come influenzano selezione tabelle, ranking e descrizioni, oltre al rendering finale |
|
||||
| Drift | Sync fisica è autoritativa; annotazioni sono una revisione separata | Servono semantiche per endpoint rinominati/eliminati, candidate stale, orphan e riapprovazione |
|
||||
|
||||
Le analisi già presenti nel repository trattano l'integrazione catalogo→core, la selezione pubblicabile e la riparazione degli indici come questioni ancora aperte; le loro proposte non sono decisioni implementate (`docs/research/2026-08-23-thothii-metadata-publication-qdrant-seams.md:20-43`; `docs/research/2026-08-23-thothii-metadata-publication-qdrant-seams.md:239-301`). Anche il piano di migrazione rinvia il lifecycle/admin delle relationship logiche (`docs/plans/2026-08-26-metadata-catalog-from-thothai.md:681-691`).
|
||||
|
||||
## Direzione confermata nel grill
|
||||
|
||||
Il 31 agosto 2026 è stato concordato il seguente flusso minimo:
|
||||
|
||||
1. Le foreign key dichiarate continuano a essere sincronizzate come Catalog Relationship fisiche.
|
||||
2. Le foreign key ipotetiche sono salvate una sola volta come Logical Relationship fra colonna
|
||||
sorgente e colonna destinazione; le tabelle sono ricavate dalle colonne e la UI le presenta nel
|
||||
relativo contesto tabella.
|
||||
3. Una relationship inferita è marcata `generated`; una relationship aggiunta dall'utente non lo è.
|
||||
4. La cancellazione logica conserva il record e impedisce a una ricostruzione di riattivarlo.
|
||||
5. La cancellazione fisica rimuove il record; una ricostruzione può ricrearlo se viene nuovamente
|
||||
inferito.
|
||||
6. La ricostruzione è additiva: conserva le relationship attive già presenti, non rimuove quelle
|
||||
non più inferibili e non modifica le relationship manuali.
|
||||
7. L'inferenza usa nomi, primary key e compatibilità dei tipi. Non campiona valori del DWH.
|
||||
8. La relationship è l'unica fonte di verità: non viene duplicata in stringhe `fk_field` sulle
|
||||
colonne.
|
||||
9. I nomi vengono confrontati senza distinzione fra maiuscole/minuscole e normalizzando snake case,
|
||||
kebab case e camel case. La regola riconosce anche casi come `user_id → users.id`, richiede tipi
|
||||
compatibili e una sola destinazione possibile; riconosce inoltre un nome PK non generico con un
|
||||
unico proprietario e la convenzione `*time_key → dim_time.<PK singola>`. Le colonne sorgenti
|
||||
possono appartenere a PK composite, mentre nomi generici isolati come `id`, `key`, `code` e `pk`
|
||||
non costituiscono evidenza. I casi ambigui vengono ignorati e non si usa fuzzy matching o un LLM.
|
||||
10. La ricostruzione parte da un'azione amministrativa esplicita `Rebuild generated relationships`,
|
||||
separata dalla sincronizzazione dello schema.
|
||||
11. Le relationship cancellate logicamente restano consultabili tramite filtro e possono essere
|
||||
riattivate con un'azione `Restore`.
|
||||
|
||||
Non restano decisioni di dominio aperte per il flusso minimo. La progettazione UX e il seam tecnico
|
||||
sono descritti nelle sezioni seguenti.
|
||||
|
||||
## Lacuna UX emersa nel grill
|
||||
|
||||
La vista Fleet `Relationships` esiste, ma nella UI di produzione non ha oggi un punto di ingresso
|
||||
raggiungibile. La riga del database espone sincronizzazione, tabelle, dettagli, modifica e rimozione,
|
||||
ma non le relationship; inoltre i tab `Overview / Tables / Relationships` appartengono soltanto alla
|
||||
presentazione legacy. Il test di navigazione esistente esercita anch'esso la presentazione legacy,
|
||||
non quella Fleet (`frontend/src/shell/database-management/DatabaseGrid.tsx:122-180`;
|
||||
`frontend/src/shell/database-management/DatabaseForm.tsx:442-475`;
|
||||
`frontend/src/shell/database-management/DatabaseForm.tsx:517-546`;
|
||||
`frontend/src/shell/DatabaseManagementPage.test.tsx:2635-2703`).
|
||||
|
||||
Se aperta programmaticamente, la vista mostra soltanto `Physical relationships` in sola lettura. La
|
||||
toolbar contiene ricerca, conteggio, `Refresh`, un selettore azione con esecuzione esplicita e
|
||||
`Sync history`; la griglia offre unicamente `Details`, che apre il drawer della relationship fisica.
|
||||
Sono assenti ingresso visibile, aggiunta manuale, ricostruzione delle generated relationship, origine,
|
||||
stato attivo/cancellato, cancellazione logica, cancellazione fisica e ripristino
|
||||
(`frontend/src/shell/database-management/DatabaseRelationships.tsx:114-225`).
|
||||
|
||||
I pattern Fleet già consolidati da riutilizzare sono:
|
||||
|
||||
- una sola griglia nel livello corrente, con breadcrumb e controllo Back;
|
||||
- azioni di pagina nel selettore `Choose an action…` con `Run action` e motivo visibile quando
|
||||
indisponibili;
|
||||
- azioni della singola riga nella colonna finale fissata a destra;
|
||||
- form e dettagli in un drawer modeless che restituisce il focus al controllo di origine;
|
||||
- conferme distruttive inline o nel drawer, non tramite una nuova pagina;
|
||||
- toast per accettazione o errore e feedback persistente soltanto per le operazioni lunghe;
|
||||
- card di errore con Retry ed empty state che indica la prossima azione possibile.
|
||||
|
||||
ThothAI non offre un modello UX da copiare. L'inferenza è nascosta fra 21 bulk action della lista
|
||||
database, non mostra avanzamento in tempo reale e restituisce soltanto messaggi a fine richiesta. Il
|
||||
CRUD manuale vive in un'altra schermata Django Admin, non distingue origine o stato, offre soltanto
|
||||
la cancellazione fisica e il form di aggiunta ha una validazione server strutturalmente incoerente
|
||||
con le select popolate dal browser
|
||||
(`Thoth/ThothAI/backend/thoth_core/admin_models/admin_sqldb.py:252-274`;
|
||||
`Thoth/ThothAI/backend/thoth_core/admin_models/admin_relationship.py:58-119`;
|
||||
`Thoth/ThothAI/backend/thoth_core/admin_models/admin_relationship.py:218-313`).
|
||||
|
||||
## UX confermata
|
||||
|
||||
Il 31 agosto 2026 sono state confermate le seguenti scelte:
|
||||
|
||||
1. La colonna Actions della riga database espone un accesso diretto `Relationships`, accanto a
|
||||
`Tables`. La vista conserva breadcrumb e controllo `Back to databases` esistenti.
|
||||
2. La pagina presenta una sola griglia `Relationship map`, contenente relationship `Physical`,
|
||||
`Generated` e `Manual`. Le colonne sono Source table, Source column, Target table, Target column,
|
||||
Origin, Status e Actions. Constraint e regole update/delete rimangono nel drawer delle FK fisiche.
|
||||
3. Un filtro visibile seleziona `Active`, `Excluded` o `All`; il valore predefinito è `Active`. Le FK
|
||||
fisiche sono consultabili ma non modificabili da questa vista.
|
||||
4. `Add relationship` è il pulsante primario visibile nella toolbar. Apre il drawer standard con i
|
||||
quattro campi Source table, Source column, Target table e Target column e i comandi `Cancel` e
|
||||
`Add relationship`. Il flusso minimo gestisce una sola coppia di colonne e mostra la validazione
|
||||
accanto al campo interessato.
|
||||
5. `Rebuild generated relationships` entra nell'attuale selettore `Choose an action…`, insieme a
|
||||
`Synchronize physical relationships` e `Synchronize full schema`, con esecuzione esplicita tramite
|
||||
`Run action`. `Refresh` ricarica la griglia; `Sync history` resta riservato alla sincronizzazione
|
||||
fisica.
|
||||
6. Il drawer di dettaglio contiene le azioni sulle relationship logiche. `Exclude` realizza la
|
||||
cancellazione logica, `Delete permanently` quella fisica e `Restore` riattiva una relationship
|
||||
esclusa. La conferma avviene nel drawer e spiega rispettivamente che la ricostruzione non
|
||||
riattiverà un record escluso e potrà invece ricreare un record eliminato definitivamente.
|
||||
7. La ricostruzione non introduce un nuovo sistema di job o di storico. Durante l'esecuzione mostra
|
||||
`Rebuilding…`; al termine un toast riporta added, already present, excluded e ambiguous. Add,
|
||||
Exclude, Delete permanently e Restore producono toast specifici; gli errori mantengono aperto il
|
||||
contesto corrente. L'empty state propone di ricostruire dai nomi o aggiungere manualmente.
|
||||
8. L'inferenza non usa AI. È codice deterministico nel backend del catalogo, basato su normalizzazione
|
||||
dei nomi, primary key/unicità, compatibilità dei tipi e assenza di ambiguità. Non usa LLM,
|
||||
embedding o campionamento dei dati. L'AI consuma la mappa risultante per comprendere lo schema,
|
||||
ma non la costruisce.
|
||||
|
||||
## Seam tecnico confermato
|
||||
|
||||
Il Catalog PostgreSQL è l'unica fonte modificabile delle Logical Relationship. Le relationship
|
||||
fisiche e logiche restano in modelli distinti, coerentemente con ADR-0006, mentre un servizio profondo
|
||||
espone a API e UI una sola mappa discriminata per origine e stato.
|
||||
|
||||
All'avvio o alla ripresa di una sessione, il backend materializza le sole relationship attive in una
|
||||
snapshot JSON canonica e immutabile, collegata alla stessa lease della configurazione runtime. La
|
||||
snapshot comprende anche le FK fisiche e conserva l'ordine delle coppie composite. Se due record hanno
|
||||
gli stessi endpoint, la FK fisica ha precedenza.
|
||||
|
||||
Quando la snapshot è presente, l'harness la usa come fonte esclusiva delle relationship e continua a
|
||||
leggere dalle annotazioni Git-pinned soltanto descrizioni, sinonimi, concetti e altri metadati. Non
|
||||
scrive `annotations.yaml` e non interroga direttamente il Catalog. Una snapshot dichiarata ma assente,
|
||||
invalida o incoerente con lo schema fisico fallisce esplicitamente; un runtime legacy che non dichiara
|
||||
la snapshot mantiene il precedente comportamento di compatibilità.
|
||||
|
||||
Questa proiezione non è un secondo store: non può essere modificata, viene eliminata insieme alla
|
||||
configurazione runtime e una sessione Pi vede una mappa stabile per tutta la propria vita. La decisione
|
||||
duratura è registrata in ADR-0012.
|
||||
|
||||
La proiezione e la ricostruzione sono ammesse soltanto dopo una sincronizzazione completa della
|
||||
versione corrente del database e vengono serializzate con le mutazioni del catalogo. Un catalogo mai
|
||||
sincronizzato, reso stale da una modifica della configurazione o invalidato da metadata cleanup non
|
||||
può quindi diventare accidentalmente la fonte esclusiva del runtime. Il cleanup esplicito di una
|
||||
tabella o colonna endpoint è anche il confine distruttivo del tombstone: rimuove definitivamente la
|
||||
relationship esclusa, perché conservarla richiederebbe una seconda identità testuale denormalizzata.
|
||||
Reference in New Issue
Block a user