Salta ai contenuti

La superficie di query (OSL-SQL)

POST /osl/query è l’unico endpoint di consumo. Analitica strutturata, metriche, retrieval vettoriale governato e attraversamento del grafo si esprimono tutti qui — non impari cinque superfici per leggere un dominio.

POST /osl/query
Authorization: Bearer <jwt> # attested delegation chain, see Authentication
Content-Type: application/json

Il body è una di tre forme mutuamente esclusive (mescolarle è 400 E0513); tutte e tre passano per la stessa cucitura governata e condividono un unico request_id:

Body Cos’è
{ "osql": "…" } OSL-SQL — il dialetto SQL semantico (la superficie canonica)
{ "sql": "…" } Un SELECT grezzo di sola lettura sull’allowlist del tenant (lo slice SQL)
{ "metrics": […], "group_by": […], … } Query di metriche in stile MetricFlow

osql è un dialetto SQL semantico. Nomina solo oggetti semantici — osl.entities.<model> e le colonne dichiarate, oppure le table function RETRIEVE(...) / TRAVERSE(...). Non nomina mai una tabella fisica o una colonna grezza.

Il motore fa il parsing di osql (via sqlglot, dialetto trino) e lo abbassa a un IR di operatori chiuso — la scala deterministica su cui il modello di governance applica l’enforcement. L’abbassamento è totale e default-deny: qualsiasi costrutto che non si abbassa a un nodo IR noto viene rifiutato.

lower() classifica ogni query in esattamente una forma e ne valida la grammatica:

SELECT segment, COUNT(*) AS n
FROM osl.entities.Customer
WHERE country IN ('ES','FR','DE')
GROUP BY segment
ORDER BY n DESC
LIMIT 100

Si abbassa a to_trino() → la cucitura SQL governata. I join tra entità strutturate sono consentiti solo come equijoin su chiave sigillata (vedi sotto).

L’unico nodo cross-relazionale è un equijoin il cui ON è un’uguaglianza scalare su una chiave di entità sigillata (le entity_key_columns del modello, o il campo scalar_indexed di un facet) su entrambe le gambe. Qualsiasi altro JOIN … ON400 E0514.

Non c’è sintassi che passi un secondo argomento relazionale a un operatore di similarità o LLM — un join semantico relazione×relazione in query time (il non-goal R3) è una proprietà strutturale della grammatica, non una blocklist da mantenere.

X JOIN RETRIEVE(...) ON X.k = R.k non è un join relazionale di due tabelle fisiche. Viene eseguito così:

  1. RETRIEVE (governato) → passages, ciascuno con la propria chiave denormalizzata.
  2. Gate in chiaro → i valori delle chiavi, oppure rifiuta (403 E0515).
  3. Pre-filtro strutturato: … WHERE k IN (<keys>)to_trino → SQL governato (la lista IN è costruita con la conversione di literal di sqlglot — a prova di injection).
  4. Hash join in-cella sulla chiave sigillata, proiettando le colonne di ciascuna gamba.

Nella forma RETRIEVE, <op>(col) nel SELECT è un operatore di proiezione (RFC 0004). Sono mappe unarie — mai un join.

Classe Operatori Gira su Se manca
Encoder (deterministico) SIMILARITY, CLASSIFY, TAG, RANK, DEDUP, OUTLIER, CLUSTER Sugli embedding già presenti nel chunk
Decoder (inferenza) richiede LLM EXTRACT, SUMMARIZE, SCORE, GENERATE, COMPLETE Un backend LLM 503 E0521

{ "sql": "<SELECT>" } è un SELECT di sola lettura validato contro l’allowlist del tenant con il punto di applicazione delle policy applicato (row filter + projection dallo snapshot composto). { "metrics": …, "group_by": … } segue la semantica di MetricFlow. Entrambi condividono la stessa cucitura governata di osql.

Codice Significato
200 Successo
400 Malformato / fuori grammatica (E0511), oggetto non dichiarato (E0512), il body mescola forme (E0513), JOIN non su chiave sigillata (E0514), sql vuoto (E0246)
403 Deny del PDP (E2101/E2102), chiave cross-modale non in chiaro (E0515), oggetto fuori dall’allowlist (E0512)
422 Valido ma non risolvibile — es. MetricFlow non ancora implementato (E1001)
503 Manca lo snapshot del PDP (E2001, fail-closed), op decoder senza backend LLM (E0521)
502 Fallimento di esecuzione di Trino (E0510)

Vedi Errori e codici di stato per l’envelope completo.