matrx-orm 3.1.69__tar.gz → 3.1.71__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.69 → matrx_orm-3.1.71}/PKG-INFO +2 -2
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/core/FEATURE.md +16 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/query/FEATURE.md +13 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/query/executor.py +14 -1
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/state.py +8 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/pyproject.toml +2 -2
- matrx_orm-3.1.71/tests/level1/test_bulk_hydration_offload.py +38 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_managed_write_guard.py +2 -2
- matrx_orm-3.1.71/tests/level1/test_rls_session_scope.py +103 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_state_cache.py +11 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/.gitignore +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/AGENTS.md +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/BASE_CLASS_METHODS.md +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/CLAUDE.md +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/MODEL_API.md +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/README.md +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/database/orm/extended/managers/ai_model_base.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/docs/migrations.md +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/MODULE_README.md +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/__init__.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/adapters/MODULE_README.md +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/adapters/__init__.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/adapters/async_postgresql.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/adapters/base_adapter.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/adapters/postgresql.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/adapters/postgrest_client_adapter.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/adapters/supabase_adapter.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/admin/__init__.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/admin/router.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/api/__init__.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/api/auth.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/api/config.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/api/handlers.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/api/protocol.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/api/server.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/associations.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/cache_debug.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/catalog.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/catalog_sql.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/client/MODULE_README.md +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/client/__init__.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/client/postgres_connection.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/client/postgrest.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/client/supabase_auth.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/client/supabase_config.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/client/supabase_manager.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/core/EXTENDED-TASK.md +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/core/MODULE_README.md +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/core/RELATIONS-TASKS.md +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/core/TASKS.md +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/core/__init__.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/core/async_db_manager.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/core/base.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/core/config.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/core/connections.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/core/db_function.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/core/diagnostics.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/core/expressions.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/core/extended.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/core/fields.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/core/introspection.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/core/listen.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/core/loop_filters.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/core/model_dto.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/core/model_view.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/core/one_database.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/core/paginator.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/core/pool_maintenance.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/core/pydantic_bridge.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/core/registry.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/core/relations.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/core/resilience.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/core/rls_session.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/core/signals.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/core/tls.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/core/transaction.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/core/types.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/core/write_queue.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/entity.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/error_handling.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/exceptions.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/extended/__init__.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/extended/app_error_handler.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/identity_rule.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/middleware/__init__.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/middleware/base.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/migrations/MODULE_README.md +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/migrations/__init__.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/migrations/cli.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/migrations/ddl.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/migrations/diff.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/migrations/executor.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/migrations/integration.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/migrations/loader.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/migrations/operations.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/migrations/state.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/migrations/table_filter.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/operations/MODULE_README.md +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/operations/__init__.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/operations/bulk_update_values.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/operations/conflict.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/operations/conflict_writes.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/operations/create.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/operations/db_functions.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/operations/delete.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/operations/dynamic_admin.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/operations/dynamic_crud.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/operations/graph_walk.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/operations/latest_rows.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/operations/queue_claim.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/operations/read.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/operations/relational_backfill.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/operations/staging_load.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/operations/update.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/platform_db.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/py.typed +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/python_sql/MODULE_README.md +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/python_sql/__init__.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/python_sql/db_objects.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/python_sql/table_detailed_relationships.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/python_sql/table_typescript_relationship.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/query/MODULE_README.md +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/query/__init__.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/query/builder.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/schema_builder/MODULE_README.md +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/schema_builder/__init__.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/schema_builder/code_handler.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/schema_builder/codegen_writer.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/schema_builder/columns.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/schema_builder/common.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/schema_builder/diff_preview.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/schema_builder/entity_capabilities.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/schema_builder/generator.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/schema_builder/helpers/__init__.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/schema_builder/helpers/base_generators.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/schema_builder/helpers/entity_generators.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/schema_builder/helpers/git_checker.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/schema_builder/package_wiring.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/schema_builder/relationships.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/schema_builder/runner.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/schema_builder/schema.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/schema_builder/schema_manager.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/schema_builder/tables.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/schema_builder/views.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/secrets_battery/FEATURE.md +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/secrets_battery/__init__.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/secrets_battery/attachments.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/secrets_battery/crypto.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/secrets_battery/items.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/secrets_battery/model.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/secrets_battery/org_service.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/secrets_battery/service.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/session/README.md +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/session/__init__.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/session/coalesce.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/session/dag.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/session/errors.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/session/fallback.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/session/flush.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/session/lifecycle.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/session/managed.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/session/op.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/session/reads.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/session/session.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/session/telemetry.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/sql_executor/MODULE_README.md +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/sql_executor/__init__.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/sql_executor/executor.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/sql_executor/queries.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/sql_executor/registry.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/sql_executor/types.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/sql_executor/utils.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/utils/__init__.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/utils/sql_utils.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/matrx_orm/utils/type_converters.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/scripts/git-branches.sh +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/scripts/publish.sh +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/scripts/release.sh +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/MODULE_README.md +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/__init__.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/conftest.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/MODULE_README.md +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/__init__.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_admin_db_columns.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_admin_search_negation.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_admin_search_sentinels.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_advisory_lock.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_agent_message.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_api_auth.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_api_config.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_api_handlers.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_api_protocol.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_array_agg_order_by.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_associations.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_bulk_update_by_pk.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_bulk_upsert_increment_set_fields.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_cache_debug.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_call_function.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_case_when.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_catalog.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_composite_pk_filter.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_composite_pk_write_paths.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_config.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_conflict_target.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_conflict_writes.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_connection_codecs.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_connection_poison_guard.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_connection_primitives.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_count_composite_distinct.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_db_function_field.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_ddl_generator.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_derived_table.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_dirty_tracking.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_dynamic_crud.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_exceptions.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_execute_query_integrity_mapping.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_expression_primitives.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_fields.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_filter_jsonb_agg_array_index.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_from_alias.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_fts.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_func_expression_args.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_graph_walk.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_having_annotate_alias.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_insert_ignore_and_admin_primitives.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_introspect_rls_policies.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_join_and_aggregate.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_latest_rows.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_listen_notify.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_matrx_entity.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_migration_diff_types.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_migration_loader.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_model_instance.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_model_meta.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_optimistic_concurrency.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_order_by_expression.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_parameter_name_hydration.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_patch_jsonb_path.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_pool_loop_recreation.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_pool_maintenance.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_postgrest_filters.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_pydantic_bridge.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_query_builder.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_query_error_detail.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_query_executor_sql.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_query_timeout.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_querybuilder_clone_semantics.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_queue_claim.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_registry.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_registry_multi_database.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_registry_multi_schema.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_relational_backfill.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_relations.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_resilience.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_schema_exists.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_session_advisory_lock.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_subquery_filter_raw.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_subquery_in_filter.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_supabase_auth.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_supabase_config.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_tls.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_typed_join_projection.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_typed_predicates.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_update_case_expression.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_update_subquery.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_update_with_rebase.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_upsert_default_leak.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_upsert_with_conflict.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_values_jsonb_decode.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_watched_lifecycle.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level1/test_write_retry_semantics.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level2/MODULE_README.md +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level2/__init__.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level2/conftest.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level2/test_bulk_ops.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level2/test_cache_integration.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level2/test_crud.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level2/test_foreign_keys.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level2/test_m2m.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level2/test_manager.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level2/test_migrations_live.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level2/test_query_execution.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/level2/test_schema_diff.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/sample_project/.env.example +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/sample_project/README.md +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/sample_project/__init__.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/sample_project/generate.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/sample_project/generated/.gitkeep +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/sample_project/matrx_orm.yaml +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/sample_project/test_schema_generation.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/sample_project_desktop/.env.example +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/sample_project_desktop/README.md +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/sample_project_desktop/__init__.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/sample_project_desktop/client_example.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/sample_project_desktop/client_example.ts +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/sample_project_desktop/client_supabase_example.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/sample_project_desktop/server.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/schema/entity_tests.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/schema/test_base_generation.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/schema/test_composite_pk_fk_generation.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/schema/test_composite_unique_generation.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/schema/test_dto_identity_generation.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/schema/test_entity_capability_generation.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/schema/test_generate_schema.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/schema/test_junction_analysis.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/schema/test_multi_schema_output.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/schema/test_sql_expression_defaults.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/schema/test_table_relationships_query.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/schema/test_view_generation.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/schema_builder/test_focused_generation_plan.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/schema_builder/test_host_models_aggregator.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/session/__init__.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/session/conftest.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/session/test_cache_no_downgrade.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/session/test_capture_poison_proof.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/session/test_coalesce.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/session/test_dag.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/session/test_disk_spill_fallback.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/session/test_flush_individual_fallback.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/session/test_governed_write.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/session/test_managed.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/session/test_op_enqueue_site.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/session/test_reads.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/session/test_session.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/test_database_name_alias.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/test_db_functions.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/test_model_cls_refactor.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/test_no_blocking_asyncpg_tls.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/test_one_database.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/test_secrets_battery.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/test_sql_param_casts.py +0 -0
- {matrx_orm-3.1.69 → matrx_orm-3.1.71}/tests/test_sync_twins_guard.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.71
|
|
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
|
|
@@ -25,7 +25,7 @@ Requires-Python: >=3.13
|
|
|
25
25
|
Requires-Dist: asyncpg>=0.31.0
|
|
26
26
|
Requires-Dist: cryptography>=43.0
|
|
27
27
|
Requires-Dist: gitpython>=3.1.40
|
|
28
|
-
Requires-Dist: matrx-utils>=2.0.
|
|
28
|
+
Requires-Dist: matrx-utils>=2.0.14
|
|
29
29
|
Requires-Dist: psycopg-pool>=3.2.5
|
|
30
30
|
Requires-Dist: psycopg[binary]>=3.2.5
|
|
31
31
|
Requires-Dist: python-dotenv>=1.0
|
|
@@ -55,8 +55,24 @@ Pinned by `tests/level1/test_pool_maintenance.py`.
|
|
|
55
55
|
|
|
56
56
|
Verified against code: 2026-08-13.
|
|
57
57
|
|
|
58
|
+
## Transaction-pooled role scope
|
|
59
|
+
|
|
60
|
+
**A restricted role is always transaction-local.** `rls_session()` opens the
|
|
61
|
+
outermost `transaction()` before it sets JWT claims with
|
|
62
|
+
`set_config(..., true)` and issues `SET LOCAL ROLE`. Success and early return
|
|
63
|
+
commit; exceptions roll back; cancellation-shielded teardown finishes before
|
|
64
|
+
the connection returns to the pool. No path can leave `authenticated` on a
|
|
65
|
+
reused server connection.
|
|
66
|
+
|
|
67
|
+
Pinned by `tests/level1/test_rls_session_scope.py` and
|
|
68
|
+
`tests/level1/test_connection_poison_guard.py`.
|
|
69
|
+
|
|
70
|
+
Verified against code: 2026-08-15.
|
|
71
|
+
|
|
58
72
|
## Change log
|
|
59
73
|
|
|
74
|
+
- 2026-08-15: Pinned transaction-local RLS role cleanup across success,
|
|
75
|
+
exception, and early-return paths.
|
|
60
76
|
- 2026-08-14: Public models now render `public.table` SQL targets; bare public
|
|
61
77
|
relation names can no longer disable application logs or write-failure capture.
|
|
62
78
|
- 2026-08-13: Registry, admin, recovery, catalog, and diagnostic identities are now exact `schema.table`; bare names are accepted only as uncached discovery terms and ambiguity fails loudly.
|
|
@@ -11,6 +11,14 @@ remain fast. A large reconciliation, export, or historical scan must call
|
|
|
11
11
|
merges pending session state, but it does not insert every result into the
|
|
12
12
|
process identity cache.
|
|
13
13
|
|
|
14
|
+
Hydration of 200 or more rows runs in a worker thread. Model construction,
|
|
15
|
+
relationship attachment, and identity-cache insertion are synchronous Python
|
|
16
|
+
work and must not monopolize an API event loop for a large result set. Complete
|
|
17
|
+
primary-key cache misses are O(1): after the direct dictionary lookup misses,
|
|
18
|
+
the state manager never scans the cache for an identity it already knows is
|
|
19
|
+
absent. Historical/request-local scans should still use `use_cache=False` so
|
|
20
|
+
they do not grow the process cache with rows that will not be reused.
|
|
21
|
+
|
|
14
22
|
`Model.all(use_cache=False)` exposes the same behavior. The default remains
|
|
15
23
|
`True` for compatibility. The cache bypass controls only cache population; it
|
|
16
24
|
does not weaken database authorization, filters, hydration, or transaction
|
|
@@ -23,3 +31,8 @@ inherits the model query's default `*` projection. A grouped aggregate with no
|
|
|
23
31
|
explicit `.values(...)` projects the grouped columns plus its aggregate
|
|
24
32
|
expressions. These shapes keep every generated aggregate valid in PostgreSQL
|
|
25
33
|
without forcing callers to add a redundant projection.
|
|
34
|
+
|
|
35
|
+
## Change log
|
|
36
|
+
|
|
37
|
+
- 2026-08-15 — Moved bulk hydration off the event loop, made complete-primary-key
|
|
38
|
+
misses constant-time, and documented cache bypass for request-local scans.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import asyncio
|
|
3
4
|
import json
|
|
4
5
|
import re
|
|
5
6
|
from types import NotImplementedType
|
|
@@ -24,6 +25,11 @@ if TYPE_CHECKING:
|
|
|
24
25
|
|
|
25
26
|
debug = False
|
|
26
27
|
|
|
28
|
+
# Model hydration is synchronous Python work. Small result sets stay inline;
|
|
29
|
+
# large sets move to a worker thread so one read cannot freeze every coroutine
|
|
30
|
+
# on the process event loop.
|
|
31
|
+
_BULK_HYDRATION_THREAD_THRESHOLD = 200
|
|
32
|
+
|
|
27
33
|
_SKIP_PARAM = object()
|
|
28
34
|
|
|
29
35
|
_PARAM_RE = re.compile(r"\$(\d+)")
|
|
@@ -264,6 +270,9 @@ class QueryExecutor:
|
|
|
264
270
|
hydrate = getattr(self.model, "_hydrate", None)
|
|
265
271
|
return hydrate(row) if hydrate is not None else self.model(**row)
|
|
266
272
|
|
|
273
|
+
def _hydrate_rows(self, rows: list[dict[str, Any]]) -> list[Any]:
|
|
274
|
+
return [self._make(row) for row in rows]
|
|
275
|
+
|
|
267
276
|
def _decode_row_dict(self, row: dict[str, Any]) -> dict[str, Any]:
|
|
268
277
|
"""Apply field ``from_db_value`` so ``.values()`` matches Model hydration.
|
|
269
278
|
|
|
@@ -1521,8 +1530,12 @@ class QueryExecutor:
|
|
|
1521
1530
|
return []
|
|
1522
1531
|
select_related = self._full_query_dict.get("select_related", [])
|
|
1523
1532
|
if select_related:
|
|
1533
|
+
if len(results) >= _BULK_HYDRATION_THREAD_THRESHOLD:
|
|
1534
|
+
return await asyncio.to_thread(self._hydrate_with_select_related, results)
|
|
1524
1535
|
return self._hydrate_with_select_related(results)
|
|
1525
|
-
|
|
1536
|
+
if len(results) >= _BULK_HYDRATION_THREAD_THRESHOLD:
|
|
1537
|
+
return await asyncio.to_thread(self._hydrate_rows, results)
|
|
1538
|
+
return self._hydrate_rows(results)
|
|
1526
1539
|
|
|
1527
1540
|
async def first(self) -> Any:
|
|
1528
1541
|
"""Fetch the first matching row as a model instance."""
|
|
@@ -154,6 +154,14 @@ class ModelState:
|
|
|
154
154
|
original_error=e,
|
|
155
155
|
) from e
|
|
156
156
|
|
|
157
|
+
# A complete primary-key lookup has exactly one possible
|
|
158
|
+
# cache slot. If that direct key missed (or was stale), a
|
|
159
|
+
# full scan cannot find a different valid match. Falling
|
|
160
|
+
# through to _find_in_cache made cache_bulk O(n²): every new
|
|
161
|
+
# row scanned every row already cached for the model.
|
|
162
|
+
if cache_key is not None:
|
|
163
|
+
return None
|
|
164
|
+
|
|
157
165
|
# Try finding by criteria if direct lookup fails
|
|
158
166
|
try:
|
|
159
167
|
cached_record = self._find_in_cache(**kwargs)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "matrx-orm"
|
|
3
|
-
version = "3.1.
|
|
3
|
+
version = "3.1.71"
|
|
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.14",
|
|
45
45
|
"pyyaml>=6.0.3",
|
|
46
46
|
"python-dotenv>=1.0",
|
|
47
47
|
"cryptography>=43.0",
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import threading
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
|
|
8
|
+
from matrx_orm.query.executor import (
|
|
9
|
+
_BULK_HYDRATION_THREAD_THRESHOLD,
|
|
10
|
+
QueryExecutor,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@pytest.mark.asyncio
|
|
15
|
+
async def test_large_result_hydration_runs_off_the_event_loop() -> None:
|
|
16
|
+
loop_thread = threading.get_ident()
|
|
17
|
+
hydration_threads: list[int] = []
|
|
18
|
+
|
|
19
|
+
class ModelStub:
|
|
20
|
+
@staticmethod
|
|
21
|
+
def _hydrate(row):
|
|
22
|
+
hydration_threads.append(threading.get_ident())
|
|
23
|
+
return row
|
|
24
|
+
|
|
25
|
+
executor = object.__new__(QueryExecutor)
|
|
26
|
+
executor.model = ModelStub
|
|
27
|
+
executor._full_query_dict = {}
|
|
28
|
+
rows = [{"id": index} for index in range(_BULK_HYDRATION_THREAD_THRESHOLD)]
|
|
29
|
+
|
|
30
|
+
async def execute():
|
|
31
|
+
await asyncio.sleep(0)
|
|
32
|
+
return rows
|
|
33
|
+
|
|
34
|
+
executor._execute = execute
|
|
35
|
+
|
|
36
|
+
assert await executor.all() == rows
|
|
37
|
+
assert hydration_threads
|
|
38
|
+
assert loop_thread not in hydration_threads
|
|
@@ -77,8 +77,8 @@ class TestStrictGuard:
|
|
|
77
77
|
M.assert_managed_write_allowed("INSERT INTO public.cx_message (id) VALUES ($1)")
|
|
78
78
|
|
|
79
79
|
def test_same_bare_name_in_another_schema_does_not_match(self):
|
|
80
|
-
M._STRICT_TABLES.add("
|
|
81
|
-
M.assert_managed_write_allowed("UPDATE
|
|
80
|
+
M._STRICT_TABLES.add("agent.definition")
|
|
81
|
+
M.assert_managed_write_allowed("UPDATE tool.definition SET x=1")
|
|
82
82
|
|
|
83
83
|
def test_quoted_qualified_target_matches_exact_key(self):
|
|
84
84
|
M._STRICT_TABLES.add("chat.message")
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from matrx_orm.adapters import AdapterRegistry
|
|
8
|
+
from matrx_orm.core.rls_session import rls_session
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class _Connection:
|
|
12
|
+
def __init__(self) -> None:
|
|
13
|
+
self.calls: list[tuple[str, tuple[Any, ...]]] = []
|
|
14
|
+
self.terminated = False
|
|
15
|
+
|
|
16
|
+
async def execute(self, sql: str, *args: Any) -> None:
|
|
17
|
+
self.calls.append((sql, args))
|
|
18
|
+
|
|
19
|
+
def terminate(self) -> None:
|
|
20
|
+
self.terminated = True
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class _ConnectionContext:
|
|
24
|
+
def __init__(self, conn: _Connection) -> None:
|
|
25
|
+
self.conn = conn
|
|
26
|
+
self.exited = False
|
|
27
|
+
|
|
28
|
+
async def __aenter__(self) -> _Connection:
|
|
29
|
+
return self.conn
|
|
30
|
+
|
|
31
|
+
async def __aexit__(self, *_exc: Any) -> None:
|
|
32
|
+
self.exited = True
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class _Adapter:
|
|
36
|
+
def __init__(self, conn: _Connection) -> None:
|
|
37
|
+
self.context = _ConnectionContext(conn)
|
|
38
|
+
|
|
39
|
+
def get_connection(self) -> _ConnectionContext:
|
|
40
|
+
return self.context
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _install_adapter(monkeypatch: pytest.MonkeyPatch) -> tuple[_Connection, _Adapter]:
|
|
44
|
+
conn = _Connection()
|
|
45
|
+
adapter = _Adapter(conn)
|
|
46
|
+
monkeypatch.setattr(AdapterRegistry, "get", lambda _database: adapter)
|
|
47
|
+
return conn, adapter
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@pytest.mark.asyncio
|
|
51
|
+
async def test_rls_session_commits_transaction_local_role(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
52
|
+
conn, adapter = _install_adapter(monkeypatch)
|
|
53
|
+
|
|
54
|
+
async with rls_session(
|
|
55
|
+
{"sub": "user-id", "role": "authenticated"},
|
|
56
|
+
role="authenticated",
|
|
57
|
+
database="test",
|
|
58
|
+
):
|
|
59
|
+
pass
|
|
60
|
+
|
|
61
|
+
assert [sql for sql, _args in conn.calls] == [
|
|
62
|
+
"BEGIN",
|
|
63
|
+
"select set_config('request.jwt.claims', $1, true)",
|
|
64
|
+
"SET LOCAL statement_timeout = 30000",
|
|
65
|
+
"SET LOCAL ROLE authenticated",
|
|
66
|
+
"COMMIT",
|
|
67
|
+
]
|
|
68
|
+
assert adapter.context.exited is True
|
|
69
|
+
assert conn.terminated is False
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@pytest.mark.asyncio
|
|
73
|
+
async def test_rls_session_rolls_back_on_exception(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
74
|
+
conn, adapter = _install_adapter(monkeypatch)
|
|
75
|
+
|
|
76
|
+
with pytest.raises(LookupError):
|
|
77
|
+
async with rls_session(
|
|
78
|
+
{"sub": "user-id", "role": "authenticated"},
|
|
79
|
+
role="authenticated",
|
|
80
|
+
database="test",
|
|
81
|
+
):
|
|
82
|
+
raise LookupError("abort")
|
|
83
|
+
|
|
84
|
+
assert conn.calls[-1][0] == "ROLLBACK"
|
|
85
|
+
assert all(sql != "COMMIT" for sql, _args in conn.calls)
|
|
86
|
+
assert adapter.context.exited is True
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@pytest.mark.asyncio
|
|
90
|
+
async def test_rls_session_early_return_still_closes_scope(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
91
|
+
conn, adapter = _install_adapter(monkeypatch)
|
|
92
|
+
|
|
93
|
+
async def return_from_inside_scope() -> str:
|
|
94
|
+
async with rls_session(
|
|
95
|
+
{"sub": "user-id", "role": "authenticated"},
|
|
96
|
+
role="authenticated",
|
|
97
|
+
database="test",
|
|
98
|
+
):
|
|
99
|
+
return "done"
|
|
100
|
+
|
|
101
|
+
assert await return_from_inside_scope() == "done"
|
|
102
|
+
assert conn.calls[-1][0] == "COMMIT"
|
|
103
|
+
assert adapter.context.exited is True
|
|
@@ -176,6 +176,17 @@ class TestStaleness:
|
|
|
176
176
|
|
|
177
177
|
|
|
178
178
|
class TestFindInCache:
|
|
179
|
+
def test_complete_primary_key_miss_never_scans_the_whole_cache(self):
|
|
180
|
+
M = _make_model("FICPK")
|
|
181
|
+
state = ModelState(M)
|
|
182
|
+
_run(state.cache(M(id=str(uuid4()), name="cached")))
|
|
183
|
+
|
|
184
|
+
def fail_if_scanned(**_kwargs):
|
|
185
|
+
raise AssertionError("complete PK misses must stay O(1)")
|
|
186
|
+
|
|
187
|
+
state._find_in_cache = fail_if_scanned
|
|
188
|
+
assert _run(state.get(id=str(uuid4()))) is None
|
|
189
|
+
|
|
179
190
|
def test_find_by_criteria(self):
|
|
180
191
|
M = _make_model("FIC")
|
|
181
192
|
state = ModelState(M)
|
|
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
|
|
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
|