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/queryAuthorization: Bearer <jwt> # attested delegation chain, see AuthenticationContent-Type: application/jsonIl 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 |
OSL-SQL
Sezione intitolata “OSL-SQL”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.
L’abbassamento è il confine di sicurezza
Sezione intitolata “L’abbassamento è il confine di sicurezza”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.
Le cinque forme
Sezione intitolata “Le cinque forme”lower() classifica ogni query in esattamente una forma e ne valida la grammatica:
SELECT segment, COUNT(*) AS nFROM osl.entities.CustomerWHERE country IN ('ES','FR','DE')GROUP BY segmentORDER BY n DESCLIMIT 100Si abbassa a to_trino() → la cucitura SQL governata. I join tra entità
strutturate sono consentiti solo come equijoin su chiave sigillata (vedi sotto).
SELECT SUMMARIZE(text) AS summary, scoreFROM RETRIEVE(facet => 'meeting_transcripts', query => 'european equity exposure', top_k => 5)Si abbassa allo store Lance governato + retrieval. Una colonna nuda passa così
com’è; un <op>(col) nel SELECT è un operatore di
proiezione.
SELECT concepto, amount_eurFROM TRAVERSE(anchor => 'customer', target => 'orders', keys => ('c-anna', 'c-bruno'))Si abbassa all’attraversamento L2 governato a partire dalle chiavi ancora (salto dopo salto, cardinalità limitata + dedup).
SELECT name, segment, recent_meeting_topicsFROM osl.entities.CustomerWHERE customer_id IN ('9c1e…f04a', 'e4f8…2a91')La forma “record” entity-first: colonne strutturate + evidenza Lance + attributi derivati per le chiavi date, in un’unica risoluzione.
SELECT x.segment, r.text, r.scoreFROM osl.entities.Customer xJOIN RETRIEVE(facet => 'meeting_transcripts', query => 'churn risk') r ON x.customer_id = r.customer_id“La relazione è un filtro”: RETRIEVE viene eseguito per primo, le sue chiavi pre-filtrano la gamba strutturale, poi un hash join in-cella sulla chiave sigillata. Vedi sotto.
R3 è inesprimibile, non bloccato
Sezione intitolata “R3 è inesprimibile, non bloccato”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 … ON → 400 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.
Il join cross-modale, eseguito
Sezione intitolata “Il join cross-modale, eseguito”X JOIN RETRIEVE(...) ON X.k = R.k non è un join relazionale di due tabelle
fisiche. Viene eseguito così:
- RETRIEVE (governato) → passages, ciascuno con la propria chiave denormalizzata.
- Gate in chiaro → i valori delle chiavi, oppure rifiuta (
403 E0515). - Pre-filtro strutturato:
… WHERE k IN (<keys>)→to_trino→ SQL governato (la listaINè costruita con la conversione di literal di sqlglot — a prova di injection). - Hash join in-cella sulla chiave sigillata, proiettando le colonne di ciascuna gamba.
Operatori di proiezione
Sezione intitolata “Operatori di proiezione”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 |
Body SQL grezzo e MetricFlow
Sezione intitolata “Body SQL grezzo e MetricFlow”{ "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.
Codici di stato
Sezione intitolata “Codici di stato”| 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.