Типы источников¶
Модель выполнения¶
В конечном счёте каждый запрос выполняется через движок федерации, который обеспечивает федерацию по всем источникам. Источники делятся на три категории в зависимости от их связности. [tool-verified: provisa/core/models.py lines 84–132] (REQ-550)
| Категория | Есть прямой драйвер | Есть федеративный коннектор | Примеры |
|---|---|---|---|
| Прямая поддержка (direct-capable) | Да | Да | PostgreSQL, MySQL, MariaDB, SingleStore, SQL Server, Oracle, DuckDB |
| Только федерация | Нет | Да | Redshift, Druid, Exasol, Hive, Iceberg, Delta Lake, Hive (на базе S3) |
| Прямое чтение (реплика) | Да | Да | Snowflake, Databricks, ClickHouse — драйвер читает данные и создаёт реплику; запросы выполняются против реплики в активном движке |
| Материализация → Федерация | Нет | Нет | REST/OpenAPI, удалённый GraphQL, gRPC, Neo4j Cypher, SPARQL, WebSocket, RSS, CSV, SQLite, Parquet, Ingest (приёмник push-данных), GovData, SharePoint, Splunk |
Источники с прямой поддержкой выполняют однопоточные (single-source) запросы через свой нативный драйвер (менее 100 мс), минуя движок федерации (REQ-027, REQ-229). Они сохраняют полную поддержку коннектора и участвуют в федерации при объединении с другими источниками (REQ-028).
Источники только с федерацией всегда опрашиваются через слой федерации. Прямого драйвера не существует (REQ-229).
Источники с прямым чтением (реплика) имеют DirectDriver, который читает данные из хранилища нативно (в формате Arrow, где это возможно), создаёт реплику в хранилище материализации активного движка, а затем запросы выполняются против этой реплики. См. Хранилища как именованные источники.
Источники материализации не имеют федеративного коннектора. Provisa получает их данные (при запуске или во время выполнения запроса) и кеширует их как Parquet в S3 или в PostgreSQL, делая их достижимыми для движка федерации в межисточниковых запросах (REQ-309).
Все источники¶
Provisa регистрирует 54 типа источников. Приведённые ниже таблицы охватывают все 54; индекс — это просто счётчик. [tool-verified: provisa/core/models.py SourceType; Kaggle учитывается как отдельный источник, хотя внутренне регистрируется через коннектор files]
| # | Группа | Типы источников |
|---|---|---|
| 1–13 | RDBMS | postgresql, mysql, mariadb, singlestore, sqlserver, oracle, duckdb, cockroachdb, yugabytedb, greenplum, tidb, firebird, airport |
| 14–20 | Облачные хранилища данных | snowflake, bigquery, databricks, redshift, fabric, synapse, trino |
| 21–25 | Аналитика / OLAP | clickhouse, druid, exasol, elasticsearch, pinot |
| 26–30 | Data lake / открытые табличные форматы | iceberg, delta_lake, hudi, hive, hive_s3 |
| 31–33 | NoSQL | mongodb, cassandra, redis |
| 34–36 | Потоковая передача | kafka, websocket, rss |
| 37 | Приёмник push-данных | ingest |
| 38–39 | Граф и семантика | neo4j, sparql |
| 40–43 | На основе файлов | sqlite, csv, parquet, files |
| 44–45 | Наблюдаемость и прочее | google_sheets, prometheus |
| 46–47 | Корпоративный SaaS | sharepoint, splunk |
| 48–50 | API-источники | openapi, graphql_remote, grpc_remote |
| 51 | GovData | govdata |
| 52–53 | Проверки качества данных | soda, great_expectations |
| 54 | Наборы данных Kaggle | Kaggle (загружается через форму Sources; регистрируется как источник files — см. Наборы данных Kaggle) |
Справочник по каждому типу источника, поддерживаемому Provisa. «Прямой драйвер» означает, что однопоточные запросы выполняются напрямую к источнику (менее 100 мс) (REQ-027). «Имя коннектора» — это федеративный коннектор, используемый, когда источник участвует в многопоточных (multi-source) JOIN (REQ-028). [tool-verified: provisa/core/source_registry.py SOURCE_TO_DIALECT; provisa/federation/trino_connectors.py trino_connector_name]
RDBMS¶
| Тип источника | Прямой драйвер | Имя коннектора | Диалект | Мутации |
|---|---|---|---|---|
postgresql |
asyncpg | postgresql | postgres | Да |
mysql |
aiomysql | mysql | mysql | Да |
mariadb |
aiomysql | mariadb | mysql | Да |
singlestore |
— | singlestore | singlestore | Федеративные |
sqlserver |
aioodbc | sqlserver | tsql | Да |
oracle |
oracledb | oracle | oracle | Да |
duckdb |
duckdb | memory | duckdb | Да |
cockroachdb |
asyncpg (протокол pg) | postgresql | postgres | Да |
yugabytedb |
asyncpg (протокол pg) | postgresql | postgres | Да |
greenplum |
asyncpg (протокол pg) | postgresql | postgres | Да |
tidb |
aiomysql (протокол mysql) | mysql | mysql | Да |
firebird |
— | — (расширение DuckDB) | — | Нет |
airport |
— | — (расширение DuckDB) | — | Нет |
Базы данных, совместимые по протоколу, повторно используют JDBC-драйвер, нативный асинхронный драйвер и диалект базового протокола — CockroachDB, YugabyteDB и Greenplum используют протокол PostgreSQL; TiDB использует протокол MySQL. Им требуются только записи в реестре, без нового кода коннектора. [tool-verified: provisa/core/source_registry.py _PG_WIRE_TYPES, _MYSQL_WIRE_TYPES] (REQ-950)
firebird (Firebird 3/4/5) и airport (сервер Arrow Flight) — зарегистрированные типы источников, достигаемые на месте через community-расширения DuckDB, когда DuckDB является активным движком — без прямого драйвера, без федеративного коннектора. [tool-verified: provisa/core/models.py lines 44, 93] (REQ-899)
Облачные хранилища данных¶
[tool-verified: executor/drivers/snowflake.py, executor/drivers/databricks.py, executor/drivers/registry.py]
| Тип источника | Прямой драйвер | Имя коннектора | Диалект | Мутации | Примечания |
|---|---|---|---|---|---|
snowflake |
SnowflakeDriver | snowflake | snowflake | Федеративные | Чтение через snowflake-connector-python; создаёт реплику; account/warehouse/role в federation_hints (REQ-988) |
bigquery |
— | bigquery | bigquery | Федеративные | Нет DirectDriver; достигается через движок федерации или через ATTACH движка BigQuery |
databricks |
DatabricksDriver | delta_lake | databricks | Федеративные | Чтение через databricks-sql-connector (Cloud Fetch, Arrow); создаёт реплику; http_path обязателен в federation_hints (REQ-987) |
redshift |
— | redshift | redshift | Федеративные | — |
fabric |
MssqlWarehouseDriver | — | tsql | Федеративные | Microsoft Fabric Warehouse; T-SQL поверх TDS, аутентификация Azure AD; создаёт реплику (REQ-995) |
synapse |
MssqlWarehouseDriver | — | tsql | Федеративные | Azure Synapse SQL; T-SQL поверх TDS, аутентификация Azure AD; создаёт реплику (REQ-995) |
trino |
SQLAlchemyDriver | — | — | Федеративные | Удалённый координатор Trino/Presto, чтение через диалект trino SQLAlchemy; создаёт реплику на любом движке (REQ-994) |
Аналитика / OLAP¶
[tool-verified: executor/drivers/clickhouse.py]
| Тип источника | Прямой драйвер | Имя коннектора | Диалект | Мутации | Примечания |
|---|---|---|---|---|---|
clickhouse |
ClickHouseDriver | clickhouse | clickhouse | Федеративные | Чтение через clickhouse-connect (HTTP); secure: "true" в federation_hints для TLS (REQ-986) |
druid |
— | druid | druid | Нет | — |
exasol |
— | exasol | exasol | Нет | — |
elasticsearch |
HTTP (нативные движки) | elasticsearch (Trino) | — | Нет | В Trino коннектор читает его, а свойства берёт из DSL сопоставления (mapping DSL) этого типа [tool-verified: trino_connectors.py:309]; на любом другом движке Provisa читает индекс по HTTP (индексы и сопоставление для регистрации таблицы, scroll-чтение для создания реплики) и загружает строки [tool-verified: provisa/elasticsearch/fetch.py, provisa/events/source_loader.py make_elasticsearch_loader] (REQ-1672) |
pinot |
— | pinot | — | Нет | Коннектор Trino pinot; pinot.controller-urls = хост:порт контроллера Pinot [tool-verified: trino_connectors.py:199] |
Data Lake / открытые табличные форматы¶
Эти типы источников работают только через федерацию — без прямого драйвера, без диалекта. [tool-verified: LAKE_ONLY_SOURCES in provisa/core/source_registry.py] (REQ-229)
| Тип источника | Имя коннектора | Путешествие во времени (Time Travel) | Примечания |
|---|---|---|---|
iceberg |
iceberg | Да (аргумент as_of, REQ-372) |
— |
delta_lake |
delta_lake | Да (аргумент as_of, REQ-372) |
— |
hive |
hive | Нет | — |
hudi |
— (движок ClickHouse Hudi, без копирования — REQ-1178) |
Нет | Нет федеративного коннектора; достигается на месте, когда активным движком является ClickHouse |
hive_s3 |
hive | Нет | Hive на базе S3 |
NoSQL¶
mongodb, cassandra и redis имеют коннекторы Trino (redis строит свои свойства из DSL сопоставления этого типа). [tool-verified: provisa/federation/trino_connectors.py; provisa/core/models.py] (REQ-017, REQ-1097)
| Тип источника | Имя коннектора | Мутации |
|---|---|---|
mongodb |
mongodb | Нет |
cassandra |
cassandra (Trino); чтение CQL через cassandra-driver на любом другом движке | Нет |
redis |
redis (Trino); чтение через redis-py без HTTP на любом другом движке | Нет |
Потоковая передача¶
| Тип источника | Механизм | Мутации |
|---|---|---|
kafka |
Федеративный коннектор Kafka; схема через Confluent Schema Registry (Avro, Protobuf, JSON Schema), ручное определение или вывод по образцу (REQ-147, REQ-150) | Только приёмник (sink) (REQ-176) |
websocket |
Внешний источник WebSocket — подключение, подписка, получение событий; результаты материализуются (REQ-338) | Нет |
rss |
Лента RSS 2.0 / Atom — опрос, водяной знак по pubDate/updated; результаты материализуются (REQ-342, REQ-343) | Нет |
Приёмник push-данных¶
| Тип источника | Механизм | Мутации |
|---|---|---|
ingest |
Внешние сервисы отправляют события через POST JSON; результаты материализуются (REQ-331, REQ-335) | Нет |
Граф и семантика¶
| Тип источника | Механизм | Мутации |
|---|---|---|
neo4j |
Cypher через HTTP API, результаты кешируются в PostgreSQL (REQ-295) | Нет |
sparql |
SPARQL 1.1 через POST, результаты кешируются в PostgreSQL (REQ-297) | Нет |
На основе файлов¶
Два механизма охватывают файлы. Оба используют поле path вместо host/port. [tool-verified: provisa/core/models.py] (REQ-553)
Однофайловые источники — sqlite, csv, parquet указывают path на один файл.
| Тип источника | Транспорты | Мутации |
|---|---|---|
sqlite |
локальный | Да |
csv |
локальный | Нет |
parquet |
локальный, s3:// |
Нет |
Приватным бакетам нужны учётные данные (регион AWS и ключи из окружения). Для CSV через s3:// или http(s)://, или для регистрации многих файлов сразу, используйте источник files. [tool-verified: provisa/file_source/source.py]
Источник files — указывает path на glob-шаблон, рекурсивно обходит его и регистрирует директорию как федеративный каталог таблиц. Он читает множество форматов через множество транспортов; наборы ниже приведены из файлового коннектора (форк kenstott/calcite). [tool-verified: provisa/core/catalog.py files branch and provisa/core/models.py SOURCE_TO_CONNECTOR; format and transport lists from the calcite file adapter — FileSchema.java, storage/StorageProviderFactory.java]
| Форматы | Транспорты |
|---|---|
| CSV, TSV, JSON, YAML, Excel (XLS/XLSX), Parquet, Arrow и документы, преобразуемые в таблицы — HTML, Markdown, DOCX, PPTX | Локальная файловая система, HTTP(S), s3://, hdfs://, ftp:///ftps://, sftp://, iceberg://, SharePoint (REST и Microsoft Graph) |
- id: sales_files
type: files
path: s3://bucket/sales/**/*.csv # glob; local and http(s):// also supported
На движке DuckDB files читается нативно — представление-сканер read_csv_auto для каждого <table>.csv в разрешённой директории (REQ-229) [tool-verified: provisa/federation/connector_duckdb.py DuckDBFilesConnector]. На движке без собственного коннектора files строки загружаются через тот же встроенный в коннектор сервер Calcite pgwire (pgwire-file), который используют sharepoint/splunk (REQ-954) — см. Корпоративные SaaS-коннекторы ниже. Сквозное покрытие UI (форма Sources → регистрация таблицы → SQL-запрос) и путь загрузки через pgwire подтверждены в REQ-1694.
Наборы данных Kaggle (REQ-1780, REQ-1781, REQ-1782, REQ-1783)¶
Kaggle — это платформа для загрузки файлов. Загруженный набор данных Kaggle регистрируется как источник типа files и опрашивается через тот же коннектор pgwire-file, который используют любые другие источники files — в перечислении SourceType нет отдельного типа kaggle. [tool-verified: provisa/kaggle/downloader.py; provisa/core/models.py SourceType — no kaggle literal]
Добавление набора данных. Откройте Sources → Subscriptions → Kaggle. Форма выполняет два последовательных шага, потому что собственная конечная точка поиска наборов данных Kaggle требует аутентификации — селектор не может появиться до проверки токена. [tool-verified: provisa-ui/src/pages/sources/KaggleFormSection.tsx] (REQ-1783)
- Токен — введите API-токен Kaggle и нажмите «Validate». Проверка вызывает
POST https://www.kaggle.com/api/v1/datasets/create/newс пустым телом.401означает недействительность; любой другой ответ означает валидность (собственная валидация полезной нагрузки Kaggle срабатывает до создания какого-либо набора данных — ничего не сохраняется). [tool-verified:provisa/kaggle/client.pyvalidate_token] (REQ-1782) - Селектор — поле поиска в реальном времени по мере ввода запрашивает
GET /api/v1/datasets/list. Каждый результат показывает заголовок и описание набора данных. Выберите один, затем нажмите «Add Dataset».
Нажатие «Add Dataset» загружает пакет из GET /api/v1/datasets/download/{owner}/{ref}, распаковывает элементы CSV и Parquet в <PROVISA_DATA_DIR>/kaggle/<owner>/<ref>/<file-stem>/<file-name> и создаёт один источник типа files, чей path — эта корневая директория. Токен никогда не сохраняется на стороне сервера. [tool-verified: provisa/kaggle/downloader.py stage_dataset; provisa-ui/src/pages/sources/KaggleFormSection.tsx handleConfirmDataset] (REQ-1780, REQ-1781)
После добавления источника зарегистрируйте его таблицы через обычный экран регистрации таблицы. Рекурсивное обнаружение директорий коннектора pgwire-file перечисляет каждый файл как отдельную таблицу — тот же механизм, что используют другие источники files (REQ-1690). (REQ-1783)
Ограничение v1. Пакет, содержащий файл .sqlite или .db, отклоняется сразу с чёткой ошибкой. Загружаются только файлы CSV и Parquet. [tool-verified: provisa/kaggle/downloader.py UnsupportedKaggleDataset, _UNSUPPORTED_EXTENSIONS]
Именование таблиц. Каждый файл попадает в собственную поддиректорию <file-stem>/ под корнем набора данных. Коннектор pgwire-file именует итоговую таблицу как <subdir>__<stem> после нормализации SMART_CASING. Например: StatewiseTestingDetails.csv попадает в statewise_testing_details/StatewiseTestingDetails.csv и становится таблицей statewise_testing_details__statewise_testing_details. Удвоение основы ожидаемо для однофайловых наборов данных. Многофайловый набор данных даёт одну пару на файл: orders__orders, customers__customers. (REQ-471)
Обновление. Чтобы повторно получить набор данных после публикации Kaggle новой версии, вызовите GraphQL-мутацию refreshKaggleSource с идентификатором источника и действительным токеном. Она заново загружает файлы на месте и очищает кеш конечной точки pgwire-file, чтобы коннектор подхватил любые изменения схемы при следующем запросе. kaggle_owner и kaggle_ref, сохранённые в federation_hints при создании, определяют, какой набор данных обновлять. [tool-verified: provisa/api/admin/schema_mutation.py refresh_kaggle_source] (REQ-1780)
Нет статического пути конфигурации YAML. Источники Kaggle создаются только через форму Sources. Источник Kaggle, экспортированный в YAML, отображается как type: files с kaggle_owner и kaggle_ref в federation_hints. Повторная загрузка из Kaggle требует потока обновления через UI или мутации refreshKaggleSource — указание path в YAML на уже загруженную директорию — альтернатива для сред без доступа к интернету (air-gapped).
Наблюдаемость и прочее¶
prometheus имеет коннектор Trino (свойства строятся из DSL сопоставления этого типа). google_sheets — зарегистрированный тип источника без коннектора Trino, материализующийся через конвейер API-кеша. [tool-verified: provisa/federation/trino_connectors.py:314; provisa/core/models.py lines 87–88]
| Тип источника | Имя коннектора | Мутации |
|---|---|---|
google_sheets |
— (материализуется) | Нет |
prometheus |
prometheus | Нет |
Корпоративные SaaS-коннекторы¶
SharePoint и Splunk регистрируются через коннекторы Apache Calcite (форк kenstott/calcite). Ни у одного нет прямого драйвера — Provisa запускает встроенный в коннектор сервер Calcite pgwire (pgwire-sharepoint, pgwire-splunk) и обращается к нему как к обычной конечной точке PostgreSQL. На движке DuckDB эта конечная точка подключается вживую через расширение postgres: регистрация таблицы перечисляет таблицы коннектора из подключённого каталога, запросы читают коннектор на месте, а фильтры и проекции проталкиваются в Calcite (REQ-1690) [tool-verified: provisa/federation/connector_duckdb.py _DuckDBPgwireConnector]. Любой другой движок загружает строки в хранилище материализации для федерации (REQ-954). Пакеты загружаются под конкретную ОС/архитектуру из закреплённого релиза kenstott/calcite (pgwire-<connector>-<version>-<os>-<arch>.tar.gz; macOS arm64, Linux x86_64, Windows x86_64) [tool-verified: provisa/runtime_deps/pgwire_bundles.py]. Оба коннектора всегда включают сопоставление имён без учёта регистра, что соответствует собственной регистронезависимой семантике каждого продукта (REQ-725, REQ-730). [tool-verified: provisa/core/models.py lines 99–100; provisa/federation/trino_connectors.py lines 223–286]
sharepoint¶
Списки SharePoint перечисляются как схемы и представляются в виде запрашиваемых таблиц (REQ-726, REQ-731). Два метода аутентификации: CLIENT_CREDENTIALS (по умолчанию) и на основе сертификата через PFX-сертификат (REQ-727). Секретные значения в mapping разрешаются через движок секретов до достижения коннектора (REQ-729). [tool-verified: provisa/federation/trino_connectors.py lines 230–252]
| Поле источника | Свойство коннектора | Примечания |
|---|---|---|
base_url или host |
site-url |
URL сайта SharePoint |
username |
client-id |
ID клиента приложения Azure |
password |
client-secret |
Секрет клиента приложения Azure |
database |
tenant-id |
UUID клиента (тенанта) Azure |
mapping.auth_type |
auth-type |
CLIENT_CREDENTIALS (по умолчанию) или CERTIFICATE |
mapping.certificate_path |
certificate-path |
Путь к PFX при auth_type: CERTIFICATE — должен быть АБСОЛЮТНЫМ |
mapping.certificate_password |
certificate-password |
Пароль PFX — ключ должен присутствовать, пустая строка для PFX без пароля |
Аутентификация по сертификату на движках, отличных от Trino, несёт два дополнительных правила, оба применяются при построении операнда model.json сервера Calcite pgwire (REQ-1693). certificate_path должен быть абсолютным: сервер работает с директорией своего пакета как рабочей директорией, поэтому относительный путь разрешается внутри кеша runtime-зависимостей, и PFX не находится. certificate_password должен присутствовать в mapping, даже если у PFX нет пароля — в этом случае это пустая строка; адаптер Calcite полностью отклоняет пустой (null) пароль, а отсутствующий ключ трактуется как ошибка конфигурации, а не молча читается как пустой пароль. Отсутствующее или относительное значение вызывает MissingConnectorConfig с именем поля. [tool-verified: provisa/federation/pgwire_replica.py _sharepoint_operand]
Когда коннектор не предоставляет information_schema.columns, зарегистрируйте таблицу с явными определениями столбцов (полученными из Microsoft Graph API) через мутацию registerTable (REQ-732).
- id: hr-sharepoint
type: sharepoint
base_url: https://kenstott.sharepoint.com
username: ${env:SP_CLIENT_ID}
password: ${env:SP_CLIENT_SECRET}
database: ${env:SP_TENANT_ID}
mapping:
auth_type: CLIENT_CREDENTIALS
Аутентификация по сертификату, с абсолютным путём и всегда присутствующим паролем:
- id: hr-sharepoint
type: sharepoint
base_url: https://kenstott.sharepoint.com
username: ${env:SP_CLIENT_ID}
database: ${env:SP_TENANT_ID}
mapping:
auth_type: CERTIFICATE
certificate_path: /etc/provisa/certs/sharepoint.pfx
certificate_password: ${env:SP_CERT_PASSWORD}
splunk¶
Результаты поиска Splunk доступны для запросов как таблицы (например, internal_server) (REQ-721). URL коннектора берётся из base_url либо формируется как https://{host}:{port} со значением порта по умолчанию 8089 (REQ-722). Аутентификация: когда mapping.use_token равно true (по умолчанию), password передаётся как API-токен; когда false, username и password передаются как отдельные учётные данные (REQ-723). [tool-verified: provisa/federation/trino_connectors.py lines 262–286]
| Поле источника | Свойство коннектора | Примечания |
|---|---|---|
base_url / host + port |
url |
base_url, иначе https://host:port (порт по умолчанию 8089) |
password |
token или password |
токен, когда use_token: true |
username |
user |
только когда use_token: false |
database |
app |
ограничить конкретным приложением Splunk |
mapping.datamodel_filter |
datamodel-filter |
фильтр по модели данных |
mapping.disable_ssl_validation |
disable-ssl-validation |
для самоподписанных сертификатов (REQ-724) |
На пути pgwire-replica (любой движок, кроме Trino) те же четыре опциональных настройки становятся ключами операнда Calcite model.json: app, token/username+password, datamodelFilter и disableSslValidation — последние два приводятся к типам, которые ожидает SplunkSchemaFactory: строка и булево значение (REQ-1694). [tool-verified: provisa/federation/pgwire_replica.py _splunk_operand]
- id: ops-splunk
type: splunk
host: splunk
port: 8089
password: ${env:SPLUNK_TOKEN}
mapping:
use_token: true
disable_ssl_validation: true
API-источники¶
Зарегистрируйте любую HTTP-конечную точку как запрашиваемую таблицу. [tool-verified: provisa/core/models.py SourceType enum] (REQ-314, REQ-307, REQ-322)
| Тип API | Обнаружение | Вывод типов столбцов |
|---|---|---|
openapi |
Разбор спецификации OpenAPI (REQ-314, REQ-316) | Примитивы → нативные типы, объекты → JSONB |
graphql_remote |
Интроспекция схемы (REQ-307, REQ-308) | Примитивы → нативные типы, объекты → JSONB |
grpc_remote |
Server reflection (REQ-322, REQ-325) | Примитивы → нативные типы, объекты → JSONB |
Ответы API получаются, кешируются в PostgreSQL (настраиваемый TTL) и представляются как типы GraphQL (REQ-309, REQ-318, REQ-327). Кешированные таблицы участвуют в федеративных запросах наравне с любым другим источником (REQ-313).
Правила JSONB: сложные столбцы (объекты, массивы), хранящиеся как JSONB, не поддерживают фильтрацию (REQ-119). Доступ к вложенным полям использует извлечение через ->> в SQL (REQ-151). Связи объявляются между таблицами через скалярные столбцы внешнего ключа — столбцы-блобы JSONB не являются целями соединения (join). Используйте продвижение JSONB (JSONB promotion), чтобы преобразовать вложенные поля в нативные скалярные столбцы, когда требуется фильтрация или соединение по ним (REQ-119).
GovData¶
Открытые данные правительства США. Доступ разделён по тематическим группам. [tool-verified: provisa/core/models.py lines 543–609]
Каждый источник govdata выбирает одну тему. Эта тема определяет, какие схемы GovData доступны. Схемы ref и geo всегда включены как связующие схемы — они не перечислены по темам, но присутствуют всегда. [tool-verified: provisa/core/models.py line 562–563 comment]
| Тема | Открываемые схемы |
|---|---|
COMMERCE |
sec, patents |
ECONOMY |
econ |
EDUCATION |
census, edu |
HEALTH |
health |
CYBER |
cyber_threat, cyber_vuln |
PUBLIC_SAFETY |
crime |
ENVIRONMENT |
lands |
WEATHER |
weather |
GOVERNMENT |
fedregister, fec |
ALL |
Все схемы выше |
sources:
- id: federal-commerce
type: govdata
subject: COMMERCE
domain_id: federal-analytics
description: U.S. commerce and securities data
| Поле | Обязательно | По умолчанию | Описание |
|---|---|---|---|
id |
Да | — | Уникальный идентификатор |
subject |
Да | — | Одно из значений темы выше |
domain_id |
Да | — | Домен, которому принадлежит этот источник |
description |
Нет | "" |
Описание для человека |
Где хранится пароль источника¶
Пароль источника никогда не хранится рядом с остальными настройками подключения. Строка sources
в плоскости управления содержит столбец password_ref, хранящий ссылку —
${env:PG_PASSWORD}, ${secret:SNOWFLAKE_KEY} — которая разрешается в момент, когда источник
подключается, внутри организации, от имени которой выполняется запрос (REQ-1695). [tool-verified:
provisa/core/schema_org.py, provisa/core/repositories/source.py]
${env:VAR} читает переменную окружения процесса развёртывания и не требует привязки. ${secret:NAME}
именует секрет, которым владеет организация, поэтому он разрешается только в рамках собственных
операций этой организации: интроспекционные точки администрирования и терминал запросов, к которым
обращается каждая поверхность, устанавливают эту привязку. [tool-verified: provisa/pgwire/_pipeline.py _execute_plan]
Куда указывает ссылка, зависит от того, как был зарегистрирован источник:
- Из конфигурации. Вы сами пишете ссылку.
${env:VAR}читает переменную окружения процесса развёртывания;${secret:NAME}читает хранилище секретов организации (см. Секреты). Файл — это запись, и Provisa копирует ссылку вpassword_refдословно. - Из формы Sources. Ссылка, введённая в поле пароля, также сохраняется дословно. Буквальный
пароль записывается в хранилище секретов организации под именем
source_<id>_password— зашифрованно, и его нельзя прочитать обратно по имени — а строка сохраняет${secret:source_<id>_password}, который его именует. [tool-verified:provisa/api/admin/schema_common.pypersist_source_password]
Повторный ввод пароля для существующего источника ротирует эту одну запись хранилища, а не создаёт
вторую. Удаление источника убирает запись, созданную для него Provisa, и только её: ссылка, которую
вы написали сами, именует секрет, которым владеете вы по своим собственным причинам, и её никто не
трогает.
[tool-verified: provisa/api/admin/schema_mutation.py delete_source]
password_ref не переносится между окружениями (REQ-1491). Ветка (branch) или скопированное окружение
предоставляет собственные значения подключения, а хранилище секретов, которое именует ссылка, принадлежит
тому окружению, которое её предоставило. [tool-verified: provisa/core/env_classes.py BINDING_COLUMNS]
Проверки качества данных (REQ-1443)¶
Проверка качества данных — это тип источника, а не подсистема. Результат её сканирования — это данные: результат проверки — это наблюдение, поэтому он проходит через обычный путь источника и наследует периодичность, свежесть, события, происхождение (lineage), управление (governance), RLS, сетку (grid) и экспорт наравне с любым другим источником. [tool-verified: provisa/core/models.py lines 110–116 SourceType.soda, SourceType.great_expectations; provisa/events/source_loader.py make_dq_loader]
Поддерживаются два варианта, и выбор — это в той же мере выбор лицензии, что и функции.
| Тип источника | Диалект контракта | Дополнение (extra) | Лицензия | Разрешено в облачной плоскости |
|---|---|---|---|---|
soda |
Soda contract YAML | pip install .[soda] (soda-postgres) |
Elastic License 2.0 | Отказано — см. ниже |
great_expectations |
Expectation suite JSON | pip install .[gx] (great-expectations[postgresql]) |
Apache 2.0 | Разрешено |
Elastic License 2.0 запрещает предоставлять программное обеспечение третьим лицам как хостинговую или управляемую услугу, а запуск Soda внутри плоскости SaaS от имени тенанта — это именно оно. config/capabilities.yaml содержит это разделение как cloud_eligible: false для опции soda, и хостинговая плоскость читает этот флаг. Хостинговое развёртывание, которому нужен Soda, обращается к конечной точке Soda, предоставленной оператором и управляемой им самим. [tool-verified: config/capabilities.yaml lines 197–203]
Provisa ничего не поставляет и не линкует. Сканирование выполняется в дочернем интерпретаторе (python -m provisa.dq.worker) — единственном месте, куда импортируется soda_core или great_expectations, поэтому доступный из исходников проверщик никогда не достигает серверного процесса, а падение проверщика убивает подпроцесс, а не цикл событий. [tool-verified: provisa/dq/runner.py build_command, run_contract]
Источник указывает на собственную конечную точку pgwire Provisa. Именно это позволяет одному драйверу postgres проверять таблицу с бэкендом Snowflake или Iceberg: проверщик сканирует федеративное представление, а не базовую систему. Поскольку к этому подключению применяется политика, идентичность сканирования объявляется явно, а не наследуется — отфильтрованный набор строк никогда не должен приводить к молчаливому прохождению проверки.
sources:
- id: dq
type: soda
domain_id: sales-analytics
description: Soda contract scans over the governed estate
mapping:
host: localhost
port: 5439 # Provisa's pgwire endpoint
database: provisa
user: dq_scanner # the scan identity, declared explicitly
password: ${env:PROVISA_DQ_PASSWORD}
Одна таблица результатов на контракт, и контракт — это вся регистрация. Таблица содержит dq_contract — текст контракта дословно — и больше ничего о своей структуре. Столбцы, водяной знак и продвижения (promotions) выводятся автоматически. [tool-verified: provisa/dq/registration.py derive_checker_table]
tables:
- source_id: dq
schema_name: quality
table_name: orders_scan
domain_id: sales-analytics
change_signal: ttl_probe
cache_ttl: 3600
columns:
- name: scan_id # declared only to carry visible_to; replaced at parse
visible_to: [analyst, admin]
dq_contract: |
dataset: provisa/sales/orders
columns:
- name: customer_id
checks:
- missing:
threshold:
metric: percent
must_be_less_than: 1
checks:
- row_count:
must_be_greater_than: 0
Что регистрация выводит из этого текста:
- Происхождение (lineage). Контракт уже называет свой целевой набор данных, поэтому регистрация разбирает его так же, как
extract_inputsразбирает SQL (REQ-939), и разрешает его до управляемой (governed) таблицы. Одно определение, никакой второй копии, способной разойтись. Контракт, называющий неуправляемый набор данных, падает сразу при регистрации, а не загружает строки, которые никто не запрашивал. - Столбцы. Конверт результата принадлежит проверщику, а не оператору — 16 поставляемых столбцов от
scan_idдоdiagnostics. Объявленные столбцы читаются только ради ихvisible_to, которое должно быть единогласным, а затем заменяются. [tool-verified:provisa/dq/results.py_ENVELOPE,results_columns] - Водяной знак.
scan_timeстановится водяным знаком, что делает загрузку добавлением (append) (REQ-982). История сканирований накапливается без отдельной подсистемы истории. - Продвижения (promotions).
freshness_max_timestampиdataset_rows_testedпродвигаются из jsonb-поляdiagnosticsв типизированные столбцы (REQ-119). Добавляйте другие так же, как для любого другого jsonb-столбца. [tool-verified:provisa/dq/results.pyDQ_PROMOTIONS]
Синхронизация во времени не вводит новых полей. change_signal вместе с cache_ttl задают периодичность опроса; mv_debounce_quiet и mv_debounce_max_delay сворачивают всплеск на входе в одно сканирование (REQ-963); календарный шаг (grain) делает его периодическим (REQ-962); expected_events удерживает сканирование до тех пор, пока его входные данные не станут свежими в пределах окна (REQ-961). Цикл опроса и есть планировщик сканирований.
outcome — одно из значений pass, fail, warn, error, skipped. Ни одно из них не является вердиктом — принудительное применение (enforcement), если требуется, — это отдельное объявление позже: preflight-проверка или материализованное представление поверх загруженных результатов. Поскольку загруженное наблюдение не несёт обязательства детерминизма (REQ-964), здесь допустимы недетерминированные проверки, которые никогда не могли бы стоять на preflight-шлюзе — оценка аномалий, изменение в скользящем окне, свежесть относительно текущего момента.
Контракт создаётся в UI, на панели качества данных экрана редактирования таблицы, и текст контракта там всегда является источником истины. Пробный запуск (dry run) выполняет контракт против живой таблицы и показывает результаты без их загрузки — именно так вы обнаруживаете контракт, имя набора данных которого разрешилось в неожиданное место и иначе загрузил бы только проходящие строки.
Пользовательские коннекторы (REQ-1177)¶
Нативные движки федерации — Postgres, DuckDB и ClickHouse — получают достижимость до нового типа источника, когда оператор объявляет для него коннектор в config/custom_connectors.yaml. Код не требуется. [tool-verified: provisa/federation/custom_connectors.py load_custom_connectors; provisa/federation/engine.py build_pg_engine, build_duckdb_engine, build_clickhouse_engine]
Расширяемость коннекторов сама по себе появилась раньше. Движок Trino давно расширяем на собственном уровне — один универсальный JDBC-коннектор, параметризуемый под тип источника, тело каталога .properties на тип, и собственные плагины Provisa для коннекторов Trino (Splunk, SharePoint, Calcite). [tool-verified: provisa/federation/trino_connectors.py _TrinoJdbcConnector, _TRINO_JDBC_TYPES; trino/plugins/trino-splunk, trino/plugins/trino-sharepoint, trino/plugins/trino-calcite] REQ-1177 привносит ту же конфигурационно-управляемую расширяемость в два нативных, безкластерных движка, которые ранее имели фиксированный набор коннекторов.
Конфигурация поставляется пустой. Встроенные коннекторы обеспечивают достижимость «из коробки»; всё в этом файле создаётся оператором. [tool-verified: config/custom_connectors.yaml line 52: connectors: []] Установите PROVISA_CUSTOM_CONNECTORS, чтобы указать на другой путь (полезно для тестов).
Виды дескрипторов¶
| Движок | Вид | Механизм | Что предоставляет дескриптор |
|---|---|---|---|
postgres |
pg_fdw |
SQL/MED (стандарт ISO) | extension, server_options, user_mapping, supports_import, table_options, remote_schema |
duckdb |
duckdb_attach |
INSTALL/LOAD + ATTACH | extension, probe_symbol, attach_template, remote_schema |
duckdb |
duckdb_scan |
INSTALL/LOAD + представление-сканер | extension, probe_symbol, scan_template |
clickhouse |
clickhouse_database |
CREATE DATABASE ENGINE=… (автоматически раскрывает каждую удалённую таблицу) |
ch_engine, engine_template |
clickhouse |
clickhouse_table |
CREATE TABLE ENGINE=… на таблицу (столбцы из реестра) |
ch_engine, engine_template (может содержать {table}) |
clickhouse |
clickhouse_scan |
CREATE TABLE ENGINE=…, ClickHouse сам выводит схему |
ch_engine, engine_template |
Postgres универсален. SQL/MED — это стандарт ISO, поэтому каждый соответствующий ему FDW разделяет одну и ту же форму DDL: CREATE SERVER … FOREIGN DATA WRAPPER <fdw> OPTIONS(…), опционально CREATE USER MAPPING, затем либо IMPORT FOREIGN SCHEMA (когда supports_import: true), либо явный CREATE FOREIGN TABLE для каждой таблицы (когда false). Дескриптор pg_fdw предоставляет только специфичную для конкретного FDW часть — имя расширения, ключи опций сервера, ключи сопоставления пользователей, флаг импорта, опции таблицы. Поэтому любой FDW, соответствующий стандарту, можно настроить только через конфигурацию. [tool-verified: provisa/federation/custom_connectors.py GenericPgFdwConnector.details lines 98–125]
DuckDB поддерживает два механизма. Расширение, раскрывающее каталог через ATTACH, использует duckdb_attach; раскрывающее табличную функцию для чтения использует duckdb_scan. Расширение, не подходящее ни под один из этих шаблонов, не поддерживается. [tool-verified: provisa/federation/custom_connectors.py GenericDuckDbAttachConnector, GenericDuckDbScanConnector]
ClickHouse поддерживает три механизма, по одному на форму движка интеграции: реляционный движок DATABASE, который автоматически раскрывает каждую удалённую таблицу (clickhouse_database, например Redis/MySQL), потабличный движок, столбцы которого предоставляет реестр (clickhouse_table, например мост JDBC/ODBC — engine_template может содержать placeholder {table}, который среда выполнения подставляет), и движок файлов/lake/URL, схему которого выводит сам ClickHouse (clickhouse_scan, например HDFS/URL). SQLite (движок DATABASE, файл, без сервера) и Hudi (lakehouse, без копирования) поставляются «из коробки». [tool-verified: provisa/federation/custom_connectors.py GenericClickHouseDatabaseConnector, GenericClickHouseTableConnector, GenericClickHouseScanConnector; provisa/federation/clickhouse_connectors.py ClickHouseSqliteConnector, ClickHouseHudiConnector] (REQ-1178)
Неизвестное значение kind приводит к падению сразу при старте — опечатка в дескрипторе не должна молча оставлять тип источника недостижимым. [tool-verified: provisa/federation/custom_connectors.py load_custom_connectors lines 178–197]
Проверка доступности (probe gating)¶
Доступность проверяется в момент подключения по стандартному каталогу обнаружения каждого движка:
- Postgres — проверяет
pg_extension, затемpg_available_extensions. [tool-verified:provisa/federation/connector_duckdb.py_probe_pg_extensionlines 333–344] - DuckDB — выполняет
INSTALL/LOADи проверяетduckdb_functions()на наличие объявленногоprobe_symbol. [tool-verified:provisa/federation/connector_duckdb.py_DuckDBExtensionConnector.probelines 160–180] - ClickHouse — проверяет
system.table_enginesна наличие объявленногоch_engine; отсутствие в сборке приводит к падению сразу. [tool-verified:provisa/federation/custom_connectors.py_probe_clickhouse_engine]
Объявленное расширение, которое не устанавливается, приводит к падению сразу. Никакого молчаливого пропуска, никакого запасного варианта (fallback). Коннектор, чья проверка не проходит, просто неактивен для этого развёртывания.
Переменные шаблона¶
Каждое значение server_options, значение user_mapping, attach_template и scan_template может использовать placeholder-ы вида {field}. Доступные поля: [tool-verified: provisa/federation/custom_connectors.py _source_fields lines 53–63]
{id}, {host}, {port}, {database}, {username}, {password}, {path}, {schema_name}, {table_name}, а также любой ключ из federation_hints. Шаблоны подключения DuckDB также получают {alias} — внутренний псевдоним каталога, который Provisa присваивает подключённой базе данных.
Шаблон, ссылающийся на неизвестное поле, приводит к падению сразу в момент подключения, выявляя несоответствие дескриптора и источника до того, как некорректный DDL достигнет движка.
Примеры¶
Postgres — MongoDB через mongo_fdw (без импорта схемы; столбцы предоставляются на уровне таблицы)
# config/custom_connectors.yaml
connectors:
- engine: postgres
source_type: mongodb
kind: pg_fdw
extension: mongo_fdw
mechanism: attach_r
server_options:
address: "{host}"
port: "{port}"
user_mapping:
username: "{username}"
password: "{password}"
supports_import: false
table_options:
database: "{database}"
collection: "{table_name}"
DuckDB — файлы Excel через read_xlsx (табличная функция-сканер)
- engine: duckdb
source_type: xlsx
kind: duckdb_scan
extension: excel
install_from_community: false
probe_symbol: read_xlsx
scan_template: "read_xlsx('{path}')"
[tool-verified: config/custom_connectors.yaml commented examples, lines 26–50]
Когда любой из дескрипторов на месте, регистрация источника с объявленным source_type направляется через пользовательский коннектор при условии успешной проверки доступности. Никаких других изменений конфигурации не требуется.
Хранилища как именованные источники¶
Snowflake, Databricks и ClickHouse можно зарегистрировать как именованные источники независимо от того, какой движок федерации активен. [tool-verified: executor/drivers/snowflake.py (REQ-988), executor/drivers/databricks.py (REQ-987), executor/drivers/clickhouse.py (REQ-986)]
При регистрации Provisa читает хранилище через DirectDriver источника и создаёт реплику в хранилище материализации активного движка. Затем запрос выполняется против этой реплики. Это отличается от традиционного пути с прямой поддержкой (asyncpg, aiomysql), где движок полностью минуется — здесь движок по-прежнему выполняет запрос, но против локальной реплики, а не через сеть к хранилищу при каждом запросе.
Чтение выполняется в формате Arrow там, где хранилище это поддерживает: Databricks использует Cloud Fetch, Snowflake использует fetch_arrow_table, а ClickHouse использует нативный колоночный HTTP-интерфейс.
Расширенные параметры подключения, которые не помещаются в стандартные поля host/port/username/password, идут в federation_hints:
sources:
- id: my-databricks
type: databricks
host: my-workspace.azuredatabricks.net
password: ${env:DATABRICKS_TOKEN}
federation_hints:
http_path: /sql/1.0/warehouses/xxxx # required — the SQL Warehouse connection detail
- id: my-snowflake
type: snowflake
host: org.snowflakecomputing.com
username: svc_provisa
password: ${env:SNOWFLAKE_PASSWORD}
federation_hints:
account: myorg-myaccount # required — Snowflake account identifier
warehouse: COMPUTE_WH # optional — virtual warehouse to use
role: PROVISA_ROLE # optional — Snowflake role
- id: my-clickhouse
type: clickhouse
host: ch.example.com
port: 8123
database: analytics
username: default
password: ${env:CLICKHOUSE_PASSWORD}
federation_hints:
secure: "true" # optional — enables TLS on the HTTP interface
Регистрация в качестве именованного источника не зависит от выбора того же хранилища в качестве движка федерации. Источник Snowflake на движке DuckDB создаёт реплику в DuckDB, а не в Snowflake.
Данные объектных/lake-хранилищ в облаке (файлы parquet, csv, iceberg, delta_lake в S3 / GCS / R2) — это отдельный тип источника, который подключается на месте, когда активный движок имеет ATTACH-коннектор для этого типа. Реплика не создаётся — движок сканирует объектное хранилище напрямую. Учётные данные для таких источников также идут в federation_hints:
sources:
- id: r2-events
type: parquet
path: s3://my-bucket/events/2026/*.parquet
federation_hints:
access_key_id: ${env:R2_ACCESS_KEY}
secret_access_key: ${env:R2_SECRET}
account_id: ${env:R2_ACCOUNT_ID} # Cloudflare R2 account (S3-compatible)
Поля конфигурации источника¶
Все источники разделяют общий набор полей. [tool-verified: provisa/core/models.py Source class, lines 138–204]
| Поле | Обязательно | По умолчанию | Описание |
|---|---|---|---|
id |
Да | — | Уникальный идентификатор; буквенно-цифровой, с дефисами/подчёркиваниями |
type |
Да | — | Тип источника (см. таблицы выше) |
host |
Нет | "" |
Имя хоста или IP |
port |
Нет | 0 |
Номер порта |
database |
Нет | "" |
Имя базы данных |
username |
Нет | "" |
Имя пользователя |
password |
Нет | "" |
Пароль; используйте ${env:VAR} или ${secret:NAME} вместо буквального значения (см. ниже) |
path |
Нет | null |
Путь к файлу или облачный URI для файловых и объектных/lake-источников |
base_url |
Нет | null |
Базовый URL для источников OpenAPI |
pool_min |
Нет | 1 |
Минимальный размер пула соединений (REQ-052) |
pool_max |
Нет | 5 |
Максимальный размер пула соединений (REQ-052) |
use_pgbouncer |
Нет | false |
Направлять соединения через PgBouncer (REQ-053) |
pgbouncer_port |
Нет | 6432 |
Порт PgBouncer (REQ-053) |
cache_enabled |
Нет | true |
Включить кеширование ответов API |
cache_ttl |
Нет | null |
TTL кеша в секундах; наследует глобальное значение по умолчанию, если null |
cache_catalog |
Нет | null |
Федеративный каталог для кеша API; по умолчанию — собственный каталог источника |
cache_schema |
Нет | api_cache |
Схема внутри каталога кеша |
naming_convention |
Нет | null |
Переопределить глобальное соглашение об именовании для этого источника (REQ-194) |
federation_hints |
Нет | {} |
Параметры сессии, передаваемые движку федерации, и расширенные параметры подключения для источников-хранилищ (REQ-278, REQ-281) |
mapping |
Нет | {} |
Настройки коннектора, специфичные для типа, для NoSQL- и SaaS-источников (например, auth_type SharePoint, use_token Splunk) (REQ-251) |
allowed_domains |
Нет | [] |
Ограничить источник конкретными доменами; пусто = без ограничений |
description |
Нет | "" |
Описание для человека |
Источники Kafka¶
Топики Kafka настраиваются отдельно под ключом kafka_sources, ключом выступает id зарегистрированного источника kafka. [tool-verified: config/provisa.yaml lines 138–151] (REQ-147)
kafka_sources:
- id: kafka-support
topics:
- id: tickets
topic: support.tickets
domain_id: sales-analytics
description: "Inbound support tickets"
default_window: 1h
columns:
- name: id
- name: subject
- name: status
- name: created_at
| Поле | Описание |
|---|---|
id |
Должен совпадать с id источника с type: kafka |
topics[].id |
Логическое имя этого топика внутри Provisa |
topics[].topic |
Имя топика Kafka |
topics[].domain_id |
Домен, которому принадлежит этот топик |
topics[].description |
Описание для человека |
topics[].default_window |
Временное окно по умолчанию для оконных запросов (например, 1h) (REQ-148) |
topics[].columns |
Определения столбцов для схемы топика (REQ-150) |
Видимость столбцов¶
Поле visible_to на каждом столбце — это список ID ролей, которым виден этот столбец. [tool-verified: provisa/core/models.py Column class line 248; config/provisa.yaml lines 39–51]
columns:
- name: email
visible_to: [admin] # only admin role sees this column
- name: region
visible_to: [admin, analyst] # both roles see this column
Столбцы, отсутствующие в списке visible_to роли, не появляются в GraphQL-схеме этой роли и не могут запрашиваться или упоминаться в фильтрах (REQ-039).
Связи¶
Связи соединяют две зарегистрированные таблицы и появляются как вложенные поля в GraphQL. [tool-verified: provisa/core/models.py Relationship class lines 323–343; config/provisa.yaml lines 103–110] (REQ-019)
relationships:
- id: orders-to-customers
source_table_id: orders
target_table_id: customers
source_column: customer_id
target_column: id
cardinality: many-to-one
| Поле | Обязательно | Описание |
|---|---|---|
id |
Да | Уникальный идентификатор этой связи |
source_table_id |
Да | Таблица, содержащая внешний ключ |
target_table_id |
Да | Таблица, на которую ссылаются; пусто для вычисляемых связей |
source_column |
Да | Столбец в исходной таблице |
target_column |
Да | Столбец в целевой таблице; пусто для вычисляемых связей |
cardinality |
Да | many-to-one или one-to-many (REQ-019) |
materialize |
Нет | Автоматически создать материализованное представление для межисточниковых соединений (REQ-158). На связи, опирающейся на связующую таблицу (junction), представление охватывает двухшаговый обход, а не прямое соединение (REQ-1586) |
refresh_interval |
Нет | Интервал обновления материализованного представления в секундах (по умолчанию: 300) |
target_function_name |
Нет | Имя функции БД для вычисляемых связей |
function_arg |
Нет | Какой аргумент функции получает значение исходного столбца |
alias |
Нет | Читаемое имя типа связи (например, WORKS_FOR) |
graphql_alias |
Нет | Именует поле SDL, которое эта связь предоставляет в родительском типе. Если отсутствует, имя выводится из field_name целевой таблицы и кардинальности связи. [tool-verified: provisa/compiler/schema_gen.py:1050] |
disable_cypher |
Нет | Если true, исключить эту связь из рёбер графа Cypher |
source_json_key |
Нет | Извлечь этот ключ из исходного столбца как JSON-объект перед JOIN |
via_table |
Нет | Имя зарегистрированной таблицы связующего звена (junction), через которое проходит это ребро. Указание этого поля делает ребро опирающимся на junction; оставление пустым делает его ребром по внешнему ключу (REQ-1586) |
via_source_column |
Нет | Столбец junction, сопоставляемый с source_column. Через запятую и позиционно для составного ключа |
via_target_column |
Нет | Столбец junction, сопоставляемый с target_column |
via_type_column |
Нет | Столбец-дискриминатор, когда одна junction-таблица несёт несколько типов связей |
via_type_value |
Нет | Значение дискриминатора, к которому привязано это ребро |
via_label_source |
Нет | Какая номинация именует тип Cypher: column (значение дискриминатора), table (имя таблицы junction) или fixed (объявленный псевдоним). Все приводятся к верхнему регистру со знаком подчёркивания |
Связи, опирающиеся на junction¶
Ассоциативную таблицу можно объявить как полноценную связь Cypher вместо узла, так что её собственные столбцы становятся атрибутами этой связи: (REQ-1586)
relationships:
- id: pets-bonded-pair
source_table_id: pets
target_table_id: pets
source_column: id
target_column: id
cardinality: one-to-many
via_table: pet_companions
via_source_column: pet_id
via_target_column: companion_pet_id
via_type_column: relation_type
via_type_value: bonded pair
via_label_source: column
Junction-таблица регистрируется как обычная таблица и должна быть зарегистрирована до того, как связь
сможет её назвать. Объявляйте её по одному разу на каждое значение дискриминатора: три строки над
pet_companions дают BONDED_PAIR, LITTERMATE и SHARES_ENCLOSURE как три разных типа Cypher,
каждый из которых несёт оставшиеся столбцы строки junction в качестве свойств ребра. Поставляемая
демо-конфигурация объявляет именно это.
Ребро junction — это связь Cypher, а не поле соединения GraphQL: эмиттер соединений GraphQL строит
свою секцию ON для одной пары столбцов и не имеет места для второго шага, поэтому junction-рёбра
исключаются из генерируемого SDL и из pg_constraint. [tool-verified: provisa/compiler/schema_gen.py:304]
Junction-таблица остаётся запрашиваемой как собственное корневое поле и удаляется со стороны узлов
графовой схемы Cypher, чтобы никогда не появляться как метка узла.
materialize: true работает на ребре junction, и то, что материализуется, — это обход, а не
прямое соединение pets-к-pets: представление содержит исходный шаг, шаг junction,
дискриминатор и собственные столбцы junction рядом со столбцами цели. Поскольку junction — это
третье звено соединения, пересекает ли ребро источники, оценивается по всем трём таблицам —
junction в источнике, отличном от источника двух связываемых им таблиц, материализуется, даже если
эти две таблицы совпадают. Одно объявление материализует один тип ребра, поэтому представление,
построенное для bonded pair, никогда не отвечает на обход littermate.
Значения кардинальности [tool-verified: provisa/core/models.py Cardinality enum, lines 79–81]:
many-to-one— каждая строка источника сопоставляется с одной строкой цели (FK к PK)one-to-many— каждая строка источника сопоставляется с несколькими строками цели (обратное предыдущему)
Правила безопасности на уровне строк¶
Правила RLS вставляют предложения WHERE во время выполнения запроса, привязанные к роли и, опционально, к таблице или домену. [tool-verified: provisa/core/models.py RLSRule class lines 391–395; config/provisa.yaml lines 128–131] (REQ-041)
rls_rules:
- table_id: orders # applies to orders table only
role_id: analyst
filter: "region = current_setting('provisa.user_region')"
- domain_id: sales-analytics # applies to every table in domain (REQ-402)
role_id: analyst
filter: "tenant_id = current_setting('provisa.tenant_id')"
Когда для одной и той же роли существуют и правило на уровне домена, и правило на уровне таблицы, правило на уровне таблицы имеет приоритет (REQ-403).
| Поле | Обязательно | Описание |
|---|---|---|
table_id |
Условно | Таблица, к которой применяется правило; взаимоисключающе с domain_id |
domain_id |
Условно | Домен, к которому применяется правило; применяется ко всем таблицам домена (REQ-402) |
role_id |
Да | Роль, к которой применяется это правило |
filter |
Да | SQL-предикат, вставляемый в WHERE; может ссылаться на переменные сессии (REQ-041) |
Функции и вебхуки¶
Функции БД¶
Отслеживайте функцию базы данных и представляйте её как запрос или мутацию GraphQL. [tool-verified: provisa/core/models.py Function class lines 423–438; config/provisa.yaml lines 152–164] (REQ-205)
Источники баз данных также могут автоматически обнаруживать свои хранимые процедуры и функции из каталога поставщика (pg_proc, information_schema.routines или эквивалентов у других поставщиков), устраняя необходимость вручную регистрировать каждую из них. Обнаружение читает prokind и provolatile: неизменяемые/стабильные (immutable/stable) функции регистрируются как параметризованные отношения (аргументы процедуры становятся параметрами запроса, в той же форме, что и GET-таблицы OpenAPI), а изменчивые (volatile) процедуры регистрируются как мутации/отслеживаемые функции. Обнаруженные процедуры проходят через управление (governance) на Этапе 2 так же, как и зарегистрированные вручную. [tool-verified: provisa/api/admin/introspect.py:541, provisa/api/admin/introspect.py:593] (REQ-887)
functions:
- name: get_customers_by_region
source_id: sales-pg
schema: public
function_name: get_customers_by_region
returns: customers
domain_id: sales-analytics
description: "Returns customers filtered by region"
visible_to: [admin, analyst]
kind: query
arguments:
- name: p_region
type: String
| Поле | Обязательно | По умолчанию | Описание |
|---|---|---|---|
name |
Да | — | Имя поля GraphQL |
source_id |
Да | — | Источник, содержащий функцию |
schema |
Нет | public |
Схема базы данных |
function_name |
Да | — | Фактическое имя функции базы данных |
returns |
Да | — | ID зарегистрированной таблицы, которую возвращает функция (REQ-207) |
arguments |
Нет | [] |
Список определений аргументов {name, type} (REQ-211) |
visible_to |
Нет | [] |
Роли, которым разрешено вызывать эту функцию |
writable_by |
Нет | [] |
Роли, которым разрешено вызывать это как мутацию |
domain_id |
Нет | "" |
Домен, которому принадлежит эта функция |
description |
Нет | null |
Описание поля GraphQL |
kind |
Нет | mutation |
"query" или "mutation" (REQ-205) |
Вебхуки¶
Представьте внешнюю HTTP-конечную точку как запрос или мутацию GraphQL. [tool-verified: provisa/core/models.py Webhook class lines 441–455; config/provisa.yaml lines 166–178] (REQ-209)
webhooks:
- name: notify_support
url: http://localhost:9999/notify
method: POST
timeout_ms: 3000
domain_id: sales-analytics
description: "Send a support notification"
visible_to: [admin]
kind: mutation
arguments:
- name: message
type: String
| Поле | Обязательно | По умолчанию | Описание |
|---|---|---|---|
name |
Да | — | Имя поля GraphQL |
url |
Да | — | URL конечной точки вебхука |
method |
Нет | POST |
HTTP-метод |
timeout_ms |
Нет | 5000 |
Таймаут запроса в миллисекундах |
returns |
Нет | null |
ID зарегистрированной таблицы, или null для встроенного типа |
inline_return_type |
Нет | [] |
Список полей {name, type} для пользовательских форм возвращаемого значения (REQ-210) |
arguments |
Нет | [] |
Список определений аргументов {name, type} |
visible_to |
Нет | [] |
Роли, которым разрешено вызывать этот вебхук |
domain_id |
Нет | "" |
Домен, которому принадлежит этот вебхук |
description |
Нет | null |
Описание поля GraphQL |
kind |
Нет | mutation |
"query" или "mutation" |
Аутентификация¶
Аутентификация настраивается под ключом auth. [tool-verified: provisa/core/models.py AuthConfig class lines 467–477] (REQ-120)
| Провайдер | Описание |
|---|---|
none |
Без аутентификации; все запросы обрабатываются как default_role |
firebase |
Firebase Authentication; требует project_id и service_account_key (REQ-121) |
keycloak |
Keycloak OIDC (REQ-122) |
oauth |
Универсальный OAuth 2.0 (REQ-123) |
simple |
Имя пользователя/пароль без внешнего провайдера (REQ-124) |
auth:
provider: firebase
assignments_source: provisa # "claims" or "provisa"
default_role: analyst
default_assignments:
- role_id: analyst
domain_id: "*"
firebase:
project_id: ${env:FIREBASE_PROJECT_ID}
service_account_key: ${env:FIREBASE_SERVICE_ACCOUNT_KEY}
assignments_source: claims читает назначения ролей из утверждений (claims) JWT. assignments_source: provisa читает их из собственного хранилища назначений Provisa. [tool-verified: provisa/core/models.py line 476] (REQ-551)
Маршрутизация выполнения¶
Прямое выполнение — однопоточные запросы к RDBMS направляются к нативному драйверу для задержки менее 100 мс (REQ-027). Для поддержки этого пути источникам требуются как запись в SOURCE_TO_DIALECT, так и запись в SOURCE_TO_CONNECTOR (REQ-229).
Федеративное выполнение — многопоточные запросы и источники без прямого драйвера направляются через движок федерации (REQ-028). Provisa включает встроенный движок федерации; для крупномасштабных развёртываний укажите на собственный совместимый кластер (REQ-226).
Статистика — при регистрации Provisa выполняет ANALYZE для каждой опубликованной таблицы, чтобы подготовить оптимизатор на основе стоимости (количество строк, доля null-значений, число различных значений, min/max). Ошибки логируются и не блокируют регистрацию (REQ-275).
Графовые и семантические источники¶
Neo4j¶
Зарегистрируйте графовую базу данных Neo4j как запрашиваемый источник. Стюарды пишут запросы Cypher, проецирующие скалярные значения; Provisa кеширует результаты и представляет их как типы GraphQL (REQ-295).
Запросы Cypher должны использовать акцессоры свойств в предложении RETURN (RETURN n.id AS id, n.name AS name) — возврат объектов узлов отклоняется в момент регистрации (REQ-296).
Регистрация через файл конфигурации (REQ-1668)¶
Объявите источник neo4j и его таблицы в YAML. Каждая таблица требует query_template (Cypher-запрос, порождающий её строки) и типизированные столбцы. Ключ query_template недопустим для любого другого типа источника. [tool-verified: provisa/core/config_loader.py:456-511]
Источнику требуются host, port и database. [tool-verified: provisa/core/config_loader.py:456-468] Строки получаются через POST-запрос {"statements": [{"statement": <cypher>}]} к /db/<database>/tx/commit (HTTP transaction API Neo4j). Ответ с непустым списком errors трактуется как неудавшийся запрос, а не как пустой результат. [tool-verified: provisa/neo4j/source.py:71-82, provisa/api_source/caller.py:344-347, provisa/api_source/normalizers.py:49-74]
Каждый столбец требует data_type. Загрузчик сопоставляет типы конфигурации с типом столбца API, используемым во время запроса [tool-verified: provisa/neo4j/persist.py:31-64]:
Тип data_type конфигурации |
Тип API |
|---|---|
varchar, text, string, char |
string |
integer, int, bigint, smallint |
integer |
float, double, real, decimal, numeric, number |
number |
boolean, bool |
boolean |
json, jsonb |
jsonb |
varchar(N) и decimal(10,2) принимаются — используется базовый тип до скобки.
Регистрация сохраняет строку api_sources и по одной строке api_endpoints на таблицу, поэтому таблицы переживают перезапуск без повторного чтения файла. REST-конечные точки администратора под /admin/sources/neo4j записывают те же строки. [tool-verified: provisa/neo4j/persist.py:77-120]
sources:
- id: graph
type: neo4j
host: neo4j
port: 7474
database: neo4j
cache_ttl: 300
tables:
- source_id: graph
schema: neo4j
table: person_skills
query_template: >-
MATCH (p:Person)-[:HAS_SKILL]->(s:Skill)
RETURN p.name AS name, s.skill AS skill, p.experience AS years
columns:
- name: name
data_type: varchar
- name: skill
data_type: varchar
- name: years
data_type: integer
Регистрация таблицы в UI (REQ-1670)¶
У источника neo4j нет таблиц для перечисления, поэтому форма регистрации таблицы запрашивает саму таблицу вместо того, чтобы предлагать её на выбор. [tool-verified: provisa-ui/src/pages/tables/RegisterTableForm.tsx (isNeo4j)]
- Выберите источник neo4j и домен. Селекторы схемы и таблицы, флажок обнаружения и селектор водяного знака не отображаются; источник никогда не интроспектируется.
- Введите имя таблицы и Cypher-запрос. Cypher должен проецировать скаляры (
RETURN a.name AS name); проекция, возвращающая узел или список, сообщается как ошибка. - Нажмите Preview. Форма выполняет Cypher с
LIMIT 5через GraphQL-запросneo4jPreviewи заполняет список столбцов из полученных строк, типизированных какtext,integer,double,booleanилиjson. [tool-verified:provisa/api/admin/_neo4j_registration.pypreview_neo4j] Неудачный предпросмотр оставляет Cypher в редакторе и показывает сообщение. - Настройте видимость, псевдонимы или маскирование как для любой таблицы, затем зарегистрируйте. Форма отказывается отправляться до тех пор, пока предпросмотр не типизировал столбцы, а сервер отказывается регистрировать таблицу neo4j без Cypher (
schema.neo4j_query_required). [tool-verified:provisa/api/admin/schema_mutation_ops.pypersist_neo4j_registration]
Cypher хранится вместе с таблицей как queryTemplate, отображается в представлении чтения таблицы и сохраняется точно так же, как при регистрации через файл конфигурации: строка api_sources и строка api_endpoints, которые гидратируются при следующем запуске. Редактирование таблицы заново сохраняет отредактированный Cypher.
Регистрация через admin REST¶
# Register via admin API (no YAML config required)
POST /admin/sources/neo4j
{
"source_id": "graph",
"host": "neo4j",
"port": 7474,
"database": "neo4j"
}
# Register a table (preview + validate before persisting)
POST /admin/sources/neo4j/graph/tables
{
"table_name": "person_skills",
"cypher": "MATCH (p:Person)-[:HAS_SKILL]->(s:Skill) RETURN p.name AS name, s.skill AS skill, p.experience AS years",
"ttl": 300
}
Конечная точка предпросмотра (POST /admin/sources/neo4j/{id}/preview) возвращает примеры строк и блокирует регистрацию, если Cypher возвращает объекты узлов (REQ-296).
SPARQL¶
Зарегистрируйте любое совместимое с SPARQL 1.1 хранилище триплетов (Apache Jena Fuseki, Virtuoso, Stardog и т. д.) как запрашиваемый источник (REQ-297).
Запросы должны быть запросами SELECT. Имена переменных в предложении SELECT автоматически становятся именами столбцов (REQ-297).
# Register via admin API
POST /admin/sources/sparql
{
"source_id": "knowledge-graph",
"endpoint_url": "http://fuseki:3030/ds/sparql",
"default_graph_uri": "http://example.org/graph"
}
# Register a table (executes LIMIT 5 probe to validate and infer columns)
POST /admin/sources/sparql/knowledge-graph/tables
{
"table_name": "product_categories",
"sparql_query": "SELECT ?product ?label ?category WHERE { ?product a :Product ; rdfs:label ?label ; :hasCategory ?category . }",
"ttl": 600
}
Оба коннектора используют конвейер кеша API-источника — результаты хранятся в PostgreSQL с настраиваемым TTL, что делает их доступными для межисточниковых федеративных JOIN (REQ-295, REQ-297, REQ-299).
Регистрация через файл конфигурации и через UI (REQ-1683)¶
host источника sparql — это URL его конечной точки SPARQL (форма Sources хранит его так же). Каждая таблица под ним содержит query_template — SELECT-запрос, чьи переменные становятся столбцами; каждая привязка (binding) имеет тип text. [tool-verified: provisa/core/config_loader.py _validate_neo4j_sources, _handle_sparql_table]
sources:
- id: sparql-demo
type: sparql
host: http://localhost:23030/provisa/query
tables:
- source_id: sparql-demo
domain_id: shelter
schema: sparql
table: volunteer
query_template: >-
PREFIX s: <http://provisa.dev/shelter#>
SELECT ?volunteer_id ?name WHERE { ?v a s:Volunteer ; s:id ?volunteer_id ; s:name ?name }
columns:
- { name: volunteer_id, data_type: text, visible_to: [org_admin] }
- { name: name, data_type: text, visible_to: [org_admin] }
Регистрация таблицы работает так же, как для Neo4j: выберите источник, введите имя таблицы и SELECT-запрос, нажмите Preview (запрос sparqlPreview выполняет его с LIMIT 5 и заполняет список столбцов), затем зарегистрируйте. Регистрация сохраняет строку api_sources и строку api_endpoints (POST в форме form-encoded на путь конечной точки, нормализатор sparql_bindings) — те же строки, что записывает регистрация через файл конфигурации, а нативный движок загружает строки через ту же цепочку получения данных, что и Neo4j. [tool-verified: provisa/api/admin/_query_api_registration.py, provisa/sparql/persist.py]
Примеры подключения¶
PostgreSQL¶
- id: sales-pg
type: postgresql
host: postgres
port: 5432
database: provisa
username: provisa
password: ${env:PG_PASSWORD}
Snowflake¶
- id: analytics-sf
type: snowflake
host: org.snowflakecomputing.com
port: 443
database: ANALYTICS
username: svc_provisa
password: ${env:SNOWFLAKE_PASSWORD}
federation_hints:
account: myorg-myaccount
warehouse: COMPUTE_WH
Databricks¶
- id: lakehouse-db
type: databricks
host: my-workspace.azuredatabricks.net
password: ${env:DATABRICKS_TOKEN}
federation_hints:
http_path: /sql/1.0/warehouses/xxxx
MongoDB¶
- id: reviews-mongo
type: mongodb
host: mongodb
port: 27017
database: provisa
username: ""
password: ""
Межисточниковый запрос¶
{
orders(where: {region: {eq: "us"}}) {
id
amount
customers { # PostgreSQL
name
email
}
productReviews { # MongoDB (federated)
rating
comment
}
}
}
Однопоточные части маршрутизируются напрямую (REQ-027). Межисточниковые JOIN федерируются с автоматическим приведением типов (REQ-028, REQ-552).