matrx-orm 3.1.217__tar.gz → 3.1.218__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.217 → matrx_orm-3.1.218}/PKG-INFO +1 -1
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/core/async_db_manager.py +6 -1
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/core/db_function.py +20 -8
- matrx_orm-3.1.218/matrx_orm/core/foreign_session_state.py +112 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/core/transaction.py +5 -1
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/pyproject.toml +1 -1
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_rls_session_scope.py +8 -4
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_transaction_gucs_one_round_trip.py +7 -3
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/test_call_function_as.py +26 -10
- matrx_orm-3.1.218/tests/test_foreign_session_state.py +110 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/.gitignore +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/AGENTS.md +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/BASE_CLASS_METHODS.md +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/CHANGELOG.md +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/CLAUDE.md +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/MODEL_API.md +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/README.md +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/database/orm/extended/managers/ai_model_base.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/docs/migrations.md +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/FEATURE.md +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/MODULE_README.md +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/__init__.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/adapters/MODULE_README.md +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/adapters/__init__.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/adapters/async_postgresql.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/adapters/base_adapter.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/adapters/postgrest_client_adapter.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/adapters/supabase_adapter.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/admin/FEATURE.md +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/admin/__init__.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/admin/router.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/api/__init__.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/api/auth.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/api/config.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/api/handlers.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/api/protocol.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/api/server.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/associations.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/cache_debug.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/catalog.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/catalog_sql.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/client/MODULE_README.md +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/client/__init__.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/client/postgres_connection.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/client/postgrest.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/client/supabase_auth.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/client/supabase_config.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/client/supabase_manager.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/core/EXTENDED-TASK.md +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/core/FEATURE.md +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/core/MODULE_README.md +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/core/RELATIONS-TASKS.md +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/core/TASKS.md +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/core/__init__.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/core/admin_lane.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/core/base.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/core/config.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/core/connections.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/core/diagnostics.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/core/expressions.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/core/extended.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/core/fields.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/core/introspection.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/core/listen.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/core/loop_filters.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/core/model_dto.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/core/model_view.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/core/one_database.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/core/paginator.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/core/pool_maintenance.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/core/pool_watch.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/core/pooler_role.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/core/pydantic_bridge.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/core/read_write.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/core/registry.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/core/relations.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/core/resilience.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/core/rls_session.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/core/session_context.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/core/signals.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/core/statement_ceiling.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/core/tls.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/core/types.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/core/write_queue.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/core/write_retry.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/entity.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/error_handling.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/exceptions.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/extended/__init__.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/extended/app_error_handler.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/identity_rule.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/local_sqlite.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/middleware/__init__.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/middleware/base.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/migrations/MODULE_README.md +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/migrations/__init__.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/migrations/cli.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/migrations/ddl.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/migrations/diff.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/migrations/executor.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/migrations/integration.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/migrations/loader.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/migrations/operations.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/migrations/state.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/migrations/table_filter.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/no_org_assignment.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/operations/FEATURE.md +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/operations/MODULE_README.md +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/operations/__init__.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/operations/bulk_update_values.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/operations/catalog_select.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/operations/conflict.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/operations/conflict_writes.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/operations/create.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/operations/db_functions.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/operations/delete.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/operations/dynamic_admin.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/operations/dynamic_crud.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/operations/graph_walk.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/operations/latest_rows.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/operations/queue_claim.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/operations/read.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/operations/relational_backfill.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/operations/staging_load.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/operations/update.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/platform_access.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/platform_db.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/py.typed +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/pytest_lock_guard.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/pytest_plugin.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/python_sql/MODULE_README.md +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/python_sql/__init__.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/python_sql/db_objects.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/python_sql/table_detailed_relationships.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/python_sql/table_typescript_relationship.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/query/FEATURE.md +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/query/MODULE_README.md +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/query/__init__.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/query/builder.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/query/executor.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/resources/__init__.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/resources/additional-assigners-2026-09-12.json +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/resources/org_assignment_trigger_baseline.json +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/schema_builder/FEATURE.md +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/schema_builder/MODULE_README.md +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/schema_builder/__init__.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/schema_builder/code_handler.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/schema_builder/codegen_writer.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/schema_builder/columns.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/schema_builder/common.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/schema_builder/diff_preview.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/schema_builder/entity_capabilities.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/schema_builder/excluded_targets.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/schema_builder/generator.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/schema_builder/helpers/__init__.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/schema_builder/helpers/base_generators.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/schema_builder/helpers/entity_generators.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/schema_builder/helpers/git_checker.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/schema_builder/package_wiring.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/schema_builder/relationships.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/schema_builder/runner.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/schema_builder/schema.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/schema_builder/schema_manager.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/schema_builder/tables.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/schema_builder/views.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/secrets_battery/FEATURE.md +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/secrets_battery/__init__.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/secrets_battery/attachments.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/secrets_battery/backup_restore.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/secrets_battery/crypto.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/secrets_battery/host_authorization.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/secrets_battery/items.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/secrets_battery/model.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/secrets_battery/mutation_receipts.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/secrets_battery/native_enable.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/secrets_battery/native_lifecycle.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/secrets_battery/native_passkeys.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/secrets_battery/native_source.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/secrets_battery/org_service.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/secrets_battery/org_vault_gate.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/secrets_battery/service.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/secrets_battery/vault_history.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/session/README.md +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/session/__init__.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/session/coalesce.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/session/dag.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/session/errors.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/session/fallback.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/session/flush.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/session/lifecycle.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/session/managed.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/session/op.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/session/reads.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/session/session.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/session/telemetry.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/sql_executor/MODULE_README.md +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/sql_executor/__init__.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/sql_executor/executor.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/sql_executor/queries.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/sql_executor/registry.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/sql_executor/types.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/sql_executor/utils.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/state.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/utils/__init__.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/utils/sql_utils.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/utils/type_converters.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/matrx_orm/write_actor.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/scripts/git-branches.sh +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/scripts/publish.sh +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/scripts/release.sh +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/MODULE_README.md +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/__init__.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/conftest.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/MODULE_README.md +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/__init__.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_adapter_registry.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_admin_db_columns.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_admin_response_rendering.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_admin_search_negation.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_admin_search_sentinels.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_advisory_lock.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_agent_message.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_api_auth.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_api_config.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_api_handlers.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_api_protocol.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_array_agg_order_by.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_association_trigger_actor_naming.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_associations.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_associations_wiring.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_bulk_hydration_offload.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_bulk_update_by_pk.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_bulk_upsert_increment_set_fields.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_cache_debug.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_call_function.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_case_when.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_catalog.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_column_scoped_writes.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_committed_row_guard.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_composite_pk_filter.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_composite_pk_write_paths.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_config.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_conflict_target.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_conflict_writes.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_connection_codecs.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_connection_poison_guard.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_connection_primitives.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_count_composite_distinct.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_count_limit_param_leak.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_db_function_field.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_ddl_generator.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_deadlock_retry.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_derived_table.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_dirty_tracking.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_dynamic_crud.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_exceptions.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_execute_query_integrity_mapping.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_expression_primitives.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_fields.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_filter_jsonb_agg_array_index.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_fk_cycle_resolution.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_from_alias.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_fts.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_func_expression_args.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_graph_walk.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_having_annotate_alias.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_insert_ignore_and_admin_primitives.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_introspect_rls_policies.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_join_and_aggregate.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_latest_rows.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_listen_notify.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_local_sqlite.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_lock_contention_retry.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_managed_write_guard.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_matrx_entity.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_migration_diff_types.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_migration_loader.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_model_instance.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_model_meta.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_optimistic_concurrency.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_order_by_expression.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_org_assignment_inventory.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_orm_banners_are_client_safe.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_parameter_name_hydration.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_patch_jsonb_path.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_percentile_cont.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_persisted_count.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_person_scoped_session_bypasses_shared_cache.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_pool_create_never_wedges.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_pool_holder_census.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_pool_loop_recreation.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_pool_maintenance.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_postgrest_filters.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_pydantic_bridge.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_query_builder.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_query_error_detail.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_query_executor_sql.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_query_timeout.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_querybuilder_clone_semantics.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_queue_claim.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_read_connection_retry.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_read_write_declared.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_registry.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_registry_multi_database.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_registry_multi_schema.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_relational_backfill.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_relations.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_resilience.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_schema_exists.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_session_advisory_lock.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_standalone_write_session_context.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_state_cache.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_subquery_filter_raw.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_subquery_in_filter.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_subquery_nested_expressions.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_supabase_auth.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_supabase_config.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_tls.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_transaction_database_identity.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_transaction_pool_release_sends_nothing.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_typed_join_projection.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_typed_predicates.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_update_case_expression.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_update_subquery.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_update_with_rebase.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_upsert_default_leak.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_upsert_with_conflict.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_values_jsonb_decode.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_watched_lifecycle.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_write_queue_deadline.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_write_retry_semantics.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level2/MODULE_README.md +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level2/__init__.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level2/conftest.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level2/test_bulk_ops.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level2/test_cache_integration.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level2/test_crud.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level2/test_foreign_keys.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level2/test_m2m.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level2/test_manager.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level2/test_migrations_live.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level2/test_query_execution.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level2/test_schema_diff.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level2/verify_committed_row_guard_pg17.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/live_pooler_role.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/native_source_cases.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/native_source_oracle.rs +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/sample_project/.env.example +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/sample_project/README.md +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/sample_project/__init__.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/sample_project/generate.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/sample_project/generated/.gitkeep +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/sample_project/matrx_orm.yaml +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/sample_project/test_schema_generation.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/sample_project_desktop/.env.example +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/sample_project_desktop/README.md +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/sample_project_desktop/__init__.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/sample_project_desktop/client_example.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/sample_project_desktop/client_example.ts +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/sample_project_desktop/client_supabase_example.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/sample_project_desktop/server.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/schema/entity_tests.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/schema/test_base_generation.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/schema/test_composite_pk_fk_generation.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/schema/test_composite_unique_generation.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/schema/test_dto_identity_generation.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/schema/test_entity_capability_generation.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/schema/test_enum_ordering.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/schema/test_excluded_fk_targets.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/schema/test_generate_schema.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/schema/test_junction_analysis.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/schema/test_multi_schema_output.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/schema/test_schema_analysis.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/schema/test_sql_expression_defaults.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/schema/test_table_relationships_query.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/schema/test_view_generation.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/schema_builder/test_array_default_parsing.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/schema_builder/test_focused_generation_plan.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/schema_builder/test_host_models_aggregator.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/schema_builder/test_never_filter_columns.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/session/__init__.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/session/conftest.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/session/test_a_failed_capture_still_names_the_failure.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/session/test_cache_no_downgrade.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/session/test_capture_poison_proof.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/session/test_coalesce.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/session/test_dag.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/session/test_disk_spill_fallback.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/session/test_flush_individual_fallback.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/session/test_governed_write.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/session/test_managed.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/session/test_op_enqueue_site.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/session/test_reads.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/session/test_record_error_bounded_insert.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/session/test_record_error_storm.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/session/test_session.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/session/test_spill_guard_plugin.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/test_a_backend_handed_over_mid_transaction_never_takes_our_writes.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/test_a_list_of_uuids_binds_as_uuid_array.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/test_a_read_only_backend_never_refuses_our_writes.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/test_a_sealed_field_shows_no_character_of_its_value.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/test_array_arg_element_type.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/test_attachment_mutation_serialization.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/test_backup_alias_conflict.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/test_backup_history_restore.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/test_backup_restore_live.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/test_call_function_none_mode.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/test_composite_fk_is_not_a_column_fk.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/test_database_name_alias.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/test_db_functions.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/test_dsn_password_masking.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/test_dynamic_select_unregistered.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/test_immutable_insert_or_exact_verify.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/test_keyed_digest.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/test_model_cls_refactor.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/test_native_enable.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/test_native_lifecycle.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/test_native_lifecycle_fences.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/test_native_passkey_receipts.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/test_native_source.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/test_no_blocking_asyncpg_tls.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/test_one_database.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/test_order_by_desc_nulls_last.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/test_org_vault_gate.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/test_package_wiring_views.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/test_pooler_role_guard.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/test_secret_audit_names_its_organization.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/test_secrets_battery.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/test_secrets_minting_keys.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/test_shared_lock_guard.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/test_sql_param_casts.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/test_statement_ceiling_warning.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/test_sync_twins_guard.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/test_update_result_truthiness.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/test_user_visible_lookup.py +0 -0
- {matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/test_vault_aggregate_recovery_stateful.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: matrx-orm
|
|
3
|
-
Version: 3.1.
|
|
3
|
+
Version: 3.1.218
|
|
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
|
|
@@ -19,6 +19,7 @@ from matrx_orm.core.config import (
|
|
|
19
19
|
resolve_pool_mode,
|
|
20
20
|
)
|
|
21
21
|
from matrx_orm.error_handling import handle_orm_operation
|
|
22
|
+
from matrx_orm.core.foreign_session_state import run_clearing_foreign_restrict
|
|
22
23
|
from matrx_orm.core.pooler_role import (
|
|
23
24
|
VERIFY_EMPTY_READS,
|
|
24
25
|
assert_no_session_role_change,
|
|
@@ -1463,7 +1464,11 @@ class AsyncDatabaseManager:
|
|
|
1463
1464
|
in_transaction=False,
|
|
1464
1465
|
)
|
|
1465
1466
|
try:
|
|
1466
|
-
results = await
|
|
1467
|
+
results = await run_clearing_foreign_restrict(
|
|
1468
|
+
conn,
|
|
1469
|
+
lambda: conn.fetch(query, *args, timeout=driver_timeout),
|
|
1470
|
+
what=f"execute_query {query[:80]!r}",
|
|
1471
|
+
)
|
|
1467
1472
|
if read_attempt > 0:
|
|
1468
1473
|
logger.warning(
|
|
1469
1474
|
"[read-retry] SELECT HEALED on attempt %d after a "
|
|
@@ -69,6 +69,7 @@ from dataclasses import dataclass
|
|
|
69
69
|
from typing import Any, Literal
|
|
70
70
|
|
|
71
71
|
from matrx_orm.core.async_db_manager import AsyncDatabaseManager
|
|
72
|
+
from matrx_orm.core.foreign_session_state import run_clearing_foreign_restrict
|
|
72
73
|
from matrx_orm.core.statement_ceiling import DriverTimeout, resolve_driver_timeout
|
|
73
74
|
from matrx_orm.core.transaction import get_active_connection
|
|
74
75
|
from matrx_orm.core.write_retry import (
|
|
@@ -336,7 +337,11 @@ async def call_function(
|
|
|
336
337
|
|
|
337
338
|
async def _attempt() -> Any:
|
|
338
339
|
async with AsyncDatabaseManager.get_connection(database) as conn:
|
|
339
|
-
|
|
340
|
+
# Outside a transaction: a parse-time refusal caused by another pooler
|
|
341
|
+
# client's leaked restrict_nonsystem_relation_kind re-runs once, clearing it.
|
|
342
|
+
return await run_clearing_foreign_restrict(
|
|
343
|
+
conn, lambda: invoke(conn), what=f"call_function {schema}.{function}"
|
|
344
|
+
)
|
|
340
345
|
|
|
341
346
|
# An RPC reaches Postgres on a RAW connection, bypassing execute_query
|
|
342
347
|
# entirely, so it needs THE ONE loop explicitly — otherwise a deadlock inside
|
|
@@ -401,13 +406,20 @@ async def call_function_as(
|
|
|
401
406
|
operation=f"call_function_as {schema}.{function}",
|
|
402
407
|
in_transaction=False,
|
|
403
408
|
)
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
409
|
+
|
|
410
|
+
async def run() -> Any:
|
|
411
|
+
if mode == "none":
|
|
412
|
+
await conn.execute(sql, *bound_args, timeout=driver_timeout)
|
|
413
|
+
return None
|
|
414
|
+
if mode == "rows":
|
|
415
|
+
rows = await conn.fetch(sql, *bound_args, timeout=driver_timeout)
|
|
416
|
+
return [dict(r) for r in rows]
|
|
417
|
+
return await conn.fetchval(sql, *bound_args, timeout=driver_timeout)
|
|
418
|
+
|
|
419
|
+
# Same parse-time refusal class as call_function (foreign_session_state).
|
|
420
|
+
return await run_clearing_foreign_restrict(
|
|
421
|
+
conn, run, what=f"call_function_as {schema}.{function}"
|
|
422
|
+
)
|
|
411
423
|
|
|
412
424
|
return await run_with_lock_retry(_attempt, config_name=database, query=sql, args=bound_args)
|
|
413
425
|
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"""Session state another client left on a shared server connection.
|
|
2
|
+
|
|
3
|
+
WHY THIS EXISTS. We reach Postgres through Supavisor in TRANSACTION mode: every
|
|
4
|
+
client shares the same pool of server connections, and Supavisor does not reset
|
|
5
|
+
session state between clients. ``pg_dump`` (17.x) begins every run with a
|
|
6
|
+
SESSION-level ``SET restrict_nonsystem_relation_kind = 'view, foreign-table'``.
|
|
7
|
+
A dump pointed at the transaction pooler (port 6543) leaves that setting on
|
|
8
|
+
whichever server connection the ``SET`` landed on, and every later statement of
|
|
9
|
+
ours that touches a view through that connection is refused at parse time::
|
|
10
|
+
|
|
11
|
+
55000 ObjectNotInPrerequisiteStateError:
|
|
12
|
+
access to non-system view "organization_member" is restricted
|
|
13
|
+
|
|
14
|
+
Measured 2026-10-05 05:49-09:04 UTC: ~50 platform errors (SEO runs list, mandate
|
|
15
|
+
resolution, membership checks, file downloads), each pinned to a few poisoned
|
|
16
|
+
backends that kept failing for minutes.
|
|
17
|
+
|
|
18
|
+
THE DEFENCE (two halves, both here so they cannot drift apart):
|
|
19
|
+
|
|
20
|
+
1. Every ORM transaction clears the setting transaction-locally at BEGIN
|
|
21
|
+
(:data:`CLEARED_GUCS`, merged into ``transaction._opening_statement_gucs``).
|
|
22
|
+
It rides the one opening statement, so it costs nothing.
|
|
23
|
+
2. A statement OUTSIDE a transaction cannot carry a ``SET LOCAL`` in the same
|
|
24
|
+
round trip. When one is refused with exactly this parse-time error, it is
|
|
25
|
+
re-run ONCE inside a transaction that clears the setting first. The refusal
|
|
26
|
+
happens during parse/rewrite, before anything executes, so the re-run is
|
|
27
|
+
side-effect free. The cause is logged as a WARNING once per connection so it
|
|
28
|
+
never hides.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
from __future__ import annotations
|
|
32
|
+
|
|
33
|
+
import logging
|
|
34
|
+
import weakref
|
|
35
|
+
from typing import Any, Awaitable, Callable, TypeVar
|
|
36
|
+
|
|
37
|
+
logger = logging.getLogger(__name__)
|
|
38
|
+
|
|
39
|
+
RESTRICT_GUC = "restrict_nonsystem_relation_kind"
|
|
40
|
+
|
|
41
|
+
# Merged into every top-level transaction's opening GUCs. '' = no restriction
|
|
42
|
+
# (the server default); set_config(..., true) scopes it to the transaction.
|
|
43
|
+
CLEARED_GUCS: dict[str, str] = {RESTRICT_GUC: ""}
|
|
44
|
+
|
|
45
|
+
_OBJECT_NOT_IN_PREREQUISITE_STATE = "55000"
|
|
46
|
+
|
|
47
|
+
T = TypeVar("T")
|
|
48
|
+
|
|
49
|
+
_warned: "weakref.WeakSet[Any]" = weakref.WeakSet()
|
|
50
|
+
_warned_ids: set[int] = set()
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def is_foreign_restrict_refusal(exc: BaseException) -> bool:
|
|
54
|
+
"""True iff ``exc`` is the parse-time refusal caused by a leaked restrict GUC."""
|
|
55
|
+
if getattr(exc, "sqlstate", None) != _OBJECT_NOT_IN_PREREQUISITE_STATE:
|
|
56
|
+
return False
|
|
57
|
+
text = str(exc)
|
|
58
|
+
return "access to non-system" in text and "is restricted" in text
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _warn_once(conn: Any, exc: BaseException, what: str) -> None:
|
|
62
|
+
try:
|
|
63
|
+
if conn in _warned:
|
|
64
|
+
return
|
|
65
|
+
_warned.add(conn)
|
|
66
|
+
except TypeError: # not weak-referenceable
|
|
67
|
+
if id(conn) in _warned_ids:
|
|
68
|
+
return
|
|
69
|
+
_warned_ids.add(id(conn))
|
|
70
|
+
logger.warning(
|
|
71
|
+
"[foreign-session-state] %s was refused because the shared server connection "
|
|
72
|
+
"carries %s from another client (a pg_dump through the transaction pooler sets it "
|
|
73
|
+
"session-wide and Supavisor does not reset it): %s — re-running once in a "
|
|
74
|
+
"transaction that clears it",
|
|
75
|
+
what,
|
|
76
|
+
RESTRICT_GUC,
|
|
77
|
+
exc,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
async def clear_in_transaction(conn: Any) -> None:
|
|
82
|
+
"""Clear the restrict GUC for the current transaction on ``conn``."""
|
|
83
|
+
await conn.execute("SELECT set_config($1, '', true)", RESTRICT_GUC)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
async def run_clearing_foreign_restrict(
|
|
87
|
+
conn: Any, run: Callable[[], Awaitable[T]], *, what: str = "statement"
|
|
88
|
+
) -> T:
|
|
89
|
+
"""Run ``run()`` (one statement on ``conn``, outside any transaction).
|
|
90
|
+
|
|
91
|
+
On the leaked-restrict refusal, run it once more inside a transaction on the
|
|
92
|
+
same client connection that clears the setting first. Any other error, or a
|
|
93
|
+
second refusal, propagates unchanged.
|
|
94
|
+
"""
|
|
95
|
+
try:
|
|
96
|
+
return await run()
|
|
97
|
+
except Exception as exc:
|
|
98
|
+
if not is_foreign_restrict_refusal(exc):
|
|
99
|
+
raise
|
|
100
|
+
_warn_once(conn, exc, what)
|
|
101
|
+
async with conn.transaction():
|
|
102
|
+
await clear_in_transaction(conn)
|
|
103
|
+
return await run()
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
__all__ = [
|
|
107
|
+
"CLEARED_GUCS",
|
|
108
|
+
"RESTRICT_GUC",
|
|
109
|
+
"clear_in_transaction",
|
|
110
|
+
"is_foreign_restrict_refusal",
|
|
111
|
+
"run_clearing_foreign_restrict",
|
|
112
|
+
]
|
|
@@ -460,9 +460,13 @@ def _opening_statement_gucs(opening_gucs: dict[str, str]) -> dict[str, str]:
|
|
|
460
460
|
(a later name wins, exactly as sequential ``set_config`` calls did). ``role``
|
|
461
461
|
is always LAST: everything before it is set while still the privileged role.
|
|
462
462
|
"""
|
|
463
|
+
from matrx_orm.core.foreign_session_state import CLEARED_GUCS
|
|
463
464
|
from matrx_orm.core.session_context import session_context_gucs
|
|
464
465
|
|
|
465
|
-
|
|
466
|
+
# CLEARED_GUCS first: session state another pooler client may have left on this
|
|
467
|
+
# server connection (pg_dump's restrict_nonsystem_relation_kind) is cleared for
|
|
468
|
+
# every transaction; anything the opener declares still wins.
|
|
469
|
+
merged = {**CLEARED_GUCS, **_TRANSACTION_CEILINGS, **session_context_gucs(), **opening_gucs}
|
|
466
470
|
if "role" in merged:
|
|
467
471
|
merged["role"] = merged.pop("role")
|
|
468
472
|
return merged
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "matrx-orm"
|
|
3
|
-
version = "3.1.
|
|
3
|
+
version = "3.1.218"
|
|
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" }
|
|
@@ -77,10 +77,11 @@ async def test_rls_session_commits_transaction_local_role(monkeypatch: pytest.Mo
|
|
|
77
77
|
assert [sql for sql, _args in conn.calls] == [
|
|
78
78
|
"BEGIN",
|
|
79
79
|
"SELECT set_config($1, $2, true), set_config($3, $4, true), "
|
|
80
|
-
"set_config($5, $6, true), set_config($7, $8, true)",
|
|
80
|
+
"set_config($5, $6, true), set_config($7, $8, true), set_config($9, $10, true)",
|
|
81
81
|
"COMMIT",
|
|
82
82
|
]
|
|
83
83
|
assert conn.calls[1][1] == (
|
|
84
|
+
"restrict_nonsystem_relation_kind", "",
|
|
84
85
|
"request.jwt.claims", '{"sub":"user-id","role":"authenticated"}',
|
|
85
86
|
"statement_timeout", "30000",
|
|
86
87
|
"idle_in_transaction_session_timeout", "30000",
|
|
@@ -116,10 +117,12 @@ async def test_lock_timeout_is_transaction_local_and_precedes_the_role(
|
|
|
116
117
|
assert [sql for sql, _args in conn.calls] == [
|
|
117
118
|
"BEGIN",
|
|
118
119
|
"SELECT set_config($1, $2, true), set_config($3, $4, true), "
|
|
119
|
-
"set_config($5, $6, true), set_config($7, $8, true), set_config($9, $10, true)"
|
|
120
|
+
"set_config($5, $6, true), set_config($7, $8, true), set_config($9, $10, true), "
|
|
121
|
+
"set_config($11, $12, true)",
|
|
120
122
|
"COMMIT",
|
|
121
123
|
]
|
|
122
124
|
assert conn.calls[1][1] == (
|
|
125
|
+
"restrict_nonsystem_relation_kind", "",
|
|
123
126
|
"request.jwt.claims", '{"role":"matrx_provisioner"}',
|
|
124
127
|
"statement_timeout", "180000",
|
|
125
128
|
"lock_timeout", "55000",
|
|
@@ -174,8 +177,9 @@ async def test_declared_context_and_rls_settings_share_one_round_trip_role_last(
|
|
|
174
177
|
assert [sql for sql, _ in conn.calls][0::2] == ["BEGIN", "COMMIT"]
|
|
175
178
|
assert len(conn.calls) == 3
|
|
176
179
|
names = conn.calls[1][1][0::2]
|
|
177
|
-
assert names[0] == "
|
|
178
|
-
assert names[1] == "
|
|
180
|
+
assert names[0] == "restrict_nonsystem_relation_kind" # foreign session state cleared first
|
|
181
|
+
assert names[1] == "lock_timeout"
|
|
182
|
+
assert names[2] == "app.actor_tier"
|
|
179
183
|
assert names.index("request.jwt.claims") < names.index("role")
|
|
180
184
|
assert names[-1] == "role"
|
|
181
185
|
|
{matrx_orm-3.1.217 → matrx_orm-3.1.218}/tests/level1/test_transaction_gucs_one_round_trip.py
RENAMED
|
@@ -66,8 +66,9 @@ async def test_begin_sets_ceilings_and_declared_context_in_one_round_trip(monkey
|
|
|
66
66
|
gucs = [call for call in conn.calls if "set_config" in call[0]]
|
|
67
67
|
assert len(gucs) == 1, statements
|
|
68
68
|
sql, args = gucs[0]
|
|
69
|
-
assert sql.count("set_config(") ==
|
|
69
|
+
assert sql.count("set_config(") == 5
|
|
70
70
|
assert args == (
|
|
71
|
+
"restrict_nonsystem_relation_kind", "",
|
|
71
72
|
"lock_timeout", "5000",
|
|
72
73
|
"app.actor_tier", "agent",
|
|
73
74
|
"app.actor_system", "flashcards",
|
|
@@ -76,14 +77,17 @@ async def test_begin_sets_ceilings_and_declared_context_in_one_round_trip(monkey
|
|
|
76
77
|
|
|
77
78
|
|
|
78
79
|
@pytest.mark.asyncio
|
|
79
|
-
async def
|
|
80
|
+
async def test_nothing_declared_emits_only_the_foreign_state_clear(monkeypatch) -> None:
|
|
80
81
|
conn = _Connection()
|
|
81
82
|
monkeypatch.setattr(AdapterRegistry, "get", lambda _db: _Adapter(conn))
|
|
82
83
|
monkeypatch.setattr(transaction_module, "_TRANSACTION_CEILINGS", {})
|
|
83
84
|
configure_session_context(None)
|
|
84
85
|
async with transaction_module.transaction("quiet"):
|
|
85
86
|
pass
|
|
86
|
-
|
|
87
|
+
# The one GUC every transaction carries: another pooler client's leaked
|
|
88
|
+
# restrict_nonsystem_relation_kind is cleared (matrx_orm.core.foreign_session_state).
|
|
89
|
+
assert [sql for sql, _ in conn.calls] == ["BEGIN", "SELECT set_config($1, $2, true)", "COMMIT"]
|
|
90
|
+
assert conn.calls[1][1] == ("restrict_nonsystem_relation_kind", "")
|
|
87
91
|
|
|
88
92
|
|
|
89
93
|
def test_a_bad_guc_name_is_refused_before_it_reaches_sql() -> None:
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
The kernel's set form (``iam.accessible_entity_ids``) reads the person from
|
|
4
4
|
``auth.uid()``. The primitive must (1) never ride the caller's ambient transaction,
|
|
5
|
-
(2) set the claims transaction-local BEFORE the call,
|
|
6
|
-
transaction, and (3) refuse an empty person instead of returning the kernel's
|
|
5
|
+
(2) set the claims transaction-local BEFORE the call, in the same single statement
|
|
6
|
+
(its own transaction), and (3) refuse an empty person instead of returning the kernel's
|
|
7
7
|
empty answer as if it were real.
|
|
8
8
|
"""
|
|
9
9
|
|
|
@@ -52,7 +52,14 @@ def log(monkeypatch):
|
|
|
52
52
|
return calls
|
|
53
53
|
|
|
54
54
|
|
|
55
|
-
async def
|
|
55
|
+
async def test_claims_are_set_transaction_local_in_the_same_single_statement(log):
|
|
56
|
+
"""ONE round trip: a MATERIALIZED CTE sets the claims, the call is correlated to it.
|
|
57
|
+
|
|
58
|
+
The single statement is its own transaction, so the claims end with it. No
|
|
59
|
+
BEGIN/COMMIT and no separate set_config trip (four trips before 2026-09-27). The
|
|
60
|
+
restrict_nonsystem_relation_kind clear (foreign_session_state) is NOT sent on a
|
|
61
|
+
healthy connection; it only rides a re-run after the parse-time refusal.
|
|
62
|
+
"""
|
|
56
63
|
out = await db_function.call_function_as(
|
|
57
64
|
"11111111-1111-4111-8111-111111111111",
|
|
58
65
|
"db",
|
|
@@ -62,21 +69,30 @@ async def test_claims_are_set_inside_one_transaction_before_the_call(log):
|
|
|
62
69
|
db_function.TypedArg("viewer", "public.permission_level"),
|
|
63
70
|
)
|
|
64
71
|
assert out == ["a", "b"]
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
72
|
+
assert [c[0] for c in log] == ["acquire", "fetchval"]
|
|
73
|
+
_, sql, args = log[1]
|
|
74
|
+
assert sql == (
|
|
75
|
+
"WITH _claims AS MATERIALIZED (SELECT set_config('request.jwt.claims', $1, true) AS c) "
|
|
76
|
+
'SELECT (SELECT "iam"."accessible_entity_ids"($2, $3::"public"."permission_level") '
|
|
77
|
+
"WHERE _claims.c IS NOT NULL) FROM _claims"
|
|
78
|
+
)
|
|
79
|
+
assert json.loads(args[0]) == {
|
|
70
80
|
"sub": "11111111-1111-4111-8111-111111111111",
|
|
71
81
|
"role": "authenticated",
|
|
72
82
|
}
|
|
73
|
-
assert
|
|
83
|
+
assert args[1:] == ("processed_document", "viewer")
|
|
84
|
+
assert "restrict_nonsystem_relation_kind" not in sql
|
|
74
85
|
|
|
75
86
|
|
|
76
87
|
async def test_rows_mode_selects_from_the_function(log):
|
|
77
88
|
rows = await db_function.call_function_as("u", "db", "platform", "search_items", "q", mode="rows")
|
|
78
89
|
assert rows == [{"id": 1}]
|
|
79
|
-
assert
|
|
90
|
+
assert [c[0] for c in log] == ["acquire", "fetch"]
|
|
91
|
+
assert log[1][1] == (
|
|
92
|
+
"WITH _claims AS MATERIALIZED (SELECT set_config('request.jwt.claims', $1, true) AS c) "
|
|
93
|
+
'SELECT f.* FROM _claims, LATERAL (SELECT * FROM "platform"."search_items"($2) '
|
|
94
|
+
"WHERE _claims.c IS NOT NULL) f"
|
|
95
|
+
)
|
|
80
96
|
|
|
81
97
|
|
|
82
98
|
async def test_an_empty_person_is_refused_not_answered_empty(log):
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"""A pooled server connection poisoned by another client's restrict GUC never fails our reads.
|
|
2
|
+
|
|
3
|
+
pg_dump through the transaction pooler leaves ``restrict_nonsystem_relation_kind = 'view'``
|
|
4
|
+
on a shared server connection; every view read through it is refused at parse time (55000).
|
|
5
|
+
This fake connection behaves exactly like that backend: it refuses a view read unless the
|
|
6
|
+
current transaction cleared the setting. Live proof: scripts/verify_foreign_restrict_guc_live.py.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import asyncio
|
|
12
|
+
from contextlib import asynccontextmanager
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
import asyncpg
|
|
16
|
+
import pytest
|
|
17
|
+
|
|
18
|
+
from matrx_orm.core import foreign_session_state as fss
|
|
19
|
+
from matrx_orm.core.transaction import _opening_statement_gucs
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _refusal() -> Exception:
|
|
23
|
+
exc = asyncpg.exceptions.ObjectNotInPrerequisiteStateError(
|
|
24
|
+
'access to non-system view "organization_member" is restricted'
|
|
25
|
+
)
|
|
26
|
+
exc.sqlstate = "55000"
|
|
27
|
+
return exc
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class PoisonedConnection:
|
|
31
|
+
"""Session carries restrict=view; a transaction may clear it locally."""
|
|
32
|
+
|
|
33
|
+
def __init__(self) -> None:
|
|
34
|
+
self.session_restrict = "view"
|
|
35
|
+
self.local_restrict: str | None = None
|
|
36
|
+
self.in_tx = False
|
|
37
|
+
self.executed: list[str] = []
|
|
38
|
+
self.view_reads = 0
|
|
39
|
+
|
|
40
|
+
@asynccontextmanager
|
|
41
|
+
async def transaction(self):
|
|
42
|
+
self.in_tx = True
|
|
43
|
+
try:
|
|
44
|
+
yield
|
|
45
|
+
finally:
|
|
46
|
+
self.in_tx = False
|
|
47
|
+
self.local_restrict = None
|
|
48
|
+
|
|
49
|
+
async def execute(self, sql: str, *args: Any) -> str:
|
|
50
|
+
self.executed.append(sql)
|
|
51
|
+
if "set_config" in sql and args and args[0] == fss.RESTRICT_GUC:
|
|
52
|
+
assert self.in_tx, "the clear must be transaction-local"
|
|
53
|
+
self.local_restrict = ""
|
|
54
|
+
return "SELECT 1"
|
|
55
|
+
|
|
56
|
+
async def fetchval(self, sql: str, *args: Any) -> Any:
|
|
57
|
+
effective = self.local_restrict if self.local_restrict is not None else self.session_restrict
|
|
58
|
+
if "view" in effective:
|
|
59
|
+
raise _refusal()
|
|
60
|
+
self.view_reads += 1
|
|
61
|
+
return 42
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def test_every_transaction_clears_the_restrict_guc_first() -> None:
|
|
65
|
+
gucs = _opening_statement_gucs({})
|
|
66
|
+
assert gucs[fss.RESTRICT_GUC] == ""
|
|
67
|
+
# An opener can still declare its own value, and role stays last.
|
|
68
|
+
assert _opening_statement_gucs({fss.RESTRICT_GUC: "view"})[fss.RESTRICT_GUC] == "view"
|
|
69
|
+
assert list(_opening_statement_gucs({"role": "authenticated"}))[-1] == "role"
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def test_statement_outside_a_transaction_heals_on_a_poisoned_connection(caplog) -> None:
|
|
73
|
+
conn = PoisonedConnection()
|
|
74
|
+
with caplog.at_level("WARNING"):
|
|
75
|
+
value = asyncio.run(
|
|
76
|
+
fss.run_clearing_foreign_restrict(conn, lambda: conn.fetchval("select v"), what="t")
|
|
77
|
+
)
|
|
78
|
+
assert value == 42
|
|
79
|
+
assert conn.view_reads == 1
|
|
80
|
+
assert conn.session_restrict == "view" # session never touched — only the transaction
|
|
81
|
+
assert any(fss.RESTRICT_GUC in r.getMessage() for r in caplog.records)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def test_warning_is_once_per_connection(caplog) -> None:
|
|
85
|
+
conn = PoisonedConnection()
|
|
86
|
+
with caplog.at_level("WARNING"):
|
|
87
|
+
for _ in range(3):
|
|
88
|
+
asyncio.run(fss.run_clearing_foreign_restrict(conn, lambda: conn.fetchval("x")))
|
|
89
|
+
assert sum(fss.RESTRICT_GUC in r.getMessage() for r in caplog.records) == 1
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def test_other_55000_errors_are_not_rerun() -> None:
|
|
93
|
+
conn = PoisonedConnection()
|
|
94
|
+
calls = 0
|
|
95
|
+
|
|
96
|
+
async def run() -> Any:
|
|
97
|
+
nonlocal calls
|
|
98
|
+
calls += 1
|
|
99
|
+
exc = asyncpg.exceptions.ObjectNotInPrerequisiteStateError("currval is not yet defined")
|
|
100
|
+
exc.sqlstate = "55000"
|
|
101
|
+
raise exc
|
|
102
|
+
|
|
103
|
+
with pytest.raises(asyncpg.exceptions.ObjectNotInPrerequisiteStateError):
|
|
104
|
+
asyncio.run(fss.run_clearing_foreign_restrict(conn, run))
|
|
105
|
+
assert calls == 1
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def test_classifier() -> None:
|
|
109
|
+
assert fss.is_foreign_restrict_refusal(_refusal())
|
|
110
|
+
assert not fss.is_foreign_restrict_refusal(RuntimeError("access to non-system view x is restricted"))
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|