mainsequence-metatable 0.1.28.dev48__tar.gz → 0.1.28.dev49__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/PKG-INFO +1 -1
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/adr/api/0007-database-enforced-table-access.md +19 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/pyproject.toml +1 -1
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/mainsequence_metatable.egg-info/PKG-INFO +1 -1
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/mainsequence_metatable.egg-info/SOURCES.txt +1 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/backends/access.py +159 -14
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/backends/hosted_access.py +2 -2
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/backends/postgresql_access.py +133 -68
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/backends/sqlite_access.py +3 -1
- mainsequence_metatable-0.1.28.dev49/tests/permissions/test_incremental_security.py +198 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/.agents/skills/analyze-with-sql/SKILL.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/.agents/skills/client/metatables-local-development/SKILL.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/.agents/skills/client/metatables-meta-tables/SKILL.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/.agents/skills/client/metatables-migrations/SKILL.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/.agents/skills/client/metatables-table-discovery/SKILL.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/.agents/skills/client/metatables-time-index-table-updates/SKILL.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/.agents/skills/client/metatables-upgrade-legacy-app/SKILL.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/.agents/skills/discover-tables/SKILL.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/.agents/skills/ms_tau_sdk/PINNED_FROM.txt +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/.agents/skills/ms_tau_sdk/tau_a2a_runtime_adapter/SKILL.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/.agents/skills/ms_tau_sdk/tau_local_development/SKILL.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/.agents/skills/ms_tau_sdk/tau_project_customization/SKILL.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/.agents/skills/ms_tau_sdk/tau_repository_integration/SKILL.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/.agents/skills/ms_tau_sdk/tau_security_and_access/SKILL.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/.mainsequence/workflows/metatables-api.yaml +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/LICENSE +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/MANIFEST.in +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/README.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/alembic.ini +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/api/metatables/main.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/compose.database-tests.yml +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/adr/agent/0019-metatables-analyst-agent.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/adr/agent/0020-analyst-reads-as-its-requester.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/adr/agent/0021-api-serves-the-analyst-tools-over-mcp.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/adr/api/0001-unified-api-storage-and-local-sqlite.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/adr/api/0002-application-administration-and-table-ownership.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/adr/api/0004-approved-application-migrations.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/adr/api/0005-run-logs-and-local-file-capture.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/adr/api/0006-historical-run-graphs.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/adr/api/0007-database-roles-and-privileges.svg +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/adr/api/0007-query-enforcement-path.svg +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/adr/api/0008-mysql-mssql-table-workflows.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/adr/api/0009-shared-credential-store.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/adr/api/0010-import-external-tables-and-views.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/adr/api/0012-bounded-data-transfer-and-safe-retries.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/adr/api/0013-application-owned-migrations.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/adr/api/0014-main-sequence-release-jobs-and-production-migrations.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/adr/api/0015-timescale-compression-and-retention-policies.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/adr/api/0016-impact-preflight.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/adr/api/0017-hosted-request-identity.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/adr/api/0018-search-document-pipeline.svg +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/adr/api/0018-table-search.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/adr/api/0022-concurrent-requests-and-connection-reuse.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/adr/client/0003-api-endpoint-resolution.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/adr/client/0011-cli-local-development-with-managed-admin.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/adr/index.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/api/authentication-and-errors.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/api/data-sources.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/api/openapi.json +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/api/overview.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/api/reference.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/api/tables-and-migrations.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/api/time-index-and-updates.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/assets/branding/metatables.svg +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/assets/diagrams/local-client-experience.svg +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/assets/diagrams/namespace-access.svg +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/client/bounded-transfers.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/client/build-an-updater.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/client/define-and-migrate-tables.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/client/deploy-application-migrations.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/client/installation-and-connection.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/client/local-storage.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/client/query-and-mutate.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/client/quickstart.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/client/read-existing-time-index-tables.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/client/register-existing-tables.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/client/tutorial.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/client/upgrade-legacy-app.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/concepts/architecture.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/concepts/data-sources.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/concepts/identity-and-scope.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/concepts/permissions-namespaces-labels.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/concepts/runs-and-execution-graphs.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/concepts/table-contracts-and-lifecycle.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/concepts/time-index-tables-and-updates.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/contributing/architecture-and-boundaries.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/contributing/database-backends.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/contributing/documentation.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/contributing/releasing.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/contributing/testing.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/examples/README.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/examples/__init__.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/examples/bounded_reader.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/examples/data_sources/README.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/examples/data_sources/mssql.json +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/examples/data_sources/mysql.json +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/examples/data_sources/postgresql.json +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/examples/data_sources/register.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/examples/external_table.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/examples/local_app/README.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/examples/local_app/__init__.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/examples/local_app/__main__.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/examples/local_app/migrations/__init__.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/examples/local_app/migrations/env.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/examples/local_app/migrations/script.py.mako +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/examples/local_app/migrations/versions/metatables_local_example/0001.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/examples/local_app/tables.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/examples/query.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/examples/quickstart.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/examples/reader.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/examples/scripts/__init__.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/examples/scripts/setup_metatables.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/examples/tables.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/examples/tutorial/README.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/examples/tutorial/__init__.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/examples/tutorial/__main__.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/examples/tutorial/data/README.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/examples/tutorial/data/yahoo_spy_qqq_iwm_2026-06-15_2026-07-17.csv +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/examples/tutorial/frames.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/examples/tutorial/migrations/__init__.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/examples/tutorial/migrations/env.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/examples/tutorial/migrations/registry.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/examples/tutorial/migrations/script.py.mako +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/examples/tutorial/migrations/versions/__init__.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/examples/tutorial/migrations/versions/metatables_market_tutorial/0001_create_market_tutorial_tables.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/examples/tutorial/migrations/versions/metatables_market_tutorial/0002_create_rolling_volatility.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/examples/tutorial/migrations/versions/metatables_market_tutorial/__init__.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/examples/tutorial/operations.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/examples/tutorial/tables.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/examples/tutorial/updaters.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/examples/updater.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/index.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/operations/catalog-migrations.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/operations/configuration.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/operations/hosted-runtime.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/operations/local-runtime.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/operations/recovery-and-observability.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/operations/run-history.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/operations/run-logs.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/reference/capabilities.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/reference/cli.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/reference/python.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/security/index.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/security/ownership-and-sharing.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/docs/security/permissions.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/mkdocs.yml +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/scripts/__init__.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/scripts/build_reference.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/scripts/check_boundaries.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/scripts/check_client_api_coverage.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/scripts/check_distribution_sources.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/scripts/check_docs.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/scripts/check_examples_debug.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/scripts/check_project.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/scripts/dev_release_version.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/scripts/test_databases.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/scripts/test_postgresql.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/setup.cfg +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/mainsequence_metatable.egg-info/dependency_links.txt +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/mainsequence_metatable.egg-info/entry_points.txt +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/mainsequence_metatable.egg-info/requires.txt +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/mainsequence_metatable.egg-info/top_level.txt +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/__init__.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/_admin_source.json +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/_build.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/_skill_bundle.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/analysis.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/analyst/__init__.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/__init__.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/__init__.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/auth/__init__.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/auth/boundary.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/auth/local_actor.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/bootstrap.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/configuration.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/connection_count.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/credential_admin.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/development.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/development_client.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/error_logging.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/main.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/mcp.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/platform_cache.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/routes/__init__.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/routes/access_map.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/routes/caller.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/routes/column_search.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/routes/contract_validation.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/routes/data_sources.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/routes/managed_finalize.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/routes/migration_connection.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/routes/namespaces.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/routes/run_logs.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/routes/runtime_context.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/routes/schema_graph.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/routes/security.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/routes/table_cascade.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/routes/table_crud.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/routes/table_delete.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/routes/table_filter.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/routes/table_import.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/routes/table_introspection.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/routes/table_patch.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/routes/table_registration.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/routes/table_response.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/routes/table_search.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/routes/table_search_document.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/routes/table_sql.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/routes/table_update_pipeline.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/routes/time_index_delete.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/routes/time_index_reads.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/routes/time_index_stats.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/routes/timescale_policies.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/routes/update_batch.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/routes/update_chunk_insert.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/routes/update_constants.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/routes/update_crud.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/routes/update_details.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/routes/update_graph.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/routes/update_index_statistics.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/routes/update_registration.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/routes/update_runs.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/routes/update_statistics.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/runtime.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/runtime_control.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/runtime_database.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/source_passwords.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/app/transfer.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/__init__.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/backends/__init__.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/backends/adapters/__init__.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/backends/adapters/maintenance/__init__.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/backends/adapters/maintenance/postgresql.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/backends/adapters/maintenance/runtime.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/backends/adapters/maintenance/temporal.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/backends/adapters/maintenance/timescaledb.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/backends/adapters/maintenance/validation.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/backends/adapters/postgresql_cascade_delete.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/backends/adapters/postgresql_connection.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/backends/adapters/postgresql_introspection.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/backends/adapters/postgresql_table_delete.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/backends/adapters/postgresql_tables.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/backends/adapters/postgresql_time_index.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/backends/adapters/postgresql_time_index_delete.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/backends/adapters/postgresql_time_index_queries.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/backends/adapters/postgresql_time_index_stats.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/backends/adapters/postgresql_time_index_write.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/backends/adapters/source_validation.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/backends/adapters/sqlite_connection.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/backends/adapters/sqlite_introspection.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/backends/adapters/table_cursor.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/backends/catalog.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/backends/catalog_ddl.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/backends/catalog_types.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/backends/connection_pool.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/backends/contracts.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/backends/hosted.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/backends/migration_connection.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/backends/native.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/backends/operations.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/backends/pages.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/backends/registry.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/backends/relation_tables.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/backends/relational.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/backends/restricted_sql.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/backends/streaming.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/backends/timescale.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/contracts/__init__.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/contracts/relation_import.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/contracts/storage_layouts.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/contracts/table_contracts.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/contracts/values.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/domain/__init__.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/domain/catalog_rules.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/domain/data_source_rules.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/domain/index_statistics.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/domain/label_rules.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/domain/permission_rules.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/domain/update_constants.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/integrations/__init__.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/integrations/catalog_data_sources.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/integrations/credential_keys.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/integrations/credential_store.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/integrations/data_source_provider.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/integrations/local_credentials.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/integrations/local_workspace.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/integrations/platform_principals.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/integrations/run_logs.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/integrations/runtime_binding.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/integrations/runtime_data_sources.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/integrations/source_validation.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/migrations/__init__.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/migrations/env.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/migrations/script.py.mako +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/migrations/versions/0001_initial.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/migrations/versions/0002_security_model.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/migrations/versions/0003_run_log_capture.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/migrations/versions/0004_historical_run_graphs.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/migrations/versions/0005_database_access.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/migrations/versions/0006_local_credentials.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/migrations/versions/0007_external_relation_names.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/migrations/versions/0008_upload_receipts.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/migrations/versions/0009_timescale_policies.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/migrations/versions/0010_cascade_targets.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/migrations/versions/0011_table_search.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/migrations/versions/0012_table_creator.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/migrations/versions/__init__.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/operations/__init__.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/operations/bootstrap_migrations.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/operations/catalog_schema.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/operations/create_table.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/operations/finalize_managed.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/operations/import_relations.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/operations/introspect_table.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/operations/read_time_index.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/operations/register_catalog.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/persistence/__init__.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/persistence/cascade.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/persistence/catalog.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/persistence/database.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/persistence/grants.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/persistence/impact.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/persistence/labels.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/persistence/locking.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/persistence/models.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/persistence/physical_operations.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/persistence/projections.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/persistence/run_graphs.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/persistence/schema_graph.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/persistence/sqlite_types.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/persistence/table_body_filter.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/persistence/table_queries.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/persistence/table_response.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/persistence/update_graph.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/persistence/update_pipeline.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/persistence/updates.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/persistence/upload_receipts.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/presentation/data_source_summary.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/search/__init__.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/search/documents.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/search/embeddings.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/search/index.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/search/ranking.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/search/text.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/backend/search/worker.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/metatables/__init__.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/metatables/configuration.yaml +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/metatables/main.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/api/request_timing.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/cli/__init__.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/cli/admin_install.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/cli/app.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/cli/filters.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/cli/local.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/cli/migrations.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/cli/project.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/cli/skills.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/compiled_sql/__init__.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/compiled_sql/mssql.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/compiled_sql/v1.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/dtype_codec.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/endpoint.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/examples/README.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/examples/__init__.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/examples/bounded_reader.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/examples/data_sources/README.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/examples/data_sources/mssql.json +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/examples/data_sources/mysql.json +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/examples/data_sources/postgresql.json +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/examples/data_sources/register.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/examples/external_table.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/examples/local_app/README.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/examples/local_app/__init__.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/examples/local_app/__main__.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/examples/local_app/migrations/__init__.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/examples/local_app/migrations/env.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/examples/local_app/migrations/script.py.mako +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/examples/local_app/migrations/versions/metatables_local_example/0001.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/examples/local_app/tables.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/examples/query.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/examples/quickstart.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/examples/reader.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/examples/scripts/__init__.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/examples/scripts/setup_metatables.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/examples/tables.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/examples/tutorial/README.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/examples/tutorial/__init__.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/examples/tutorial/__main__.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/examples/tutorial/data/README.md +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/examples/tutorial/data/yahoo_spy_qqq_iwm_2026-06-15_2026-07-17.csv +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/examples/tutorial/frames.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/examples/tutorial/migrations/__init__.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/examples/tutorial/migrations/env.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/examples/tutorial/migrations/registry.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/examples/tutorial/migrations/script.py.mako +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/examples/tutorial/migrations/versions/__init__.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/examples/tutorial/migrations/versions/metatables_market_tutorial/0001_create_market_tutorial_tables.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/examples/tutorial/migrations/versions/metatables_market_tutorial/0002_create_rolling_volatility.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/examples/tutorial/migrations/versions/metatables_market_tutorial/__init__.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/examples/tutorial/operations.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/examples/tutorial/tables.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/examples/tutorial/updaters.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/examples/updater.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/future_registry.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/hashing.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/instrumentation.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/local_connection.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/local_files.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/local_run_logs.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/log_contract.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/migrations/__init__.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/migrations/alembic.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/migrations/connection.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/migrations/env.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/migrations/provider.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/migrations/registry.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/migrations/runner.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/migrations/scaffold.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/migrations/sqlite.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/migrations/templates/__init__.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/migrations/templates/env.py.mako +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/migrations/templates/script.py.mako +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/migrations/tls.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/models.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/pydantic_metadata.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/run_logging.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/runtime.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/runtime_context.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/runtime_contract.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/schema_names.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/sdk_compat.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/sqlalchemy_contracts.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/storage_layout_contract.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/time_index_table_refs.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/transfer.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/transfer_contract.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/transport.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/update_configuration_contract.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/updaters/__init__.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/updaters/configuration.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/updaters/dependencies.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/updaters/managers.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/updaters/namespacing.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/updaters/runner.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/updaters/updaters.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/updaters/utils.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/src/metatables/upload.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/analyst/test_agent_surface.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/analyst/test_api_search.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/analyst/test_mcp.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/analyst/test_tools.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/auth/test_bootstrap_failure_cause.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/auth/test_caller_endpoint.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/auth/test_data_source_identity.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/auth/test_development_client.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/auth/test_hosted_agent.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/auth/test_hosted_environment_target.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/auth/test_local_mode.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/auth/test_platform_fact_cache.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/auth/test_request_identity.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/auth/test_runtime_bootstrap.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/auth/test_runtime_database.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/auth/test_runtime_scaffold.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/auth/test_runtime_selection.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/auth/test_runtime_storage_binding.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/auth/test_source_passwords.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/catalog/test_catalog_foundation.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/catalog/test_contract_projection.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/catalog/test_database_config.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/catalog/test_index_statistics.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/catalog/test_migration.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/conftest.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/contracts/capabilities.json +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/contracts/client_signatures.json +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/contracts/database_operations.json +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/contracts/test_bounded_transfer.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/contracts/test_compiler_runtime_source.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/contracts/test_relation_batching.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/contracts/test_relation_import.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/contracts/test_runtime_contract_compat.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/contracts/test_storage_layouts.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/contracts/test_table_contracts.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/database_backend/Dockerfile +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/database_backend/Dockerfile.dockerignore +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/database_backend/__init__.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/database_backend/conftest.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/database_backend/provider/__init__.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/database_backend/provider/env.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/database_backend/provider/versions/0001_measurement.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/database_backend/test_api_contract.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/database_backend/test_application_migration.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/database_backend/test_bootstrap_recovery.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/database_backend/test_catalog_contract.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/database_backend/test_credential_catalog.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/database_backend/test_physical_contract.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/database_backend/test_relation_import.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/database_backend/test_runtime_bootstrap.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/database_backend/test_sql_security.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/database_backend/test_update_client.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/database_operations/test_physical_operations.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/database_operations/test_postgresql_operations.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/database_operations/test_runtime_and_temporal.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/documentation/test_project_checks.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/e2e/test_admin_installation.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/e2e/test_api_endpoint.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/e2e/test_application_owned_migrations.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/e2e/test_catalog_outage.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/e2e/test_client_api_contract.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/e2e/test_client_api_coverage.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/e2e/test_client_migration_scaffold.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/e2e/test_client_migration_tls.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/e2e/test_client_package.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/e2e/test_client_signatures.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/e2e/test_client_value_behaviors.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/e2e/test_data_source_registry.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/e2e/test_local_actor_client_api.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/e2e/test_local_cli_workflow.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/e2e/test_local_client_selection.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/e2e/test_managed_finalize_client_api.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/e2e/test_namespaces_admin_http.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/e2e/test_public_imports.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/e2e/test_remaining_client_routes.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/e2e/test_run_graphs.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/e2e/test_run_logs.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/e2e/test_runtime_upgrade.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/e2e/test_sdk_local_startup.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/e2e/test_sqlite_runtime.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/e2e/test_table_delete_client_api.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/e2e/test_table_filter_client_api.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/e2e/test_table_introspection_client_api.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/e2e/test_table_patch_client_api.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/e2e/test_table_search_api.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/e2e/test_table_search_document_api.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/e2e/test_table_search_scale.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/e2e/test_table_stats_api.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/e2e/test_table_update_pipeline.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/e2e/test_table_updates_http.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/e2e/test_time_index_delete_client_api.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/e2e/test_time_index_reads_client_api.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/e2e/test_timescale_policies_client_api.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/e2e/test_update_batch_client_api.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/e2e/test_update_chunk_insert_client_api.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/e2e/test_update_dependency_priority_client_api.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/e2e/test_update_index_statistics_client_api.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/e2e/test_update_registration_client_api.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/examples/test_documented_examples.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/examples/test_postgresql_examples.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/examples/test_setup_launch.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/examples/test_tutorial.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/integrations/test_connection_reuse.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/integrations/test_credential_store.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/integrations/test_data_source_rules.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/integrations/test_local_data_sources.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/integrations/test_local_run_logs.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/integrations/test_postgresql_connection.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/integrations/test_postgresql_sql.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/integrations/test_postgresql_tables.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/integrations/test_postgresql_time_index_queries.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/integrations/test_request_timing.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/integrations/test_serving_policy.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/integrations/test_source_validation.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/integrations/test_sqlite_operations.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/labels/test_labels.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/operations/test_create_table_operation.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/operations/test_create_table_postgres.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/operations/test_physical_operation_journal.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/operations/test_register_catalog.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/operations/test_search_documents.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/operations/test_timescale_policies.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/packaging/test_client_skills.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/packaging/test_dev_release_version.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/packaging/test_distribution_sources.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/packaging/test_editable_build.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/packaging/test_installed_distribution.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/permissions/test_access_map.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/permissions/test_governed_sql_scope.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/permissions/test_local_actor_membership.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/permissions/test_migration_boundary.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/permissions/test_namespace_grants.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/permissions/test_security_api.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/permissions/test_table_body_filter.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/permissions/test_table_grants.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/permissions/test_table_queries.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/tests/permissions/test_workload_principals.py +0 -0
- {mainsequence_metatable-0.1.28.dev48 → mainsequence_metatable-0.1.28.dev49}/uv.lock +0 -0
|
@@ -32,6 +32,25 @@
|
|
|
32
32
|
> Reconciliation still records each table's cascade targets, but only so the
|
|
33
33
|
> [impact pre-flight](0016-impact-preflight.md) can show their effects.
|
|
34
34
|
|
|
35
|
+
> Amendment (2026-10-09): On PostgreSQL and TimescaleDB, a catalog change applies
|
|
36
|
+
> only what it changed. Reconciliation used to rebuild every role, re-inspect every
|
|
37
|
+
> table and re-secure every routine on each change, all under the catalog lock, so
|
|
38
|
+
> one registration could hold the lock for minutes and stop every other request.
|
|
39
|
+
> Now a change:
|
|
40
|
+
>
|
|
41
|
+
> - re-inspects the policies of the tables it changed, plus the tables whose
|
|
42
|
+
> recorded cascade targets they affect;
|
|
43
|
+
> - rebuilds only roles that are new or whose definition changed, and grants or
|
|
44
|
+
> revokes only the memberships and privileges that differ from the applied
|
|
45
|
+
> manifest;
|
|
46
|
+
> - re-secures routines only when the owned schemas, routines or `PUBLIC` grants
|
|
47
|
+
> changed since the last sweep.
|
|
48
|
+
>
|
|
49
|
+
> The full sweep still runs when the runtime DataSource changes, at initialization
|
|
50
|
+
> (the migration Job of every deployment) and from the admin repair action. Those
|
|
51
|
+
> are when grants changed outside MetaTables are repaired, not on every catalog
|
|
52
|
+
> change. MySQL and SQL Server keep the full sweep on every change.
|
|
53
|
+
|
|
35
54
|
|
|
36
55
|
Date: 2026-09-29
|
|
37
56
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "mainsequence-metatable"
|
|
7
|
-
version = "0.1.28.
|
|
7
|
+
version = "0.1.28.dev49"
|
|
8
8
|
description = "Main Sequence MetaTables Python client and standalone API"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.13"
|
|
@@ -572,6 +572,7 @@ tests/packaging/test_editable_build.py
|
|
|
572
572
|
tests/packaging/test_installed_distribution.py
|
|
573
573
|
tests/permissions/test_access_map.py
|
|
574
574
|
tests/permissions/test_governed_sql_scope.py
|
|
575
|
+
tests/permissions/test_incremental_security.py
|
|
575
576
|
tests/permissions/test_local_actor_membership.py
|
|
576
577
|
tests/permissions/test_migration_boundary.py
|
|
577
578
|
tests/permissions/test_namespace_grants.py
|
|
@@ -33,9 +33,73 @@ class ExecutionIdentity:
|
|
|
33
33
|
writable: frozenset[tuple[str, str]]
|
|
34
34
|
|
|
35
35
|
|
|
36
|
+
@dataclass(frozen=True)
|
|
37
|
+
class ManifestChanges:
|
|
38
|
+
"""The statements that move the database from one applied manifest to the next.
|
|
39
|
+
|
|
40
|
+
``reset`` roles are new or changed: each is rebuilt completely, so every one of
|
|
41
|
+
its memberships and privileges is granted again. ``retired`` roles are rebuilt
|
|
42
|
+
with nothing. Other roles change only by the memberships and privileges that
|
|
43
|
+
differ.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
reset: frozenset
|
|
47
|
+
retired: frozenset
|
|
48
|
+
revoke_memberships: tuple
|
|
49
|
+
revoke_privileges: tuple
|
|
50
|
+
revoke_schemas: tuple
|
|
51
|
+
grant_memberships: tuple
|
|
52
|
+
grant_privileges: tuple
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def manifest_changes(previous, desired):
|
|
56
|
+
"""Diff two manifests; None when ``previous`` is not a complete applied manifest."""
|
|
57
|
+
if not (
|
|
58
|
+
isinstance(previous, dict)
|
|
59
|
+
and isinstance(previous.get("roles"), dict)
|
|
60
|
+
and isinstance(previous.get("memberships"), list)
|
|
61
|
+
and isinstance(previous.get("privileges"), list)
|
|
62
|
+
):
|
|
63
|
+
return None
|
|
64
|
+
before, roles = previous["roles"], desired["roles"]
|
|
65
|
+
reset = frozenset(name for name, definition in roles.items() if before.get(name) != definition)
|
|
66
|
+
retired = frozenset(set(before) - set(roles))
|
|
67
|
+
rebuilt = reset | retired
|
|
68
|
+
old_memberships = {tuple(row) for row in previous["memberships"]}
|
|
69
|
+
new_memberships = {tuple(row) for row in desired["memberships"]}
|
|
70
|
+
old_privileges = {(n, s, t, bool(w)) for n, s, t, w in previous["privileges"]}
|
|
71
|
+
new_privileges = {(n, s, t, bool(w)) for n, s, t, w in desired["privileges"]}
|
|
72
|
+
revoke_privileges = tuple(sorted(
|
|
73
|
+
row for row in old_privileges - new_privileges if row[0] not in rebuilt
|
|
74
|
+
))
|
|
75
|
+
kept_schemas = {(name, schema) for name, schema, _, _ in new_privileges}
|
|
76
|
+
return ManifestChanges(
|
|
77
|
+
reset=reset,
|
|
78
|
+
retired=retired,
|
|
79
|
+
# Rebuilding a role already revokes every membership it holds.
|
|
80
|
+
revoke_memberships=tuple(sorted(
|
|
81
|
+
row for row in old_memberships - new_memberships if row[0] not in rebuilt
|
|
82
|
+
)),
|
|
83
|
+
revoke_privileges=revoke_privileges,
|
|
84
|
+
revoke_schemas=tuple(sorted(
|
|
85
|
+
{(name, schema) for name, schema, _, _ in revoke_privileges} - kept_schemas
|
|
86
|
+
)),
|
|
87
|
+
grant_memberships=tuple(sorted(
|
|
88
|
+
row for row in new_memberships if row[0] in reset or row not in old_memberships
|
|
89
|
+
)),
|
|
90
|
+
grant_privileges=tuple(sorted(
|
|
91
|
+
row for row in new_privileges if row[0] in reset or row not in old_privileges
|
|
92
|
+
)),
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
|
|
36
96
|
class DatabaseAccess:
|
|
37
97
|
"""Backend facet for setup, mirroring and restricted session admission."""
|
|
38
98
|
|
|
99
|
+
# A catalog change applies only what it changed; the full sweep stays for
|
|
100
|
+
# initialization and repair (ADR 0007, amended 2026-10-09).
|
|
101
|
+
incremental = False
|
|
102
|
+
|
|
39
103
|
def __init__(self, backend):
|
|
40
104
|
self.backend = backend
|
|
41
105
|
self.source = backend.source
|
|
@@ -87,16 +151,20 @@ class DatabaseAccess:
|
|
|
87
151
|
deleted or their keys updated, as (schema, table, on_delete, on_update)."""
|
|
88
152
|
raise NotImplementedError
|
|
89
153
|
|
|
90
|
-
def refresh_policies(self, session):
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
)
|
|
154
|
+
def refresh_policies(self, session, tables=None):
|
|
155
|
+
"""Inspect every table's policy, or only those of the table UIDs in ``tables``."""
|
|
156
|
+
query = (
|
|
157
|
+
select(MetaTable)
|
|
158
|
+
.where(MetaTable.data_source_uid == self.source.uid)
|
|
159
|
+
.execution_options(populate_existing=True)
|
|
97
160
|
)
|
|
161
|
+
policies = select(SQLTablePolicy)
|
|
162
|
+
if tables is not None:
|
|
163
|
+
query = query.where(MetaTable.uid.in_(tables))
|
|
164
|
+
policies = policies.where(SQLTablePolicy.table_uid.in_(tables))
|
|
165
|
+
tables = list(session.scalars(query))
|
|
98
166
|
current = {t.uid for t in tables}
|
|
99
|
-
for old in session.scalars(
|
|
167
|
+
for old in session.scalars(policies):
|
|
100
168
|
if old.table_uid not in current:
|
|
101
169
|
session.delete(old)
|
|
102
170
|
for table in tables:
|
|
@@ -129,6 +197,59 @@ class DatabaseAccess:
|
|
|
129
197
|
]
|
|
130
198
|
session.flush()
|
|
131
199
|
|
|
200
|
+
def policy_scope(self, session, changed):
|
|
201
|
+
"""The tables whose policies a change to ``changed`` can affect.
|
|
202
|
+
|
|
203
|
+
A policy records its table's cascade targets, the tables whose foreign keys
|
|
204
|
+
act on its rows, so the scope adds the tables the changed ones reference and
|
|
205
|
+
the tables whose recorded targets name them. Returns None, meaning every
|
|
206
|
+
table, when a reference cannot be read.
|
|
207
|
+
"""
|
|
208
|
+
import sqlalchemy as sa
|
|
209
|
+
|
|
210
|
+
changed = set(changed)
|
|
211
|
+
tables = {
|
|
212
|
+
t.uid: t
|
|
213
|
+
for t in session.scalars(
|
|
214
|
+
select(MetaTable).where(MetaTable.data_source_uid == self.source.uid)
|
|
215
|
+
)
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
def key(schema, name):
|
|
219
|
+
return self.backend.catalog.registration_schema(self.source, schema), name
|
|
220
|
+
|
|
221
|
+
by_name = {key(t.physical_schema, t.physical_table_name): uid for uid, t in tables.items()}
|
|
222
|
+
names = {key(t.physical_schema, t.physical_table_name) for uid, t in tables.items() if uid in changed}
|
|
223
|
+
names |= {
|
|
224
|
+
(policy.schema_name, policy.table_name)
|
|
225
|
+
for policy in session.scalars(
|
|
226
|
+
select(SQLTablePolicy).where(SQLTablePolicy.table_uid.in_(changed))
|
|
227
|
+
)
|
|
228
|
+
}
|
|
229
|
+
scope = set(changed)
|
|
230
|
+
inspector = sa.inspect(session.connection())
|
|
231
|
+
for uid in sorted(changed & set(tables), key=str):
|
|
232
|
+
table = tables[uid]
|
|
233
|
+
if table.provisioning_status != "active":
|
|
234
|
+
continue
|
|
235
|
+
try:
|
|
236
|
+
references = inspector.get_foreign_keys(
|
|
237
|
+
table.physical_table_name, schema=table.physical_schema
|
|
238
|
+
)
|
|
239
|
+
except Exception:
|
|
240
|
+
return None
|
|
241
|
+
for reference in references:
|
|
242
|
+
schema = reference.get("referred_schema") or table.physical_schema
|
|
243
|
+
scope.add(by_name.get(key(schema, reference["referred_table"])))
|
|
244
|
+
for policy in session.scalars(select(SQLTablePolicy)):
|
|
245
|
+
if any(
|
|
246
|
+
key(target.get("schema"), target.get("table")) in names
|
|
247
|
+
for target in policy.cascade_targets or ()
|
|
248
|
+
):
|
|
249
|
+
scope.add(policy.table_uid)
|
|
250
|
+
scope.discard(None)
|
|
251
|
+
return scope
|
|
252
|
+
|
|
132
253
|
def plan(self, session):
|
|
133
254
|
"""Read only catalog facts. No submitted statement or physical inspection."""
|
|
134
255
|
roles, memberships, privileges = {}, set(), set()
|
|
@@ -190,18 +311,33 @@ class DatabaseAccess:
|
|
|
190
311
|
memberships.add((login, role("team", team, write)))
|
|
191
312
|
return dict(roles=roles, memberships=sorted(memberships), privileges=sorted(privileges))
|
|
192
313
|
|
|
193
|
-
def reconcile(self, session, *, inspect_objects=False):
|
|
314
|
+
def reconcile(self, session, *, inspect_objects=False, tables=None):
|
|
315
|
+
"""Mirror the catalog into database grants.
|
|
316
|
+
|
|
317
|
+
``inspect_objects`` is the full sweep: every policy is inspected and every
|
|
318
|
+
role rebuilt, which also repairs grants changed outside MetaTables.
|
|
319
|
+
Otherwise only the policies ``tables`` can affect are inspected, and an
|
|
320
|
+
incremental backend applies only the grants that changed.
|
|
321
|
+
"""
|
|
194
322
|
state = session.get(SQLSecurityState, 1)
|
|
195
323
|
if state is None or not state.ready:
|
|
196
324
|
raise SQLExecutionError(code="sql_security_not_initialized")
|
|
197
325
|
if inspect_objects:
|
|
198
326
|
self.refresh_policies(session)
|
|
327
|
+
elif tables:
|
|
328
|
+
self.refresh_policies(session, tables=self.policy_scope(session, tables))
|
|
199
329
|
desired = self.plan(session)
|
|
200
|
-
self.apply(session, desired, state.manifest)
|
|
330
|
+
self.apply(session, desired, state.manifest, full=inspect_objects)
|
|
201
331
|
state.manifest, state.pending = desired, False
|
|
202
332
|
session.flush()
|
|
203
333
|
|
|
204
|
-
def
|
|
334
|
+
def reconcile_change(self, session, *, tables, full=False):
|
|
335
|
+
"""Reconcile one catalog unit of work just before it commits."""
|
|
336
|
+
if full or not self.incremental:
|
|
337
|
+
return self.reconcile(session, inspect_objects=True)
|
|
338
|
+
return self.reconcile(session, tables=tables)
|
|
339
|
+
|
|
340
|
+
def apply(self, session, desired, previous, *, full=True):
|
|
205
341
|
raise NotImplementedError
|
|
206
342
|
|
|
207
343
|
def admit(self, session, actor, *, write, reconcile=True):
|
|
@@ -318,6 +454,13 @@ def _changed(session, *_):
|
|
|
318
454
|
|
|
319
455
|
with session.no_autoflush:
|
|
320
456
|
for row in session.new | session.dirty | session.deleted:
|
|
457
|
+
if isinstance(row, MetaTable) and row.data_source_uid == source_uid:
|
|
458
|
+
# Rows, not UIDs: a new row has its UID only once it is flushed.
|
|
459
|
+
session.info.setdefault("sql_security_tables", []).append(row)
|
|
460
|
+
elif isinstance(row, DataSource) and (row.is_runtime or row.is_default):
|
|
461
|
+
session.info["sql_security_full"] = True
|
|
462
|
+
elif session.info.get("sql_security_changed"):
|
|
463
|
+
continue
|
|
321
464
|
relevant = (
|
|
322
465
|
isinstance(row, MetaTable)
|
|
323
466
|
and row.data_source_uid == source_uid
|
|
@@ -347,7 +490,6 @@ def _changed(session, *_):
|
|
|
347
490
|
)
|
|
348
491
|
if relevant:
|
|
349
492
|
session.info["sql_security_changed"] = True
|
|
350
|
-
break
|
|
351
493
|
|
|
352
494
|
|
|
353
495
|
def _before_commit(session):
|
|
@@ -361,14 +503,15 @@ def _before_commit(session):
|
|
|
361
503
|
):
|
|
362
504
|
return
|
|
363
505
|
session.flush()
|
|
506
|
+
tables = {row.uid for row in session.info.pop("sql_security_tables", ())}
|
|
507
|
+
full = session.info.pop("sql_security_full", False)
|
|
364
508
|
if not session.info.pop("sql_security_changed", False):
|
|
365
509
|
return
|
|
366
510
|
session.info["sql_security_syncing"] = True
|
|
367
511
|
try:
|
|
368
512
|
backend = resolver()
|
|
369
513
|
backend.catalog.lock(session)
|
|
370
|
-
access =
|
|
371
|
-
access.reconcile(session, inspect_objects=True)
|
|
514
|
+
backend.access.reconcile_change(session, tables=tables, full=full)
|
|
372
515
|
finally:
|
|
373
516
|
session.info.pop("sql_security_syncing", None)
|
|
374
517
|
|
|
@@ -391,6 +534,8 @@ def _after_transaction_end(session, transaction):
|
|
|
391
534
|
committed = session.info.pop("sql_commit_finished", False)
|
|
392
535
|
access = session.info.pop("sql_deferred_access", None)
|
|
393
536
|
session.info.pop("sql_security_changed", None)
|
|
537
|
+
session.info.pop("sql_security_tables", None)
|
|
538
|
+
session.info.pop("sql_security_full", None)
|
|
394
539
|
session.info.pop("sql_admission_uncommitted", None)
|
|
395
540
|
if committed and access is not None:
|
|
396
541
|
try:
|
|
@@ -164,7 +164,7 @@ class MySQLAccess(HostedAccess):
|
|
|
164
164
|
state.pending = False
|
|
165
165
|
session.commit()
|
|
166
166
|
|
|
167
|
-
def apply(self, session, desired, previous):
|
|
167
|
+
def apply(self, session, desired, previous, *, full=True):
|
|
168
168
|
q = mysql_identifier
|
|
169
169
|
with ExitStack() as resources:
|
|
170
170
|
engine = self.backend.catalog.open_engine(self.source, resources)
|
|
@@ -288,7 +288,7 @@ class MSSQLAccess(HostedAccess):
|
|
|
288
288
|
)
|
|
289
289
|
return {(schema, name, referential_action(delete), referential_action(update)) for schema, name, delete, update in rows}
|
|
290
290
|
|
|
291
|
-
def apply(self, session, desired, previous):
|
|
291
|
+
def apply(self, session, desired, previous, *, full=True):
|
|
292
292
|
connection = session.connection()
|
|
293
293
|
connection.engine.hide_parameters = True
|
|
294
294
|
q = ms_identifier
|
|
@@ -5,7 +5,7 @@ from contextlib import contextmanager
|
|
|
5
5
|
|
|
6
6
|
from sqlalchemy import text
|
|
7
7
|
|
|
8
|
-
from metatables.api.backend.backends.access import DatabaseAccess
|
|
8
|
+
from metatables.api.backend.backends.access import DatabaseAccess, manifest_changes
|
|
9
9
|
from metatables.api.backend.backends.contracts import SQLExecutionError
|
|
10
10
|
from metatables.api.backend.persistence.models import SQLSecurityState
|
|
11
11
|
|
|
@@ -24,6 +24,8 @@ USER_SCHEMA = "{schema} !~ '^pg_' AND {schema} <> 'information_schema'"
|
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
class PostgreSQLAccess(DatabaseAccess):
|
|
27
|
+
incremental = True
|
|
28
|
+
|
|
27
29
|
def prerequisite_problems(self, connection):
|
|
28
30
|
schema = getattr(self.source.connection, "default_schema", None) or "public"
|
|
29
31
|
login, database, creates_roles, creates_schemas, catalog_owner, tables = connection.execute(text(
|
|
@@ -229,82 +231,84 @@ class PostgreSQLAccess(DatabaseAccess):
|
|
|
229
231
|
targets.add((*hypertable, delete, update))
|
|
230
232
|
return targets
|
|
231
233
|
|
|
232
|
-
def
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
return
|
|
234
|
+
def object_fingerprint(self, connection):
|
|
235
|
+
"""What the routine sweep secures: owned schemas and routines, and PUBLIC grants.
|
|
236
|
+
|
|
237
|
+
A change to any of them, such as a migration that adds a routine or grants
|
|
238
|
+
to PUBLIC, makes the next reconciliation sweep them again.
|
|
239
|
+
"""
|
|
240
|
+
schemas = USER_SCHEMA.format(schema="n.nspname")
|
|
241
|
+
return connection.execute(text(
|
|
242
|
+
"SELECT md5(concat_ws('|', "
|
|
243
|
+
"(SELECT string_agg(n.oid::text, ',' ORDER BY n.oid) FROM pg_namespace n WHERE "
|
|
244
|
+
+ schemas + " AND " + OWNED.format(owner="n.nspowner") + "), "
|
|
245
|
+
"(SELECT string_agg(p.oid::text || ':' || p.prosecdef::text, ',' ORDER BY p.oid) "
|
|
246
|
+
"FROM pg_proc p JOIN pg_namespace n ON n.oid=p.pronamespace WHERE "
|
|
247
|
+
+ schemas + " AND " + OWNED.format(owner="p.proowner") + "), "
|
|
248
|
+
"(SELECT count(*)::text FROM pg_class c JOIN pg_namespace n ON n.oid=c.relnamespace "
|
|
249
|
+
"CROSS JOIN LATERAL aclexplode(c.relacl) acl WHERE acl.grantee=0 AND "
|
|
250
|
+
+ schemas + " AND " + OWNED.format(owner="c.relowner") + "), "
|
|
251
|
+
"(SELECT count(*)::text FROM pg_attribute a JOIN pg_class c ON c.oid=a.attrelid "
|
|
252
|
+
"JOIN pg_namespace n ON n.oid=c.relnamespace CROSS JOIN LATERAL aclexplode(a.attacl) acl "
|
|
253
|
+
"WHERE acl.grantee=0 AND n.nspname !~ '^pg_' AND " + OWNED.format(owner="c.relowner") + ")"
|
|
254
|
+
"))"
|
|
255
|
+
)).scalar()
|
|
256
|
+
|
|
257
|
+
def reconcile(self, session, *, inspect_objects=False, tables=None):
|
|
258
|
+
state = session.get(SQLSecurityState, 1)
|
|
259
|
+
manifest = (state.manifest if state else None) or {}
|
|
260
|
+
connection = session.connection()
|
|
261
|
+
objects = self.object_fingerprint(connection)
|
|
262
|
+
if inspect_objects or manifest.get("objects") != objects:
|
|
263
|
+
roles = set(self.plan(session)["roles"]) | set(manifest.get("roles", {}))
|
|
264
|
+
self.secure_routines(connection, roles)
|
|
265
|
+
objects = self.object_fingerprint(connection)
|
|
266
|
+
super().reconcile(session, inspect_objects=inspect_objects, tables=tables)
|
|
267
|
+
state = session.get(SQLSecurityState, 1)
|
|
268
|
+
state.manifest = {**state.manifest, "objects": objects}
|
|
269
|
+
session.flush()
|
|
240
270
|
|
|
241
|
-
def apply(self, session, desired, previous):
|
|
271
|
+
def apply(self, session, desired, previous, *, full=True):
|
|
242
272
|
connection = session.connection()
|
|
243
273
|
connection.engine.hide_parameters = True
|
|
244
|
-
roles = desired["roles"]
|
|
245
274
|
invoker = self.role("invoker", "routines")
|
|
246
|
-
self._role(connection, invoker)
|
|
247
275
|
database = connection.exec_driver_sql("SELECT current_database()").scalar()
|
|
276
|
+
changes = None if full else manifest_changes(previous, desired)
|
|
277
|
+
if changes is None:
|
|
278
|
+
return self._apply_all(connection, desired, previous, database=database, invoker=invoker)
|
|
279
|
+
roles = desired["roles"]
|
|
280
|
+
for name in sorted(changes.reset):
|
|
281
|
+
self._role(connection, name, self.password(name) if roles[name]["login"] else None)
|
|
282
|
+
for name in sorted(changes.reset | changes.retired):
|
|
283
|
+
login = name in roles and bool(roles[name]["login"])
|
|
284
|
+
self._reset_role(connection, name, login=login, database=database, invoker=invoker)
|
|
285
|
+
for member, parent in changes.revoke_memberships:
|
|
286
|
+
connection.exec_driver_sql(f"REVOKE {quote(parent)} FROM {quote(member)}")
|
|
287
|
+
for name, schema, table, _ in changes.revoke_privileges:
|
|
288
|
+
relation = f"{quote(schema)}.{quote(table)}"
|
|
289
|
+
# A dropped table took its grants with it.
|
|
290
|
+
if connection.execute(text("SELECT to_regclass(:relation) IS NOT NULL"), {"relation": relation}).scalar():
|
|
291
|
+
connection.exec_driver_sql(f"REVOKE ALL ON TABLE {relation} FROM {quote(name)}")
|
|
292
|
+
for name, schema in changes.revoke_schemas:
|
|
293
|
+
if connection.execute(text("SELECT to_regnamespace(:schema) IS NOT NULL"), {"schema": quote(schema)}).scalar():
|
|
294
|
+
connection.exec_driver_sql(f"REVOKE USAGE ON SCHEMA {quote(schema)} FROM {quote(name)}")
|
|
295
|
+
for member, parent in changes.grant_memberships:
|
|
296
|
+
connection.exec_driver_sql(f"GRANT {quote(parent)} TO {quote(member)}")
|
|
297
|
+
for schema in sorted({schema for _, schema, _, _ in changes.grant_privileges} - {"metatables"}):
|
|
298
|
+
connection.exec_driver_sql(f"GRANT USAGE ON SCHEMA {quote(schema)} TO {quote(invoker)}")
|
|
299
|
+
self._grant_privileges(connection, changes.grant_privileges)
|
|
300
|
+
|
|
301
|
+
def _apply_all(self, connection, desired, previous, *, database, invoker):
|
|
302
|
+
"""The full rebuild: every role is reset and granted again, repairing drift."""
|
|
303
|
+
roles = desired["roles"]
|
|
304
|
+
self._role(connection, invoker)
|
|
248
305
|
for name, definition in roles.items():
|
|
249
306
|
self._role(connection, name, self.password(name) if definition["login"] else None)
|
|
250
307
|
names = set(roles) | set(previous.get("roles", {})) | {invoker}
|
|
251
308
|
schemas = self.owned_schemas(connection)
|
|
252
309
|
for name in sorted(names):
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
"SELECT 1 FROM pg_class WHERE relowner=(SELECT oid FROM pg_roles WHERE rolname=:name) UNION ALL SELECT 1 FROM pg_namespace WHERE nspowner=(SELECT oid FROM pg_roles WHERE rolname=:name) UNION ALL SELECT 1 FROM pg_proc WHERE proowner=(SELECT oid FROM pg_roles WHERE rolname=:name) UNION ALL SELECT 1 FROM pg_database WHERE datdba=(SELECT oid FROM pg_roles WHERE rolname=:name)"
|
|
256
|
-
),
|
|
257
|
-
{"name": name},
|
|
258
|
-
).first()
|
|
259
|
-
if owned:
|
|
260
|
-
raise SQLExecutionError(code="sql_principal_permission_drift")
|
|
261
|
-
# Repair inherited drift as well as direct table privileges.
|
|
262
|
-
parents = (
|
|
263
|
-
connection.execute(
|
|
264
|
-
text(
|
|
265
|
-
"SELECT parent.rolname FROM pg_auth_members m JOIN pg_roles child ON child.oid=m.member JOIN pg_roles parent ON parent.oid=m.roleid WHERE child.rolname=:name"
|
|
266
|
-
),
|
|
267
|
-
{"name": name},
|
|
268
|
-
)
|
|
269
|
-
.scalars()
|
|
270
|
-
.all()
|
|
271
|
-
)
|
|
272
|
-
for parent in parents:
|
|
273
|
-
connection.exec_driver_sql(f"REVOKE {quote(parent)} FROM {quote(name)}")
|
|
274
|
-
connection.exec_driver_sql(
|
|
275
|
-
f"REVOKE ALL ON DATABASE {quote(database)} FROM {quote(name)}"
|
|
276
|
-
)
|
|
277
|
-
# Revoke this role's grants on owned objects; other owners' grants are theirs.
|
|
278
|
-
granted = connection.execute(
|
|
279
|
-
text(
|
|
280
|
-
"SELECT 'SCHEMA', quote_ident(n.nspname) FROM pg_namespace n CROSS JOIN LATERAL aclexplode(n.nspacl) acl "
|
|
281
|
-
"JOIN pg_roles r ON r.oid=acl.grantee WHERE r.rolname=:name AND " + OWNED.format(owner="n.nspowner")
|
|
282
|
-
+ " UNION SELECT CASE WHEN c.relkind='S' THEN 'SEQUENCE' ELSE 'TABLE' END, "
|
|
283
|
-
"quote_ident(n.nspname) || '.' || quote_ident(c.relname) FROM pg_class c "
|
|
284
|
-
"JOIN pg_namespace n ON n.oid=c.relnamespace CROSS JOIN LATERAL aclexplode(c.relacl) acl "
|
|
285
|
-
"JOIN pg_roles r ON r.oid=acl.grantee WHERE r.rolname=:name AND " + OWNED.format(owner="c.relowner")
|
|
286
|
-
),
|
|
287
|
-
{"name": name},
|
|
288
|
-
).all()
|
|
289
|
-
for kind, target in granted:
|
|
290
|
-
connection.exec_driver_sql(f"REVOKE ALL ON {kind} {target} FROM {quote(name)}")
|
|
291
|
-
# Table-level REVOKE does not remove separately granted column ACLs.
|
|
292
|
-
columns = connection.execute(
|
|
293
|
-
text(
|
|
294
|
-
"SELECT n.nspname, c.relname, a.attname FROM pg_attribute a JOIN pg_class c ON c.oid=a.attrelid JOIN pg_namespace n ON n.oid=c.relnamespace CROSS JOIN LATERAL aclexplode(a.attacl) acl JOIN pg_roles r ON r.oid=acl.grantee WHERE r.rolname=:name AND n.nspname !~ '^pg_' AND "
|
|
295
|
-
+ OWNED.format(owner="c.relowner")
|
|
296
|
-
),
|
|
297
|
-
{"name": name},
|
|
298
|
-
).all()
|
|
299
|
-
for schema, table, column in columns:
|
|
300
|
-
connection.exec_driver_sql(
|
|
301
|
-
f"REVOKE ALL ({quote(column)}) ON TABLE {quote(schema)}.{quote(table)} FROM {quote(name)}"
|
|
302
|
-
)
|
|
303
|
-
if name in roles and roles[name]["login"]:
|
|
304
|
-
connection.exec_driver_sql(
|
|
305
|
-
f"GRANT CONNECT ON DATABASE {quote(database)} TO {quote(name)}"
|
|
306
|
-
)
|
|
307
|
-
connection.exec_driver_sql(f"GRANT {quote(invoker)} TO {quote(name)}")
|
|
310
|
+
login = name in roles and bool(roles[name]["login"])
|
|
311
|
+
self._reset_role(connection, name, login=login, database=database, invoker=invoker)
|
|
308
312
|
for schema in schemas:
|
|
309
313
|
if schema != "metatables":
|
|
310
314
|
connection.exec_driver_sql(
|
|
@@ -312,13 +316,74 @@ class PostgreSQLAccess(DatabaseAccess):
|
|
|
312
316
|
)
|
|
313
317
|
for member, parent in desired["memberships"]:
|
|
314
318
|
connection.exec_driver_sql(f"GRANT {quote(parent)} TO {quote(member)}")
|
|
315
|
-
|
|
319
|
+
self._grant_privileges(connection, desired["privileges"])
|
|
320
|
+
|
|
321
|
+
def _grant_privileges(self, connection, privileges):
|
|
322
|
+
for name, schema, table, write in privileges:
|
|
316
323
|
connection.exec_driver_sql(f"GRANT USAGE ON SCHEMA {quote(schema)} TO {quote(name)}")
|
|
317
324
|
permissions = "SELECT, INSERT, UPDATE, DELETE" if write else "SELECT"
|
|
318
325
|
connection.exec_driver_sql(
|
|
319
326
|
f"GRANT {permissions} ON TABLE {quote(schema)}.{quote(table)} TO {quote(name)}"
|
|
320
327
|
)
|
|
321
328
|
|
|
329
|
+
def _reset_role(self, connection, name, *, login, database, invoker):
|
|
330
|
+
"""Leave ``name`` with only CONNECT and the routine invoker, if it logs in."""
|
|
331
|
+
owned = connection.execute(
|
|
332
|
+
text(
|
|
333
|
+
"SELECT 1 FROM pg_class WHERE relowner=(SELECT oid FROM pg_roles WHERE rolname=:name) UNION ALL SELECT 1 FROM pg_namespace WHERE nspowner=(SELECT oid FROM pg_roles WHERE rolname=:name) UNION ALL SELECT 1 FROM pg_proc WHERE proowner=(SELECT oid FROM pg_roles WHERE rolname=:name) UNION ALL SELECT 1 FROM pg_database WHERE datdba=(SELECT oid FROM pg_roles WHERE rolname=:name)"
|
|
334
|
+
),
|
|
335
|
+
{"name": name},
|
|
336
|
+
).first()
|
|
337
|
+
if owned:
|
|
338
|
+
raise SQLExecutionError(code="sql_principal_permission_drift")
|
|
339
|
+
# Repair inherited drift as well as direct table privileges.
|
|
340
|
+
parents = (
|
|
341
|
+
connection.execute(
|
|
342
|
+
text(
|
|
343
|
+
"SELECT parent.rolname FROM pg_auth_members m JOIN pg_roles child ON child.oid=m.member JOIN pg_roles parent ON parent.oid=m.roleid WHERE child.rolname=:name"
|
|
344
|
+
),
|
|
345
|
+
{"name": name},
|
|
346
|
+
)
|
|
347
|
+
.scalars()
|
|
348
|
+
.all()
|
|
349
|
+
)
|
|
350
|
+
for parent in parents:
|
|
351
|
+
connection.exec_driver_sql(f"REVOKE {quote(parent)} FROM {quote(name)}")
|
|
352
|
+
connection.exec_driver_sql(
|
|
353
|
+
f"REVOKE ALL ON DATABASE {quote(database)} FROM {quote(name)}"
|
|
354
|
+
)
|
|
355
|
+
# Revoke this role's grants on owned objects; other owners' grants are theirs.
|
|
356
|
+
granted = connection.execute(
|
|
357
|
+
text(
|
|
358
|
+
"SELECT 'SCHEMA', quote_ident(n.nspname) FROM pg_namespace n CROSS JOIN LATERAL aclexplode(n.nspacl) acl "
|
|
359
|
+
"JOIN pg_roles r ON r.oid=acl.grantee WHERE r.rolname=:name AND " + OWNED.format(owner="n.nspowner")
|
|
360
|
+
+ " UNION SELECT CASE WHEN c.relkind='S' THEN 'SEQUENCE' ELSE 'TABLE' END, "
|
|
361
|
+
"quote_ident(n.nspname) || '.' || quote_ident(c.relname) FROM pg_class c "
|
|
362
|
+
"JOIN pg_namespace n ON n.oid=c.relnamespace CROSS JOIN LATERAL aclexplode(c.relacl) acl "
|
|
363
|
+
"JOIN pg_roles r ON r.oid=acl.grantee WHERE r.rolname=:name AND " + OWNED.format(owner="c.relowner")
|
|
364
|
+
),
|
|
365
|
+
{"name": name},
|
|
366
|
+
).all()
|
|
367
|
+
for kind, target in granted:
|
|
368
|
+
connection.exec_driver_sql(f"REVOKE ALL ON {kind} {target} FROM {quote(name)}")
|
|
369
|
+
# Table-level REVOKE does not remove separately granted column ACLs.
|
|
370
|
+
columns = connection.execute(
|
|
371
|
+
text(
|
|
372
|
+
"SELECT n.nspname, c.relname, a.attname FROM pg_attribute a JOIN pg_class c ON c.oid=a.attrelid JOIN pg_namespace n ON n.oid=c.relnamespace CROSS JOIN LATERAL aclexplode(a.attacl) acl JOIN pg_roles r ON r.oid=acl.grantee WHERE r.rolname=:name AND n.nspname !~ '^pg_' AND "
|
|
373
|
+
+ OWNED.format(owner="c.relowner")
|
|
374
|
+
),
|
|
375
|
+
{"name": name},
|
|
376
|
+
).all()
|
|
377
|
+
for schema, table, column in columns:
|
|
378
|
+
connection.exec_driver_sql(
|
|
379
|
+
f"REVOKE ALL ({quote(column)}) ON TABLE {quote(schema)}.{quote(table)} FROM {quote(name)}"
|
|
380
|
+
)
|
|
381
|
+
if login:
|
|
382
|
+
connection.exec_driver_sql(
|
|
383
|
+
f"GRANT CONNECT ON DATABASE {quote(database)} TO {quote(name)}"
|
|
384
|
+
)
|
|
385
|
+
connection.exec_driver_sql(f"GRANT {quote(invoker)} TO {quote(name)}")
|
|
386
|
+
|
|
322
387
|
@contextmanager
|
|
323
388
|
def connection(self, identity, *, write, timeout):
|
|
324
389
|
import psycopg
|
|
@@ -10,6 +10,8 @@ from metatables.api.backend.persistence.models import Base, SQLSecurityState
|
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
class SQLiteAccess(DatabaseAccess):
|
|
13
|
+
incremental = True
|
|
14
|
+
|
|
13
15
|
def submit(self, cursor, statement, parameters):
|
|
14
16
|
cursor.execute(statement, parameters)
|
|
15
17
|
|
|
@@ -75,7 +77,7 @@ class SQLiteAccess(DatabaseAccess):
|
|
|
75
77
|
)
|
|
76
78
|
return targets
|
|
77
79
|
|
|
78
|
-
def apply(self, session, desired, previous):
|
|
80
|
+
def apply(self, session, desired, previous, *, full=True):
|
|
79
81
|
# The immutable grant snapshot is installed on each dedicated connection.
|
|
80
82
|
return None
|
|
81
83
|
|