mainsequence-metatable 0.1.8.dev15__tar.gz → 0.1.8.dev17__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.8.dev15/src/mainsequence_metatable.egg-info → mainsequence_metatable-0.1.8.dev17}/PKG-INFO +1 -1
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/adr/api/0001-unified-api-storage-and-local-sqlite.md +10 -1
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/adr/api/0007-database-enforced-table-access.md +15 -4
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/api/data-sources.md +14 -2
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/operations/catalog-migrations.md +1 -1
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/pyproject.toml +1 -1
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17/src/mainsequence_metatable.egg-info}/PKG-INFO +1 -1
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/app/bootstrap.py +25 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/backends/access.py +4 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/backends/postgresql_access.py +88 -59
- mainsequence_metatable-0.1.8.dev17/tests/auth/test_data_source_identity.py +190 -0
- mainsequence_metatable-0.1.8.dev15/tests/auth/test_data_source_identity.py +0 -85
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/.agents/skills/client/metatables-local-development/SKILL.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/.agents/skills/client/metatables-meta-tables/SKILL.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/.agents/skills/client/metatables-migrations/SKILL.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/.agents/skills/client/metatables-time-index-table-updates/SKILL.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/.agents/skills/client/metatables-upgrade-legacy-app/SKILL.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/.mainsequence/workflows/metatables-api.yaml +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/LICENSE +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/MANIFEST.in +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/README.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/alembic.ini +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/api/metatables/main.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/compose.database-tests.yml +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/adr/api/0002-application-administration-and-table-ownership.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/adr/api/0004-approved-application-migrations.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/adr/api/0005-run-logs-and-local-file-capture.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/adr/api/0006-historical-run-graphs.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/adr/api/0007-database-roles-and-privileges.svg +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/adr/api/0007-query-enforcement-path.svg +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/adr/api/0008-mysql-mssql-table-workflows.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/adr/api/0009-shared-credential-store.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/adr/api/0010-import-external-tables-and-views.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/adr/api/0012-bounded-data-transfer-and-safe-retries.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/adr/api/0013-application-owned-migrations.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/adr/api/0014-main-sequence-release-jobs-and-production-migrations.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/adr/client/0003-api-endpoint-resolution.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/adr/client/0011-cli-local-development-with-managed-admin.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/adr/index.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/api/authentication-and-errors.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/api/openapi.json +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/api/overview.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/api/reference.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/api/tables-and-migrations.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/api/time-index-and-updates.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/assets/branding/metatables.svg +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/assets/diagrams/local-client-experience.svg +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/client/bounded-transfers.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/client/build-an-updater.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/client/define-and-migrate-tables.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/client/installation-and-connection.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/client/local-storage.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/client/query-and-mutate.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/client/quickstart.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/client/read-existing-time-index-tables.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/client/register-existing-tables.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/client/tutorial.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/client/upgrade-legacy-app.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/concepts/architecture.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/concepts/data-sources.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/concepts/identity-and-scope.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/concepts/permissions-namespaces-labels.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/concepts/runs-and-execution-graphs.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/concepts/table-contracts-and-lifecycle.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/concepts/time-index-tables-and-updates.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/contributing/architecture-and-boundaries.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/contributing/database-backends.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/contributing/documentation.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/contributing/releasing.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/contributing/testing.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/examples/README.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/examples/__init__.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/examples/bounded_reader.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/examples/data_sources/README.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/examples/data_sources/mssql.json +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/examples/data_sources/mysql.json +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/examples/data_sources/postgresql.json +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/examples/data_sources/register.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/examples/external_table.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/examples/local_app/README.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/examples/local_app/__init__.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/examples/local_app/__main__.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/examples/local_app/migrations/__init__.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/examples/local_app/migrations/env.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/examples/local_app/migrations/script.py.mako +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/examples/local_app/migrations/versions/metatables_local_example/0001.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/examples/local_app/tables.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/examples/query.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/examples/quickstart.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/examples/reader.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/examples/scripts/__init__.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/examples/scripts/setup_metatables.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/examples/tables.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/examples/tutorial/README.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/examples/tutorial/__init__.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/examples/tutorial/__main__.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/examples/tutorial/data/README.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/examples/tutorial/data/yahoo_spy_qqq_iwm_2026-06-15_2026-07-17.csv +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/examples/tutorial/frames.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/examples/tutorial/migrations/__init__.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/examples/tutorial/migrations/env.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/examples/tutorial/migrations/registry.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/examples/tutorial/migrations/script.py.mako +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/examples/tutorial/migrations/versions/__init__.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/examples/tutorial/migrations/versions/metatables_market_tutorial/0001_create_market_tutorial_tables.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/examples/tutorial/migrations/versions/metatables_market_tutorial/0002_create_rolling_volatility.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/examples/tutorial/migrations/versions/metatables_market_tutorial/__init__.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/examples/tutorial/operations.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/examples/tutorial/tables.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/examples/tutorial/updaters.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/examples/updater.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/index.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/operations/configuration.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/operations/hosted-runtime.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/operations/local-runtime.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/operations/recovery-and-observability.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/operations/run-history.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/operations/run-logs.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/reference/capabilities.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/reference/cli.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/reference/python.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/security/index.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/security/ownership-and-sharing.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/security/permissions.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/mkdocs.yml +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/scripts/__init__.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/scripts/build_reference.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/scripts/check_boundaries.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/scripts/check_client_api_coverage.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/scripts/check_distribution_sources.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/scripts/check_docs.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/scripts/check_examples_debug.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/scripts/check_project.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/scripts/dev_release_version.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/scripts/test_databases.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/scripts/test_postgresql.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/setup.cfg +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/mainsequence_metatable.egg-info/SOURCES.txt +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/mainsequence_metatable.egg-info/dependency_links.txt +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/mainsequence_metatable.egg-info/entry_points.txt +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/mainsequence_metatable.egg-info/requires.txt +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/mainsequence_metatable.egg-info/top_level.txt +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/__init__.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/_admin_source.json +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/_build.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/_skill_bundle.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/__init__.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/app/__init__.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/app/auth/__init__.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/app/auth/boundary.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/app/auth/caller_assertions.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/app/auth/local_actor.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/app/configuration.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/app/credential_admin.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/app/development.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/app/development_client.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/app/main.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/app/platform_cache.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/app/routes/__init__.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/app/routes/column_search.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/app/routes/contract_validation.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/app/routes/data_sources.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/app/routes/managed_finalize.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/app/routes/migration_connection.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/app/routes/namespaces.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/app/routes/run_logs.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/app/routes/runtime_context.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/app/routes/schema_graph.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/app/routes/security.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/app/routes/table_cascade.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/app/routes/table_crud.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/app/routes/table_delete.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/app/routes/table_filter.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/app/routes/table_import.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/app/routes/table_introspection.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/app/routes/table_patch.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/app/routes/table_registration.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/app/routes/table_response.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/app/routes/table_search_document.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/app/routes/table_sql.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/app/routes/table_update_pipeline.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/app/routes/time_index_delete.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/app/routes/time_index_reads.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/app/routes/time_index_stats.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/app/routes/update_batch.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/app/routes/update_chunk_insert.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/app/routes/update_constants.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/app/routes/update_crud.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/app/routes/update_details.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/app/routes/update_graph.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/app/routes/update_index_statistics.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/app/routes/update_registration.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/app/routes/update_runs.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/app/routes/update_statistics.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/app/runtime.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/app/runtime_control.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/app/source_passwords.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/app/source_registrations.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/app/transfer.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/__init__.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/backends/__init__.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/backends/adapters/__init__.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/backends/adapters/maintenance/__init__.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/backends/adapters/maintenance/postgresql.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/backends/adapters/maintenance/runtime.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/backends/adapters/maintenance/temporal.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/backends/adapters/maintenance/timescaledb.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/backends/adapters/maintenance/validation.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/backends/adapters/postgresql_cascade_delete.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/backends/adapters/postgresql_connection.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/backends/adapters/postgresql_introspection.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/backends/adapters/postgresql_table_delete.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/backends/adapters/postgresql_tables.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/backends/adapters/postgresql_time_index.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/backends/adapters/postgresql_time_index_delete.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/backends/adapters/postgresql_time_index_queries.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/backends/adapters/postgresql_time_index_stats.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/backends/adapters/postgresql_time_index_write.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/backends/adapters/source_validation.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/backends/adapters/sqlite_connection.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/backends/adapters/sqlite_introspection.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/backends/adapters/table_cursor.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/backends/catalog.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/backends/catalog_ddl.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/backends/catalog_types.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/backends/contracts.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/backends/hosted.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/backends/hosted_access.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/backends/migration_connection.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/backends/native.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/backends/operations.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/backends/pages.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/backends/registry.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/backends/relation_tables.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/backends/relational.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/backends/restricted_sql.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/backends/sqlite_access.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/backends/streaming.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/contracts/__init__.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/contracts/relation_import.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/contracts/storage_layouts.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/contracts/table_contracts.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/contracts/values.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/domain/__init__.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/domain/catalog_rules.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/domain/data_source_rules.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/domain/index_statistics.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/domain/label_rules.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/domain/permission_rules.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/domain/update_constants.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/integrations/__init__.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/integrations/catalog_data_sources.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/integrations/credential_keys.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/integrations/credential_store.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/integrations/data_source_provider.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/integrations/local_credentials.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/integrations/local_workspace.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/integrations/platform_principals.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/integrations/run_logs.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/integrations/runtime_binding.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/integrations/runtime_data_sources.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/integrations/source_validation.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/migrations/__init__.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/migrations/env.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/migrations/script.py.mako +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/migrations/versions/0001_initial.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/migrations/versions/0002_security_model.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/migrations/versions/0003_run_log_capture.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/migrations/versions/0004_historical_run_graphs.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/migrations/versions/0005_database_access.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/migrations/versions/0006_local_credentials.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/migrations/versions/0007_external_relation_names.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/migrations/versions/0008_upload_receipts.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/migrations/versions/__init__.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/operations/__init__.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/operations/bootstrap_migrations.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/operations/catalog_schema.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/operations/create_table.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/operations/finalize_managed.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/operations/import_relations.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/operations/introspect_table.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/operations/read_time_index.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/operations/register_catalog.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/operations/search_documents.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/persistence/__init__.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/persistence/cascade.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/persistence/catalog.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/persistence/database.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/persistence/grants.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/persistence/labels.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/persistence/locking.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/persistence/models.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/persistence/physical_operations.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/persistence/projections.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/persistence/run_graphs.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/persistence/schema_graph.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/persistence/sqlite_types.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/persistence/table_body_filter.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/persistence/table_queries.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/persistence/table_response.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/persistence/update_graph.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/persistence/update_pipeline.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/persistence/updates.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/persistence/upload_receipts.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/backend/presentation/data_source_summary.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/metatables/__init__.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/metatables/configuration.yaml +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/api/metatables/main.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/cli/__init__.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/cli/admin_install.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/cli/app.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/cli/filters.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/cli/local.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/cli/migrations.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/cli/project.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/cli/skills.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/compiled_sql/__init__.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/compiled_sql/mssql.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/compiled_sql/v1.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/dtype_codec.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/endpoint.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/examples/README.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/examples/__init__.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/examples/bounded_reader.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/examples/data_sources/README.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/examples/data_sources/mssql.json +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/examples/data_sources/mysql.json +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/examples/data_sources/postgresql.json +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/examples/data_sources/register.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/examples/external_table.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/examples/local_app/README.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/examples/local_app/__init__.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/examples/local_app/__main__.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/examples/local_app/migrations/__init__.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/examples/local_app/migrations/env.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/examples/local_app/migrations/script.py.mako +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/examples/local_app/migrations/versions/metatables_local_example/0001.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/examples/local_app/tables.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/examples/query.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/examples/quickstart.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/examples/reader.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/examples/scripts/__init__.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/examples/scripts/setup_metatables.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/examples/tables.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/examples/tutorial/README.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/examples/tutorial/__init__.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/examples/tutorial/__main__.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/examples/tutorial/data/README.md +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/examples/tutorial/data/yahoo_spy_qqq_iwm_2026-06-15_2026-07-17.csv +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/examples/tutorial/frames.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/examples/tutorial/migrations/__init__.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/examples/tutorial/migrations/env.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/examples/tutorial/migrations/registry.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/examples/tutorial/migrations/script.py.mako +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/examples/tutorial/migrations/versions/__init__.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/examples/tutorial/migrations/versions/metatables_market_tutorial/0001_create_market_tutorial_tables.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/examples/tutorial/migrations/versions/metatables_market_tutorial/0002_create_rolling_volatility.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/examples/tutorial/migrations/versions/metatables_market_tutorial/__init__.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/examples/tutorial/operations.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/examples/tutorial/tables.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/examples/tutorial/updaters.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/examples/updater.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/future_registry.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/hashing.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/instrumentation.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/local_connection.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/local_files.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/local_run_logs.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/log_contract.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/migrations/__init__.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/migrations/alembic.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/migrations/connection.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/migrations/env.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/migrations/provider.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/migrations/registry.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/migrations/runner.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/migrations/scaffold.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/migrations/templates/__init__.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/migrations/templates/env.py.mako +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/migrations/templates/script.py.mako +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/migrations/tls.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/models.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/pydantic_metadata.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/run_logging.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/runtime.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/runtime_context.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/runtime_contract.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/schema_names.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/sdk_compat.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/sqlalchemy_contracts.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/time_index_table_refs.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/transfer.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/transfer_contract.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/transport.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/update_configuration_contract.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/updaters/__init__.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/updaters/configuration.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/updaters/dependencies.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/updaters/managers.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/updaters/namespacing.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/updaters/runner.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/updaters/updaters.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/updaters/utils.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/src/metatables/upload.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/auth/test_caller_assertions.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/auth/test_development_client.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/auth/test_hosted_environment_target.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/auth/test_local_mode.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/auth/test_platform_fact_cache.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/auth/test_runtime_bootstrap.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/auth/test_runtime_scaffold.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/auth/test_runtime_selection.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/auth/test_runtime_storage_binding.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/auth/test_source_passwords.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/catalog/test_catalog_foundation.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/catalog/test_contract_projection.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/catalog/test_database_config.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/catalog/test_index_statistics.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/catalog/test_migration.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/conftest.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/contracts/capabilities.json +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/contracts/client_signatures.json +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/contracts/database_operations.json +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/contracts/test_bounded_transfer.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/contracts/test_compiler_runtime_source.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/contracts/test_relation_batching.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/contracts/test_relation_import.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/contracts/test_storage_layouts.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/contracts/test_table_contracts.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/database_backend/Dockerfile +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/database_backend/Dockerfile.dockerignore +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/database_backend/__init__.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/database_backend/conftest.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/database_backend/provider/__init__.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/database_backend/provider/env.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/database_backend/provider/versions/0001_measurement.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/database_backend/test_api_contract.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/database_backend/test_application_migration.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/database_backend/test_bootstrap_recovery.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/database_backend/test_catalog_contract.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/database_backend/test_credential_catalog.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/database_backend/test_physical_contract.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/database_backend/test_relation_import.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/database_backend/test_runtime_bootstrap.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/database_backend/test_sql_security.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/database_backend/test_update_client.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/database_operations/test_physical_operations.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/database_operations/test_postgresql_operations.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/database_operations/test_runtime_and_temporal.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/documentation/test_project_checks.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/e2e/test_admin_installation.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/e2e/test_api_endpoint.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/e2e/test_application_owned_migrations.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/e2e/test_catalog_outage.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/e2e/test_client_api_contract.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/e2e/test_client_api_coverage.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/e2e/test_client_migration_scaffold.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/e2e/test_client_migration_tls.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/e2e/test_client_package.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/e2e/test_client_signatures.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/e2e/test_client_value_behaviors.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/e2e/test_data_source_registry.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/e2e/test_local_actor_client_api.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/e2e/test_local_cli_workflow.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/e2e/test_local_client_selection.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/e2e/test_managed_finalize_client_api.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/e2e/test_namespaces_admin_http.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/e2e/test_public_imports.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/e2e/test_remaining_client_routes.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/e2e/test_run_graphs.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/e2e/test_run_logs.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/e2e/test_runtime_transition.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/e2e/test_runtime_upgrade.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/e2e/test_sdk_local_startup.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/e2e/test_sqlite_runtime.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/e2e/test_table_delete_client_api.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/e2e/test_table_filter_client_api.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/e2e/test_table_introspection_client_api.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/e2e/test_table_patch_client_api.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/e2e/test_table_search_document_api.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/e2e/test_table_stats_api.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/e2e/test_table_update_pipeline.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/e2e/test_table_updates_http.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/e2e/test_time_index_delete_client_api.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/e2e/test_time_index_reads_client_api.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/e2e/test_update_batch_client_api.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/e2e/test_update_chunk_insert_client_api.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/e2e/test_update_dependency_priority_client_api.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/e2e/test_update_index_statistics_client_api.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/e2e/test_update_registration_client_api.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/examples/test_documented_examples.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/examples/test_postgresql_examples.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/examples/test_setup_launch.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/examples/test_tutorial.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/integrations/test_credential_store.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/integrations/test_data_source_rules.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/integrations/test_local_data_sources.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/integrations/test_local_run_logs.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/integrations/test_postgresql_connection.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/integrations/test_postgresql_sql.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/integrations/test_postgresql_tables.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/integrations/test_postgresql_time_index_queries.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/integrations/test_source_validation.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/integrations/test_sqlite_operations.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/labels/test_labels.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/operations/test_create_table_operation.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/operations/test_create_table_postgres.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/operations/test_physical_operation_journal.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/operations/test_register_catalog.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/operations/test_search_documents.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/packaging/test_client_skills.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/packaging/test_dev_release_version.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/packaging/test_distribution_sources.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/packaging/test_editable_build.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/packaging/test_installed_distribution.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/permissions/test_governed_sql_scope.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/permissions/test_local_actor_membership.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/permissions/test_migration_boundary.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/permissions/test_namespace_grants.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/permissions/test_security_api.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/permissions/test_table_body_filter.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/permissions/test_table_grants.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/tests/permissions/test_table_queries.py +0 -0
- {mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/uv.lock +0 -0
|
@@ -28,7 +28,8 @@ does not cache this API's runtime context or select its runtime mode or DataSour
|
|
|
28
28
|
Updated 2026-09-28: application Environment policy and relationships are removed.
|
|
29
29
|
Amended 2026-09-29: one enforced runtime binding and explicit bootstrap select one
|
|
30
30
|
database for system and user tables.
|
|
31
|
-
Amended 2026-10-02: each runtime mode lists and selects only its own DataSources
|
|
31
|
+
Amended 2026-10-02: each runtime mode lists and selects only its own DataSources,
|
|
32
|
+
and inspection checks the login's prerequisites before setup.
|
|
32
33
|
|
|
33
34
|
Companion decision: [Main Sequence SDK ADR 0035: Independent Git source and platform execution context](https://github.com/mainsequence-sdk/mainsequence-sdk/blob/metatables_removal/docs/adr/0035-independent-git-source-and-platform-context.md).
|
|
34
35
|
The SDK prerequisite is implemented on `metatables_removal`; the current local
|
|
@@ -172,6 +173,14 @@ configuration inserted as the runtime DataSource in that database. Failed work
|
|
|
172
173
|
never activates a partially initialized runtime. A database already initialized
|
|
173
174
|
and compatible can be selected without running migrations.
|
|
174
175
|
|
|
176
|
+
Amended 2026-10-02: before any runtime is registered, inspection also checks,
|
|
177
|
+
read-only, the login's prerequisites under
|
|
178
|
+
[ADR 0007](0007-database-enforced-table-access.md): `CREATEROLE`, and permission to
|
|
179
|
+
create the `metatables` schema and tables in the default schema. Unmet
|
|
180
|
+
prerequisites make the target incompatible, with each reason shown in Settings,
|
|
181
|
+
so setup never leaves a database half-initialized. A registered runtime is not
|
|
182
|
+
rechecked.
|
|
183
|
+
|
|
175
184
|
Settings, runtime context, mode selection and bootstrap actions do not depend on
|
|
176
185
|
catalog sessions or catalog memberships. They retain the existing request admission.
|
|
177
186
|
The caller who configures a pending target controls its initialization; an existing
|
|
@@ -6,6 +6,14 @@
|
|
|
6
6
|
> conflicting application-migration execution and credential restrictions;
|
|
7
7
|
> governed API operations and MetaTables system migrations remain separate.
|
|
8
8
|
|
|
9
|
+
> Amendment (2026-10-02): MetaTables secures only what it owns: its `metatables`
|
|
10
|
+
> schema, the tables it manages and the roles it creates. Database-wide defaults
|
|
11
|
+
> and `PUBLIC` access to other owners' objects, including extensions and other
|
|
12
|
+
> applications sharing the database, are the database administrator's
|
|
13
|
+
> responsibility. The login needs `CREATEROLE` and permission to create the
|
|
14
|
+
> `metatables` schema and tables in the DataSource's default schema; it need not
|
|
15
|
+
> own the database.
|
|
16
|
+
|
|
9
17
|
|
|
10
18
|
Date: 2026-09-29
|
|
11
19
|
|
|
@@ -160,12 +168,14 @@ client.
|
|
|
160
168
|
- **Ownership.** Tables are owned by `mt_owner`, a role that cannot log in, because
|
|
161
169
|
an owner holds every privilege. The API's own role manages grants and schema
|
|
162
170
|
changes and never runs caller SQL.
|
|
163
|
-
- **Defaults.** PostgreSQL's default `PUBLIC`
|
|
164
|
-
|
|
171
|
+
- **Defaults.** On objects MetaTables owns, PostgreSQL's default `PUBLIC`
|
|
172
|
+
privileges are revoked, including for future tables and routines. Database-wide
|
|
173
|
+
defaults (connecting, creating objects and temporary tables) and other owners'
|
|
174
|
+
objects belong to the database administrator.
|
|
165
175
|
- **Privileged routines.** Restricted caller roles must not be able to execute
|
|
166
176
|
application routines with elevated owner privileges, including
|
|
167
177
|
`SECURITY DEFINER` routines. Initialization establishes this restriction for
|
|
168
|
-
|
|
178
|
+
the routines MetaTables owns, existing and future, covering direct grants, inherited
|
|
169
179
|
roles and `PUBLIC`. Revoking a direct User grant alone is insufficient if
|
|
170
180
|
another grant still permits execution. Schema changes and extension installation
|
|
171
181
|
preserve the restriction before exposing new or changed routines to callers.
|
|
@@ -261,7 +271,8 @@ and [MySQL implicit commits](https://dev.mysql.com/doc/refman/8.4/en/implicit-co
|
|
|
261
271
|
|
|
262
272
|
- The explorer can run any PostgreSQL query, and the API has no SQL parser left
|
|
263
273
|
to get wrong.
|
|
264
|
-
- The DataSource login needs `CREATEROLE
|
|
274
|
+
- The DataSource login needs `CREATEROLE` and the right to create the `metatables`
|
|
275
|
+
schema; it does not need to own the database.
|
|
265
276
|
- Physical table names become visible to explorer users.
|
|
266
277
|
- Cascading foreign keys are unavailable where the explorer is enabled.
|
|
267
278
|
- Team membership reaches the database at the pace of the existing one-hour
|
{mainsequence_metatable-0.1.8.dev15 → mainsequence_metatable-0.1.8.dev17}/docs/api/data-sources.md
RENAMED
|
@@ -173,14 +173,26 @@ uses the selected database as its SQL schema. SQL Server 2022 uses `dbo` for the
|
|
|
173
173
|
system schema; application providers may use approved schemas in that database.
|
|
174
174
|
|
|
175
175
|
The API database login manages native caller identities as well as schema. PostgreSQL
|
|
176
|
-
requires `CREATEROLE`,
|
|
177
|
-
|
|
176
|
+
requires `CREATEROLE`, permission to create the `metatables` schema in the runtime
|
|
177
|
+
database and tables in its default schema, and management rights on `mt_owner` if
|
|
178
|
+
it already exists. It does not need to own the database. It stores the catalog in the private `metatables`
|
|
178
179
|
schema. MySQL requires account/role administration, privilege disclosure and grant
|
|
179
180
|
rights on the selected database, `TRIGGER` visibility, and permission to cancel
|
|
180
181
|
other caller sessions. SQL Server requires login/user/role administration,
|
|
181
182
|
`VIEW DEFINITION` and grant management. Managed database services must provide
|
|
182
183
|
these capabilities; missing permissions leave caller SQL disabled.
|
|
183
184
|
|
|
185
|
+
Settings checks these PostgreSQL requirements as soon as you select a database
|
|
186
|
+
without a MetaTables runtime, before any migration runs. A missing one shows
|
|
187
|
+
**Needs attention** with the reason, for example `metatables_dev lacks CREATEROLE`;
|
|
188
|
+
**Run MetaTables migrations** stays unavailable and nothing is written.
|
|
189
|
+
|
|
190
|
+
The database may be shared with other applications. Setup revokes `PUBLIC` access
|
|
191
|
+
only on objects MetaTables owns: its `metatables` schema, the tables it manages and
|
|
192
|
+
the roles it creates. Database-wide defaults and other owners' objects, including
|
|
193
|
+
extensions such as TimescaleDB and pgvector, are the database administrator's
|
|
194
|
+
responsibility.
|
|
195
|
+
|
|
184
196
|
Hosted sources require a password Secret to derive per-User database credentials.
|
|
185
197
|
MySQL global mandatory roles are unsupported. Registration admits ordinary tables
|
|
186
198
|
only; it does not transfer external ownership. Privileged routines and triggers
|
|
@@ -10,7 +10,7 @@ system tables, including `data_source`, alongside user tables. There is no separ
|
|
|
10
10
|
catalog connection to configure. Local and hosted use the same initialization flow.
|
|
11
11
|
|
|
12
12
|
1. For Hosted mode, register a PostgreSQL, TimescaleDB, MySQL or MSSQL connection under **MetaTables → Data Sources**. This registration is available before the hosted catalog is initialized.
|
|
13
|
-
2. Open **Settings**, choose Hosted runtime mode, and select that registered DataSource from the list. Local mode prefills the single local SQLite runtime file, shared across Git branches, and uses **Check DataSource**. Choosing a hosted DataSource and switching modes are separate actions.
|
|
13
|
+
2. Open **Settings**, choose Hosted runtime mode, and select that registered DataSource from the list. Local mode prefills the single local SQLite runtime file, shared across Git branches, and uses **Check DataSource**. Choosing a hosted DataSource and switching modes are separate actions. For a database without a MetaTables runtime, selection also checks that the login can secure it; **Needs attention** lists what is missing ([database privileges](../api/data-sources.md#database-privileges-and-sql-behavior)) and no migration can run until another database is selected.
|
|
14
14
|
3. For an empty or outdated database, click **Run MetaTables migrations**. This
|
|
15
15
|
explicitly runs the packaged Alembic history. After success, the API saves the
|
|
16
16
|
proposal as the runtime DataSource inside the newly initialized registry.
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "mainsequence-metatable"
|
|
7
|
-
version = "0.1.8.
|
|
7
|
+
version = "0.1.8.dev17"
|
|
8
8
|
description = "Main Sequence MetaTables Python client and standalone API"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.13"
|
|
@@ -58,6 +58,14 @@ class BootstrapError(RuntimeError):
|
|
|
58
58
|
pass
|
|
59
59
|
|
|
60
60
|
|
|
61
|
+
def insufficient_privilege(exc):
|
|
62
|
+
"""The database's own message for a missing privilege, never SQL text or parameters."""
|
|
63
|
+
orig = getattr(exc, "orig", None)
|
|
64
|
+
code = getattr(orig, "pgcode", None) or getattr(orig, "sqlstate", None)
|
|
65
|
+
message = getattr(getattr(orig, "diag", None), "message_primary", None)
|
|
66
|
+
return message if code == "42501" and message else None
|
|
67
|
+
|
|
68
|
+
|
|
61
69
|
class DestroyLocalInput(BaseModel):
|
|
62
70
|
model_config = ConfigDict(extra="forbid")
|
|
63
71
|
path: str
|
|
@@ -254,6 +262,8 @@ class RuntimeBootstrap:
|
|
|
254
262
|
return
|
|
255
263
|
if set(self.current) != set(self.expected):
|
|
256
264
|
self.status = "migration_required"
|
|
265
|
+
if not self.current:
|
|
266
|
+
self.require_prerequisites(connection)
|
|
257
267
|
return
|
|
258
268
|
from metatables.api.backend.operations.catalog_schema import validate_system_schema
|
|
259
269
|
validate_system_schema(connection, self.backend.catalog)
|
|
@@ -261,6 +271,8 @@ class RuntimeBootstrap:
|
|
|
261
271
|
row = session.scalar(select(DataSource).where(DataSource.is_runtime.__eq__(True)))
|
|
262
272
|
if row is None:
|
|
263
273
|
self.status = "registration_required"
|
|
274
|
+
with engine.connect() as connection:
|
|
275
|
+
self.require_prerequisites(connection)
|
|
264
276
|
elif not self.matches(row):
|
|
265
277
|
self.status = "incompatible"
|
|
266
278
|
self.error = "The stored runtime DataSource does not match this connection."
|
|
@@ -286,6 +298,15 @@ class RuntimeBootstrap:
|
|
|
286
298
|
self.status = "unavailable"
|
|
287
299
|
self.error = "Cannot verify the selected database or its Alembic history. Check configuration, Secret access and the API's migration version."
|
|
288
300
|
|
|
301
|
+
def require_prerequisites(self, connection):
|
|
302
|
+
# Before any setup DDL: a database the login cannot secure must never be
|
|
303
|
+
# half-initialized. A registered runtime may hold external objects.
|
|
304
|
+
problems = self.backend.access.prerequisite_problems(connection)
|
|
305
|
+
if problems:
|
|
306
|
+
self.status = "incompatible"
|
|
307
|
+
self.error = ("MetaTables cannot secure this database: " + "; ".join(problems) + ". Select a database "
|
|
308
|
+
"owned by the DataSource login, as described in DataSource database privileges.")
|
|
309
|
+
|
|
289
310
|
def matches(self, row):
|
|
290
311
|
return (row.class_type == self.candidate["class_type"]
|
|
291
312
|
and row.configuration == self.candidate["configuration"]
|
|
@@ -347,6 +368,10 @@ class RuntimeBootstrap:
|
|
|
347
368
|
if isinstance(exc, SQLExecutionError):
|
|
348
369
|
self.error = f"Database access setup failed ({exc.code}). Check the DataSource password Secret and database permissions, then retry setup."
|
|
349
370
|
raise BootstrapError(self.error) from None
|
|
371
|
+
if privilege_error := insufficient_privilege(exc):
|
|
372
|
+
self.error = (f"Database setup failed: {privilege_error}. The DataSource login must own the runtime "
|
|
373
|
+
"database and every object in it; see the DataSource database privileges.")
|
|
374
|
+
raise BootstrapError(self.error) from None
|
|
350
375
|
self.error = "MetaTables migration or registration failed. The runtime was not activated; inspect the database before retrying."
|
|
351
376
|
raise BootstrapError(self.error) from None
|
|
352
377
|
|
|
@@ -60,6 +60,10 @@ class DatabaseAccess:
|
|
|
60
60
|
).hexdigest()
|
|
61
61
|
)
|
|
62
62
|
|
|
63
|
+
def prerequisite_problems(self, connection):
|
|
64
|
+
"""Read-only reasons this login cannot secure the database; checked before setup."""
|
|
65
|
+
return []
|
|
66
|
+
|
|
63
67
|
def initialize(self, session):
|
|
64
68
|
raise NotImplementedError
|
|
65
69
|
|
|
@@ -13,18 +13,47 @@ def quote(value):
|
|
|
13
13
|
return '"' + str(value).replace('"', '""') + '"'
|
|
14
14
|
|
|
15
15
|
|
|
16
|
+
# MetaTables secures only objects this login owns, directly or through
|
|
17
|
+
# membership (mt_owner). PUBLIC access to anything else in the database is its
|
|
18
|
+
# administrator's responsibility (ADR 0007, amended 2026-10-02).
|
|
19
|
+
OWNED = "pg_has_role(current_user, {owner}, 'MEMBER')"
|
|
20
|
+
USER_SCHEMA = "{schema} !~ '^pg_' AND {schema} <> 'information_schema'"
|
|
21
|
+
|
|
22
|
+
|
|
16
23
|
class PostgreSQLAccess(DatabaseAccess):
|
|
24
|
+
def prerequisite_problems(self, connection):
|
|
25
|
+
schema = getattr(self.source.connection, "default_schema", None) or "public"
|
|
26
|
+
login, database, creates_roles, creates_schemas, catalog_owner, tables = connection.execute(text(
|
|
27
|
+
"SELECT current_user, current_database(), r.rolcreaterole OR r.rolsuper, "
|
|
28
|
+
"has_database_privilege(current_database(), 'CREATE'), "
|
|
29
|
+
"(SELECT pg_get_userbyid(nspowner) FROM pg_namespace WHERE nspname = 'metatables' "
|
|
30
|
+
"AND NOT pg_has_role(current_user, nspowner, 'MEMBER')), "
|
|
31
|
+
"(SELECT has_schema_privilege(oid, 'USAGE') AND has_schema_privilege(oid, 'CREATE') "
|
|
32
|
+
"FROM pg_namespace WHERE nspname = :schema) "
|
|
33
|
+
"FROM pg_roles r WHERE r.rolname = current_user"
|
|
34
|
+
), {"schema": schema}).one()
|
|
35
|
+
problems = []
|
|
36
|
+
if not creates_roles:
|
|
37
|
+
problems.append(f"{login} lacks CREATEROLE")
|
|
38
|
+
if catalog_owner:
|
|
39
|
+
problems.append(f'schema "metatables" belongs to {catalog_owner}')
|
|
40
|
+
elif not creates_schemas:
|
|
41
|
+
problems.append(f'{login} cannot create schemas in database "{database}"')
|
|
42
|
+
if not tables:
|
|
43
|
+
problems.append(f'{login} cannot create tables in schema "{schema}"')
|
|
44
|
+
return problems
|
|
45
|
+
|
|
17
46
|
def initialize(self, session):
|
|
18
47
|
self.identity_secret()
|
|
19
48
|
connection = session.connection()
|
|
20
49
|
self._role(connection, "mt_owner")
|
|
21
50
|
api = connection.exec_driver_sql("SELECT current_user").scalar()
|
|
22
51
|
connection.exec_driver_sql(f"GRANT mt_owner TO {quote(api)}")
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
52
|
+
if connection.exec_driver_sql(
|
|
53
|
+
"SELECT has_schema_privilege(oid, 'CREATE WITH GRANT OPTION') FROM pg_namespace WHERE nspname='public'"
|
|
54
|
+
).scalar():
|
|
55
|
+
connection.exec_driver_sql("GRANT USAGE, CREATE ON SCHEMA public TO mt_owner")
|
|
26
56
|
connection.exec_driver_sql("REVOKE ALL ON SCHEMA metatables FROM PUBLIC")
|
|
27
|
-
connection.exec_driver_sql("REVOKE CREATE ON SCHEMA public FROM PUBLIC")
|
|
28
57
|
state = session.get(SQLSecurityState, 1)
|
|
29
58
|
if state is None:
|
|
30
59
|
state = SQLSecurityState(id=1, ready=True, pending=True, manifest={})
|
|
@@ -59,39 +88,46 @@ class PostgreSQLAccess(DatabaseAccess):
|
|
|
59
88
|
else:
|
|
60
89
|
connection.exec_driver_sql(f"ALTER ROLE {quote(name)} NOLOGIN")
|
|
61
90
|
|
|
91
|
+
def owned_schemas(self, connection):
|
|
92
|
+
return connection.exec_driver_sql(
|
|
93
|
+
"SELECT nspname FROM pg_namespace WHERE " + USER_SCHEMA.format(schema="nspname")
|
|
94
|
+
+ " AND " + OWNED.format(owner="nspowner")
|
|
95
|
+
).scalars().all()
|
|
96
|
+
|
|
62
97
|
def secure_routines(self, connection, managed_roles):
|
|
63
|
-
"""Inspect only at initialization/schema changes/reconciliation.
|
|
98
|
+
"""Inspect only at initialization/schema changes/reconciliation.
|
|
99
|
+
|
|
100
|
+
Covers the objects this login owns; other owners' objects are left to
|
|
101
|
+
the database administrator.
|
|
102
|
+
"""
|
|
64
103
|
invoker = self.role("invoker", "routines")
|
|
65
104
|
self._role(connection, invoker)
|
|
66
|
-
|
|
67
|
-
connection.exec_driver_sql(
|
|
68
|
-
"SELECT nspname FROM pg_namespace WHERE nspname !~ '^pg_' AND nspname <> 'information_schema'"
|
|
69
|
-
)
|
|
70
|
-
.scalars()
|
|
71
|
-
.all()
|
|
72
|
-
)
|
|
73
|
-
for schema in schemas:
|
|
74
|
-
connection.exec_driver_sql(
|
|
75
|
-
f"REVOKE ALL ON ALL TABLES IN SCHEMA {quote(schema)} FROM PUBLIC"
|
|
76
|
-
)
|
|
105
|
+
for schema in self.owned_schemas(connection):
|
|
77
106
|
if schema != "metatables":
|
|
78
107
|
connection.exec_driver_sql(
|
|
79
108
|
f"GRANT USAGE ON SCHEMA {quote(schema)} TO {quote(invoker)}"
|
|
80
109
|
)
|
|
110
|
+
public_tables = connection.exec_driver_sql(
|
|
111
|
+
"SELECT DISTINCT n.nspname, c.relname FROM pg_class c JOIN pg_namespace n ON n.oid=c.relnamespace "
|
|
112
|
+
"CROSS JOIN LATERAL aclexplode(c.relacl) acl WHERE acl.grantee=0 AND c.relkind IN ('r','p','v','m','f') AND "
|
|
113
|
+
+ USER_SCHEMA.format(schema="n.nspname") + " AND " + OWNED.format(owner="c.relowner")
|
|
114
|
+
).all()
|
|
115
|
+
for schema, table in public_tables:
|
|
116
|
+
connection.exec_driver_sql(f"REVOKE ALL ON TABLE {quote(schema)}.{quote(table)} FROM PUBLIC")
|
|
81
117
|
public_columns = connection.exec_driver_sql(
|
|
82
|
-
"SELECT n.nspname, c.relname, a.attname FROM pg_attribute a JOIN pg_class c ON c.oid=a.attrelid
|
|
118
|
+
"SELECT n.nspname, c.relname, a.attname FROM pg_attribute a JOIN pg_class c ON c.oid=a.attrelid "
|
|
119
|
+
"JOIN pg_namespace n ON n.oid=c.relnamespace CROSS JOIN LATERAL aclexplode(a.attacl) acl "
|
|
120
|
+
"WHERE acl.grantee=0 AND n.nspname !~ '^pg_' AND " + OWNED.format(owner="c.relowner")
|
|
83
121
|
).all()
|
|
84
122
|
for schema, table, column in public_columns:
|
|
85
123
|
connection.exec_driver_sql(
|
|
86
124
|
f"REVOKE ALL ({quote(column)}) ON TABLE {quote(schema)}.{quote(table)} FROM PUBLIC"
|
|
87
125
|
)
|
|
88
|
-
owners = (
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
)
|
|
92
|
-
|
|
93
|
-
.all()
|
|
94
|
-
)
|
|
126
|
+
owners = connection.exec_driver_sql(
|
|
127
|
+
"SELECT DISTINCT r.rolname FROM pg_proc p JOIN pg_roles r ON r.oid=p.proowner "
|
|
128
|
+
"JOIN pg_namespace n ON n.oid=p.pronamespace WHERE " + USER_SCHEMA.format(schema="n.nspname")
|
|
129
|
+
+ " AND " + OWNED.format(owner="p.proowner")
|
|
130
|
+
).scalars().all()
|
|
95
131
|
owners = set(owners) | {
|
|
96
132
|
"mt_owner",
|
|
97
133
|
connection.exec_driver_sql("SELECT current_user").scalar(),
|
|
@@ -108,24 +144,17 @@ class PostgreSQLAccess(DatabaseAccess):
|
|
|
108
144
|
grantees = "PUBLIC, " + ", ".join(
|
|
109
145
|
quote(role) for role in sorted((set(managed_roles) | {invoker}) & existing)
|
|
110
146
|
)
|
|
111
|
-
|
|
112
|
-
for noun in ("FUNCTIONS", "PROCEDURES"):
|
|
113
|
-
connection.exec_driver_sql(
|
|
114
|
-
f"REVOKE ALL ON ALL {noun} IN SCHEMA {quote(schema)} FROM {grantees}"
|
|
115
|
-
)
|
|
116
|
-
if schema != "metatables":
|
|
117
|
-
connection.exec_driver_sql(
|
|
118
|
-
f"GRANT EXECUTE ON ALL {noun} IN SCHEMA {quote(schema)} TO {quote(invoker)}"
|
|
119
|
-
)
|
|
120
|
-
# Revoke elevated routines within the same transaction before publishing
|
|
121
|
-
# permissions. Batch schema grants avoid an objects-by-principals loop.
|
|
147
|
+
# Restricted callers never execute routines with elevated owner privileges.
|
|
122
148
|
routines = connection.exec_driver_sql(
|
|
123
|
-
"SELECT n.nspname, p.proname, pg_get_function_identity_arguments(p.oid), p.
|
|
149
|
+
"SELECT n.nspname, p.proname, pg_get_function_identity_arguments(p.oid), p.prosecdef "
|
|
150
|
+
"FROM pg_proc p JOIN pg_namespace n ON n.oid=p.pronamespace WHERE "
|
|
151
|
+
+ USER_SCHEMA.format(schema="n.nspname") + " AND " + OWNED.format(owner="p.proowner")
|
|
124
152
|
).all()
|
|
125
|
-
for schema, name, arguments,
|
|
126
|
-
noun = "PROCEDURE" if kind == "p" else "FUNCTION"
|
|
153
|
+
for schema, name, arguments, elevated in routines:
|
|
127
154
|
target = f"{quote(schema)}.{quote(name)}({arguments})"
|
|
128
|
-
connection.exec_driver_sql(f"REVOKE ALL ON
|
|
155
|
+
connection.exec_driver_sql(f"REVOKE ALL ON ROUTINE {target} FROM {grantees}")
|
|
156
|
+
if schema != "metatables" and not elevated:
|
|
157
|
+
connection.exec_driver_sql(f"GRANT EXECUTE ON ROUTINE {target} TO {quote(invoker)}")
|
|
129
158
|
|
|
130
159
|
def validate_table(self, connection, table):
|
|
131
160
|
schema = table.physical_schema or "public"
|
|
@@ -135,7 +164,7 @@ class PostgreSQLAccess(DatabaseAccess):
|
|
|
135
164
|
raise SQLExecutionError(code="catalog_table_registration_denied")
|
|
136
165
|
relation = f"{quote(schema)}.{quote(table.physical_table_name)}"
|
|
137
166
|
row = connection.execute(
|
|
138
|
-
text("SELECT oid, relkind FROM pg_class WHERE oid=to_regclass(:relation)"),
|
|
167
|
+
text("SELECT oid, relkind, " + OWNED.format(owner="relowner") + " AS owned FROM pg_class WHERE oid=to_regclass(:relation)"),
|
|
139
168
|
{"relation": relation},
|
|
140
169
|
).first()
|
|
141
170
|
if row is not None and row.relkind == "v":
|
|
@@ -159,7 +188,8 @@ class PostgreSQLAccess(DatabaseAccess):
|
|
|
159
188
|
),
|
|
160
189
|
{"oid": row.oid},
|
|
161
190
|
).first()
|
|
162
|
-
|
|
191
|
+
if row.owned:
|
|
192
|
+
connection.exec_driver_sql(f"REVOKE ALL ON TABLE {relation} FROM PUBLIC")
|
|
163
193
|
if table.management_mode == "platform_managed":
|
|
164
194
|
connection.exec_driver_sql(f"ALTER TABLE {relation} OWNER TO mt_owner")
|
|
165
195
|
return trigger is None
|
|
@@ -183,13 +213,7 @@ class PostgreSQLAccess(DatabaseAccess):
|
|
|
183
213
|
for name, definition in roles.items():
|
|
184
214
|
self._role(connection, name, self.password(name) if definition["login"] else None)
|
|
185
215
|
names = set(roles) | set(previous.get("roles", {})) | {invoker}
|
|
186
|
-
schemas = (
|
|
187
|
-
connection.exec_driver_sql(
|
|
188
|
-
"SELECT nspname FROM pg_namespace WHERE nspname !~ '^pg_' AND nspname <> 'information_schema'"
|
|
189
|
-
)
|
|
190
|
-
.scalars()
|
|
191
|
-
.all()
|
|
192
|
-
)
|
|
216
|
+
schemas = self.owned_schemas(connection)
|
|
193
217
|
for name in sorted(names):
|
|
194
218
|
owned = connection.execute(
|
|
195
219
|
text(
|
|
@@ -215,20 +239,25 @@ class PostgreSQLAccess(DatabaseAccess):
|
|
|
215
239
|
connection.exec_driver_sql(
|
|
216
240
|
f"REVOKE ALL ON DATABASE {quote(database)} FROM {quote(name)}"
|
|
217
241
|
)
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
)
|
|
242
|
+
# Revoke this role's grants on owned objects; other owners' grants are theirs.
|
|
243
|
+
granted = connection.execute(
|
|
244
|
+
text(
|
|
245
|
+
"SELECT 'SCHEMA', quote_ident(n.nspname) FROM pg_namespace n CROSS JOIN LATERAL aclexplode(n.nspacl) acl "
|
|
246
|
+
"JOIN pg_roles r ON r.oid=acl.grantee WHERE r.rolname=:name AND " + OWNED.format(owner="n.nspowner")
|
|
247
|
+
+ " UNION SELECT CASE WHEN c.relkind='S' THEN 'SEQUENCE' ELSE 'TABLE' END, "
|
|
248
|
+
"quote_ident(n.nspname) || '.' || quote_ident(c.relname) FROM pg_class c "
|
|
249
|
+
"JOIN pg_namespace n ON n.oid=c.relnamespace CROSS JOIN LATERAL aclexplode(c.relacl) acl "
|
|
250
|
+
"JOIN pg_roles r ON r.oid=acl.grantee WHERE r.rolname=:name AND " + OWNED.format(owner="c.relowner")
|
|
251
|
+
),
|
|
252
|
+
{"name": name},
|
|
253
|
+
).all()
|
|
254
|
+
for kind, target in granted:
|
|
255
|
+
connection.exec_driver_sql(f"REVOKE ALL ON {kind} {target} FROM {quote(name)}")
|
|
228
256
|
# Table-level REVOKE does not remove separately granted column ACLs.
|
|
229
257
|
columns = connection.execute(
|
|
230
258
|
text(
|
|
231
|
-
"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_'"
|
|
259
|
+
"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 "
|
|
260
|
+
+ OWNED.format(owner="c.relowner")
|
|
232
261
|
),
|
|
233
262
|
{"name": name},
|
|
234
263
|
).all()
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
"""A DataSource keeps its registration identity when its catalog is initialized."""
|
|
2
|
+
from contextlib import nullcontext
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from types import SimpleNamespace
|
|
5
|
+
from uuid import UUID, uuid4
|
|
6
|
+
|
|
7
|
+
from sqlalchemy import select
|
|
8
|
+
from sqlalchemy.orm import sessionmaker
|
|
9
|
+
|
|
10
|
+
from metatables.api.app.bootstrap import RuntimeBootstrap
|
|
11
|
+
from metatables.api.app.runtime import RuntimeConfig
|
|
12
|
+
from metatables.api.app.runtime_control import write_state
|
|
13
|
+
from metatables.api.app.source_registrations import read_registry, reconcile_runtime
|
|
14
|
+
from metatables.api.backend.persistence.database import create_catalog_engine, upgrade_catalog
|
|
15
|
+
from metatables.api.backend.persistence.models import DataSource
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def test_hosted_bootstrap_preserves_registered_datasource_uid(tmp_path, monkeypatch):
|
|
19
|
+
configuration = tmp_path / "configuration.yaml"
|
|
20
|
+
bootstrap = RuntimeBootstrap(RuntimeConfig(local_mode=False, configuration_path=configuration), None)
|
|
21
|
+
uid, user = uuid4(), uuid4()
|
|
22
|
+
bootstrap.selected_source_uid = str(uid)
|
|
23
|
+
bootstrap.candidate = dict(display_name="Hosted", class_type="postgresql", configuration={
|
|
24
|
+
"host": "db.example.invalid", "database_name": "tables", "database_user": "tables"})
|
|
25
|
+
engine = create_catalog_engine(f"sqlite:///{tmp_path / 'catalog.sqlite'}", allow_sqlite=True)
|
|
26
|
+
upgrade_catalog(engine)
|
|
27
|
+
bootstrap.engine = engine
|
|
28
|
+
bootstrap.factory = sessionmaker(engine, expire_on_commit=False)
|
|
29
|
+
|
|
30
|
+
@dataclass
|
|
31
|
+
class Source:
|
|
32
|
+
uid: UUID | None = None
|
|
33
|
+
|
|
34
|
+
bootstrap.backend = SimpleNamespace(source=Source())
|
|
35
|
+
# Keep the actual migration/registration transaction; isolate hosted driver
|
|
36
|
+
# and schema-access setup, which the engine contract tests cover separately.
|
|
37
|
+
monkeypatch.setattr(bootstrap, "migration_lock", nullcontext)
|
|
38
|
+
monkeypatch.setattr("metatables.api.backend.backends.registry.backend_for", lambda source:
|
|
39
|
+
SimpleNamespace(access=SimpleNamespace(initialize=lambda session: None)))
|
|
40
|
+
|
|
41
|
+
def inspect_registration():
|
|
42
|
+
with bootstrap.factory() as session:
|
|
43
|
+
row = session.scalar(select(DataSource).where(DataSource.is_runtime.is_(True)))
|
|
44
|
+
bootstrap.status = "ready" if row else "registration_required"
|
|
45
|
+
bootstrap.uid = row.uid if row else None
|
|
46
|
+
monkeypatch.setattr(bootstrap, "inspect", inspect_registration)
|
|
47
|
+
try:
|
|
48
|
+
bootstrap.migrate(user)
|
|
49
|
+
with bootstrap.factory() as session:
|
|
50
|
+
rows = session.scalars(select(DataSource)).all()
|
|
51
|
+
assert len(rows) == 1
|
|
52
|
+
assert rows[0].uid == uid
|
|
53
|
+
assert rows[0].is_runtime and rows[0].is_default
|
|
54
|
+
assert bootstrap.uid == uid
|
|
55
|
+
bootstrap.migrate(user)
|
|
56
|
+
with bootstrap.factory() as session:
|
|
57
|
+
assert session.scalar(select(DataSource.uid)) == uid
|
|
58
|
+
finally:
|
|
59
|
+
bootstrap.close()
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def test_legacy_runtime_alias_is_reconciled_without_losing_other_sources(tmp_path):
|
|
63
|
+
bootstrap = RuntimeBootstrap(RuntimeConfig(local_mode=False,
|
|
64
|
+
configuration_path=tmp_path / "configuration.yaml"), None)
|
|
65
|
+
alias, canonical, other = uuid4(), uuid4(), uuid4()
|
|
66
|
+
config = {"host": "db.example.invalid", "database_name": "tables", "database_user": "tables"}
|
|
67
|
+
bootstrap.candidate = dict(display_name="Hosted", class_type="postgresql", configuration=config)
|
|
68
|
+
bootstrap.selected_source_uid = str(alias)
|
|
69
|
+
from datetime import datetime, timezone
|
|
70
|
+
row = DataSource(uid=canonical, display_name="Hosted", class_type="postgresql", configuration=config,
|
|
71
|
+
status="AVAILABLE", storage_access_mode="read_write", created_at=datetime.now(timezone.utc))
|
|
72
|
+
path = bootstrap.saved_path.with_name("runtime-source-candidates.json")
|
|
73
|
+
path.parent.mkdir(parents=True)
|
|
74
|
+
original = {"uid": str(alias), **bootstrap.candidate}
|
|
75
|
+
untouched = {"uid": str(other), "display_name": "Another source"}
|
|
76
|
+
write_state(path, {str(alias): original, str(other): untouched})
|
|
77
|
+
reconcile_runtime(bootstrap, row, persist=False)
|
|
78
|
+
saved = read_registry(path)
|
|
79
|
+
assert set(saved) == {str(canonical), str(other)}
|
|
80
|
+
assert saved[str(other)] == untouched
|
|
81
|
+
assert saved[str(canonical)]["configuration"] == config
|
|
82
|
+
assert bootstrap.selected_source_uid == str(canonical)
|
|
83
|
+
restarted = RuntimeBootstrap(bootstrap.config, None)
|
|
84
|
+
assert restarted.initial_candidate() == bootstrap.candidate
|
|
85
|
+
assert restarted.selected_source_uid == str(canonical)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def test_setup_reports_the_database_privilege_error_without_sql_or_parameters(tmp_path, monkeypatch):
|
|
89
|
+
import pytest
|
|
90
|
+
from sqlalchemy.exc import ProgrammingError
|
|
91
|
+
|
|
92
|
+
from metatables.api.app.bootstrap import BootstrapError
|
|
93
|
+
|
|
94
|
+
bootstrap = RuntimeBootstrap(RuntimeConfig(local_mode=False,
|
|
95
|
+
configuration_path=tmp_path / "configuration.yaml"), None)
|
|
96
|
+
bootstrap.selected_source_uid = str(uuid4())
|
|
97
|
+
bootstrap.candidate = dict(display_name="Hosted", class_type="timescale_db", configuration={
|
|
98
|
+
"host": "db.example.invalid", "database_name": "tables", "database_user": "tables"})
|
|
99
|
+
engine = create_catalog_engine(f"sqlite:///{tmp_path / 'catalog.sqlite'}", allow_sqlite=True)
|
|
100
|
+
bootstrap.engine = engine
|
|
101
|
+
bootstrap.factory = sessionmaker(engine, expire_on_commit=False)
|
|
102
|
+
|
|
103
|
+
@dataclass
|
|
104
|
+
class Source:
|
|
105
|
+
uid: UUID | None = None
|
|
106
|
+
|
|
107
|
+
bootstrap.backend = SimpleNamespace(source=Source())
|
|
108
|
+
monkeypatch.setattr(bootstrap, "migration_lock", nullcontext)
|
|
109
|
+
monkeypatch.setattr(bootstrap, "inspect", lambda: setattr(bootstrap, "status", "registration_required"))
|
|
110
|
+
# A TimescaleDB runtime whose extension objects belong to the superuser.
|
|
111
|
+
denied = SimpleNamespace(pgcode="42501", diag=SimpleNamespace(message_primary="permission denied for table job_errors"))
|
|
112
|
+
|
|
113
|
+
def initialize(session):
|
|
114
|
+
raise ProgrammingError('REVOKE ALL ON ALL TABLES IN SCHEMA "_timescaledb_internal" FROM PUBLIC',
|
|
115
|
+
{"password": "do-not-show"}, denied)
|
|
116
|
+
|
|
117
|
+
monkeypatch.setattr("metatables.api.backend.backends.registry.backend_for", lambda source:
|
|
118
|
+
SimpleNamespace(access=SimpleNamespace(initialize=initialize)))
|
|
119
|
+
try:
|
|
120
|
+
with pytest.raises(BootstrapError) as raised:
|
|
121
|
+
bootstrap.migrate(uuid4())
|
|
122
|
+
finally:
|
|
123
|
+
bootstrap.close()
|
|
124
|
+
message = str(raised.value)
|
|
125
|
+
assert "permission denied for table job_errors" in message
|
|
126
|
+
assert "must own the runtime database" in message
|
|
127
|
+
assert "REVOKE" not in message and "do-not-show" not in message
|
|
128
|
+
assert bootstrap.error == message
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def _fresh_hosted_bootstrap(tmp_path, problems):
|
|
132
|
+
bootstrap = RuntimeBootstrap(RuntimeConfig(local_mode=False,
|
|
133
|
+
configuration_path=tmp_path / "configuration.yaml"), None)
|
|
134
|
+
bootstrap.candidate = dict(display_name="Development", class_type="timescale_db", configuration={
|
|
135
|
+
"host": "db.example.invalid", "database_name": "development", "database_user": "metatables_dev"})
|
|
136
|
+
engine = create_catalog_engine(f"sqlite:///{tmp_path / 'catalog.sqlite'}", allow_sqlite=True)
|
|
137
|
+
bootstrap.engine = engine
|
|
138
|
+
bootstrap.factory = sessionmaker(engine, expire_on_commit=False)
|
|
139
|
+
bootstrap.backend = SimpleNamespace(access=SimpleNamespace(prerequisite_problems=lambda connection: problems))
|
|
140
|
+
return bootstrap
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def test_unsecurable_database_is_blocked_before_any_setup_ddl(tmp_path, monkeypatch):
|
|
144
|
+
import pytest
|
|
145
|
+
|
|
146
|
+
from metatables.api.app.bootstrap import BootstrapError
|
|
147
|
+
|
|
148
|
+
bootstrap = _fresh_hosted_bootstrap(tmp_path, [
|
|
149
|
+
'metatables_dev does not own database "development"',
|
|
150
|
+
"312 object(s) are owned by postgres (extension timescaledb), e.g. schema _timescaledb_internal",
|
|
151
|
+
])
|
|
152
|
+
monkeypatch.setattr("metatables.api.app.bootstrap.upgrade_catalog",
|
|
153
|
+
lambda engine: pytest.fail("Setup DDL ran on a database the login cannot secure"))
|
|
154
|
+
try:
|
|
155
|
+
bootstrap.inspect()
|
|
156
|
+
assert bootstrap.status == "incompatible"
|
|
157
|
+
assert 'metatables_dev does not own database "development"' in bootstrap.error
|
|
158
|
+
assert "extension timescaledb" in bootstrap.error
|
|
159
|
+
with pytest.raises(BootstrapError, match="cannot secure this database"):
|
|
160
|
+
bootstrap.migrate(uuid4())
|
|
161
|
+
finally:
|
|
162
|
+
bootstrap.close()
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def test_securable_fresh_database_still_requires_migrations(tmp_path):
|
|
166
|
+
bootstrap = _fresh_hosted_bootstrap(tmp_path, [])
|
|
167
|
+
try:
|
|
168
|
+
bootstrap.inspect()
|
|
169
|
+
assert bootstrap.status == "migration_required" and bootstrap.error is None
|
|
170
|
+
finally:
|
|
171
|
+
bootstrap.close()
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def test_postgresql_prerequisites_name_only_what_metatables_needs():
|
|
175
|
+
from metatables.api.backend.backends.postgresql_access import PostgreSQLAccess
|
|
176
|
+
|
|
177
|
+
def check(row, schema="public"):
|
|
178
|
+
access = PostgreSQLAccess.__new__(PostgreSQLAccess)
|
|
179
|
+
access.source = SimpleNamespace(connection=SimpleNamespace(default_schema=schema))
|
|
180
|
+
connection = SimpleNamespace(execute=lambda *args: SimpleNamespace(one=lambda: row))
|
|
181
|
+
return access.prerequisite_problems(connection)
|
|
182
|
+
|
|
183
|
+
# A shared database qualifies: the login need not own it or anyone else's objects.
|
|
184
|
+
assert check(("metatables_dev", "development", True, True, None, True)) == []
|
|
185
|
+
assert check(("app", "shared", False, False, None, False), schema="tables") == [
|
|
186
|
+
"app lacks CREATEROLE",
|
|
187
|
+
'app cannot create schemas in database "shared"',
|
|
188
|
+
'app cannot create tables in schema "tables"',
|
|
189
|
+
]
|
|
190
|
+
assert check(("app", "shared", True, False, "someone", True)) == ['schema "metatables" belongs to someone']
|