matrx-orm 3.1.244__tar.gz → 3.1.246__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.244 → matrx_orm-3.1.246}/PKG-INFO +2 -2
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/associations.py +6 -1
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/core/transaction.py +26 -2
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/entity.py +2 -1
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/platform_access.py +1 -1
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/secrets_battery/items.py +3 -1
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/secrets_battery/org_service.py +3 -5
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/secrets_battery/service.py +3 -2
- matrx_orm-3.1.246/matrx_orm/secrets_battery/use_stamp.py +27 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/session/fallback.py +72 -2
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/pyproject.toml +2 -2
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_call_function.py +4 -4
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_db_function_field.py +2 -2
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_deadlock_retry.py +2 -2
- matrx_orm-3.1.246/tests/level1/test_registry_reads_outside_the_person_session.py +49 -0
- matrx_orm-3.1.246/tests/session/test_record_error_group_open.py +152 -0
- matrx_orm-3.1.246/tests/test_a_failed_use_stamp_never_fails_the_read.py +48 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/.gitignore +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/AGENTS.md +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/BASE_CLASS_METHODS.md +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/CHANGELOG.md +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/CLAUDE.md +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/MODEL_API.md +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/README.md +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/database/orm/extended/managers/ai_model_base.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/docs/migrations.md +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/FEATURE.md +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/MODULE_README.md +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/__init__.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/adapters/MODULE_README.md +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/adapters/__init__.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/adapters/async_postgresql.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/adapters/base_adapter.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/adapters/postgrest_client_adapter.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/adapters/supabase_adapter.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/admin/FEATURE.md +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/admin/__init__.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/admin/router.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/api/__init__.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/api/auth.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/api/config.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/api/handlers.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/api/protocol.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/api/server.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/cache_debug.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/catalog.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/catalog_sql.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/client/MODULE_README.md +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/client/__init__.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/client/postgres_connection.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/client/postgrest.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/client/supabase_auth.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/client/supabase_config.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/client/supabase_manager.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/core/EXTENDED-TASK.md +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/core/FEATURE.md +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/core/MODULE_README.md +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/core/RELATIONS-TASKS.md +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/core/TASKS.md +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/core/__init__.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/core/admin_lane.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/core/async_db_manager.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/core/base.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/core/config.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/core/connections.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/core/db_function.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/core/diagnostics.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/core/expressions.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/core/extended.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/core/fields.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/core/foreign_session_state.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/core/introspection.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/core/listen.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/core/loop_filters.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/core/model_dto.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/core/model_view.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/core/one_database.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/core/paginator.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/core/pool_maintenance.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/core/pool_watch.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/core/pooler_role.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/core/pydantic_bridge.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/core/read_write.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/core/registry.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/core/relations.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/core/resilience.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/core/rls_session.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/core/session_context.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/core/signals.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/core/statement_ceiling.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/core/tls.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/core/types.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/core/write_queue.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/core/write_retry.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/error_handling.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/exceptions.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/extended/__init__.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/extended/app_error_handler.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/identity_rule.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/local_sqlite.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/middleware/__init__.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/middleware/base.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/migrations/MODULE_README.md +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/migrations/__init__.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/migrations/cli.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/migrations/ddl.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/migrations/diff.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/migrations/executor.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/migrations/integration.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/migrations/loader.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/migrations/operations.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/migrations/state.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/migrations/table_filter.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/no_org_assignment.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/operations/FEATURE.md +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/operations/MODULE_README.md +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/operations/__init__.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/operations/bulk_update_values.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/operations/catalog_select.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/operations/conflict.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/operations/conflict_writes.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/operations/create.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/operations/db_functions.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/operations/delete.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/operations/dynamic_admin.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/operations/dynamic_crud.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/operations/graph_walk.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/operations/latest_rows.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/operations/queue_claim.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/operations/read.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/operations/relational_backfill.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/operations/staging_load.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/operations/update.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/platform_db.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/py.typed +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/pytest_lock_guard.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/pytest_plugin.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/python_sql/MODULE_README.md +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/python_sql/__init__.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/python_sql/db_objects.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/python_sql/table_detailed_relationships.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/python_sql/table_typescript_relationship.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/query/FEATURE.md +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/query/MODULE_README.md +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/query/__init__.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/query/builder.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/query/executor.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/resources/__init__.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/resources/additional-assigners-2026-09-12.json +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/resources/org_assignment_trigger_baseline.json +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/schema_builder/FEATURE.md +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/schema_builder/MODULE_README.md +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/schema_builder/__init__.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/schema_builder/code_handler.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/schema_builder/codegen_writer.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/schema_builder/columns.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/schema_builder/common.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/schema_builder/diff_preview.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/schema_builder/entity_capabilities.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/schema_builder/excluded_targets.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/schema_builder/generator.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/schema_builder/helpers/__init__.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/schema_builder/helpers/base_generators.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/schema_builder/helpers/entity_generators.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/schema_builder/helpers/git_checker.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/schema_builder/package_wiring.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/schema_builder/relationships.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/schema_builder/runner.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/schema_builder/schema.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/schema_builder/schema_manager.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/schema_builder/tables.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/schema_builder/views.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/secrets_battery/FEATURE.md +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/secrets_battery/__init__.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/secrets_battery/attachments.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/secrets_battery/backup_restore.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/secrets_battery/crypto.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/secrets_battery/host_authorization.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/secrets_battery/model.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/secrets_battery/mutation_receipts.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/secrets_battery/native_enable.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/secrets_battery/native_lifecycle.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/secrets_battery/native_passkeys.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/secrets_battery/native_source.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/secrets_battery/org_vault_gate.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/secrets_battery/vault_history.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/session/README.md +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/session/__init__.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/session/coalesce.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/session/dag.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/session/errors.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/session/flush.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/session/lifecycle.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/session/managed.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/session/op.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/session/reads.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/session/session.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/session/telemetry.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/sql_executor/MODULE_README.md +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/sql_executor/__init__.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/sql_executor/executor.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/sql_executor/queries.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/sql_executor/registry.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/sql_executor/types.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/sql_executor/utils.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/state.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/utils/__init__.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/utils/sql_utils.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/utils/type_converters.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/matrx_orm/write_actor.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/scripts/git-branches.sh +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/scripts/publish.sh +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/scripts/release.sh +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/MODULE_README.md +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/__init__.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/conftest.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/MODULE_README.md +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/__init__.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_adapter_registry.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_admin_db_columns.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_admin_response_rendering.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_admin_search_negation.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_admin_search_sentinels.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_advisory_lock.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_agent_message.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_api_auth.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_api_config.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_api_handlers.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_api_protocol.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_array_agg_order_by.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_association_trigger_actor_naming.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_associations.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_associations_wiring.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_borrowed_role_never_reaches_server_calls.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_bulk_hydration_offload.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_bulk_update_by_pk.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_bulk_upsert_increment_set_fields.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_cache_debug.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_case_when.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_catalog.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_column_scoped_writes.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_committed_row_guard.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_composite_pk_filter.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_composite_pk_write_paths.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_config.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_conflict_target.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_conflict_writes.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_connection_codecs.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_connection_poison_guard.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_connection_primitives.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_count_composite_distinct.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_count_limit_param_leak.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_ddl_generator.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_derived_table.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_dirty_tracking.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_dynamic_crud.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_exceptions.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_execute_query_integrity_mapping.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_expression_primitives.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_fetch_on_init_holds_whole_table.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_fields.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_filter_jsonb_agg_array_index.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_fk_cycle_resolution.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_from_alias.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_fts.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_func_expression_args.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_graph_walk.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_guarded_read_never_reruns_empty.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_having_annotate_alias.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_insert_ignore_and_admin_primitives.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_introspect_rls_policies.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_join_and_aggregate.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_latest_rows.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_listen_notify.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_local_sqlite.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_lock_contention_retry.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_managed_write_guard.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_matrx_entity.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_migration_diff_types.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_migration_loader.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_model_instance.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_model_meta.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_optimistic_concurrency.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_order_by_expression.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_org_assignment_inventory.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_orm_banners_are_client_safe.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_parameter_name_hydration.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_patch_jsonb_path.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_percentile_cont.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_persisted_count.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_person_scoped_session_bypasses_shared_cache.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_pool_create_never_wedges.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_pool_holder_census.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_pool_loop_recreation.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_pool_maintenance.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_postgrest_filters.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_pydantic_bridge.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_query_builder.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_query_error_detail.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_query_executor_sql.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_query_timeout.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_querybuilder_clone_semantics.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_queue_claim.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_read_connection_retry.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_read_write_declared.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_registry.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_registry_multi_database.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_registry_multi_schema.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_relational_backfill.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_relations.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_resilience.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_rls_session_scope.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_schema_exists.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_session_advisory_lock.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_standalone_write_session_context.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_state_cache.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_subquery_filter_raw.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_subquery_in_filter.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_subquery_nested_expressions.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_supabase_auth.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_supabase_config.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_sync_pool_declares_read_write.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_tls.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_transaction_database_identity.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_transaction_gucs_one_round_trip.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_transaction_pool_release_sends_nothing.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_typed_join_projection.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_typed_predicates.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_update_case_expression.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_update_subquery.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_update_with_rebase.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_upsert_default_leak.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_upsert_with_conflict.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_values_jsonb_decode.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_watched_lifecycle.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_write_queue_deadline.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level1/test_write_retry_semantics.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level2/MODULE_README.md +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level2/__init__.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level2/conftest.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level2/test_bulk_ops.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level2/test_cache_integration.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level2/test_crud.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level2/test_foreign_keys.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level2/test_m2m.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level2/test_manager.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level2/test_migrations_live.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level2/test_query_execution.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level2/test_schema_diff.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/level2/verify_committed_row_guard_pg17.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/live_pooler_role.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/native_source_cases.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/native_source_oracle.rs +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/sample_project/.env.example +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/sample_project/README.md +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/sample_project/__init__.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/sample_project/generate.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/sample_project/generated/.gitkeep +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/sample_project/matrx_orm.yaml +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/sample_project/test_schema_generation.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/sample_project_desktop/.env.example +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/sample_project_desktop/README.md +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/sample_project_desktop/__init__.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/sample_project_desktop/client_example.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/sample_project_desktop/client_example.ts +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/sample_project_desktop/client_supabase_example.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/sample_project_desktop/server.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/schema/entity_tests.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/schema/test_base_generation.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/schema/test_composite_pk_fk_generation.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/schema/test_composite_unique_generation.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/schema/test_dto_identity_generation.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/schema/test_entity_capability_generation.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/schema/test_enum_ordering.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/schema/test_excluded_fk_targets.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/schema/test_generate_schema.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/schema/test_junction_analysis.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/schema/test_multi_schema_output.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/schema/test_schema_analysis.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/schema/test_sql_expression_defaults.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/schema/test_table_relationships_query.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/schema/test_view_generation.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/schema_builder/test_array_default_parsing.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/schema_builder/test_enum_array_column.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/schema_builder/test_focused_generation_plan.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/schema_builder/test_host_models_aggregator.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/schema_builder/test_never_filter_columns.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/session/__init__.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/session/conftest.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/session/test_a_failed_capture_still_names_the_failure.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/session/test_a_grouped_update_writes_only_each_rows_own_fields.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/session/test_cache_no_downgrade.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/session/test_capture_poison_proof.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/session/test_coalesce.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/session/test_dag.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/session/test_disk_spill_fallback.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/session/test_flush_individual_fallback.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/session/test_governed_write.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/session/test_managed.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/session/test_op_enqueue_site.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/session/test_pending_insert_honors_operator_filters.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/session/test_reads.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/session/test_record_error_bounded_insert.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/session/test_record_error_storm.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/session/test_session.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/session/test_spill_guard_plugin.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/session/test_tests_never_write_live_system_error.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/test_a_backend_handed_over_mid_transaction_never_takes_our_writes.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/test_a_bulk_cache_never_stalls_the_loop.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/test_a_keyword_column_refuses_generation.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/test_a_list_of_uuids_binds_as_uuid_array.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/test_a_read_only_backend_never_refuses_our_writes.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/test_a_sealed_field_shows_no_character_of_its_value.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/test_array_arg_element_type.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/test_attachment_mutation_serialization.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/test_backup_alias_conflict.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/test_backup_history_restore.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/test_backup_restore_live.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/test_call_function_as.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/test_call_function_none_mode.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/test_composite_fk_is_not_a_column_fk.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/test_database_name_alias.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/test_db_functions.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/test_dsn_password_masking.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/test_dynamic_select_unregistered.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/test_first_asks_the_database_for_one_row.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/test_foreign_session_state.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/test_immutable_insert_or_exact_verify.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/test_instance_update_runs_expressions.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/test_keyed_digest.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/test_model_cls_refactor.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/test_native_enable.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/test_native_lifecycle.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/test_native_lifecycle_fences.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/test_native_passkey_receipts.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/test_native_source.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/test_no_blocking_asyncpg_tls.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/test_one_database.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/test_order_by_desc_nulls_last.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/test_org_vault_gate.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/test_package_wiring_views.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/test_pooler_role_guard.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/test_secret_audit_names_its_organization.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/test_secrets_battery.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/test_secrets_minting_keys.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/test_shared_lock_guard.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/test_sql_param_casts.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/test_state_cache_sweeps_stale.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/test_statement_ceiling_warning.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/test_sync_twins_guard.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/test_update_result_truthiness.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/tests/test_user_visible_lookup.py +0 -0
- {matrx_orm-3.1.244 → matrx_orm-3.1.246}/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.246
|
|
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.84
|
|
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
|
|
@@ -71,6 +71,8 @@ from collections.abc import Awaitable, Callable
|
|
|
71
71
|
|
|
72
72
|
from matrx_utils import vcprint
|
|
73
73
|
|
|
74
|
+
from matrx_orm.core.transaction import outside_transaction
|
|
75
|
+
|
|
74
76
|
if TYPE_CHECKING:
|
|
75
77
|
from matrx_orm.core.base import Model
|
|
76
78
|
|
|
@@ -209,7 +211,10 @@ class EntityRegistry:
|
|
|
209
211
|
return self._cache[token]
|
|
210
212
|
model: type[Model] | None = None
|
|
211
213
|
if self._etype_model is not None:
|
|
212
|
-
|
|
214
|
+
# Vocabulary, never person data: read off the caller's session, or a person's RLS
|
|
215
|
+
# hides the row and the miss below is cached for every later caller.
|
|
216
|
+
etype_model = self._etype_model
|
|
217
|
+
row = await outside_transaction(lambda: etype_model.get_or_none(**{self._token_field: token}))
|
|
213
218
|
active = self._active_field is None or getattr(row, self._active_field, True)
|
|
214
219
|
if row is not None and active:
|
|
215
220
|
schema = getattr(row, self._schema_field, None)
|
|
@@ -36,16 +36,19 @@ reuses the connection when present — this behavior is unchanged.
|
|
|
36
36
|
from __future__ import annotations
|
|
37
37
|
|
|
38
38
|
import asyncio
|
|
39
|
+
import contextvars
|
|
39
40
|
import os
|
|
40
41
|
import re
|
|
41
42
|
import uuid
|
|
42
|
-
from collections.abc import AsyncIterator
|
|
43
|
+
from collections.abc import AsyncIterator, Awaitable, Callable
|
|
43
44
|
from contextlib import asynccontextmanager
|
|
44
45
|
from contextvars import ContextVar, Token
|
|
45
|
-
from typing import Any
|
|
46
|
+
from typing import Any, TypeVar
|
|
46
47
|
|
|
47
48
|
from matrx_orm.exceptions import DatabaseError
|
|
48
49
|
|
|
50
|
+
_T = TypeVar("_T")
|
|
51
|
+
|
|
49
52
|
# ContextVar holding the active connection (if any) for the current task tree.
|
|
50
53
|
_active_connection: ContextVar[Any | None] = ContextVar("_active_connection", default=None)
|
|
51
54
|
# The canonical registered database that owns ``_active_connection``. A raw
|
|
@@ -69,6 +72,27 @@ def in_person_scoped_session() -> bool:
|
|
|
69
72
|
_savepoint_depth: ContextVar[int] = ContextVar("_savepoint_depth", default=0)
|
|
70
73
|
|
|
71
74
|
|
|
75
|
+
async def outside_transaction(fn: Callable[[], Awaitable[_T]]) -> _T:
|
|
76
|
+
"""Await ``fn()`` on the process's own connection, outside the caller's transaction.
|
|
77
|
+
|
|
78
|
+
For process-wide VOCABULARY reads (the entity-type registry) whose answer must not
|
|
79
|
+
depend on who is acting: inside ``rls_session`` a person's RLS may hide the row, and a
|
|
80
|
+
cached answer outlives the person. The read runs as its own task with every transaction
|
|
81
|
+
ContextVar at its default, so it never touches (or ends) the caller's pinned connection,
|
|
82
|
+
and the caller's session is unchanged when it returns. Never use it for person data.
|
|
83
|
+
"""
|
|
84
|
+
ctx = contextvars.copy_context()
|
|
85
|
+
|
|
86
|
+
def _clear() -> None:
|
|
87
|
+
_active_connection.set(None)
|
|
88
|
+
_active_database.set(None)
|
|
89
|
+
_person_scoped.set(False)
|
|
90
|
+
_savepoint_depth.set(0)
|
|
91
|
+
|
|
92
|
+
ctx.run(_clear)
|
|
93
|
+
return await asyncio.get_running_loop().create_task(fn(), context=ctx)
|
|
94
|
+
|
|
95
|
+
|
|
72
96
|
def get_active_connection() -> Any | None:
|
|
73
97
|
"""Return the connection bound to the current transaction context, or None."""
|
|
74
98
|
return _active_connection.get()
|
|
@@ -40,7 +40,8 @@ if TYPE_CHECKING:
|
|
|
40
40
|
# The canonical version log + restore function. Identical in every Matrx database
|
|
41
41
|
# (verified live on both the main and CMS projects):
|
|
42
42
|
# history.row_versions(entity_type, row_id, version, operation, row_data, actor_id, occurred_at)
|
|
43
|
-
# public.version_restore(token text, id uuid, version int) -> int (the new version)
|
|
43
|
+
# public.version_restore(token text, id uuid, version int) -> int (the new version) —
|
|
44
|
+
# a thin SQL wrapper over platform.version_restore; both exist live, the ORM calls the public door
|
|
44
45
|
VERSION_LOG_SCHEMA = "history"
|
|
45
46
|
VERSION_LOG_MODEL = "RowVersions"
|
|
46
47
|
VERSION_RESTORE_SCHEMA = "public"
|
|
@@ -1745,7 +1745,9 @@ async def resolve_field_by_id(field_id: str) -> str:
|
|
|
1745
1745
|
f"credential field {field_id} exists but does not decrypt with the "
|
|
1746
1746
|
"configured CREDENTIALS_ENCRYPTION_KEY (key drift)"
|
|
1747
1747
|
) from exc
|
|
1748
|
-
|
|
1748
|
+
from .use_stamp import stamp_last_used
|
|
1749
|
+
|
|
1750
|
+
await stamp_last_used(UserSecret, {"id": field_id})
|
|
1749
1751
|
return value
|
|
1750
1752
|
|
|
1751
1753
|
|
|
@@ -753,11 +753,9 @@ async def resolve_org_secrets(
|
|
|
753
753
|
# the same row locks in opposite orders and Postgres kills one as a
|
|
754
754
|
# deadlock victim (40P01) — four ``ai.agent.start`` invocations over 185
|
|
755
755
|
# rows of one org killed a live workflow run on 2026-09-12.
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
last_used_at=datetime.now(UTC),
|
|
760
|
-
)
|
|
756
|
+
from .use_stamp import stamp_last_used
|
|
757
|
+
|
|
758
|
+
await stamp_last_used(UserSecret, {"id__in": sorted(used_ids)}, lock_rows_in_pk_order=True)
|
|
761
759
|
return resolved
|
|
762
760
|
|
|
763
761
|
|
|
@@ -21,6 +21,7 @@ from typing import Any
|
|
|
21
21
|
|
|
22
22
|
from .crypto import decrypt_value, encrypt_value, mask_value
|
|
23
23
|
from .model import CredentialItem, UserSecret, UserSecretGrant
|
|
24
|
+
from .use_stamp import stamp_last_used
|
|
24
25
|
|
|
25
26
|
logger = logging.getLogger(__name__)
|
|
26
27
|
|
|
@@ -243,7 +244,7 @@ async def get_user_secret(
|
|
|
243
244
|
)
|
|
244
245
|
plaintext = None
|
|
245
246
|
if touch_last_used:
|
|
246
|
-
await UserSecret
|
|
247
|
+
await stamp_last_used(UserSecret, {"id": row["id"]})
|
|
247
248
|
return _row_to_dataclass(row, decrypted_value=plaintext)
|
|
248
249
|
|
|
249
250
|
|
|
@@ -312,7 +313,7 @@ async def get_org_secret(
|
|
|
312
313
|
)
|
|
313
314
|
plaintext = None
|
|
314
315
|
if touch_last_used:
|
|
315
|
-
await UserSecret
|
|
316
|
+
await stamp_last_used(UserSecret, {"id": row["id"]})
|
|
316
317
|
return _row_to_dataclass(row, decrypted_value=plaintext)
|
|
317
318
|
|
|
318
319
|
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""``last_used_at`` stamps are telemetry: a failed stamp never fails the read that used the secret."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
from datetime import UTC, datetime
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
async def stamp_last_used(model: Any, filters: dict[str, Any], **options: Any) -> bool:
|
|
13
|
+
"""Stamp ``last_used_at`` on the rows ``filters`` names; announce and drop any failure.
|
|
14
|
+
|
|
15
|
+
A timeout or lock collision on this bookkeeping UPDATE once ended a whole Notion import
|
|
16
|
+
(2026-10-22). The caller already holds the value it read, so losing the stamp loses
|
|
17
|
+
nothing a person needs. Returns whether the stamp landed.
|
|
18
|
+
"""
|
|
19
|
+
try:
|
|
20
|
+
await model.update_where(filters, last_used_at=datetime.now(UTC), **options)
|
|
21
|
+
return True
|
|
22
|
+
except Exception as exc:
|
|
23
|
+
logger.warning(
|
|
24
|
+
"[secrets] last_used_at stamp on %s skipped (%s: %s); the read continues",
|
|
25
|
+
getattr(model, "__name__", model), type(exc).__name__, exc,
|
|
26
|
+
)
|
|
27
|
+
return False
|
|
@@ -269,6 +269,16 @@ def _op_user_id(op: SessionOp, safe_user_id: str | None) -> str | None:
|
|
|
269
269
|
return None
|
|
270
270
|
|
|
271
271
|
|
|
272
|
+
#: The refusal text of the database's confidential-content guard (chat.refuse_sealed_content).
|
|
273
|
+
CONFIDENTIAL_REFUSAL_MARK = "may not be stored on the server (chat.confidential)"
|
|
274
|
+
_CONFIDENTIAL_KEPT_KEYS = ("id", "conversation_id", "user_request_id", "message_id", "role", "position")
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def _confidential_payload(payload: Any) -> Any:
|
|
278
|
+
kept = {k: payload.get(k) for k in _CONFIDENTIAL_KEPT_KEYS if isinstance(payload, dict) and k in payload}
|
|
279
|
+
return _serialize_payload({**kept, "redacted": "confidential conversation: content refused, not kept"})
|
|
280
|
+
|
|
281
|
+
|
|
272
282
|
def _op_failure_row(
|
|
273
283
|
op: SessionOp,
|
|
274
284
|
*,
|
|
@@ -285,6 +295,12 @@ def _op_failure_row(
|
|
|
285
295
|
values. Used by BOTH the DB insert and the disk spill, so a spilled file is
|
|
286
296
|
byte-identical in shape to a table row (drain re-inserts it verbatim)."""
|
|
287
297
|
row_user_id = _op_user_id(op, safe_user_id)
|
|
298
|
+
payload = _serialize_payload(op.payload)
|
|
299
|
+
if CONFIDENTIAL_REFUSAL_MARK in (error_text or ""):
|
|
300
|
+
# The database refused this row because its conversation is CONFIDENTIAL
|
|
301
|
+
# (trigger chat.refuse_sealed_content): its content must never land on our
|
|
302
|
+
# server, so the failure record keeps the row's identity, never its values.
|
|
303
|
+
payload = _confidential_payload(op.payload)
|
|
288
304
|
return {
|
|
289
305
|
"op_id": op.op_id,
|
|
290
306
|
"request_id": request_id or session_id,
|
|
@@ -294,7 +310,7 @@ def _op_failure_row(
|
|
|
294
310
|
"table_target": op.table,
|
|
295
311
|
"op_type": op.op_type,
|
|
296
312
|
"primary_key": _serialize_primary_key(op.pk_field, op.pk_value),
|
|
297
|
-
"payload":
|
|
313
|
+
"payload": payload,
|
|
298
314
|
"depends_on": json.dumps([]), # FK-inferred deps aren't tracked on the op
|
|
299
315
|
"error_text": error_text,
|
|
300
316
|
"traceback": tb_text,
|
|
@@ -677,6 +693,7 @@ async def record_error(
|
|
|
677
693
|
payload: Any | None = None,
|
|
678
694
|
context: dict[str, Any] | None = None,
|
|
679
695
|
database: str | None = None,
|
|
696
|
+
group_open: bool = False,
|
|
680
697
|
) -> None:
|
|
681
698
|
"""Universal failure capture for NON-write errors → ``system_error``.
|
|
682
699
|
|
|
@@ -721,6 +738,16 @@ async def record_error(
|
|
|
721
738
|
indistinguishable from a Codex one.
|
|
722
739
|
payload: request args/body to capture (jsonb). Serialized defensively.
|
|
723
740
|
context: free-form context (debug_label, task_name, partial cost, …).
|
|
741
|
+
group_open: ONE OPEN ROW PER SIGNATURE (error-capture rule "a count is a
|
|
742
|
+
number on an existing row, never a new row"). When True, a capture whose
|
|
743
|
+
``(kind, route, error_type)`` already has an UNRESOLVED row bumps that row's
|
|
744
|
+
``context.occurrences`` (exact, atomic, across every process and deploy) and
|
|
745
|
+
``context.last_seen_at`` instead of filing a new row; with none open it files
|
|
746
|
+
the row with ``occurrences=1``. Resolving the row zeroes the count — the next
|
|
747
|
+
occurrence reopens as one new row. For announcements that are a COUNT of a
|
|
748
|
+
known condition per site (legacy lanes, runtime bypasses), never for a crash
|
|
749
|
+
whose every instance carries its own inputs. The storm budget does not apply
|
|
750
|
+
(the grouping is the budget).
|
|
724
751
|
"""
|
|
725
752
|
# Both attribution halves ride the carried request context; a capture site
|
|
726
753
|
# that declared neither still files a fully categorized row.
|
|
@@ -739,7 +766,7 @@ async def record_error(
|
|
|
739
766
|
else:
|
|
740
767
|
tb_text = "".join(_tb.format_exception(type(exc), exc, exc.__traceback__))
|
|
741
768
|
|
|
742
|
-
admitted, folded = _storm_admit(kind, resolved_error_type, route)
|
|
769
|
+
admitted, folded = (True, 0) if group_open else _storm_admit(kind, resolved_error_type, route)
|
|
743
770
|
if not admitted:
|
|
744
771
|
# Counted, never silent: the next admitted row for this signature
|
|
745
772
|
# carries the total. ONE stdout line per window (the first fold) — a line per
|
|
@@ -787,12 +814,14 @@ async def record_error(
|
|
|
787
814
|
"route": route,
|
|
788
815
|
"context": context,
|
|
789
816
|
"payload": payload,
|
|
817
|
+
**({"group_open": True} if group_open else {}),
|
|
790
818
|
}
|
|
791
819
|
)
|
|
792
820
|
return
|
|
793
821
|
|
|
794
822
|
try:
|
|
795
823
|
await _insert_error(
|
|
824
|
+
**({"group_open": True} if group_open else {}),
|
|
796
825
|
kind=kind,
|
|
797
826
|
request_id=request_id,
|
|
798
827
|
user_id=user_id,
|
|
@@ -896,6 +925,7 @@ async def _insert_error(
|
|
|
896
925
|
payload: Any | None,
|
|
897
926
|
context: dict[str, Any] | None,
|
|
898
927
|
database: str | None,
|
|
928
|
+
group_open: bool = False,
|
|
899
929
|
) -> None:
|
|
900
930
|
from matrx_orm.adapters import AdapterRegistry
|
|
901
931
|
from matrx_orm.core.config import get_all_database_project_names
|
|
@@ -931,6 +961,13 @@ async def _insert_error(
|
|
|
931
961
|
# capture fast instead of parking a pool slot behind it.
|
|
932
962
|
await conn.execute(f"SET LOCAL lock_timeout = '{CAPTURE_LOCK_TIMEOUT_MS}ms'")
|
|
933
963
|
await conn.execute(f"SET LOCAL statement_timeout = '{CAPTURE_STATEMENT_TIMEOUT_MS}ms'")
|
|
964
|
+
if group_open:
|
|
965
|
+
if await _bump_open_error(conn, kind=kind, route=route, error_type=error_type):
|
|
966
|
+
return
|
|
967
|
+
context_json = json.dumps(
|
|
968
|
+
{**(context or {}), "occurrences": 1, "grouped": GROUPED_NOTE},
|
|
969
|
+
default=_json_default,
|
|
970
|
+
)
|
|
934
971
|
await _execute_error_insert(
|
|
935
972
|
conn,
|
|
936
973
|
kind=kind,
|
|
@@ -951,6 +988,39 @@ async def _insert_error(
|
|
|
951
988
|
|
|
952
989
|
|
|
953
990
|
|
|
991
|
+
#: Stamped on a grouped row (``record_error(group_open=True)``) so a reader knows what the count means.
|
|
992
|
+
GROUPED_NOTE = (
|
|
993
|
+
"one open row per kind+route+error_type; context.occurrences counts every capture since the row "
|
|
994
|
+
"was last resolved (all processes), context.last_seen_at the latest"
|
|
995
|
+
)
|
|
996
|
+
|
|
997
|
+
# The bump is ONE statement under a transaction-scoped advisory lock on the signature, so two
|
|
998
|
+
# processes seeing the same site at once neither lose a count nor both file a row.
|
|
999
|
+
_GROUP_LOCK_SQL = "SELECT pg_advisory_xact_lock(hashtextextended($1::text, 0))"
|
|
1000
|
+
_GROUP_BUMP_SQL = """
|
|
1001
|
+
UPDATE ops.system_error
|
|
1002
|
+
SET context = coalesce(context, '{}'::jsonb) || jsonb_build_object(
|
|
1003
|
+
'occurrences', coalesce((context->>'occurrences')::bigint, 1) + 1,
|
|
1004
|
+
'last_seen_at', now())
|
|
1005
|
+
WHERE id = (
|
|
1006
|
+
SELECT id FROM ops.system_error
|
|
1007
|
+
WHERE kind = $1::text
|
|
1008
|
+
AND route IS NOT DISTINCT FROM $2::text
|
|
1009
|
+
AND error_type IS NOT DISTINCT FROM $3::text
|
|
1010
|
+
AND resolved_at IS NULL
|
|
1011
|
+
ORDER BY occurred_at
|
|
1012
|
+
LIMIT 1)
|
|
1013
|
+
RETURNING id
|
|
1014
|
+
"""
|
|
1015
|
+
|
|
1016
|
+
|
|
1017
|
+
async def _bump_open_error(conn: Any, *, kind: str, route: str | None, error_type: str | None) -> bool:
|
|
1018
|
+
"""Count this capture on the signature's open row; False when none is open (file one)."""
|
|
1019
|
+
await conn.fetch(_GROUP_LOCK_SQL, f"system_error:{kind}|{route or ''}|{error_type or ''}")
|
|
1020
|
+
rows = await conn.fetch(_GROUP_BUMP_SQL, kind, route, error_type)
|
|
1021
|
+
return bool(rows)
|
|
1022
|
+
|
|
1023
|
+
|
|
954
1024
|
_LIVE_INSERT_ERROR = _insert_error
|
|
955
1025
|
|
|
956
1026
|
async def _execute_error_insert(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "matrx-orm"
|
|
3
|
-
version = "3.1.
|
|
3
|
+
version = "3.1.246"
|
|
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.84",
|
|
45
45
|
"pyyaml>=6.0.3",
|
|
46
46
|
"python-dotenv>=1.0",
|
|
47
47
|
"cryptography>=43.0",
|
|
@@ -159,11 +159,11 @@ class TestArrayArgBinding:
|
|
|
159
159
|
lambda database: _FakeConnCtx(conn),
|
|
160
160
|
)
|
|
161
161
|
result = await call_function(
|
|
162
|
-
"db", "
|
|
162
|
+
"db", "context", "resolve_full_context", "user-id", ArrayArg(["scope-1", "scope-2"])
|
|
163
163
|
)
|
|
164
164
|
assert result == "ok"
|
|
165
165
|
conn.fetchval.assert_awaited_once_with(
|
|
166
|
-
'SELECT "
|
|
166
|
+
'SELECT "context"."resolve_full_context"($1, $2)',
|
|
167
167
|
"user-id",
|
|
168
168
|
["scope-1", "scope-2"], timeout=None
|
|
169
169
|
)
|
|
@@ -174,9 +174,9 @@ class TestArrayArgBinding:
|
|
|
174
174
|
"matrx_orm.core.db_function.AsyncDatabaseManager.get_connection",
|
|
175
175
|
lambda database: _FakeConnCtx(conn),
|
|
176
176
|
)
|
|
177
|
-
await call_function("db", "
|
|
177
|
+
await call_function("db", "context", "resolve_full_context", "user-id", ArrayArg(None))
|
|
178
178
|
conn.fetchval.assert_awaited_once_with(
|
|
179
|
-
'SELECT "
|
|
179
|
+
'SELECT "context"."resolve_full_context"($1, $2)', "user-id", None, timeout=None
|
|
180
180
|
)
|
|
181
181
|
|
|
182
182
|
async def test_array_arg_distinct_from_plain_list_jsonb_cast(self, monkeypatch) -> None:
|
|
@@ -110,11 +110,11 @@ class TestCallFunctionRows:
|
|
|
110
110
|
)
|
|
111
111
|
|
|
112
112
|
result = await call_function(
|
|
113
|
-
"main", "
|
|
113
|
+
"main", "dictionary", "dict_list_entries_for", "user-1", "user", None, mode="rows"
|
|
114
114
|
)
|
|
115
115
|
assert result == [{"id": "1", "term": "foo"}, {"id": "2", "term": "bar"}]
|
|
116
116
|
assert conn.calls[0]["sql"] == (
|
|
117
|
-
'SELECT * FROM "
|
|
117
|
+
'SELECT * FROM "dictionary"."dict_list_entries_for"($1, $2, $3)'
|
|
118
118
|
)
|
|
119
119
|
assert conn.calls[0]["args"] == ("user-1", "user", None)
|
|
120
120
|
|
|
@@ -328,7 +328,7 @@ async def test_call_function_retries_a_rolled_back_rpc(monkeypatch):
|
|
|
328
328
|
monkeypatch.setattr("matrx_orm.core.write_retry.resolve_policy", _fake_policy)
|
|
329
329
|
clear_policy_cache()
|
|
330
330
|
|
|
331
|
-
await db_function.call_function("test", "
|
|
331
|
+
await db_function.call_function("test", "rag", "library_revoke", "g1", mode="none")
|
|
332
332
|
|
|
333
333
|
assert calls[0] == 2, (
|
|
334
334
|
f"A deadlocked RPC must be retried on its own fresh connection. Got {calls[0]} attempts."
|
|
@@ -352,7 +352,7 @@ async def test_call_function_inside_a_transaction_does_not_retry(monkeypatch):
|
|
|
352
352
|
clear_policy_cache()
|
|
353
353
|
|
|
354
354
|
with pytest.raises(LockContentionError) as caught:
|
|
355
|
-
await db_function.call_function("test", "
|
|
355
|
+
await db_function.call_function("test", "rag", "library_revoke", "g1", mode="none")
|
|
356
356
|
assert calls[0] == 1, f"expected exactly one attempt inside a transaction, got {calls[0]}"
|
|
357
357
|
assert caught.value.details["in_transaction"] is True
|
|
358
358
|
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""Level 1: a noun lookup never depends on who is acting.
|
|
2
|
+
|
|
3
|
+
``platform.entity_types`` is vocabulary, and a non-admin person's RLS hides it (a restrictive
|
|
4
|
+
``platform_admin_only`` policy). ``EntityRegistry.resolve`` read it on whatever connection the
|
|
5
|
+
caller held, so inside ``acting_as_user`` it found no row, answered ``None`` and CACHED that
|
|
6
|
+
miss for the life of the process: every later ``sources_to(..., "fc_card")`` raised
|
|
7
|
+
``UnknownEntityType('fc_card')`` — the Education Guide lost a study-media record's source (a
|
|
8
|
+
flashcard deck) on 2026-10-22. The registry now reads the noun outside the caller's session.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import pytest
|
|
12
|
+
|
|
13
|
+
from matrx_orm.associations import EntityRegistry
|
|
14
|
+
from matrx_orm.core import transaction as tx
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class _Card:
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class _HiddenUnderRls:
|
|
22
|
+
"""An entity_types stand-in that answers only off the person's session, like the live table."""
|
|
23
|
+
|
|
24
|
+
seen: list[tuple[bool, object]] = []
|
|
25
|
+
|
|
26
|
+
@classmethod
|
|
27
|
+
async def get_or_none(cls, **filters):
|
|
28
|
+
cls.seen.append((tx.in_person_scoped_session(), tx.get_active_connection()))
|
|
29
|
+
if tx.in_person_scoped_session() or tx.get_active_connection() is not None:
|
|
30
|
+
return None
|
|
31
|
+
row = type("Row", (), {})()
|
|
32
|
+
row.schema_name, row.table_name, row.is_active = "education", "fc_card", True
|
|
33
|
+
return row if filters.get("token") == "fc_card" else None
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@pytest.mark.asyncio
|
|
37
|
+
async def test_a_person_session_never_hides_a_registered_noun() -> None:
|
|
38
|
+
reg = EntityRegistry(entity_type_model=_HiddenUnderRls,
|
|
39
|
+
model_lookup=lambda s, t: _Card if (s, t) == ("education", "fc_card") else None)
|
|
40
|
+
marks = (tx._active_connection.set(object()), tx._person_scoped.set(True))
|
|
41
|
+
try:
|
|
42
|
+
assert await reg.resolve("fc_card") is _Card
|
|
43
|
+
assert await reg.require("fc_card") is _Card
|
|
44
|
+
finally:
|
|
45
|
+
tx._person_scoped.reset(marks[1])
|
|
46
|
+
tx._active_connection.reset(marks[0])
|
|
47
|
+
assert _HiddenUnderRls.seen and all(scoped is False and conn is None for scoped, conn in _HiddenUnderRls.seen)
|
|
48
|
+
# the caller's session is untouched by the lookup
|
|
49
|
+
assert tx.in_person_scoped_session() is False and tx.get_active_connection() is None
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"""ONE OPEN ROW PER SIGNATURE: ``record_error(group_open=True)`` counts on the existing row.
|
|
2
|
+
|
|
3
|
+
SUT: :func:`matrx_orm.session.fallback.record_error` with ``group_open=True`` (and ``_insert_error``).
|
|
4
|
+
|
|
5
|
+
2026-10-22: the mandate runner's legacy-lane announcements were "announced once per site" with a
|
|
6
|
+
per-PROCESS set, so every process and every 30-minute deploy re-filed the same site: 413
|
|
7
|
+
``mandate_offer_eager_legacy`` rows for ~60 sites, 56 ``mandate_bypass_runtime`` rows for 2 lines, in
|
|
8
|
+
12 hours. The error-capture rule: "a count is a number on an existing row, never a new row".
|
|
9
|
+
|
|
10
|
+
Breaks these tests name: a second capture of an open signature filing a new row, the count not being
|
|
11
|
+
exact across "processes" (fresh storm state, fresh per-process sets), a resolved row swallowing a
|
|
12
|
+
recurrence instead of reopening as one, or distinct sites collapsing into one row.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import json
|
|
18
|
+
import uuid
|
|
19
|
+
from typing import Any
|
|
20
|
+
|
|
21
|
+
import pytest
|
|
22
|
+
from matrx_orm.session import fallback
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class _Table:
|
|
26
|
+
"""``ops.system_error`` as far as the grouped door touches it."""
|
|
27
|
+
|
|
28
|
+
def __init__(self) -> None:
|
|
29
|
+
self.rows: list[dict[str, Any]] = []
|
|
30
|
+
|
|
31
|
+
def open_row(self, kind: str, route: str | None, error_type: str | None) -> dict[str, Any] | None:
|
|
32
|
+
for row in self.rows:
|
|
33
|
+
if (row["kind"], row["route"], row["error_type"]) == (kind, route, error_type) and row["resolved_at"] is None:
|
|
34
|
+
return row
|
|
35
|
+
return None
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class _Txn:
|
|
39
|
+
async def __aenter__(self):
|
|
40
|
+
return self
|
|
41
|
+
|
|
42
|
+
async def __aexit__(self, *exc):
|
|
43
|
+
return False
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class _Conn:
|
|
47
|
+
def __init__(self, table: _Table) -> None:
|
|
48
|
+
self.table = table
|
|
49
|
+
|
|
50
|
+
def transaction(self):
|
|
51
|
+
return _Txn()
|
|
52
|
+
|
|
53
|
+
async def execute(self, sql: str, *args: Any) -> None:
|
|
54
|
+
if "ops.record_system_error" in sql:
|
|
55
|
+
self.table.rows.append(
|
|
56
|
+
{
|
|
57
|
+
"id": str(uuid.uuid4()),
|
|
58
|
+
"kind": args[0],
|
|
59
|
+
"route": args[8],
|
|
60
|
+
"error_type": args[9],
|
|
61
|
+
"context": json.loads(args[13]) if args[13] else {},
|
|
62
|
+
"resolved_at": None,
|
|
63
|
+
}
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
async def fetch(self, sql: str, *args: Any) -> list[dict[str, Any]]:
|
|
67
|
+
if "UPDATE ops.system_error" in sql:
|
|
68
|
+
row = self.table.open_row(*args)
|
|
69
|
+
if row is None:
|
|
70
|
+
return []
|
|
71
|
+
row["context"]["occurrences"] = int(row["context"].get("occurrences", 1)) + 1
|
|
72
|
+
row["context"]["last_seen_at"] = "now"
|
|
73
|
+
return [{"id": row["id"]}]
|
|
74
|
+
return []
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class _Adapter:
|
|
78
|
+
def __init__(self, table: _Table) -> None:
|
|
79
|
+
self.table = table
|
|
80
|
+
|
|
81
|
+
def get_connection(self, timeout: float = 10.0):
|
|
82
|
+
conn = _Conn(self.table)
|
|
83
|
+
|
|
84
|
+
class _Ctx:
|
|
85
|
+
async def __aenter__(self_inner):
|
|
86
|
+
return conn
|
|
87
|
+
|
|
88
|
+
async def __aexit__(self_inner, *exc):
|
|
89
|
+
return False
|
|
90
|
+
|
|
91
|
+
return _Ctx()
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@pytest.fixture
|
|
95
|
+
def table(monkeypatch) -> _Table:
|
|
96
|
+
table = _Table()
|
|
97
|
+
monkeypatch.setattr("matrx_orm.adapters.AdapterRegistry.get", lambda name: _Adapter(table))
|
|
98
|
+
|
|
99
|
+
async def live(**kwargs: Any) -> None: # the real insert path, against the fake table
|
|
100
|
+
await fallback._LIVE_INSERT_ERROR(**kwargs)
|
|
101
|
+
|
|
102
|
+
monkeypatch.setattr(fallback, "_insert_error", live)
|
|
103
|
+
fallback.reset_capture_storm_state()
|
|
104
|
+
yield table
|
|
105
|
+
fallback.reset_capture_storm_state()
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
async def _sighting(site: str, kind: str = "mandate_offer_eager_legacy") -> None:
|
|
109
|
+
fallback.reset_capture_storm_state() # every sighting may be a different process / deploy
|
|
110
|
+
await fallback.record_error(
|
|
111
|
+
RuntimeError(f"legacy at {site}"), kind=kind, route=site, context={"site": site},
|
|
112
|
+
database="t", group_open=True,
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
@pytest.mark.asyncio
|
|
117
|
+
async def test_one_open_row_per_site_with_an_exact_count(table: _Table) -> None:
|
|
118
|
+
for _ in range(7):
|
|
119
|
+
await _sighting("seo.job @ aidream/services/seo/run.py:12")
|
|
120
|
+
assert len(table.rows) == 1, "seven sightings of one site are ONE open row"
|
|
121
|
+
assert table.rows[0]["context"]["occurrences"] == 7, "and the row counts every one of them"
|
|
122
|
+
assert table.rows[0]["context"]["site"] == "seo.job @ aidream/services/seo/run.py:12"
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
@pytest.mark.asyncio
|
|
126
|
+
async def test_distinct_sites_and_kinds_stay_distinct_rows(table: _Table) -> None:
|
|
127
|
+
await _sighting("a.job @ x.py:1")
|
|
128
|
+
await _sighting("a.job @ x.py:2")
|
|
129
|
+
await _sighting("a.job @ x.py:1", kind="mandate_by_name_legacy")
|
|
130
|
+
await _sighting("a.job @ x.py:1")
|
|
131
|
+
assert len(table.rows) == 3, "never merge genuinely distinct signatures"
|
|
132
|
+
counts = sorted(r["context"]["occurrences"] for r in table.rows)
|
|
133
|
+
assert counts == [1, 1, 2]
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
@pytest.mark.asyncio
|
|
137
|
+
async def test_a_recurrence_after_resolve_reopens_as_one_new_row(table: _Table) -> None:
|
|
138
|
+
await _sighting("b.job @ y.py:9")
|
|
139
|
+
await _sighting("b.job @ y.py:9")
|
|
140
|
+
table.rows[0]["resolved_at"] = "2026-10-22T23:00:00Z"
|
|
141
|
+
await _sighting("b.job @ y.py:9")
|
|
142
|
+
await _sighting("b.job @ y.py:9")
|
|
143
|
+
assert len(table.rows) == 2
|
|
144
|
+
assert [r["context"]["occurrences"] for r in table.rows] == [2, 2], "resolve zeroes the count"
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
@pytest.mark.asyncio
|
|
148
|
+
async def test_without_group_open_every_capture_is_still_its_own_row(table: _Table) -> None:
|
|
149
|
+
for _ in range(2):
|
|
150
|
+
fallback.reset_capture_storm_state()
|
|
151
|
+
await fallback.record_error(RuntimeError("crash"), kind="request_crash", route="/r", database="t")
|
|
152
|
+
assert len(table.rows) == 2, "grouping is opt-in: a crash keeps its own inputs per row"
|