Integrações¶
Escolhendo um Caminho de Conexão¶
| Tipo de cliente | Caminho recomendado | Por quê |
|---|---|---|
| Ferramentas de BI (Tableau, Power BI, Looker) | JDBC | Streaming colunar Arrow Flight pelo fio; ferramentas de BI têm um assistente JDBC embutido e se beneficiam da entrega colunar de alta vazão para grandes conjuntos de resultados |
| psql, DBeaver, qualquer ferramenta compatível com PG | pgwire (driver PG nativo) | Padrão de fricção zero — nenhum driver personalizado necessário; use o que você já tem |
| Stack de dados Python (pandas, pyarrow) | provisa-client ou ADBC bruto |
Batches Arrow em streaming; sem overhead de serialização de linha |
| Spark, DuckDB, pipelines de alta vazão | Arrow Flight (ADBC) | Streaming colunar ilimitado direto para memória Arrow |
| Serviço a serviço (contratos tipados) | Protobuf gRPC | Proto gerado por função; linhas em streaming; segurança de tipo |
| Aplicações web, scripting | HTTP (/data/graphql, /data/sql) |
Sem driver; HTTP padrão; escolha completa de linguagem de consulta |
| Clientes REST (padrão JSON:API) | GET /data/jsonapi/{table} |
Envelope JSON:API v1.0; sparse fieldsets, paginação, filtragem via parâmetros de consulta; sem driver |
pgwire — Driver PostgreSQL Nativo¶
O Provisa implementa o protocolo de fio PostgreSQL (versão de protocolo 3.0). Qualquer cliente que fale PostgreSQL se conecta sem um driver personalizado.
Habilite definindo PROVISA_PGWIRE_PORT (ex.: 5433) antes de iniciar o Provisa. Desabilitado quando não definido ou 0.
Por que pgwire em vez de JDBC?¶
O driver JDBC usa Arrow Flight como seu transporte e exige implantar o provisa-jdbc.jar. O pgwire não exige nada — se você já tem psql, DBeaver, SQLAlchemy, ou um driver JDBC PG, você já está pronto. É o caminho de menor fricção para cargas de trabalho somente SQL.
JDBC é a escolha certa para ferramentas de BI que têm um assistente de conexão JDBC embutido e se beneficiam do streaming colunar do Arrow Flight para grandes conjuntos de resultados. O pgwire aceita SQL livre contra o esquema publicado completo — as mesmas consultas, custo de configuração menor.
psql¶
DBeaver¶
- New Connection → PostgreSQL
- Host:
localhost, Port:5433 - Usuário / senha conforme configurado no Provisa
- Nenhum download de driver extra necessário
SQLAlchemy (Python)¶
from sqlalchemy import create_engine
engine = create_engine("postgresql+psycopg2://alice:secret@localhost:5433/provisa")
df = pd.read_sql("SELECT * FROM sales.orders", engine)
Ou com asyncpg:
Autenticação¶
O campo password do pacote de inicialização carrega a credencial, e o que a credencial é determina o método: um token de acesso pessoal, um token bearer OIDC ou uma senha contra o provedor configurado. Sob o provedor basic com auth.scram: true, a senha é provada por SCRAM-SHA-256 em vez de enviada. Certificados de cliente são suportados. No modo trust (none), o nome de usuário mapeia diretamente para uma função e a senha é ignorada.
A tabela completa de interface × método está no Modelo de segurança. MD5 não é suportado; habilite TLS (PROVISA_PGWIRE_CERT / PROVISA_PGWIRE_KEY) ao rodar sobre uma rede não confiável.
Limitações¶
- Somente SQL. GraphQL e Cypher não são aceitos via pgwire.
- Não é somente leitura.
COPY ... FROM STDINinsere linhas em fontespostgresql,mysql,sqlite, emariadb, e DDL é suportado (veja abaixo). - DDL (
CREATE,ALTER,DROP) é suportado e despachado para o caminho Trino ou direto; a nova tabela é registrada no contexto de compilação e é imediatamente consultável.COPY ... TO STDOUT(exportação) eCOPY ... FROM STDIN(importação) são suportados nos formatostextecsv. - Consultas a
information_schemaepg_catalogsão interceptadas e respondidas a partir de um shim de catálogo DuckDB — ferramentas de descoberta de esquema funcionam corretamente.
Driver JDBC¶
O driver JDBC do Provisa usa Arrow Flight como seu transporte subjacente. É o caminho recomendado para ferramentas de BI com um assistente de conexão JDBC.
Conexão¶
Baixe provisa-jdbc.jar (sempre a versão mais recente) e adicione-o ao caminho de driver da sua ferramenta.
URL JDBC:
A autenticação usa propriedades JDBC padrão user / password. O Provisa autentica as credenciais contra o provedor de autenticação configurado e atribui a função — o cliente não escolhe sua própria função.
Configuração de Ferramenta de BI¶
Tableau
- Manage → Drivers → Install Provisa JDBC
- Connect → Other Databases (JDBC)
- URL:
jdbc:provisa://localhost:8815 - Digite seu usuário e senha quando solicitado
DBeaver (caminho JDBC — para o caminho pgwire veja acima)
- Database → New Connection → JDBC
- Driver: adicione
provisa-jdbc.jar - URL:
jdbc:provisa://localhost:8815 - Digite seu usuário e senha na aba Authentication
Power BI — use o gateway ODBC com a ponte Provisa JDBC-ODBC (incluída no instalador).
Clientes Arrow Flight¶
O Arrow Flight (porta 8815) é o caminho recomendado para ferramentas de dados que o suportam. Os resultados fazem streaming como Arrow RecordBatches sem materializar na memória do Provisa.
Python (provisa-client)¶
O caminho Python recomendado — envolve tanto GraphQL quanto Arrow Flight:
from provisa_client import ProvisaClient
client = ProvisaClient("http://localhost:8001", username="alice", password="secret")
# Arrow Flight → pyarrow Table (high-throughput, streaming)
table = client.flight("SELECT id, amount FROM sales.orders")
# Arrow Flight → pandas DataFrame
df = client.flight_df("SELECT id, amount FROM sales.orders")
# GraphQL → DataFrame
df = client.query_df("{ orders { id amount } }")
Veja docs/python-client.md para a referência completa incluindo DB-API 2.0, dialeto SQLAlchemy, e ADBC.
Python (PyArrow bruto)¶
import pyarrow.flight as flight
client = flight.connect("grpc://localhost:8815")
ticket = flight.Ticket(b'{"query": "SELECT id, amount FROM sales.orders"}')
df = client.do_get(ticket).read_all().to_pandas()
O Flight carrega sua credencial no payload JSON, como um campo token — um token bearer do provedor ou um token de acesso pessoal. Tanto o handshake quanto cada ticket o aceitam, e ambos o validam do mesmo modo, então um cliente que se autenticou no handshake ainda apresenta o token em cada do_get. Um campo role ao lado solicita uma função; o servidor deriva as funções permitidas da identidade e substitui pelo valor autorizado, de modo que uma string de função em um ticket nunca é a identidade. (REQ-1263) Veja Modelo de segurança.
ticket = flight.Ticket(json.dumps({
"query": "SELECT id, amount FROM sales.orders",
"token": "provisa_pat_...",
"role": "analyst",
}).encode())
ADBC¶
import adbc_driver_flightsql.dbapi as adbc
conn = adbc.connect("grpc://localhost:8815", db_kwargs={"username": "alice", "password": "secret"})
cursor = conn.cursor()
cursor.execute("SELECT id, amount FROM sales.orders")
table = cursor.fetch_arrow_table()
DuckDB¶
import duckdb, pyarrow.flight as flight
client = flight.connect("grpc://localhost:8815")
ticket = flight.Ticket(b'{"query": "SELECT * FROM sales.orders"}')
arrow_table = client.do_get(ticket).read_all()
conn = duckdb.connect()
result = conn.execute("SELECT region, sum(amount) FROM arrow_table GROUP BY 1").df()
Spark (PySpark)¶
from pyspark.sql import SparkSession
spark = SparkSession.builder \
.config("spark.jars.packages", "org.apache.arrow:flight-core:14.0.0") \
.getOrCreate()
# Use ADBC Flight connector or load via pandas → Spark
Protobuf gRPC (porta 50051)¶
Caminho serviço a serviço. O Provisa gera um .proto por função na inicialização — cada função vê apenas as tabelas e colunas às quais tem acesso.
Baixe o proto para sua função:
Use grpc_server_reflection para descobrir o esquema programaticamente.
Todo RPC deve carregar uma credencial na chave de metadados authorization — um token do provedor ou um token de acesso pessoal. x-provisa-role solicita uma função do conjunto permitido da identidade; não é uma credencial e nunca foi. Certificados de cliente são suportados. Veja Modelo de segurança.
Consultas em streaming emitem uma mensagem por linha; mutações são unárias.
Invocando Comandos Entre Protocolos¶
Um comando é uma função rastreada registrada ou webhook — um chamável registrado na camada semântica do Provisa com um kind (query ou mutation) e um impl_kind que descreve como ele roda. Toda superfície roteia invocações através de um único executor governado (invoke_tracked_function) que aplica writable_by e governança uniformemente (REQ-1156). [tool-verified: provisa/api/data/action_exec.py, provisa/bolt/session.py:786-791, provisa/grpc/server.py:107-135, provisa/pgwire/function_call.py:80-88, provisa/api/flight/server.py:542-554]
impl_kind |
O que roda | Campos de vinculação |
|---|---|---|
source_procedure |
Procedimento armazenado em uma fonte registrada (padrão) | sourceId, schemaName, functionName |
script |
Script no lado do servidor | script |
http |
Chamada HTTP de saída | url, method |
grpc |
Chamada gRPC de saída para um servidor externo | target, method |
python |
Chamável Python hospedado pelo Provisa (REQ-885) | callable (ex.: demo.py_functions:random_dataset) |
Quando um comando declara um return_schema (JSON Schema com type: array, items: object), ele retorna conjunto — toda superfície o projeta como um conjunto de linhas tipado. Os comandos de demonstração random_python_set (impl_kind python) e random_grpc_set (impl_kind grpc) ilustram tanto um chamável hospedado quanto uma ponte gRPC externa retornando linhas com valores aleatórios; ambos são registrados em config/provisa-install.yaml. [tool-verified: config/provisa-install.yaml:809-856]
Matriz de protocolo¶
| Superfície | Sintaxe | Exemplo |
|---|---|---|
| GraphQL | kind=query → campo Query; kind=mutation → campo Mutation; prefixado por domínio quando domain_prefix: true |
{ ps__random_python_set(rows: 5, seed: 42) { id region amount } } |
pgwire / Arrow Flight / MCP run_sql |
SELECT * FROM fn(args) ou SELECT fn(args) |
SELECT * FROM random_python_set(5, 42) |
Cypher HTTP (POST /data/cypher) |
CALL fn(args) YIELD cols |
CALL random_python_set(5, 42) YIELD id, region, amount |
| Bolt (Neo4j Browser / driver) | CALL fn(args) — argumentos posicionais mapeiam para nomes de argumento declarados |
CALL random_python_set(3, 7) |
| Provisa gRPC (porta 50051) | Unário CallCommand(CommandRequest{name, args_json}) → CommandResponse{rows_json} |
grpcurl -d '{"name":"random_python_set","args_json":"{\"rows\":5}"}' ... ProvisaService/CallCommand |
O campo kind controla apenas o posicionamento no GraphQL — as superfícies SQL, Cypher, Bolt, e gRPC aceitam comandos query e mutation de forma idêntica.
Apollo Federation¶
O Provisa pode atuar como um subgraph Federation v2, expondo seu esquema publicado a um Apollo Router ou Apollo Gateway.
Configuração¶
Habilite federation em config.yaml:
O Provisa gera diretivas @key em colunas de chave primária e @external/@provides em relacionamentos entre subgraphs automaticamente.
Registro com Apollo Router¶
No seu supergraph.yaml:
subgraphs:
provisa-data:
routing_url: http://provisa:8001/data/graphql
schema:
subgraph_url: http://provisa:8001/data/graphql
Rode rover supergraph compose --config supergraph.yaml para gerar o esquema do supergraph.
Entidades¶
O Provisa responde a consultas _entities para joins entre subgraphs. Qualquer tabela com uma chave primária é automaticamente resolvível como uma entidade Federation.
Importação Hasura v2 / DDN¶
Veja docs/import.md para migração do Hasura para o Provisa.
Kafka¶
Veja docs/sources.md para configuração de tópico Kafka como tabelas somente leitura e sinks de resultado de consulta.
Interoperabilidade Semântica Apache Ossie (REQ-1316)¶
O Provisa troca modelos semânticos com o Apache Ossie (spec 0.2.0.dev0, incubating; anteriormente Open
Semantic Interchange) através de um adaptador de fronteira. O vocabulário interno do Provisa nunca é renomeado
para o do Ossie — a spec declara mudanças que quebram compatibilidade como prováveis, então o acoplamento é confinado ao adaptador.
[tool-verified: provisa/ossie/convert.py docstring lines 7–16; OSSIE_VERSION = "0.2.0.dev0",
provisa/ossie/convert.py line 29]
Exportação¶
A superfície de exportação canônica é um endpoint HTTP ao vivo. Ela deriva o documento Ossie do estado ao vivo em cada requisição — sem cache, sem etapa de geração.
A resposta é um documento YAML com Content-Disposition: attachment; filename=provisa.ossie.yaml.
[tool-verified: ossie_router.py lines 20–33: "THE canonical live Ossie endpoint: the semantic
model derived from live state on every read — no caching, no regeneration step"]
A página de Métricas também oferece um botão Download e uma URL de endpoint copiável no painel Ossie
Interchange, ambos apontando para o mesmo endpoint.
[tool-verified: OssieInterchangePanel.tsx lines 64–79: endpointUrl = window.location.origin + OSSIE_ENDPOINT_PATH]
O que é exportado¶
O adaptador mapeia objetos do Provisa para objetos do Ossie da seguinte forma:
| Objeto Provisa | Objeto Ossie | Notas |
|---|---|---|
Table |
dataset |
source = catalog.schema.table; chaves primárias/únicas da config de coluna e UniqueConstraint |
Column |
field |
expression = referência de coluna (dialeto ANSI_SQL); colunas de tempo ganham dimension.is_time: true |
Relationship |
relationship |
Alias usado como nome quando definido; relacionamentos computados (alvo de função) são pulados |
Metric |
metric |
name, expression (ANSI_SQL), datatype, description, ai_context — sem perdas por design |
modeling_role / modeling_history |
custom_extensions[].vendor_name="provisa" |
Somente round-trip; outras ferramentas podem ignorar |
[tool-verified: _table_to_dataset, build_ossie_model, provisa/ossie/convert.py lines 90–198;
_table_to_dataset comment at line 153: "Computed (function-target) relationships have no dataset
target — not representable in Ossie; skipping is the defined export boundary"]
Governança, RLS, linhagem, e semântica de grafo não são exportadas. Elas podem viajar no slot opcional
provisa custom_extensions para fidelidade de round-trip, mas a interoperabilidade nunca depende de outras
ferramentas o lendo. [tool-verified: provisa/ossie/convert.py docstring lines 13–15]
Tipos de coluna do Provisa desconhecidos passam verbatim; o adaptador nunca mapeia silenciosamente para um tipo
errado. [tool-verified: _map_datatype, provisa/ossie/convert.py lines 70–77: "Unknown types
pass through verbatim — mapping silently to a wrong type would corrupt the model"]
Mapeamento de tipo¶
[tool-verified: _DATATYPE_MAP, provisa/ossie/convert.py lines 35–65]
| Tipo Provisa / fonte | datatype Ossie |
|---|---|
varchar, text, char, uuid, string |
string |
int, integer, bigint, smallint, int4, int8, tinyint |
integer |
numeric, decimal, float, double, real |
number |
bool, boolean |
boolean |
date |
date |
time |
time |
timestamp, timestamptz, datetime |
timestamp |
| qualquer outro | passa verbatim |
Importação¶
A importação aceita um documento Ossie (YAML ou JSON) e retorna propostas de registro. Nada é registrado automaticamente — definições importadas nunca ignoram a etapa de revisão.
O servidor faz o parse do documento com parse_ossie_model, que valida a estrutura e retorna uma
dataclass OssieImport contendo tabelas propostas, relacionamentos, e métricas como dicts simples.
Qualquer problema estrutural é um 400 com um erro nomeado por caminho, ex.:
ossie import: missing semantic_model[0].datasets[1].source.
[tool-verified: import_ossie, provisa/api/admin/ossie_router.py lines 36–52:
"Nothing is registered here — imported definitions never bypass registration review"]
A tela de revisão¶
Na UI, o botão Import (página Metrics → painel Ossie Interchange) abre um seletor de arquivo.
Depois que o documento é enviado e analisado, um modal de revisão abre com cada tabela, relacionamento, e
métrica proposta listados como um item marcado. O modelador pode desmarcar qualquer coisa para excluí-la.
Clicar em Apply registra os itens marcados através das mutações de registro existentes — tabelas
primeiro, depois relacionamentos (que referenciam tabelas), depois métricas.
[tool-verified: OssieInterchangePanel.tsx lines 88–165: "Review screen opens with everything
checked; trimming = unchecking"; "Tables first, then relationships... then metrics — each through
the EXISTING registration mutations (REQ-1316)"]
O papel de modelagem e o histórico armazenados em um documento Ossie exportado pelo Provisa fazem round-trip corretamente
através da importação. [tool-verified: _parse_dataset custom_extensions handling,
provisa/ossie/convert.py lines 287–300: "REQ-1320: round-trip the provisa modeling metadata slot"]
Métricas Entre Protocolos (REQ-1319)¶
A definição de uma métrica governada — sua expressão, descrição, e ai_context — viaja com o
valor para toda superfície de consulta através de uma única expansão do compilador. Não há cópias. O compilador
reserva o esquema metrics para acesso SQL; cada protocolo então adiciona seu próprio canal de metadados.
[tool-verified: METRICS_SCHEMA = "metrics", provisa/compiler/metric_expand.py line 43;
REQ-1319 requirement text: "the definition (description, ai_context) travels with the value
everywhere, with no copies"]
SQL / pgwire¶
Endereça qualquer métrica como uma relação virtual no esquema metrics. As colunas de dimensão que você seleciona
tornam-se o GROUP BY:
-- Grand total
SELECT value FROM metrics.net_revenue;
-- By region
SELECT region, value FROM metrics.net_revenue GROUP BY region;
-- By region and month, filtered
SELECT region, month, value
FROM metrics.net_revenue
WHERE net_revenue.status = 'completed'
GROUP BY region, month;
O compilador expande a forma metrics.<name> para o agregado agrupado real antes da governança
rodar. Descrições de coluna são exibidas como entradas pg_description, então o DBeaver e o \d+ do psql
as mostram. [tool-verified: metric_semantic_sql, provisa/compiler/metric_expand.py lines 52–70;
REQ-1319: "description surfaced via pg_description"]
SELECT * é rejeitado — nomeie as colunas explicitamente.
[tool-verified: expand_metric_query, provisa/compiler/metric_expand.py lines 302–306]
GraphQL¶
Métricas se projetam dentro do campo raiz _aggregate como um bloco metrics.
[inferred: per REQ-1319; aggregate_gen.py not read in this session]
O texto da definição (description, ai_context) aparece na documentação de introspecção do GraphQL, então
ferramentas cientes de esquema e geração de código o captam automaticamente.
[inferred: per REQ-1319: "definition in introspection docs"]
MCP (agentes de IA)¶
Duas ferramentas expõem métricas a clientes MCP:
list_metrics— retorna todas as métricas governadas visíveis à sessão, comname,description, eai_context.query_metric— aceita um nome de métrica mais uma lista de dimensões e chama o caminho de SQL semântico do compilador, retornando o resultado agregado.
[inferred: per REQ-1319: "MCP: list_metrics and query_metric tools carrying ai_context, so agents
select governed meanings instead of composing aggregation SQL"; provisa/api/mcp/tools.py not
read in this session]
Agentes que chamam list_metrics antes de construir uma consulta selecionam uma métrica governada pelo nome
em vez de escrever SQL de agregação manualmente. O campo ai_context é o lugar para colocar o
texto de definição que orienta a seleção correta.
Arrow Flight¶
Métricas são endereçáveis como descritores de flight de métrica retornando tabelas Arrow.
[inferred: per REQ-1319: "Arrow Flight: metric flight descriptors returning Arrow tables";
provisa/api/flight/catalog.py not read in this session]
Use a mesma forma SQL metrics.<name> via o caminho de ticket Flight SQL padrão.
Bolt / Cypher (Neo4j Browser)¶
Chame uma métrica usando o procedimento provisa.metric():
[inferred: per REQ-1319: "Bolt/Cypher: a provisa.metric() procedure"; the procedure signature is inferred from the REQ text and not verified against provisa/bolt/session.py in this session]
Tabelas Fact e Dimension carregam rótulos de nó :Fact e :Dimension no grafo federado, então
o Bloom renderiza a forma de estrela automaticamente.
[inferred: per REQ-1319 and REQ-1320: "federated graph labels nodes :Fact/:Dimension so Bloom
renders the star"; provisa/cypher/label_map.py not read in this session]
Consultas em linguagem natural¶
O matcher de esquema NL resolve vocabulário de métrica em perguntas em linguagem natural diretamente para uma métrica
mais dimensões, depois gera SQL semântico. [tool-verified: resolve_metric,
provisa/nl/schema_matcher.py is exercised in test_nl_metrics.py lines 76–78:
sql = matcher.resolve_metric("What is the total revenue by region?") →
"SELECT region, value FROM metrics.total_revenue GROUP BY region"]
Tabelas fact são marcadas [fact] no prompt NL; tabelas de dimensão são marcadas [dimension]. O
matcher favorece caminhos de join fact-para-dimensão ao resolver perguntas.
[tool-verified: test_format_entities_tags_star_roles, tests/unit/test_nl_metrics.py lines 129–132:
assert "table: orders [fact] fields: amount" in block]
Streaming¶
Combine view_metrics com materialize e um sink Kafka para produzir saída de métrica push-on-change
usando a maquinaria de materialização existente. Nenhum pipeline novo é exigido.
[inferred: per REQ-1319: "Streaming: view_metrics + materialize + Kafka sink yields push-on-change
metrics from existing machinery"; implementation not verified beyond the requirement text]
Observabilidade (OTel)¶
Avaliações de métrica são rastreadas e exportáveis como métricas OpenTelemetry. [inferred: per REQ-1319: "Observability: metric evaluations traced and exportable as OTel metrics"; OTel integration code not read in this session]