matrx-orm 3.1.203__tar.gz → 3.1.205__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.
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/BASE_CLASS_METHODS.md +1 -1
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/CLAUDE.md +1 -1
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/PKG-INFO +2 -2
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/__init__.py +4 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/core/db_function.py +26 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/core/introspection.py +1 -1
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/core/session_context.py +32 -11
- matrx_orm-3.1.205/matrx_orm/matrx_orm_production_door.pth +3 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/operations/db_functions.py +8 -1
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/pytest_production_door.py +108 -33
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/schema_builder/excluded_targets.py +2 -2
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/schema_builder/schema_manager.py +1 -1
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/session/dag.py +1 -1
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/write_actor.py +4 -4
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/pyproject.toml +5 -2
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_association_trigger_actor_naming.py +7 -6
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_catalog.py +4 -4
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_rls_session_scope.py +1 -1
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_standalone_write_session_context.py +4 -4
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_transaction_database_identity.py +1 -1
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_transaction_gucs_one_round_trip.py +2 -2
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level2/verify_committed_row_guard_pg17.py +3 -3
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/schema/test_excluded_fk_targets.py +4 -4
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/session/test_dag.py +2 -2
- matrx_orm-3.1.205/tests/test_a_list_of_uuids_binds_as_uuid_array.py +42 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/.gitignore +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/AGENTS.md +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/CHANGELOG.md +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/MODEL_API.md +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/README.md +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/database/orm/extended/managers/ai_model_base.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/docs/migrations.md +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/FEATURE.md +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/MODULE_README.md +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/adapters/MODULE_README.md +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/adapters/__init__.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/adapters/async_postgresql.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/adapters/base_adapter.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/adapters/postgrest_client_adapter.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/adapters/supabase_adapter.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/admin/FEATURE.md +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/admin/__init__.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/admin/router.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/api/__init__.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/api/auth.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/api/config.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/api/handlers.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/api/protocol.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/api/server.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/associations.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/cache_debug.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/catalog.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/catalog_sql.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/client/MODULE_README.md +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/client/__init__.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/client/postgres_connection.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/client/postgrest.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/client/supabase_auth.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/client/supabase_config.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/client/supabase_manager.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/core/EXTENDED-TASK.md +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/core/FEATURE.md +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/core/MODULE_README.md +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/core/RELATIONS-TASKS.md +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/core/TASKS.md +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/core/__init__.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/core/admin_lane.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/core/async_db_manager.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/core/base.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/core/config.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/core/connections.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/core/diagnostics.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/core/expressions.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/core/extended.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/core/fields.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/core/listen.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/core/loop_filters.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/core/model_dto.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/core/model_view.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/core/one_database.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/core/paginator.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/core/pool_maintenance.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/core/pool_watch.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/core/pooler_role.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/core/pydantic_bridge.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/core/read_write.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/core/registry.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/core/relations.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/core/resilience.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/core/rls_session.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/core/signals.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/core/statement_ceiling.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/core/tls.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/core/transaction.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/core/types.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/core/write_queue.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/core/write_retry.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/entity.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/error_handling.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/exceptions.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/extended/__init__.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/extended/app_error_handler.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/identity_rule.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/local_sqlite.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/middleware/__init__.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/middleware/base.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/migrations/MODULE_README.md +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/migrations/__init__.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/migrations/cli.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/migrations/ddl.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/migrations/diff.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/migrations/executor.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/migrations/integration.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/migrations/loader.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/migrations/operations.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/migrations/state.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/migrations/table_filter.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/no_org_assignment.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/operations/FEATURE.md +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/operations/MODULE_README.md +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/operations/__init__.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/operations/bulk_update_values.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/operations/catalog_select.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/operations/conflict.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/operations/conflict_writes.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/operations/create.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/operations/delete.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/operations/dynamic_admin.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/operations/dynamic_crud.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/operations/graph_walk.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/operations/latest_rows.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/operations/queue_claim.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/operations/read.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/operations/relational_backfill.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/operations/staging_load.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/operations/update.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/platform_access.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/platform_db.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/py.typed +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/pytest_live_db_guard.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/pytest_lock_guard.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/pytest_plugin.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/python_sql/MODULE_README.md +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/python_sql/__init__.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/python_sql/db_objects.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/python_sql/table_detailed_relationships.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/python_sql/table_typescript_relationship.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/query/FEATURE.md +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/query/MODULE_README.md +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/query/__init__.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/query/builder.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/query/executor.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/resources/__init__.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/resources/additional-assigners-2026-09-12.json +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/resources/org_assignment_trigger_baseline.json +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/schema_builder/FEATURE.md +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/schema_builder/MODULE_README.md +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/schema_builder/__init__.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/schema_builder/code_handler.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/schema_builder/codegen_writer.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/schema_builder/columns.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/schema_builder/common.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/schema_builder/diff_preview.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/schema_builder/entity_capabilities.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/schema_builder/generator.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/schema_builder/helpers/__init__.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/schema_builder/helpers/base_generators.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/schema_builder/helpers/entity_generators.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/schema_builder/helpers/git_checker.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/schema_builder/package_wiring.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/schema_builder/relationships.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/schema_builder/runner.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/schema_builder/schema.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/schema_builder/tables.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/schema_builder/views.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/secrets_battery/FEATURE.md +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/secrets_battery/__init__.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/secrets_battery/attachments.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/secrets_battery/backup_restore.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/secrets_battery/crypto.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/secrets_battery/host_authorization.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/secrets_battery/items.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/secrets_battery/model.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/secrets_battery/mutation_receipts.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/secrets_battery/native_enable.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/secrets_battery/native_lifecycle.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/secrets_battery/native_passkeys.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/secrets_battery/native_source.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/secrets_battery/org_service.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/secrets_battery/org_vault_gate.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/secrets_battery/service.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/secrets_battery/vault_history.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/session/README.md +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/session/__init__.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/session/coalesce.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/session/errors.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/session/fallback.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/session/flush.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/session/lifecycle.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/session/managed.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/session/op.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/session/reads.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/session/session.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/session/telemetry.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/sql_executor/MODULE_README.md +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/sql_executor/__init__.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/sql_executor/executor.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/sql_executor/queries.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/sql_executor/registry.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/sql_executor/types.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/sql_executor/utils.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/state.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/utils/__init__.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/utils/sql_utils.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/matrx_orm/utils/type_converters.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/scripts/git-branches.sh +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/scripts/publish.sh +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/scripts/release.sh +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/MODULE_README.md +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/__init__.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/conftest.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/MODULE_README.md +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/__init__.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_adapter_registry.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_admin_db_columns.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_admin_response_rendering.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_admin_search_negation.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_admin_search_sentinels.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_advisory_lock.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_agent_message.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_api_auth.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_api_config.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_api_handlers.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_api_protocol.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_array_agg_order_by.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_associations.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_associations_wiring.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_bulk_hydration_offload.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_bulk_update_by_pk.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_bulk_upsert_increment_set_fields.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_cache_debug.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_call_function.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_case_when.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_column_scoped_writes.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_committed_row_guard.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_composite_pk_filter.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_composite_pk_write_paths.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_config.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_conflict_target.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_conflict_writes.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_connection_codecs.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_connection_poison_guard.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_connection_primitives.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_count_composite_distinct.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_count_limit_param_leak.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_db_function_field.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_ddl_generator.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_deadlock_retry.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_derived_table.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_dirty_tracking.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_dynamic_crud.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_exceptions.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_execute_query_integrity_mapping.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_expression_primitives.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_fields.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_filter_jsonb_agg_array_index.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_fk_cycle_resolution.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_from_alias.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_fts.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_func_expression_args.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_graph_walk.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_having_annotate_alias.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_insert_ignore_and_admin_primitives.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_introspect_rls_policies.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_join_and_aggregate.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_latest_rows.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_listen_notify.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_local_sqlite.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_lock_contention_retry.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_managed_write_guard.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_matrx_entity.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_migration_diff_types.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_migration_loader.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_model_instance.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_model_meta.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_optimistic_concurrency.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_order_by_expression.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_org_assignment_inventory.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_orm_banners_are_client_safe.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_parameter_name_hydration.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_patch_jsonb_path.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_percentile_cont.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_persisted_count.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_person_scoped_session_bypasses_shared_cache.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_pool_create_never_wedges.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_pool_holder_census.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_pool_loop_recreation.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_pool_maintenance.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_postgrest_filters.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_pydantic_bridge.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_query_builder.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_query_error_detail.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_query_executor_sql.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_query_timeout.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_querybuilder_clone_semantics.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_queue_claim.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_read_connection_retry.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_read_write_declared.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_registry.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_registry_multi_database.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_registry_multi_schema.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_relational_backfill.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_relations.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_resilience.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_schema_exists.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_session_advisory_lock.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_state_cache.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_subquery_filter_raw.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_subquery_in_filter.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_subquery_nested_expressions.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_supabase_auth.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_supabase_config.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_tls.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_transaction_pool_release_sends_nothing.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_typed_join_projection.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_typed_predicates.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_update_case_expression.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_update_subquery.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_update_with_rebase.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_upsert_default_leak.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_upsert_with_conflict.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_values_jsonb_decode.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_watched_lifecycle.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_write_queue_deadline.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_write_retry_semantics.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level2/MODULE_README.md +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level2/__init__.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level2/conftest.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level2/test_bulk_ops.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level2/test_cache_integration.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level2/test_crud.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level2/test_foreign_keys.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level2/test_m2m.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level2/test_manager.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level2/test_migrations_live.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level2/test_query_execution.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level2/test_schema_diff.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/live_pooler_role.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/native_source_cases.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/native_source_oracle.rs +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/sample_project/.env.example +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/sample_project/README.md +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/sample_project/__init__.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/sample_project/generate.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/sample_project/generated/.gitkeep +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/sample_project/matrx_orm.yaml +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/sample_project/test_schema_generation.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/sample_project_desktop/.env.example +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/sample_project_desktop/README.md +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/sample_project_desktop/__init__.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/sample_project_desktop/client_example.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/sample_project_desktop/client_example.ts +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/sample_project_desktop/client_supabase_example.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/sample_project_desktop/server.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/schema/entity_tests.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/schema/test_base_generation.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/schema/test_composite_pk_fk_generation.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/schema/test_composite_unique_generation.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/schema/test_dto_identity_generation.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/schema/test_entity_capability_generation.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/schema/test_enum_ordering.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/schema/test_generate_schema.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/schema/test_junction_analysis.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/schema/test_multi_schema_output.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/schema/test_schema_analysis.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/schema/test_sql_expression_defaults.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/schema/test_table_relationships_query.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/schema/test_view_generation.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/schema_builder/test_array_default_parsing.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/schema_builder/test_focused_generation_plan.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/schema_builder/test_host_models_aggregator.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/schema_builder/test_never_filter_columns.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/session/__init__.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/session/conftest.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/session/test_a_failed_capture_still_names_the_failure.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/session/test_cache_no_downgrade.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/session/test_capture_poison_proof.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/session/test_coalesce.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/session/test_disk_spill_fallback.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/session/test_flush_individual_fallback.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/session/test_governed_write.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/session/test_live_db_guard_plugin.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/session/test_managed.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/session/test_op_enqueue_site.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/session/test_reads.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/session/test_record_error_bounded_insert.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/session/test_record_error_storm.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/session/test_session.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/session/test_spill_guard_plugin.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/test_a_backend_handed_over_mid_transaction_never_takes_our_writes.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/test_a_read_only_backend_never_refuses_our_writes.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/test_array_arg_element_type.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/test_attachment_mutation_serialization.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/test_backup_alias_conflict.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/test_backup_history_restore.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/test_backup_restore_live.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/test_call_function_as.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/test_call_function_none_mode.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/test_composite_fk_is_not_a_column_fk.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/test_database_name_alias.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/test_db_functions.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/test_dsn_password_masking.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/test_dynamic_select_unregistered.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/test_immutable_insert_or_exact_verify.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/test_keyed_digest.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/test_model_cls_refactor.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/test_native_enable.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/test_native_lifecycle.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/test_native_lifecycle_fences.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/test_native_passkey_receipts.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/test_native_source.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/test_no_blocking_asyncpg_tls.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/test_one_database.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/test_order_by_desc_nulls_last.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/test_org_vault_gate.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/test_pooler_role_guard.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/test_secret_audit_names_its_organization.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/test_secrets_battery.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/test_secrets_minting_keys.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/test_shared_lock_guard.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/test_sql_param_casts.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/test_statement_ceiling_warning.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/test_sync_twins_guard.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/test_update_result_truthiness.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/test_user_visible_lookup.py +0 -0
- {matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/test_vault_aggregate_recovery_stateful.py +0 -0
|
@@ -92,7 +92,7 @@ returned count. Omit the parameter → exact legacy last-write-wins.
|
|
|
92
92
|
`MatrxEntity.update_with_rebase(id, expected_version=N, **fields)` additionally
|
|
93
93
|
auto-rebases past conflicts whose intervening writes were DISJOINT from the
|
|
94
94
|
written fields (proven from `history.row_versions` snapshots). Cross-repo
|
|
95
|
-
contract: `common-docs/systems/
|
|
95
|
+
contract: `common-docs/systems/architecture/optimistic-concurrency/FEATURE.md`.
|
|
96
96
|
|
|
97
97
|
### Atomic queue claims
|
|
98
98
|
|
|
@@ -25,7 +25,7 @@ Every table that becomes a Model needs row identity: a single-column primary key
|
|
|
25
25
|
- 🚨 **ONE CONNECTION, REQUIRED** (`matrx_orm.platform_db`): `register_platform_db(name, ...)` + `platform_connection_url()` — the ONLY sanctioned way any Matrx service or package turns env vars into a pool. One set of variables (`SUPABASE_MATRIX_*`), owned here; TLS is a deployment input (`SUPABASE_MATRIX_SSL`), never hardcoded; **RAISES** if it isn't given a database — no degraded mode. Every package's `bootstrap_db()` is one call to it. A package may absolutely run on its OWN Postgres — that is a change of VALUES, never a new variable name; what it may never have is a *second candidate* or a fallback chain. `register_database_from_env` is the low-level primitive underneath — **application and package code must not call it directly** (guard: `scripts/check_one_database.py`, blocking in release).
|
|
26
26
|
- **Core model layer**: `Model`, `BaseManager`, `BaseDTO`, `ModelView`, `model_registry`, plus 50+ field types (`CharField`, `IntegerField`, `ForeignKey`, `ManyToManyField`, `JSONField`, …).
|
|
27
27
|
- **`MatrxEntity`** (`matrx_orm/entity.py`) — **the platform entity contract as an ORM primitive.** A table registered in `platform.entity_types` generates as a `MatrxEntity` (everything else stays a plain `Model`) and gets the behavior instead of every consumer hand-wiring it: `await page.versions()` (not a query on `history.row_versions`), `await page.restore(3)` (not a `version_restore` RPC + a cache bust — `restore()` busts the cache itself), `Note.alive()` / `.dead()` / `.soft_delete()` / `.undelete()`, and `Page.capabilities()`. Capability flags (`_entity_token`, `_is_versioned`, `_has_soft_delete`, `_is_org_scoped`, `_rls_variant`) are **stamped by the generator from the LIVE DATABASE** (does the table really carry the `_version_capture` trigger? a `deleted_at` column?) — never from `entity_types`' own flags, which are wrong on 61 of its 242 rows. **The database is truth; a flag is a claim.** Using a capability an entity does not declare RAISES (`EntityCapabilityError`) and names the fix — it never returns silently-wrong data, and `is_deleted` refuses to answer from a column a partial fetch never loaded. Drift guard: `scripts/check_entity_drift.py` (loud, non-blocking, in `release.sh`).
|
|
28
|
-
- **Optimistic concurrency (`expected_version=`)** — opt-in compare-and-swap on the canonical `version` int column: `update_where` / `update_item` / instance `update()` take `expected_version=N` (`WHERE version = N`, `SET version = N + 1`; 0 rows → `DoesNotExist` if gone, `OptimisticLockError` if changed — carrying the **decision package**: `current_version`, `current_row`, `contested_fields` (current values of ONLY the fields this write attempted), and `changed_by`/`changed_at` from `history.row_versions` where versioned); `bulk_update(..., check_version=True)` guards per row; `MatrxEntity.update_with_rebase` auto-rebases past DISJOINT concurrent writes using `history.row_versions` snapshots. **Never automatic** — an unguarded write stays last-write-wins (the `_touch_row` trigger bumps `version` on every update, so auto-guarding would raise spurious conflicts). Contract: `common-docs/systems/
|
|
28
|
+
- **Optimistic concurrency (`expected_version=`)** — opt-in compare-and-swap on the canonical `version` int column: `update_where` / `update_item` / instance `update()` take `expected_version=N` (`WHERE version = N`, `SET version = N + 1`; 0 rows → `DoesNotExist` if gone, `OptimisticLockError` if changed — carrying the **decision package**: `current_version`, `current_row`, `contested_fields` (current values of ONLY the fields this write attempted), and `changed_by`/`changed_at` from `history.row_versions` where versioned); `bulk_update(..., check_version=True)` guards per row; `MatrxEntity.update_with_rebase` auto-rebases past DISJOINT concurrent writes using `history.row_versions` snapshots. **Never automatic** — an unguarded write stays last-write-wins (the `_touch_row` trigger bumps `version` on every update, so auto-guarding would raise spurious conflicts). Contract: `common-docs/systems/architecture/optimistic-concurrency/FEATURE.md`.
|
|
29
29
|
- **Change tracking** — every instance records which columns were **loaded** (a SELECTed row, or the caller's kwargs) and which were **changed**. `save()` writes only those, so `.only("id","email")` + `save()` can never NULL the columns it never fetched (it used to — silent data loss). Mutable containers (jsonb/arrays) are always rewritten when loaded, because `obj.metadata["k"]=1` cannot be seen by `__setattr__`; **reassigning** (`obj.metadata = {...}`) is tracked precisely and is the sanctioned way. `Model._hydrate()` is the ONE row→model entry point.
|
|
30
30
|
- **Upsert writes only what the caller supplied.** `upsert` / `bulk_upsert` / `upsert_with_conflict` materialize every field's Python default so the **INSERT** half is complete — but `ON CONFLICT DO UPDATE SET` covers **only the columns present in the caller's `data`** (an explicit `update_fields` still wins). Letting defaults reach the UPDATE half silently RESET every defaulted column on each re-upsert of an existing row: it once reset scraped-and-analyzed `research.rs_source` rows to `scrape_status='pending', server_attempts=0` (a watchdog then finalized them `'failed'`) and undid users' manual `is_included=false`. A caller-supplied `None` is excluded too — it is dropped from the INSERT, and `EXCLUDED.<col>` on an omitted column resolves to that column's **DEFAULT**. An upsert of only conflict keys means "ensure this row exists" and reconciles to a no-op self-assignment, never a raise (a bare `DO NOTHING` would break `RETURNING *`). Pinned by `tests/level1/test_upsert_default_leak.py`.
|
|
31
31
|
- **Read-only models** (`_read_only = True`): a model that can be queried but NEVER written through the ORM — every write path raises `ReadOnlyModelError` (enforced at `QueryExecutor.insert/bulk_insert/upsert/update/delete`, so it covers all of `create`/`save`/`update`/`delete`/`update_where`/`delete_where`/`upsert`/`bulk_*`). Used for DB-managed tables the app must not mutate. The canonical **`auth.users`** model is baked into the schema generator (`get_string_user_model`) as the full, curated Supabase auth schema (safe columns only — no password/token fields), `_read_only`, bound per-database. Read a user's profile/metadata there; mutate users only through Supabase Auth (GoTrue).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: matrx-orm
|
|
3
|
-
Version: 3.1.
|
|
3
|
+
Version: 3.1.205
|
|
4
4
|
Summary: Async-first PostgreSQL ORM with bidirectional migrations, schema introspection, many-to-many relationships, and built-in state caching
|
|
5
5
|
Project-URL: Homepage, https://github.com/AI-Matrix-Engine/aidream-current
|
|
6
6
|
Project-URL: Repository, https://github.com/AI-Matrix-Engine/aidream-current
|
|
@@ -26,7 +26,7 @@ Requires-Dist: asyncpg>=0.31.0
|
|
|
26
26
|
Requires-Dist: cbor2<7,>=6.1.3
|
|
27
27
|
Requires-Dist: cryptography>=43.0
|
|
28
28
|
Requires-Dist: gitpython>=3.1.40
|
|
29
|
-
Requires-Dist: matrx-utils>=2.0.
|
|
29
|
+
Requires-Dist: matrx-utils>=2.0.60
|
|
30
30
|
Requires-Dist: psycopg-pool>=3.2.5
|
|
31
31
|
Requires-Dist: psycopg[binary]>=3.2.5
|
|
32
32
|
Requires-Dist: python-dotenv>=1.0
|
|
@@ -263,10 +263,12 @@ from .core.session_context import (
|
|
|
263
263
|
ACTOR_AGENT_GUC,
|
|
264
264
|
ACTOR_SYSTEM_GUC,
|
|
265
265
|
ACTOR_TIER_GUC,
|
|
266
|
+
RETIRED_TIER_WORDS,
|
|
266
267
|
VALID_TIERS,
|
|
267
268
|
ActorRef,
|
|
268
269
|
ActorTier,
|
|
269
270
|
SessionContextProvider,
|
|
271
|
+
canonical_actor_tier,
|
|
270
272
|
configure_session_context,
|
|
271
273
|
current_actor,
|
|
272
274
|
current_tier,
|
|
@@ -703,9 +705,11 @@ __all__ = [
|
|
|
703
705
|
"ACTOR_AGENT_GUC",
|
|
704
706
|
"ACTOR_SYSTEM_GUC",
|
|
705
707
|
"ACTOR_TIER_GUC",
|
|
708
|
+
"RETIRED_TIER_WORDS",
|
|
706
709
|
"VALID_TIERS",
|
|
707
710
|
"ActorRef",
|
|
708
711
|
"ActorTier",
|
|
712
|
+
"canonical_actor_tier",
|
|
709
713
|
"configure_session_context",
|
|
710
714
|
"current_actor",
|
|
711
715
|
"current_tier",
|
|
@@ -150,6 +150,29 @@ class TypedArg:
|
|
|
150
150
|
return ".".join(f'"{seg}"' for seg in str(self.pg_type).split("."))
|
|
151
151
|
|
|
152
152
|
|
|
153
|
+
def uuid_list_kind(value: list[Any]) -> bool:
|
|
154
|
+
"""``True`` when ``value`` is a non-empty list of ``uuid.UUID`` — a ``uuid[]`` argument.
|
|
155
|
+
|
|
156
|
+
A list holding ONLY ``uuid.UUID`` objects can never be a jsonb argument (``json.dumps``
|
|
157
|
+
cannot encode one), so it can only mean a native ``uuid[]`` parameter; it binds as one,
|
|
158
|
+
with the explicit cast :class:`ArrayArg` would give it. A list MIXING ``uuid.UUID`` with
|
|
159
|
+
other values is ambiguous and is refused by name (VISION-REACH W2, 2026-10-02: the
|
|
160
|
+
records tool's roll-up raised ``Object of type UUID is not JSON serializable`` on every
|
|
161
|
+
call because a list of UUIDs was taken for jsonb).
|
|
162
|
+
"""
|
|
163
|
+
import uuid as _uuid
|
|
164
|
+
|
|
165
|
+
if not value or not any(isinstance(v, _uuid.UUID) for v in value):
|
|
166
|
+
return False
|
|
167
|
+
if all(isinstance(v, _uuid.UUID) for v in value):
|
|
168
|
+
return True
|
|
169
|
+
raise TypeError(
|
|
170
|
+
"call_function: a list mixes uuid.UUID objects with other values, so it is neither a "
|
|
171
|
+
"uuid[] nor a jsonb argument. Pass ArrayArg(values, \"uuid\") with every element a "
|
|
172
|
+
"UUID (or its text), or a list of plain JSON values for a jsonb parameter."
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
|
|
153
176
|
def _bind_args(args: tuple[Any, ...]) -> tuple[str, list[Any]]:
|
|
154
177
|
"""Build the ``$1, $2, ...`` placeholder list + bound values.
|
|
155
178
|
|
|
@@ -175,6 +198,9 @@ def _bind_args(args: tuple[Any, ...]) -> tuple[str, list[Any]]:
|
|
|
175
198
|
suffix = f"::{value.element_type}[]" if value.element_type else ""
|
|
176
199
|
placeholders.append(f"${idx}{suffix}")
|
|
177
200
|
bound.append(value.value)
|
|
201
|
+
elif isinstance(value, list) and uuid_list_kind(value):
|
|
202
|
+
placeholders.append(f"${idx}::uuid[]")
|
|
203
|
+
bound.append(value)
|
|
178
204
|
elif isinstance(value, dict | list):
|
|
179
205
|
placeholders.append(f"${idx}::jsonb")
|
|
180
206
|
bound.append(json.dumps(value))
|
|
@@ -64,7 +64,7 @@ async def introspect_rls_policies(
|
|
|
64
64
|
``schemas``/``relnames`` are PARALLEL arrays (zipped server-side via
|
|
65
65
|
``unnest``) — e.g. ``schemas=["public","workspace"],
|
|
66
66
|
relnames=["notes","tasks"]`` audits ``public.notes`` and
|
|
67
|
-
``
|
|
67
|
+
``projects.tasks``. Both are bound array parameters, never
|
|
68
68
|
string-interpolated. Returns one row per table that actually exists
|
|
69
69
|
(callers should treat a missing pair as "table not found").
|
|
70
70
|
|
|
@@ -9,7 +9,7 @@ reliably apply them is the moment this package opens a transaction.
|
|
|
9
9
|
The host declares a provider once at startup::
|
|
10
10
|
|
|
11
11
|
from matrx_orm import configure_session_context
|
|
12
|
-
configure_session_context(lambda: {"app.actor_tier": "
|
|
12
|
+
configure_session_context(lambda: {"app.actor_tier": "agent", "app.actor_system": "x"})
|
|
13
13
|
|
|
14
14
|
and from then on every TOP-LEVEL ``transaction()`` (which includes the one
|
|
15
15
|
``rls_session`` opens, before it drops to the restricted role) emits whatever
|
|
@@ -134,11 +134,11 @@ async def apply_session_context(connection: Any) -> None:
|
|
|
134
134
|
# The host (aidream) re-exports these names and wires ``declared_actor_gucs``
|
|
135
135
|
# into ``configure_session_context`` at startup.
|
|
136
136
|
#
|
|
137
|
-
#
|
|
138
|
-
#
|
|
137
|
+
# Tier words (DD-064): ``system`` (the platform's own machinery), ``agent`` (an
|
|
138
|
+
# agent's turn), ``user`` (a person's own action).
|
|
139
139
|
# ---------------------------------------------------------------------------
|
|
140
140
|
|
|
141
|
-
ActorTier = Literal["
|
|
141
|
+
ActorTier = Literal["system", "agent", "user"]
|
|
142
142
|
|
|
143
143
|
ACTOR_TIER_GUC = "app.actor_tier"
|
|
144
144
|
ACTOR_SYSTEM_GUC = "app.actor_system"
|
|
@@ -149,7 +149,24 @@ ACTOR_SYSTEM_GUC = "app.actor_system"
|
|
|
149
149
|
# control that saves a value nothing honours.
|
|
150
150
|
ACTOR_AGENT_GUC = "app.actor_agent"
|
|
151
151
|
|
|
152
|
-
VALID_TIERS: frozenset[str] = frozenset({"
|
|
152
|
+
VALID_TIERS: frozenset[str] = frozenset({"system", "agent", "user"})
|
|
153
|
+
|
|
154
|
+
#: DD-064 transition: the retired spellings and the word each became. The
|
|
155
|
+
#: database accepts both on every channel and answers in the new words, but
|
|
156
|
+
#: ``history.row_versions.actor_tier`` keeps the old words on rows written
|
|
157
|
+
#: before the rename, so every reader of a stored tier normalizes through
|
|
158
|
+
#: :func:`canonical_actor_tier` (mirrors ``platform.canonical_actor_tier``).
|
|
159
|
+
RETIRED_TIER_WORDS: Mapping[str, str] = {"code": "system", "ai": "agent", "human": "user"}
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def canonical_actor_tier(value: object) -> ActorTier | None:
|
|
163
|
+
"""The tier in the current words: old -> new, new passes through, else None."""
|
|
164
|
+
if not isinstance(value, str):
|
|
165
|
+
return None
|
|
166
|
+
word = value.strip().lower()
|
|
167
|
+
if word in VALID_TIERS:
|
|
168
|
+
return word # type: ignore[return-value]
|
|
169
|
+
return RETIRED_TIER_WORDS.get(word) # type: ignore[return-value]
|
|
153
170
|
|
|
154
171
|
|
|
155
172
|
@dataclass(frozen=True, slots=True)
|
|
@@ -160,7 +177,7 @@ class ActorRef:
|
|
|
160
177
|
system: str | None = None
|
|
161
178
|
#: The agent definition id, when the writer IS an agent (DD-211). A
|
|
162
179
|
#: person's write never carries one -- the database refuses the pair
|
|
163
|
-
#: ``tier="
|
|
180
|
+
#: ``tier="user"`` + an agent, because "a person did this" and "agent X
|
|
164
181
|
#: did this" are two different authors.
|
|
165
182
|
agent: str | None = None
|
|
166
183
|
|
|
@@ -198,7 +215,7 @@ def declared_actor_gucs() -> dict[str, str]:
|
|
|
198
215
|
|
|
199
216
|
This is the provider a host passes to :func:`configure_session_context`.
|
|
200
217
|
Empty when nothing declared an actor: the DB resolver then applies its
|
|
201
|
-
documented default (``
|
|
218
|
+
documented default (``user`` with an identity present, ``system`` without)
|
|
202
219
|
and logs that it did.
|
|
203
220
|
|
|
204
221
|
The WORKFLOW CHAIN rides the same provider (it is part of "who is writing":
|
|
@@ -278,20 +295,22 @@ def declaring_workflow_chain(chain: WorkflowChain | None) -> Iterator[WorkflowCh
|
|
|
278
295
|
|
|
279
296
|
|
|
280
297
|
def _validated_actor(tier: str, system: str, agent: str | None, *, caller: str) -> ActorRef:
|
|
281
|
-
|
|
298
|
+
canonical = canonical_actor_tier(tier)
|
|
299
|
+
if canonical is None:
|
|
282
300
|
raise ValueError(f"{caller} tier must be one of {sorted(VALID_TIERS)}; got {tier!r}")
|
|
301
|
+
tier = canonical
|
|
283
302
|
if not system or not system.strip():
|
|
284
303
|
raise ValueError(f"{caller} requires a non-empty system (who specifically)")
|
|
285
304
|
cleaned = (agent or "").strip() or None
|
|
286
305
|
if cleaned is not None:
|
|
287
|
-
if tier == "
|
|
306
|
+
if tier == "user":
|
|
288
307
|
# The database refuses this pair outright (23514). Refusing it here
|
|
289
308
|
# too means the door that made the mistake learns at the call site
|
|
290
309
|
# rather than on whichever write happens to land first.
|
|
291
310
|
raise ValueError(
|
|
292
|
-
f"{caller} was given tier='
|
|
311
|
+
f"{caller} was given tier='user' AND agent={cleaned!r}. A person's write carries "
|
|
293
312
|
"no agent: either the person is the author (drop the agent) or the agent is "
|
|
294
|
-
"(declare tier='
|
|
313
|
+
"(declare tier='agent' and the agent that is running)."
|
|
295
314
|
)
|
|
296
315
|
try:
|
|
297
316
|
UUID(cleaned)
|
|
@@ -357,11 +376,13 @@ __all__ = [
|
|
|
357
376
|
"ACTOR_AGENT_GUC",
|
|
358
377
|
"ACTOR_SYSTEM_GUC",
|
|
359
378
|
"ACTOR_TIER_GUC",
|
|
379
|
+
"RETIRED_TIER_WORDS",
|
|
360
380
|
"VALID_TIERS",
|
|
361
381
|
"ActorRef",
|
|
362
382
|
"ActorTier",
|
|
363
383
|
"SessionContextProvider",
|
|
364
384
|
"apply_session_context",
|
|
385
|
+
"canonical_actor_tier",
|
|
365
386
|
"emit_local_gucs",
|
|
366
387
|
"local_gucs_statement",
|
|
367
388
|
"configure_session_context",
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
# Installed by matrx-orm: in a process whose environment carries MATRX_PRODUCTION_DOOR=1 (exported by the
|
|
2
|
+
# pytest plugin matrx_orm.pytest_production_door), install the same production door before any user code.
|
|
3
|
+
import os; exec("import sys, importlib.util as u, importlib.machinery as m\ns = m.PathFinder.find_spec('matrx_orm')\nif s and s.submodule_search_locations and 'matrx_orm.pytest_production_door' not in sys.modules:\n sp = u.spec_from_file_location('matrx_orm.pytest_production_door', list(s.submodule_search_locations)[0] + '/pytest_production_door.py')\n mod = u.module_from_spec(sp); sys.modules[sp.name] = mod; sp.loader.exec_module(mod); mod.install()") if os.environ.get("MATRX_PRODUCTION_DOOR") == "1" else None
|
|
@@ -24,6 +24,8 @@ import json
|
|
|
24
24
|
import re
|
|
25
25
|
from typing import TYPE_CHECKING, Any
|
|
26
26
|
|
|
27
|
+
from matrx_orm.core.db_function import uuid_list_kind
|
|
28
|
+
|
|
27
29
|
if TYPE_CHECKING: # pragma: no cover
|
|
28
30
|
from matrx_orm.core.base import Model
|
|
29
31
|
|
|
@@ -38,7 +40,12 @@ def _render_call(function_name: str, args: tuple[Any, ...]) -> tuple[str, list[A
|
|
|
38
40
|
placeholders: list[str] = []
|
|
39
41
|
values: list[Any] = []
|
|
40
42
|
for index, arg in enumerate(args, start=1):
|
|
41
|
-
if isinstance(arg,
|
|
43
|
+
if isinstance(arg, list) and uuid_list_kind(arg):
|
|
44
|
+
# A list of uuid.UUID is a uuid[] argument; `default=str` below would have
|
|
45
|
+
# turned it into a jsonb array of text and missed the uuid[] overload.
|
|
46
|
+
placeholders.append(f"${index}::uuid[]")
|
|
47
|
+
values.append(arg)
|
|
48
|
+
elif isinstance(arg, dict | list):
|
|
42
49
|
placeholders.append(f"${index}::jsonb")
|
|
43
50
|
values.append(json.dumps(arg, ensure_ascii=False, default=str))
|
|
44
51
|
else:
|
|
@@ -21,9 +21,21 @@ and every ``psycopg_pool`` pool), ``psycopg2.connect``, ``asyncpg.connect`` / ``
|
|
|
21
21
|
plugin loads is patched on import.
|
|
22
22
|
|
|
23
23
|
THE ONE EXCEPTION: ``db.pooled_read.read_only("production")`` opens its connection inside
|
|
24
|
-
:func:`production_read_only
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
:func:`production_read_only`, and that connection is the least-privilege role ``matrx_reader`` (``pg_read_all_data``
|
|
25
|
+
only, no write grant) — the role is the door; the read-only transaction and statement check there are defence in
|
|
26
|
+
depth. Nothing else — no flag, no environment variable — opens this door. Designed doors the reader role leaves
|
|
27
|
+
open (accepted 2026-10-02): ``lo_create`` and the SECURITY DEFINER functions granted to PUBLIC, which write as
|
|
28
|
+
their owner once a session turns its read-only default off. Child Python processes carry it too: the plugin exports ``MATRX_PRODUCTION_DOOR=1`` and the
|
|
29
|
+
``matrx_orm_production_door.pth`` this package installs puts the same door in any process started with it.
|
|
30
|
+
``psql`` / ``pg_dump`` / ``pg_dumpall`` / ``pg_restore`` are matched anywhere in a command line (argv, ``bash -c`` /
|
|
31
|
+
``sh -c`` strings, ``shell=True``, ``os.system``) after ``$VAR`` / ``${VAR}`` are resolved against the child's environment.
|
|
32
|
+
|
|
33
|
+
The door stops ACCIDENTAL production writes by tests, not a determined insider. Residual, deliberately not closed:
|
|
34
|
+
- the public exception helpers and module flag (``production_read_only``, ``_READ_ONLY_PRODUCTION``) are a deliberate bypass;
|
|
35
|
+
- ``-p no:matrx_production_door`` or ``PYTEST_DISABLE_PLUGIN_AUTOLOAD`` from a package root (no root conftest) skips it;
|
|
36
|
+
- private driver internals (``pgconn``, ``asyncpg.connect_utils``) and ``importlib.reload`` of a driver bypass the wrappers;
|
|
37
|
+
- a host given only as an IP address or ``hostaddr`` (no ref, no production name) is not recognized;
|
|
38
|
+
- raw sockets, and a libpq service file or ``~/.pgpass`` host the environment does not name.
|
|
27
39
|
|
|
28
40
|
Guard: ``aidream/tests/test_tests_never_write_production.py`` (plants per bypass class).
|
|
29
41
|
"""
|
|
@@ -34,6 +46,7 @@ import contextvars
|
|
|
34
46
|
import importlib.abc
|
|
35
47
|
import importlib.util
|
|
36
48
|
import os
|
|
49
|
+
import re
|
|
37
50
|
import subprocess
|
|
38
51
|
import sys
|
|
39
52
|
from collections.abc import Iterator
|
|
@@ -55,7 +68,7 @@ class ProductionDoorRefusal(RuntimeError):
|
|
|
55
68
|
|
|
56
69
|
@contextmanager
|
|
57
70
|
def production_read_only() -> Iterator[None]:
|
|
58
|
-
"""
|
|
71
|
+
"""Admit ONE production connection: the read-only helper's, made as the least-privilege reader role."""
|
|
59
72
|
token = _READ_ONLY_PRODUCTION.set(True)
|
|
60
73
|
try:
|
|
61
74
|
yield
|
|
@@ -252,40 +265,75 @@ def _patch_orm_registry(mod: Any) -> None:
|
|
|
252
265
|
registry_cls.register = register
|
|
253
266
|
|
|
254
267
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
268
|
+
#: The libpq command-line clients that open a database connection.
|
|
269
|
+
_PG_CLIENT = re.compile(r"(?:^|[\s/;&|(`'\"])(psql|pg_dump|pg_dumpall|pg_restore)(?=$|[\s;&|)`'\"])")
|
|
270
|
+
_VAR = re.compile(r"\$\{(\w+)\}|\$(\w+)")
|
|
271
|
+
_FLAGS = {"-h": "host", "--host": "host", "-U": "user", "--username": "user", "-d": "dbname", "--dbname": "dbname"}
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def _command_text(args: Any) -> str:
|
|
275
|
+
"""The whole command line as one string: a shell string as given, an argv joined (so ``bash -c "…"`` shows)."""
|
|
276
|
+
if isinstance(args, bytes):
|
|
277
|
+
return args.decode(errors="replace")
|
|
278
|
+
if isinstance(args, str):
|
|
279
|
+
return args
|
|
280
|
+
try:
|
|
281
|
+
return " ".join(a.decode(errors="replace") if isinstance(a, bytes) else str(a) for a in args)
|
|
282
|
+
except TypeError:
|
|
283
|
+
return str(args)
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def check_command(what: str, args: Any, env: Any = None) -> None:
|
|
287
|
+
"""Refuse a psql / pg_dump / pg_dumpall / pg_restore command line whose target names production.
|
|
288
|
+
|
|
289
|
+
``$VAR`` and ``${VAR}`` are resolved against the environment the child will see, and the child's ``PG*``
|
|
290
|
+
variables count as libpq defaults, exactly as the client would read them.
|
|
291
|
+
"""
|
|
292
|
+
text = _command_text(args)
|
|
293
|
+
match = _PG_CLIENT.search(text)
|
|
294
|
+
if match is None:
|
|
295
|
+
return
|
|
296
|
+
child_env = {str(k): str(v) for k, v in (env if env is not None else os.environ).items()}
|
|
297
|
+
expanded = _VAR.sub(lambda m: child_env.get(m.group(1) or m.group(2), m.group(0)), text)
|
|
298
|
+
words = expanded.split()
|
|
299
|
+
kwargs: dict[str, str] = {}
|
|
300
|
+
for i, w in enumerate(words):
|
|
301
|
+
key, sep, value = w.partition("=")
|
|
302
|
+
if key in _FLAGS and sep:
|
|
303
|
+
kwargs[_FLAGS[key]] = value
|
|
304
|
+
elif w in _FLAGS and i + 1 < len(words):
|
|
305
|
+
kwargs[_FLAGS[w]] = words[i + 1]
|
|
306
|
+
saved = dict(os.environ)
|
|
307
|
+
try:
|
|
308
|
+
if env is not None: # the client sees the child's environment, not ours
|
|
309
|
+
os.environ.clear()
|
|
310
|
+
os.environ.update(child_env)
|
|
311
|
+
check(f"{match.group(1)} ({what})", expanded, **kwargs)
|
|
312
|
+
finally:
|
|
313
|
+
if env is not None:
|
|
314
|
+
os.environ.clear()
|
|
315
|
+
os.environ.update(saved)
|
|
262
316
|
|
|
263
317
|
|
|
264
318
|
def _patch_subprocess() -> None:
|
|
265
319
|
original = subprocess.Popen.__init__
|
|
266
|
-
if getattr(original, "_production_door", False):
|
|
267
|
-
return
|
|
320
|
+
if not getattr(original, "_production_door", False):
|
|
268
321
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
os.environ.update(saved)
|
|
285
|
-
original(self, args, *a, **kw)
|
|
286
|
-
|
|
287
|
-
__init__._production_door = True # type: ignore[attr-defined]
|
|
288
|
-
subprocess.Popen.__init__ = __init__ # type: ignore[method-assign]
|
|
322
|
+
def __init__(self: Any, args: Any, *a: Any, **kw: Any) -> None:
|
|
323
|
+
check_command("subprocess", args, kw.get("env"))
|
|
324
|
+
original(self, args, *a, **kw)
|
|
325
|
+
|
|
326
|
+
__init__._production_door = True # type: ignore[attr-defined]
|
|
327
|
+
subprocess.Popen.__init__ = __init__ # type: ignore[method-assign]
|
|
328
|
+
original_system = os.system
|
|
329
|
+
if not getattr(original_system, "_production_door", False):
|
|
330
|
+
|
|
331
|
+
def system(command: Any) -> int:
|
|
332
|
+
check_command("os.system", command)
|
|
333
|
+
return original_system(command)
|
|
334
|
+
|
|
335
|
+
system._production_door = True # type: ignore[attr-defined]
|
|
336
|
+
os.system = system
|
|
289
337
|
|
|
290
338
|
|
|
291
339
|
_PATCHERS = {
|
|
@@ -326,8 +374,33 @@ class _PatchOnImport(importlib.abc.MetaPathFinder, importlib.abc.Loader):
|
|
|
326
374
|
return spec
|
|
327
375
|
|
|
328
376
|
|
|
377
|
+
#: Exported into the environment so every child Python process installs the same door (see the .pth below).
|
|
378
|
+
CHILD_MARKER = "MATRX_PRODUCTION_DOOR"
|
|
379
|
+
_PTH_NAME = "matrx_orm_production_door.pth"
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
def ensure_child_hook() -> None:
|
|
383
|
+
"""Make sure the interpreter's site-packages carries the .pth that installs this door in a child process.
|
|
384
|
+
|
|
385
|
+
The matrx-orm wheel ships it (hatch force-include); an editable or stale install may not, so the plugin
|
|
386
|
+
writes the packaged copy beside the other .pth files on first use. Idempotent; a read-only site is left alone.
|
|
387
|
+
"""
|
|
388
|
+
import sysconfig
|
|
389
|
+
|
|
390
|
+
source = os.path.join(os.path.dirname(__file__), _PTH_NAME)
|
|
391
|
+
target = os.path.join(sysconfig.get_paths()["purelib"], _PTH_NAME)
|
|
392
|
+
try:
|
|
393
|
+
wanted = open(source, encoding="utf-8").read()
|
|
394
|
+
if not os.path.exists(target) or open(target, encoding="utf-8").read() != wanted:
|
|
395
|
+
with open(target, "w", encoding="utf-8") as fh:
|
|
396
|
+
fh.write(wanted)
|
|
397
|
+
except OSError:
|
|
398
|
+
pass
|
|
399
|
+
|
|
400
|
+
|
|
329
401
|
def install() -> None:
|
|
330
402
|
"""Wrap every driver already imported, and every one imported later. Idempotent."""
|
|
403
|
+
os.environ[CHILD_MARKER] = "1"
|
|
331
404
|
_patch_subprocess()
|
|
332
405
|
for name, patch in _PATCHERS.items():
|
|
333
406
|
if name in sys.modules:
|
|
@@ -346,8 +419,10 @@ def install() -> None:
|
|
|
346
419
|
|
|
347
420
|
|
|
348
421
|
def pytest_load_initial_conftests(early_config: Any, parser: Any, args: Any) -> None:
|
|
422
|
+
ensure_child_hook()
|
|
349
423
|
install()
|
|
350
424
|
|
|
351
425
|
|
|
352
426
|
def pytest_configure(config: Any) -> None:
|
|
427
|
+
ensure_child_hook()
|
|
353
428
|
install() # idempotent: covers a registration through the root conftest's fallback
|
|
@@ -11,8 +11,8 @@ The excluded set is qualified (``schema.table``) and spans every ``generate[]``
|
|
|
11
11
|
database, because the referencing table usually lives in another schema's run.
|
|
12
12
|
|
|
13
13
|
The same holds for a table in a schema the host never models: a foreign key follows its table's
|
|
14
|
-
oid, so once a retired table moves to ``
|
|
15
|
-
``to_schema='
|
|
14
|
+
oid, so once a retired table moves to ``deprecated`` every reference to it reads
|
|
15
|
+
``to_schema='deprecated'`` — a class the registry can never hold. A reference whose target schema
|
|
16
16
|
is neither generated nor one of the database's ``additional_schemas`` is dropped the same way.
|
|
17
17
|
"""
|
|
18
18
|
|
|
@@ -98,7 +98,7 @@ class SchemaManager:
|
|
|
98
98
|
#: ``schema.table`` names excluded from generation in ANY generate[] entry of this database:
|
|
99
99
|
#: a reference to one is dropped at load (``excluded_targets.drop_references_to``).
|
|
100
100
|
self._excluded_fk_targets: frozenset[str] = frozenset(excluded_fk_targets or ())
|
|
101
|
-
#: Schemas the host models; a reference into any other (``
|
|
101
|
+
#: Schemas the host models; a reference into any other (``deprecated``) is dropped the same way.
|
|
102
102
|
self._modeled_schemas: frozenset[str] | None = (
|
|
103
103
|
frozenset(modeled_schemas) if modeled_schemas is not None else None
|
|
104
104
|
)
|
|
@@ -136,7 +136,7 @@ def _resolve_fk_deps(
|
|
|
136
136
|
continue
|
|
137
137
|
# Resolving a bare-name FK reference can RAISE (ambiguous across
|
|
138
138
|
# databases, or a target whose model set was never imported — e.g. the
|
|
139
|
-
# `
|
|
139
|
+
# `deprecated` schema). Neither is a reason to fail a WRITE: an
|
|
140
140
|
# unresolvable target cannot be a parent row in THIS flush, so it
|
|
141
141
|
# contributes no ordering constraint. Letting the raise escape is what
|
|
142
142
|
# detonated the 2026-07-11 'Users' persistence outage — an ambiguous
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""ONE DOOR: a write to a table that refuses an unnamed actor names one here.
|
|
2
2
|
|
|
3
3
|
🚨 THE CLASS THIS CLOSES (2026-09-17). ``platform.associations`` refuses a write
|
|
4
|
-
that declares ``actor_tier=
|
|
4
|
+
that declares ``actor_tier=system`` and names no system — "an AI did it" with no
|
|
5
5
|
name is not provenance — and it reads that name from ``SET LOCAL`` GUCs, which
|
|
6
6
|
exist only inside a transaction that declared them.
|
|
7
7
|
|
|
@@ -58,7 +58,7 @@ __all__ = [
|
|
|
58
58
|
]
|
|
59
59
|
|
|
60
60
|
#: ``(schema, table)`` pairs whose database-side rule refuses a write that
|
|
61
|
-
#: declares ``actor_tier=
|
|
61
|
+
#: declares ``actor_tier=system`` and names no system. Membership is what makes
|
|
62
62
|
#: the seam speak; every other table's writes are untouched (no extra
|
|
63
63
|
#: transaction, no extra round trip).
|
|
64
64
|
ACTOR_REQUIRED_TABLES: frozenset[tuple[str, str]] = frozenset(
|
|
@@ -180,7 +180,7 @@ def writing_system() -> str:
|
|
|
180
180
|
"A write to an actor-required table could not name the code making it. "
|
|
181
181
|
f"The outermost frame the seam could read was {where}, and every frame above "
|
|
182
182
|
"it belongs to the ORM or to async plumbing. Declare the actor at the call "
|
|
183
|
-
"site — `async with actor_session('
|
|
183
|
+
"site — `async with actor_session('system', '<your system>'):` — rather than "
|
|
184
184
|
"letting an unnamed write reach the database, which would be refused there "
|
|
185
185
|
"anyway, at CASCADE time, nowhere near here."
|
|
186
186
|
)
|
|
@@ -225,7 +225,7 @@ async def actor_named_for_write(database: str) -> AsyncIterator[None]:
|
|
|
225
225
|
# the enclosing transaction — which is correct: the enclosing work had no
|
|
226
226
|
# actor either.
|
|
227
227
|
async with transaction(database=database):
|
|
228
|
-
with declaring_actor("
|
|
228
|
+
with declaring_actor("system", system) as actor:
|
|
229
229
|
await set_local_gucs(actor.as_gucs())
|
|
230
230
|
yield
|
|
231
231
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "matrx-orm"
|
|
3
|
-
version = "3.1.
|
|
3
|
+
version = "3.1.205"
|
|
4
4
|
description = "Async-first PostgreSQL ORM with bidirectional migrations, schema introspection, many-to-many relationships, and built-in state caching"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = { text = "MIT" }
|
|
@@ -41,7 +41,7 @@ dependencies = [
|
|
|
41
41
|
"gitpython>=3.1.40",
|
|
42
42
|
"psycopg[binary]>=3.2.5",
|
|
43
43
|
"psycopg-pool>=3.2.5",
|
|
44
|
-
"matrx-utils>=2.0.
|
|
44
|
+
"matrx-utils>=2.0.60",
|
|
45
45
|
"pyyaml>=6.0.3",
|
|
46
46
|
"python-dotenv>=1.0",
|
|
47
47
|
"cryptography>=43.0",
|
|
@@ -84,6 +84,9 @@ dev = [
|
|
|
84
84
|
packages = ["matrx_orm"]
|
|
85
85
|
include = ["matrx_orm/py.typed", "matrx_orm/resources/*.json"]
|
|
86
86
|
|
|
87
|
+
[tool.hatch.build.targets.wheel.force-include]
|
|
88
|
+
"matrx_orm/matrx_orm_production_door.pth" = "matrx_orm_production_door.pth"
|
|
89
|
+
|
|
87
90
|
[tool.hatch.metadata]
|
|
88
91
|
allow-direct-references = true
|
|
89
92
|
|
{matrx_orm-3.1.203 → matrx_orm-3.1.205}/tests/level1/test_association_trigger_actor_naming.py
RENAMED
|
@@ -33,6 +33,7 @@ from contextlib import asynccontextmanager
|
|
|
33
33
|
|
|
34
34
|
import pytest
|
|
35
35
|
from matrx_orm.core.session_context import (
|
|
36
|
+
canonical_actor_tier,
|
|
36
37
|
configure_session_context,
|
|
37
38
|
current_actor,
|
|
38
39
|
declared_actor_gucs,
|
|
@@ -44,11 +45,11 @@ from matrx_orm.write_actor import actor_named_for_write
|
|
|
44
45
|
def _would_the_database_refuse(gucs: dict[str, str]) -> bool:
|
|
45
46
|
"""``platform._stamp_actor_tier()``'s refusal clause (``wf_051``): an
|
|
46
47
|
unset or empty ``app.actor_system`` GUC is NULL to the database, and a
|
|
47
|
-
``
|
|
48
|
+
``system``/``agent`` tier with a NULL system is refused. A ``user`` write (or
|
|
48
49
|
one with a real name) is not."""
|
|
49
|
-
tier = gucs.get("app.actor_tier") or "
|
|
50
|
+
tier = canonical_actor_tier(gucs.get("app.actor_tier") or "") or "system"
|
|
50
51
|
system = (gucs.get("app.actor_system") or "").strip() or None
|
|
51
|
-
return tier in ("
|
|
52
|
+
return tier in ("agent", "system") and system is None
|
|
52
53
|
|
|
53
54
|
|
|
54
55
|
@pytest.fixture(autouse=True)
|
|
@@ -121,7 +122,7 @@ async def test_actor_named_for_write_names_the_actor_the_trigger_needs(monkeypat
|
|
|
121
122
|
seen_gucs = set_gucs
|
|
122
123
|
|
|
123
124
|
assert not _would_the_database_refuse(seen_gucs), seen_gucs
|
|
124
|
-
assert seen_gucs["app.actor_tier"] == "
|
|
125
|
+
assert seen_gucs["app.actor_tier"] == "system"
|
|
125
126
|
# The calling frame (this test module), never a hand-typed constant.
|
|
126
127
|
assert seen_gucs["app.actor_system"] == "level1.test_association_trigger_actor_naming"
|
|
127
128
|
assert current_actor() is None, "the declaration must not outlive the block"
|
|
@@ -145,9 +146,9 @@ async def test_actor_named_for_write_passes_through_an_existing_declaration(monk
|
|
|
145
146
|
|
|
146
147
|
monkeypatch.setattr(transaction_module, "transaction", fake_transaction)
|
|
147
148
|
|
|
148
|
-
async with declared_actor("
|
|
149
|
+
async with declared_actor("user", "user-42"):
|
|
149
150
|
async with actor_named_for_write("main"):
|
|
150
|
-
assert current_actor().tier == "
|
|
151
|
+
assert current_actor().tier == "user"
|
|
151
152
|
assert current_actor().system == "user-42"
|
|
152
153
|
|
|
153
154
|
assert opened is False, "a caller that already declared an actor is trusted as-is"
|
|
@@ -22,7 +22,7 @@ async def test_list_relations_binds_all_filters(query_spy):
|
|
|
22
22
|
"main",
|
|
23
23
|
schemas=["public"],
|
|
24
24
|
kinds=("BASE TABLE",),
|
|
25
|
-
exclude_schemas=["
|
|
25
|
+
exclude_schemas=["deprecated"],
|
|
26
26
|
) == []
|
|
27
27
|
database, sql, schemas, kinds, excluded = query_spy[0]
|
|
28
28
|
assert database == "main"
|
|
@@ -32,21 +32,21 @@ async def test_list_relations_binds_all_filters(query_spy):
|
|
|
32
32
|
assert "information_schema" not in sql
|
|
33
33
|
assert schemas == ["public"]
|
|
34
34
|
assert kinds == ["BASE TABLE"]
|
|
35
|
-
assert excluded == ["
|
|
35
|
+
assert excluded == ["deprecated"]
|
|
36
36
|
|
|
37
37
|
|
|
38
38
|
@pytest.mark.asyncio
|
|
39
39
|
async def test_relation_discovery_and_description_bind_values(query_spy):
|
|
40
40
|
await catalog.application_schemas("main", exclude_schemas=["pg_catalog"])
|
|
41
41
|
await catalog.relation_schemas(
|
|
42
|
-
"main", table="message", exclude_schemas=["
|
|
42
|
+
"main", table="message", exclude_schemas=["deprecated"]
|
|
43
43
|
)
|
|
44
44
|
await catalog.describe_relation_columns(
|
|
45
45
|
"main", schema="chat", table="message"
|
|
46
46
|
)
|
|
47
47
|
|
|
48
48
|
assert query_spy[0][2:] == (["pg_catalog"],)
|
|
49
|
-
assert query_spy[1][2:] == ("message", ["
|
|
49
|
+
assert query_spy[1][2:] == ("message", ["deprecated"])
|
|
50
50
|
assert query_spy[2][2:] == ("chat", "message")
|
|
51
51
|
# These decide EXISTENCE, so they must be privilege-independent.
|
|
52
52
|
assert "pg_namespace" in query_spy[0][1]
|
|
@@ -164,7 +164,7 @@ async def test_declared_context_and_rls_settings_share_one_round_trip_role_last(
|
|
|
164
164
|
monkeypatch: pytest.MonkeyPatch,
|
|
165
165
|
) -> None:
|
|
166
166
|
"""The host's declared context, the ceilings and the RLS settings: ONE statement."""
|
|
167
|
-
monkeypatch.setattr(session_context, "_provider", lambda: {"app.actor_tier": "
|
|
167
|
+
monkeypatch.setattr(session_context, "_provider", lambda: {"app.actor_tier": "user"})
|
|
168
168
|
monkeypatch.setattr(transaction_module, "_TRANSACTION_CEILINGS", {"lock_timeout": "5000"})
|
|
169
169
|
conn, _adapter = _install_adapter(monkeypatch)
|
|
170
170
|
|