matrx-orm 3.1.197__tar.gz → 3.1.199__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.197 → matrx_orm-3.1.199}/CHANGELOG.md +6 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/PKG-INFO +1 -1
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/__init__.py +2 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/core/FEATURE.md +11 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/core/async_db_manager.py +20 -3
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/core/db_function.py +38 -17
- matrx_orm-3.1.199/matrx_orm/core/statement_ceiling.py +170 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/pyproject.toml +1 -1
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_call_function.py +21 -3
- matrx_orm-3.1.199/tests/test_statement_ceiling_warning.py +128 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/.gitignore +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/AGENTS.md +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/BASE_CLASS_METHODS.md +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/CLAUDE.md +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/MODEL_API.md +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/README.md +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/database/orm/extended/managers/ai_model_base.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/docs/migrations.md +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/FEATURE.md +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/MODULE_README.md +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/adapters/MODULE_README.md +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/adapters/__init__.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/adapters/async_postgresql.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/adapters/base_adapter.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/adapters/postgrest_client_adapter.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/adapters/supabase_adapter.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/admin/FEATURE.md +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/admin/__init__.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/admin/router.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/api/__init__.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/api/auth.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/api/config.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/api/handlers.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/api/protocol.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/api/server.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/associations.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/cache_debug.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/catalog.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/catalog_sql.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/client/MODULE_README.md +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/client/__init__.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/client/postgres_connection.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/client/postgrest.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/client/supabase_auth.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/client/supabase_config.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/client/supabase_manager.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/core/EXTENDED-TASK.md +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/core/MODULE_README.md +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/core/RELATIONS-TASKS.md +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/core/TASKS.md +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/core/__init__.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/core/admin_lane.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/core/base.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/core/config.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/core/connections.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/core/diagnostics.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/core/expressions.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/core/extended.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/core/fields.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/core/introspection.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/core/listen.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/core/loop_filters.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/core/model_dto.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/core/model_view.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/core/one_database.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/core/paginator.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/core/pool_maintenance.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/core/pool_watch.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/core/pooler_role.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/core/pydantic_bridge.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/core/registry.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/core/relations.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/core/resilience.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/core/rls_session.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/core/session_context.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/core/signals.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/core/tls.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/core/transaction.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/core/types.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/core/write_queue.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/core/write_retry.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/entity.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/error_handling.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/exceptions.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/extended/__init__.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/extended/app_error_handler.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/identity_rule.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/local_sqlite.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/middleware/__init__.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/middleware/base.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/migrations/MODULE_README.md +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/migrations/__init__.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/migrations/cli.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/migrations/ddl.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/migrations/diff.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/migrations/executor.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/migrations/integration.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/migrations/loader.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/migrations/operations.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/migrations/state.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/migrations/table_filter.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/no_org_assignment.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/operations/FEATURE.md +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/operations/MODULE_README.md +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/operations/__init__.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/operations/bulk_update_values.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/operations/catalog_select.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/operations/conflict.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/operations/conflict_writes.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/operations/create.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/operations/db_functions.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/operations/delete.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/operations/dynamic_admin.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/operations/dynamic_crud.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/operations/graph_walk.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/operations/latest_rows.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/operations/queue_claim.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/operations/read.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/operations/relational_backfill.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/operations/staging_load.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/operations/update.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/platform_access.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/platform_db.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/py.typed +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/pytest_live_db_guard.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/pytest_lock_guard.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/pytest_plugin.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/python_sql/MODULE_README.md +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/python_sql/__init__.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/python_sql/db_objects.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/python_sql/table_detailed_relationships.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/python_sql/table_typescript_relationship.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/query/FEATURE.md +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/query/MODULE_README.md +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/query/__init__.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/query/builder.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/query/executor.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/resources/__init__.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/resources/additional-assigners-2026-09-12.json +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/resources/org_assignment_trigger_baseline.json +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/schema_builder/FEATURE.md +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/schema_builder/MODULE_README.md +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/schema_builder/__init__.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/schema_builder/code_handler.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/schema_builder/codegen_writer.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/schema_builder/columns.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/schema_builder/common.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/schema_builder/diff_preview.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/schema_builder/entity_capabilities.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/schema_builder/generator.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/schema_builder/helpers/__init__.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/schema_builder/helpers/base_generators.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/schema_builder/helpers/entity_generators.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/schema_builder/helpers/git_checker.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/schema_builder/package_wiring.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/schema_builder/relationships.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/schema_builder/runner.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/schema_builder/schema.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/schema_builder/schema_manager.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/schema_builder/tables.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/schema_builder/views.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/secrets_battery/FEATURE.md +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/secrets_battery/__init__.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/secrets_battery/attachments.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/secrets_battery/backup_restore.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/secrets_battery/crypto.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/secrets_battery/host_authorization.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/secrets_battery/items.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/secrets_battery/model.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/secrets_battery/mutation_receipts.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/secrets_battery/native_enable.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/secrets_battery/native_lifecycle.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/secrets_battery/native_passkeys.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/secrets_battery/native_source.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/secrets_battery/org_service.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/secrets_battery/org_vault_gate.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/secrets_battery/service.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/secrets_battery/vault_history.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/session/README.md +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/session/__init__.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/session/coalesce.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/session/dag.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/session/errors.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/session/fallback.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/session/flush.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/session/lifecycle.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/session/managed.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/session/op.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/session/reads.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/session/session.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/session/telemetry.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/sql_executor/MODULE_README.md +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/sql_executor/__init__.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/sql_executor/executor.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/sql_executor/queries.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/sql_executor/registry.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/sql_executor/types.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/sql_executor/utils.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/state.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/utils/__init__.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/utils/sql_utils.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/utils/type_converters.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/matrx_orm/write_actor.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/scripts/git-branches.sh +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/scripts/publish.sh +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/scripts/release.sh +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/MODULE_README.md +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/__init__.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/conftest.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/MODULE_README.md +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/__init__.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_adapter_registry.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_admin_db_columns.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_admin_response_rendering.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_admin_search_negation.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_admin_search_sentinels.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_advisory_lock.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_agent_message.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_api_auth.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_api_config.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_api_handlers.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_api_protocol.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_array_agg_order_by.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_association_trigger_actor_naming.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_associations.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_associations_wiring.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_bulk_hydration_offload.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_bulk_update_by_pk.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_bulk_upsert_increment_set_fields.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_cache_debug.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_case_when.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_catalog.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_column_scoped_writes.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_committed_row_guard.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_composite_pk_filter.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_composite_pk_write_paths.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_config.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_conflict_target.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_conflict_writes.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_connection_codecs.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_connection_poison_guard.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_connection_primitives.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_count_composite_distinct.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_count_limit_param_leak.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_db_function_field.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_ddl_generator.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_deadlock_retry.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_derived_table.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_dirty_tracking.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_dynamic_crud.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_exceptions.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_execute_query_integrity_mapping.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_expression_primitives.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_fields.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_filter_jsonb_agg_array_index.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_fk_cycle_resolution.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_from_alias.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_fts.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_func_expression_args.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_graph_walk.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_having_annotate_alias.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_insert_ignore_and_admin_primitives.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_introspect_rls_policies.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_join_and_aggregate.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_latest_rows.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_listen_notify.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_local_sqlite.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_lock_contention_retry.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_managed_write_guard.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_matrx_entity.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_migration_diff_types.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_migration_loader.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_model_instance.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_model_meta.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_optimistic_concurrency.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_order_by_expression.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_org_assignment_inventory.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_orm_banners_are_client_safe.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_parameter_name_hydration.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_patch_jsonb_path.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_percentile_cont.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_persisted_count.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_person_scoped_session_bypasses_shared_cache.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_pool_create_never_wedges.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_pool_holder_census.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_pool_loop_recreation.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_pool_maintenance.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_postgrest_filters.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_pydantic_bridge.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_query_builder.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_query_error_detail.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_query_executor_sql.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_query_timeout.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_querybuilder_clone_semantics.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_queue_claim.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_read_connection_retry.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_registry.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_registry_multi_database.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_registry_multi_schema.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_relational_backfill.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_relations.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_resilience.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_rls_session_scope.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_schema_exists.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_session_advisory_lock.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_standalone_write_session_context.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_state_cache.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_subquery_filter_raw.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_subquery_in_filter.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_subquery_nested_expressions.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_supabase_auth.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_supabase_config.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_tls.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_transaction_database_identity.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_typed_join_projection.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_typed_predicates.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_update_case_expression.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_update_subquery.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_update_with_rebase.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_upsert_default_leak.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_upsert_with_conflict.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_values_jsonb_decode.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_watched_lifecycle.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_write_queue_deadline.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level1/test_write_retry_semantics.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level2/MODULE_README.md +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level2/__init__.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level2/conftest.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level2/test_bulk_ops.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level2/test_cache_integration.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level2/test_crud.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level2/test_foreign_keys.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level2/test_m2m.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level2/test_manager.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level2/test_migrations_live.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level2/test_query_execution.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level2/test_schema_diff.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/level2/verify_committed_row_guard_pg17.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/live_pooler_role.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/native_source_cases.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/native_source_oracle.rs +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/sample_project/.env.example +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/sample_project/README.md +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/sample_project/__init__.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/sample_project/generate.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/sample_project/generated/.gitkeep +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/sample_project/matrx_orm.yaml +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/sample_project/test_schema_generation.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/sample_project_desktop/.env.example +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/sample_project_desktop/README.md +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/sample_project_desktop/__init__.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/sample_project_desktop/client_example.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/sample_project_desktop/client_example.ts +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/sample_project_desktop/client_supabase_example.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/sample_project_desktop/server.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/schema/entity_tests.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/schema/test_base_generation.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/schema/test_composite_pk_fk_generation.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/schema/test_composite_unique_generation.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/schema/test_dto_identity_generation.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/schema/test_entity_capability_generation.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/schema/test_enum_ordering.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/schema/test_generate_schema.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/schema/test_junction_analysis.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/schema/test_multi_schema_output.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/schema/test_schema_analysis.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/schema/test_sql_expression_defaults.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/schema/test_table_relationships_query.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/schema/test_view_generation.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/schema_builder/test_array_default_parsing.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/schema_builder/test_focused_generation_plan.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/schema_builder/test_host_models_aggregator.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/schema_builder/test_never_filter_columns.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/session/__init__.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/session/conftest.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/session/test_a_failed_capture_still_names_the_failure.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/session/test_cache_no_downgrade.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/session/test_capture_poison_proof.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/session/test_coalesce.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/session/test_dag.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/session/test_disk_spill_fallback.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/session/test_flush_individual_fallback.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/session/test_governed_write.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/session/test_live_db_guard_plugin.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/session/test_managed.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/session/test_op_enqueue_site.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/session/test_reads.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/session/test_record_error_bounded_insert.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/session/test_record_error_storm.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/session/test_session.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/session/test_spill_guard_plugin.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/test_array_arg_element_type.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/test_attachment_mutation_serialization.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/test_backup_alias_conflict.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/test_backup_history_restore.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/test_backup_restore_live.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/test_call_function_as.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/test_call_function_none_mode.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/test_composite_fk_is_not_a_column_fk.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/test_database_name_alias.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/test_db_functions.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/test_dsn_password_masking.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/test_dynamic_select_unregistered.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/test_immutable_insert_or_exact_verify.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/test_keyed_digest.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/test_model_cls_refactor.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/test_native_enable.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/test_native_lifecycle.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/test_native_lifecycle_fences.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/test_native_passkey_receipts.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/test_native_source.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/test_no_blocking_asyncpg_tls.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/test_one_database.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/test_order_by_desc_nulls_last.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/test_org_vault_gate.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/test_pooler_role_guard.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/test_secret_audit_names_its_organization.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/test_secrets_battery.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/test_secrets_minting_keys.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/test_shared_lock_guard.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/test_sql_param_casts.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/test_sync_twins_guard.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/test_update_result_truthiness.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/test_user_visible_lookup.py +0 -0
- {matrx_orm-3.1.197 → matrx_orm-3.1.199}/tests/test_vault_aggregate_recovery_stateful.py +0 -0
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
- **Fix: a driver timeout longer than the server's statement ceiling screams.**
|
|
6
|
+
`call_function` / `call_function_as` / `execute_query` read the connection's
|
|
7
|
+
effective `statement_timeout` and log one WARNING per call site when the
|
|
8
|
+
`timeout=` passed is longer (Postgres cancels at the ceiling regardless). New
|
|
9
|
+
`SERVER_CEILING` sentinel: `timeout=SERVER_CEILING` waits the connection's
|
|
10
|
+
ceiling + 1 s. Guard: `tests/test_statement_ceiling_warning.py`.
|
|
5
11
|
- **Fix (security): the by-id cache never answers inside `rls_session`.** The
|
|
6
12
|
StateManager cache is shared process-wide and keyed by primary key alone, so a
|
|
7
13
|
row fetched for one person (or the privileged process) was served to another
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: matrx-orm
|
|
3
|
-
Version: 3.1.
|
|
3
|
+
Version: 3.1.199
|
|
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
|
|
@@ -31,6 +31,7 @@ from .core.config import (
|
|
|
31
31
|
resolve_pool_mode,
|
|
32
32
|
)
|
|
33
33
|
from .core.db_function import ArrayArg, TypedArg, call_function, call_function_as, call_function_sync
|
|
34
|
+
from .core.statement_ceiling import SERVER_CEILING
|
|
34
35
|
from .core.connections import (
|
|
35
36
|
create_external_pool,
|
|
36
37
|
get_registered_pool,
|
|
@@ -472,6 +473,7 @@ __all__ = [
|
|
|
472
473
|
"install_asyncpg_idle_close_filter",
|
|
473
474
|
# ── DB-side function calls (RPC) ──────────────────────────────────────────
|
|
474
475
|
"call_function",
|
|
476
|
+
"SERVER_CEILING",
|
|
475
477
|
"call_function_as",
|
|
476
478
|
"call_function_sync",
|
|
477
479
|
"MatrxEntity",
|
|
@@ -126,6 +126,16 @@ for scalar, row-set, discarded-result, and active-transaction calls. Callers can
|
|
|
126
126
|
therefore distinguish an ambiguous in-flight RPC from a pool-acquire failure and
|
|
127
127
|
must choose replay policy explicitly.
|
|
128
128
|
|
|
129
|
+
**A driver `timeout=` never outlives the server's statement ceiling silently.**
|
|
130
|
+
`call_function(..., timeout=N)` / `call_function_as` / `execute_query(statement_timeout=N)`
|
|
131
|
+
set only asyncpg's wait; Postgres still cancels at the connection's `statement_timeout`
|
|
132
|
+
(the `postgres` role carries 30 s since aidream migration 0919, or a `SET LOCAL` from
|
|
133
|
+
`rls_session`). `core/statement_ceiling.py` reads that ceiling from the connection
|
|
134
|
+
(`pg_settings`, once per database for pool connections, live inside a transaction) and
|
|
135
|
+
logs one WARNING per call site when N exceeds it. `timeout=SERVER_CEILING` resolves to
|
|
136
|
+
the ceiling + 1 s, for a bounded statement that needs more than the pool's 10 s default.
|
|
137
|
+
Static twin: aidream `scripts/check_driver_timeout_vs_statement_ceiling.py`.
|
|
138
|
+
|
|
129
139
|
**Opt-in transient retries can be bounded and desynchronized.**
|
|
130
140
|
`retry_on_transient(..., jitter_ratio=...)` retains deterministic exponential
|
|
131
141
|
backoff by default; a caller recovering a replay-safe lock conflict can add at
|
|
@@ -329,6 +339,7 @@ can never disagree about who the blocker was.
|
|
|
329
339
|
Verified against the live database: 2026-08-21.
|
|
330
340
|
|
|
331
341
|
## Change log
|
|
342
|
+
- 2026-09-30: `statement_ceiling.py` — a driver timeout above the server's statement ceiling is logged once per call site; `SERVER_CEILING` sentinel. ~19 callers passed 60-600 s under the 30 s `postgres` ceiling (the nightly keyword classifier was cancelled at 30 s for eight days).
|
|
332
343
|
- 2026-09-12 (later): ONE retry mechanism. Two independent lock-retry
|
|
333
344
|
implementations for the same class landed hours apart — a `LockContentionError`
|
|
334
345
|
+ constant-budget loop inside `execute_query`, and a `TransientTransactionError`
|
|
@@ -24,6 +24,7 @@ from matrx_orm.core.pooler_role import (
|
|
|
24
24
|
assert_no_session_role_change,
|
|
25
25
|
reprove_empty_read,
|
|
26
26
|
)
|
|
27
|
+
from matrx_orm.core.statement_ceiling import resolve_driver_timeout
|
|
27
28
|
from matrx_orm.core.tls import resolve_asyncpg_ssl
|
|
28
29
|
from matrx_orm.exceptions import (
|
|
29
30
|
AdapterError,
|
|
@@ -1286,7 +1287,9 @@ class AsyncDatabaseManager:
|
|
|
1286
1287
|
per-call ``fetch(..., timeout=)`` and overrides the pool's
|
|
1287
1288
|
``command_timeout`` for this statement only — the QueryBuilder
|
|
1288
1289
|
``.timeout()`` seam for known-long reads. ``None`` keeps the pool
|
|
1289
|
-
default.
|
|
1290
|
+
default. It is a DRIVER timeout: the server's own statement_timeout
|
|
1291
|
+
still cancels at its ceiling, and a value above it is logged once per
|
|
1292
|
+
call site (``matrx_orm.core.statement_ceiling``).
|
|
1290
1293
|
"""
|
|
1291
1294
|
# Layer 1 of the pooler-role defence: SQL that would leave a borrowed role
|
|
1292
1295
|
# on a shared server connection never reaches Postgres. See
|
|
@@ -1302,6 +1305,13 @@ class AsyncDatabaseManager:
|
|
|
1302
1305
|
tx_conn = None
|
|
1303
1306
|
|
|
1304
1307
|
if tx_conn is not None:
|
|
1308
|
+
statement_timeout = await resolve_driver_timeout(
|
|
1309
|
+
tx_conn,
|
|
1310
|
+
database=config_name,
|
|
1311
|
+
timeout=statement_timeout,
|
|
1312
|
+
operation="execute_query",
|
|
1313
|
+
in_transaction=True,
|
|
1314
|
+
)
|
|
1305
1315
|
try:
|
|
1306
1316
|
results = await tx_conn.fetch(query, *args, timeout=statement_timeout)
|
|
1307
1317
|
return [dict(r) for r in results]
|
|
@@ -1411,8 +1421,15 @@ class AsyncDatabaseManager:
|
|
|
1411
1421
|
read_attempt = 0
|
|
1412
1422
|
while True:
|
|
1413
1423
|
async with cls.get_connection(config_name, timeout) as conn:
|
|
1424
|
+
driver_timeout = await resolve_driver_timeout(
|
|
1425
|
+
conn,
|
|
1426
|
+
database=config_name,
|
|
1427
|
+
timeout=statement_timeout,
|
|
1428
|
+
operation="execute_query",
|
|
1429
|
+
in_transaction=False,
|
|
1430
|
+
)
|
|
1414
1431
|
try:
|
|
1415
|
-
results = await conn.fetch(query, *args, timeout=
|
|
1432
|
+
results = await conn.fetch(query, *args, timeout=driver_timeout)
|
|
1416
1433
|
if read_attempt > 0:
|
|
1417
1434
|
logger.warning(
|
|
1418
1435
|
"[read-retry] SELECT HEALED on attempt %d after a "
|
|
@@ -1435,7 +1452,7 @@ class AsyncDatabaseManager:
|
|
|
1435
1452
|
config_name=config_name,
|
|
1436
1453
|
query=query,
|
|
1437
1454
|
args=args,
|
|
1438
|
-
statement_timeout=
|
|
1455
|
+
statement_timeout=driver_timeout,
|
|
1439
1456
|
normalize=_normalize_record,
|
|
1440
1457
|
)
|
|
1441
1458
|
if true_rows is not None:
|
|
@@ -69,6 +69,7 @@ from dataclasses import dataclass
|
|
|
69
69
|
from typing import Any, Literal
|
|
70
70
|
|
|
71
71
|
from matrx_orm.core.async_db_manager import AsyncDatabaseManager
|
|
72
|
+
from matrx_orm.core.statement_ceiling import DriverTimeout, resolve_driver_timeout
|
|
72
73
|
from matrx_orm.core.transaction import get_active_connection
|
|
73
74
|
from matrx_orm.core.write_retry import (
|
|
74
75
|
is_confidently_aborted_lock_contention,
|
|
@@ -191,14 +192,14 @@ def _validate_identifier(name: str, kind: str) -> None:
|
|
|
191
192
|
)
|
|
192
193
|
|
|
193
194
|
|
|
194
|
-
async def call_function(
|
|
195
|
+
async def call_function(
|
|
195
196
|
database: str,
|
|
196
197
|
schema: str,
|
|
197
198
|
function: str,
|
|
198
199
|
*args: Any,
|
|
199
200
|
mode: Literal["scalar", "none", "rows"] = "scalar",
|
|
200
201
|
field: str | None = None,
|
|
201
|
-
timeout:
|
|
202
|
+
timeout: DriverTimeout = None, # noqa: ASYNC109 — maps onto asyncpg's per-call timeout
|
|
202
203
|
) -> Any:
|
|
203
204
|
"""Call ``schema.function(args...)`` and return its value.
|
|
204
205
|
|
|
@@ -222,9 +223,15 @@ async def call_function( # noqa: ASYNC109 — maps onto asyncpg's per-call time
|
|
|
222
223
|
timeout: per-call driver timeout in seconds, overriding the pool's
|
|
223
224
|
``command_timeout`` (default 10s). Use for known-slow analytical
|
|
224
225
|
functions (aggregation RPCs) that legitimately exceed the pool
|
|
225
|
-
default; ``None`` keeps the pool's setting.
|
|
226
|
-
``statement_timeout`` (
|
|
227
|
-
|
|
226
|
+
default; ``None`` keeps the pool's setting. It does NOT lengthen
|
|
227
|
+
the server's ``statement_timeout`` (the role default — 30 s for
|
|
228
|
+
``postgres`` since aidream migration 0919 — or a ``SET LOCAL`` from
|
|
229
|
+
``rls_session``): Postgres still cancels at that ceiling, and a
|
|
230
|
+
timeout above it is logged once per call site as a WARNING
|
|
231
|
+
(``matrx_orm.core.statement_ceiling``). Pass
|
|
232
|
+
``timeout=SERVER_CEILING`` for a bounded statement that needs more
|
|
233
|
+
than the pool default: it waits exactly as long as the server
|
|
234
|
+
allows, read from the connection.
|
|
228
235
|
|
|
229
236
|
Returns:
|
|
230
237
|
The scalar value (mode="scalar"), ``None`` (mode="none"), or
|
|
@@ -257,21 +264,28 @@ async def call_function( # noqa: ASYNC109 — maps onto asyncpg's per-call time
|
|
|
257
264
|
else:
|
|
258
265
|
sql = f"SELECT {call}"
|
|
259
266
|
|
|
260
|
-
async def invoke(conn: Any) -> Any:
|
|
267
|
+
async def invoke(conn: Any, *, in_transaction: bool = False) -> Any:
|
|
268
|
+
driver_timeout = await resolve_driver_timeout(
|
|
269
|
+
conn,
|
|
270
|
+
database=database,
|
|
271
|
+
timeout=timeout,
|
|
272
|
+
operation=f"call_function {schema}.{function}",
|
|
273
|
+
in_transaction=in_transaction,
|
|
274
|
+
)
|
|
261
275
|
try:
|
|
262
276
|
if mode == "none":
|
|
263
|
-
await conn.execute(sql, *bound_args, timeout=
|
|
277
|
+
await conn.execute(sql, *bound_args, timeout=driver_timeout)
|
|
264
278
|
return None
|
|
265
279
|
if mode == "rows":
|
|
266
|
-
rows = await conn.fetch(sql, *bound_args, timeout=
|
|
280
|
+
rows = await conn.fetch(sql, *bound_args, timeout=driver_timeout)
|
|
267
281
|
return [dict(r) for r in rows]
|
|
268
|
-
return await conn.fetchval(sql, *bound_args, timeout=
|
|
282
|
+
return await conn.fetchval(sql, *bound_args, timeout=driver_timeout)
|
|
269
283
|
except TimeoutError as exc:
|
|
270
284
|
raise QueryTimeoutError(
|
|
271
285
|
operation=f"call_function:{schema}.{function}",
|
|
272
286
|
query=sql,
|
|
273
287
|
args=bound_args,
|
|
274
|
-
timeout=
|
|
288
|
+
timeout=driver_timeout,
|
|
275
289
|
original_error=exc,
|
|
276
290
|
) from None
|
|
277
291
|
|
|
@@ -281,7 +295,7 @@ async def call_function( # noqa: ASYNC109 — maps onto asyncpg's per-call time
|
|
|
281
295
|
# transaction's owner can redo the unit, so no retry here — the refusal is
|
|
282
296
|
# typed the same way ``execute_query``'s transaction branch types it.
|
|
283
297
|
try:
|
|
284
|
-
return await invoke(active_conn)
|
|
298
|
+
return await invoke(active_conn, in_transaction=True)
|
|
285
299
|
except Exception as exc:
|
|
286
300
|
if not is_confidently_aborted_lock_contention(exc):
|
|
287
301
|
raise
|
|
@@ -306,14 +320,14 @@ async def call_function( # noqa: ASYNC109 — maps onto asyncpg's per-call time
|
|
|
306
320
|
)
|
|
307
321
|
|
|
308
322
|
|
|
309
|
-
async def call_function_as(
|
|
323
|
+
async def call_function_as(
|
|
310
324
|
actor_id: str,
|
|
311
325
|
database: str,
|
|
312
326
|
schema: str,
|
|
313
327
|
function: str,
|
|
314
328
|
*args: Any,
|
|
315
329
|
mode: Literal["scalar", "none", "rows"] = "scalar",
|
|
316
|
-
timeout:
|
|
330
|
+
timeout: DriverTimeout = None, # noqa: ASYNC109 — maps onto asyncpg's per-call timeout
|
|
317
331
|
) -> Any:
|
|
318
332
|
"""Call ``schema.function(args...)`` on the server connection AS ``actor_id``.
|
|
319
333
|
|
|
@@ -354,13 +368,20 @@ async def call_function_as( # noqa: ASYNC109 — maps onto asyncpg's per-call t
|
|
|
354
368
|
|
|
355
369
|
async def _attempt() -> Any:
|
|
356
370
|
async with AsyncDatabaseManager.get_connection(database) as conn:
|
|
371
|
+
driver_timeout = await resolve_driver_timeout(
|
|
372
|
+
conn,
|
|
373
|
+
database=database,
|
|
374
|
+
timeout=timeout,
|
|
375
|
+
operation=f"call_function_as {schema}.{function}",
|
|
376
|
+
in_transaction=False,
|
|
377
|
+
)
|
|
357
378
|
if mode == "none":
|
|
358
|
-
await conn.execute(sql, *bound_args, timeout=
|
|
379
|
+
await conn.execute(sql, *bound_args, timeout=driver_timeout)
|
|
359
380
|
return None
|
|
360
381
|
if mode == "rows":
|
|
361
|
-
rows = await conn.fetch(sql, *bound_args, timeout=
|
|
382
|
+
rows = await conn.fetch(sql, *bound_args, timeout=driver_timeout)
|
|
362
383
|
return [dict(r) for r in rows]
|
|
363
|
-
return await conn.fetchval(sql, *bound_args, timeout=
|
|
384
|
+
return await conn.fetchval(sql, *bound_args, timeout=driver_timeout)
|
|
364
385
|
|
|
365
386
|
return await run_with_lock_retry(_attempt, config_name=database, query=sql, args=bound_args)
|
|
366
387
|
|
|
@@ -372,7 +393,7 @@ def call_function_sync(
|
|
|
372
393
|
*args: Any,
|
|
373
394
|
mode: Literal["scalar", "none", "rows"] = "scalar",
|
|
374
395
|
field: str | None = None,
|
|
375
|
-
timeout:
|
|
396
|
+
timeout: DriverTimeout = None,
|
|
376
397
|
) -> Any:
|
|
377
398
|
"""Synchronous twin of :func:`call_function`. Sync contexts only."""
|
|
378
399
|
import asyncio
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
"""A driver ``timeout=`` longer than the server's statement ceiling is a lie — say so.
|
|
2
|
+
|
|
3
|
+
``call_function(..., timeout=300)`` and ``execute_query(..., statement_timeout=300)`` set
|
|
4
|
+
the DRIVER's (asyncpg's) per-call timeout. They do not, and cannot, lengthen the
|
|
5
|
+
server's own ``statement_timeout``: that comes from the connecting role's default
|
|
6
|
+
(``ALTER ROLE … SET statement_timeout``), the pool's server settings, or a
|
|
7
|
+
``SET [LOCAL] statement_timeout`` issued as its own statement before the call
|
|
8
|
+
(``rls_session`` / ``acting_as_user(statement_timeout_ms=…)``). When the driver
|
|
9
|
+
timeout is the longer of the two, the caller believes it has minutes and Postgres
|
|
10
|
+
cancels it at the ceiling (``57014 canceling statement due to statement timeout``).
|
|
11
|
+
|
|
12
|
+
That is what froze the approved nightly keyword classifier for eight days in
|
|
13
|
+
September 2026: aidream migration 0919 capped the ``postgres`` role at 30 s on
|
|
14
|
+
purpose, and a job that passed ``timeout=300`` for a 55–128 s statement was cut at
|
|
15
|
+
30 s every night.
|
|
16
|
+
|
|
17
|
+
This module reads the ceiling from its one source of truth — the connection's own
|
|
18
|
+
effective ``statement_timeout`` (``pg_settings.setting``, milliseconds), which already
|
|
19
|
+
folds in the role default, the pool settings and any ``SET LOCAL`` — and logs a
|
|
20
|
+
WARNING, once per call site, when the driver timeout exceeds it. It never blocks the
|
|
21
|
+
call: the statement still runs exactly as before. The static twin is
|
|
22
|
+
``aidream/scripts/check_driver_timeout_vs_statement_ceiling.py``.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
import logging
|
|
28
|
+
import sys
|
|
29
|
+
from typing import Any
|
|
30
|
+
|
|
31
|
+
logger = logging.getLogger("matrx_orm.statement_ceiling")
|
|
32
|
+
|
|
33
|
+
_CEILING_SQL = "SELECT setting::bigint FROM pg_settings WHERE name = 'statement_timeout'"
|
|
34
|
+
|
|
35
|
+
#: Pool connections share one effective ceiling per database (role default + pool
|
|
36
|
+
#: settings), so it is read once per database. A transaction connection may carry a
|
|
37
|
+
#: ``SET LOCAL``, so it is always read live.
|
|
38
|
+
_POOL_CEILING_MS: dict[str, int] = {}
|
|
39
|
+
_WARNED: set[tuple[str, str, int]] = set()
|
|
40
|
+
_UNREADABLE_WARNED: set[str] = set()
|
|
41
|
+
|
|
42
|
+
_ORM_PACKAGE_MARKER = "/matrx_orm/"
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _caller_site() -> str:
|
|
46
|
+
"""The first frame outside matrx_orm — the code that chose the timeout."""
|
|
47
|
+
frame = sys._getframe(1)
|
|
48
|
+
while frame is not None:
|
|
49
|
+
filename = frame.f_code.co_filename
|
|
50
|
+
if _ORM_PACKAGE_MARKER not in filename.replace("\\", "/") and "/asyncio/" not in filename:
|
|
51
|
+
return f"{filename}:{frame.f_lineno} ({frame.f_code.co_name})"
|
|
52
|
+
frame = frame.f_back
|
|
53
|
+
return "<unknown caller>"
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
async def _read_ceiling_ms(conn: Any, *, database: str, in_transaction: bool) -> int | None:
|
|
57
|
+
if not in_transaction and database in _POOL_CEILING_MS:
|
|
58
|
+
return _POOL_CEILING_MS[database]
|
|
59
|
+
try:
|
|
60
|
+
value = await conn.fetchval(_CEILING_SQL)
|
|
61
|
+
ceiling_ms = int(value)
|
|
62
|
+
except Exception as exc: # noqa: BLE001 — the check must never break the call it guards
|
|
63
|
+
if database not in _UNREADABLE_WARNED:
|
|
64
|
+
_UNREADABLE_WARNED.add(database)
|
|
65
|
+
logger.warning(
|
|
66
|
+
"[matrx-orm] statement-ceiling check could not read statement_timeout on %r "
|
|
67
|
+
"(%s: %s); driver timeouts on this database are unchecked until it can. "
|
|
68
|
+
"Remedy: make pg_settings readable to the pool role.",
|
|
69
|
+
database,
|
|
70
|
+
type(exc).__name__,
|
|
71
|
+
exc,
|
|
72
|
+
)
|
|
73
|
+
return None
|
|
74
|
+
if not in_transaction:
|
|
75
|
+
_POOL_CEILING_MS[database] = ceiling_ms
|
|
76
|
+
return ceiling_ms
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class ServerCeiling:
|
|
80
|
+
"""Sentinel for ``timeout=``: wait exactly as long as the server itself allows.
|
|
81
|
+
|
|
82
|
+
For a statement that legitimately needs more than the pool's driver default
|
|
83
|
+
(``command_timeout``, 10 s) but is bounded under the server's ceiling: the driver
|
|
84
|
+
timeout is resolved per call from the connection's own ``statement_timeout`` plus
|
|
85
|
+
:data:`SERVER_CEILING_MARGIN_SECONDS`, so Postgres's ``57014`` — the true answer —
|
|
86
|
+
always arrives before the driver gives up, and no caller carries a second copy of
|
|
87
|
+
the ceiling as a number.
|
|
88
|
+
"""
|
|
89
|
+
|
|
90
|
+
_instance: ServerCeiling | None = None
|
|
91
|
+
|
|
92
|
+
def __new__(cls) -> ServerCeiling:
|
|
93
|
+
if cls._instance is None:
|
|
94
|
+
cls._instance = super().__new__(cls)
|
|
95
|
+
return cls._instance
|
|
96
|
+
|
|
97
|
+
def __repr__(self) -> str:
|
|
98
|
+
return "SERVER_CEILING"
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
SERVER_CEILING = ServerCeiling()
|
|
102
|
+
|
|
103
|
+
#: How much longer than the server's ceiling the driver waits under ``SERVER_CEILING``,
|
|
104
|
+
#: so the server's cancellation (with its real error) is what the caller sees.
|
|
105
|
+
SERVER_CEILING_MARGIN_SECONDS = 1.0
|
|
106
|
+
|
|
107
|
+
DriverTimeout = float | ServerCeiling | None
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
async def resolve_driver_timeout(
|
|
111
|
+
conn: Any,
|
|
112
|
+
*,
|
|
113
|
+
database: str,
|
|
114
|
+
timeout: DriverTimeout, # noqa: ASYNC109 — the caller's asyncpg timeout, inspected, never awaited
|
|
115
|
+
operation: str,
|
|
116
|
+
in_transaction: bool,
|
|
117
|
+
) -> float | None:
|
|
118
|
+
"""The driver timeout to pass to asyncpg for this call — and a scream when it lies.
|
|
119
|
+
|
|
120
|
+
``SERVER_CEILING`` resolves to the connection's ceiling plus the margin. A number
|
|
121
|
+
larger than the ceiling is passed through unchanged (the call is never blocked) and
|
|
122
|
+
logged once per call site as a WARNING naming the caller and the ceiling.
|
|
123
|
+
"""
|
|
124
|
+
if timeout is None:
|
|
125
|
+
return None
|
|
126
|
+
if isinstance(timeout, ServerCeiling):
|
|
127
|
+
ceiling_ms = await _read_ceiling_ms(conn, database=database, in_transaction=in_transaction)
|
|
128
|
+
if ceiling_ms is None:
|
|
129
|
+
return None # unreadable — already announced by _read_ceiling_ms
|
|
130
|
+
if ceiling_ms <= 0:
|
|
131
|
+
key = ("<server-ceiling-unbounded>", database, 0)
|
|
132
|
+
if key not in _WARNED:
|
|
133
|
+
_WARNED.add(key)
|
|
134
|
+
logger.warning(
|
|
135
|
+
"[matrx-orm] %s asked for timeout=SERVER_CEILING on %r but this connection has "
|
|
136
|
+
"no statement_timeout (0); the pool's driver default applies instead. Remedy: "
|
|
137
|
+
"give the role or the session a statement_timeout.",
|
|
138
|
+
_caller_site(),
|
|
139
|
+
database,
|
|
140
|
+
)
|
|
141
|
+
return None
|
|
142
|
+
return ceiling_ms / 1000 + SERVER_CEILING_MARGIN_SECONDS
|
|
143
|
+
if timeout <= 0:
|
|
144
|
+
return timeout
|
|
145
|
+
ceiling_ms = await _read_ceiling_ms(conn, database=database, in_transaction=in_transaction)
|
|
146
|
+
if not ceiling_ms or ceiling_ms <= 0:
|
|
147
|
+
# 0 means Postgres enforces no statement ceiling on this connection.
|
|
148
|
+
return timeout
|
|
149
|
+
if timeout * 1000 <= ceiling_ms:
|
|
150
|
+
return timeout
|
|
151
|
+
site = _caller_site()
|
|
152
|
+
key = (site, database, ceiling_ms)
|
|
153
|
+
if key not in _WARNED:
|
|
154
|
+
_WARNED.add(key)
|
|
155
|
+
logger.warning(
|
|
156
|
+
f"[matrx-orm] DRIVER TIMEOUT OUTLIVES THE SERVER CEILING: {site} passes timeout={timeout:g}s "
|
|
157
|
+
f"to {operation} on {database!r}, but this connection's statement_timeout is "
|
|
158
|
+
f"{ceiling_ms / 1000:g}s — Postgres cancels the statement at {ceiling_ms / 1000:g}s "
|
|
159
|
+
f"(57014) no matter what the driver waits for. Remedy: bound the statement below the "
|
|
160
|
+
f"ceiling (split it per site/span) and pass timeout=SERVER_CEILING, or declare the "
|
|
161
|
+
f"server clock with rls_session/acting_as_user(statement_timeout_ms=...)."
|
|
162
|
+
)
|
|
163
|
+
return timeout
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def reset_statement_ceiling_cache() -> None:
|
|
167
|
+
"""Test seam: forget cached ceilings and already-warned call sites."""
|
|
168
|
+
_POOL_CEILING_MS.clear()
|
|
169
|
+
_WARNED.clear()
|
|
170
|
+
_UNREADABLE_WARNED.clear()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "matrx-orm"
|
|
3
|
-
version = "3.1.
|
|
3
|
+
version = "3.1.199"
|
|
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" }
|
|
@@ -9,7 +9,7 @@ they ever reach a query string; arguments are always bound parameters.
|
|
|
9
9
|
|
|
10
10
|
from __future__ import annotations
|
|
11
11
|
|
|
12
|
-
from unittest.mock import AsyncMock
|
|
12
|
+
from unittest.mock import AsyncMock, call
|
|
13
13
|
|
|
14
14
|
import pytest
|
|
15
15
|
from matrx_orm.core import transaction as tx_module
|
|
@@ -268,11 +268,29 @@ class TestConnectionAndDriverOptions:
|
|
|
268
268
|
assert pool.databases == ["analytics"]
|
|
269
269
|
|
|
270
270
|
async def test_per_call_timeout_reaches_the_driver(self, pool) -> None:
|
|
271
|
+
# A numeric timeout must inspect the server ceiling once per pooled
|
|
272
|
+
# database, then pass the caller's requested timeout through unchanged.
|
|
273
|
+
# Keep the ceiling lookup separate from the RPC assertion: both queries
|
|
274
|
+
# are required, but only the latter is the operation under test.
|
|
275
|
+
from matrx_orm.core.statement_ceiling import _CEILING_SQL, reset_statement_ceiling_cache
|
|
276
|
+
|
|
277
|
+
reset_statement_ceiling_cache()
|
|
278
|
+
|
|
279
|
+
async def fetchval(sql, *args, timeout=None):
|
|
280
|
+
if sql == _CEILING_SQL:
|
|
281
|
+
return 30_000
|
|
282
|
+
return "ok"
|
|
283
|
+
|
|
284
|
+
pool.conn.fetchval.side_effect = fetchval
|
|
271
285
|
await call_function("db", "public", "slow_rollup", "org-1", timeout=45.0)
|
|
272
286
|
await call_function("db", "public", "slow_reconcile", "org-1", mode="none", timeout=90.0)
|
|
273
|
-
pool.conn.fetchval.
|
|
274
|
-
|
|
287
|
+
pool.conn.fetchval.assert_has_awaits(
|
|
288
|
+
[
|
|
289
|
+
call(_CEILING_SQL),
|
|
290
|
+
call('SELECT "public"."slow_rollup"($1)', "org-1", timeout=45.0),
|
|
291
|
+
]
|
|
275
292
|
)
|
|
293
|
+
assert pool.conn.fetchval.await_count == 2 # one cached ceiling read, one RPC
|
|
276
294
|
pool.conn.execute.assert_awaited_once_with(
|
|
277
295
|
'SELECT count(*) FROM "public"."slow_reconcile"($1)', "org-1", timeout=90.0
|
|
278
296
|
)
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"""A driver ``timeout=`` above the server's statement ceiling must scream, once per site.
|
|
2
|
+
|
|
3
|
+
``call_function(..., timeout=300)`` only sets asyncpg's wait. The 30 s role ceiling
|
|
4
|
+
(aidream migration 0919) still cancels the statement, which is how the nightly keyword
|
|
5
|
+
classifier froze for eight days in September 2026 without a word in any log.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import logging
|
|
11
|
+
from contextlib import asynccontextmanager
|
|
12
|
+
|
|
13
|
+
import pytest
|
|
14
|
+
|
|
15
|
+
from matrx_orm.core import db_function
|
|
16
|
+
from matrx_orm.core.statement_ceiling import reset_statement_ceiling_cache
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class _Conn:
|
|
20
|
+
def __init__(self, ceiling_ms: int) -> None:
|
|
21
|
+
self.ceiling_ms = ceiling_ms
|
|
22
|
+
self.ceiling_reads = 0
|
|
23
|
+
self.calls: list[str] = []
|
|
24
|
+
|
|
25
|
+
async def fetchval(self, sql, *args, timeout=None):
|
|
26
|
+
if "pg_settings" in sql:
|
|
27
|
+
self.ceiling_reads += 1
|
|
28
|
+
return self.ceiling_ms
|
|
29
|
+
self.calls.append(sql)
|
|
30
|
+
return 1
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@pytest.fixture(autouse=True)
|
|
34
|
+
def _fresh_cache():
|
|
35
|
+
reset_statement_ceiling_cache()
|
|
36
|
+
yield
|
|
37
|
+
reset_statement_ceiling_cache()
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _patch(monkeypatch, conn: _Conn, *, active: bool = False) -> None:
|
|
41
|
+
@asynccontextmanager
|
|
42
|
+
async def _get_connection(database, timeout=10.0):
|
|
43
|
+
yield conn
|
|
44
|
+
|
|
45
|
+
monkeypatch.setattr(db_function, "get_active_connection", lambda: conn if active else None)
|
|
46
|
+
monkeypatch.setattr(db_function.AsyncDatabaseManager, "get_connection", _get_connection)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _ceiling_warnings(caplog) -> list[str]:
|
|
50
|
+
return [r.getMessage() for r in caplog.records if "OUTLIVES THE SERVER CEILING" in r.getMessage()]
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
async def test_timeout_above_the_ceiling_warns_once_naming_caller_and_ceiling(monkeypatch, caplog):
|
|
54
|
+
conn = _Conn(ceiling_ms=30_000)
|
|
55
|
+
_patch(monkeypatch, conn)
|
|
56
|
+
caplog.set_level(logging.WARNING, logger="matrx_orm.statement_ceiling")
|
|
57
|
+
|
|
58
|
+
for _ in range(3):
|
|
59
|
+
assert await db_function.call_function("db", "seo", "slow_fn", timeout=300) == 1
|
|
60
|
+
|
|
61
|
+
warnings = _ceiling_warnings(caplog)
|
|
62
|
+
assert len(warnings) == 1, warnings
|
|
63
|
+
assert "test_statement_ceiling_warning.py" in warnings[0]
|
|
64
|
+
assert "timeout=300s" in warnings[0] and "statement_timeout is 30s" in warnings[0]
|
|
65
|
+
assert "seo.slow_fn" in warnings[0]
|
|
66
|
+
# The pool ceiling is read once per database, not once per call.
|
|
67
|
+
assert conn.ceiling_reads == 1
|
|
68
|
+
# The call itself still ran every time — the check never blocks.
|
|
69
|
+
assert len(conn.calls) == 3
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
async def test_timeout_within_the_ceiling_is_silent(monkeypatch, caplog):
|
|
73
|
+
conn = _Conn(ceiling_ms=30_000)
|
|
74
|
+
_patch(monkeypatch, conn)
|
|
75
|
+
caplog.set_level(logging.WARNING, logger="matrx_orm.statement_ceiling")
|
|
76
|
+
|
|
77
|
+
await db_function.call_function("db", "seo", "fast_fn", timeout=25)
|
|
78
|
+
await db_function.call_function("db", "seo", "fast_fn") # no timeout: nothing to read
|
|
79
|
+
|
|
80
|
+
assert _ceiling_warnings(caplog) == []
|
|
81
|
+
assert conn.ceiling_reads == 1
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
async def test_a_declared_server_clock_in_the_transaction_is_honoured(monkeypatch, caplog):
|
|
85
|
+
# rls_session/acting_as_user(statement_timeout_ms=120_000) issued SET LOCAL: the
|
|
86
|
+
# connection's live ceiling is 120 s, so a 120 s driver timeout is honest.
|
|
87
|
+
conn = _Conn(ceiling_ms=120_000)
|
|
88
|
+
_patch(monkeypatch, conn, active=True)
|
|
89
|
+
caplog.set_level(logging.WARNING, logger="matrx_orm.statement_ceiling")
|
|
90
|
+
|
|
91
|
+
await db_function.call_function("db", "seo", "long_fn", timeout=120)
|
|
92
|
+
await db_function.call_function("db", "seo", "long_fn", timeout=120)
|
|
93
|
+
|
|
94
|
+
assert _ceiling_warnings(caplog) == []
|
|
95
|
+
# A transaction connection may carry SET LOCAL, so it is read live every call.
|
|
96
|
+
assert conn.ceiling_reads == 2
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
async def test_zero_ceiling_means_unbounded_and_is_silent(monkeypatch, caplog):
|
|
100
|
+
conn = _Conn(ceiling_ms=0)
|
|
101
|
+
_patch(monkeypatch, conn)
|
|
102
|
+
caplog.set_level(logging.WARNING, logger="matrx_orm.statement_ceiling")
|
|
103
|
+
|
|
104
|
+
await db_function.call_function("db", "ops", "fn", timeout=600)
|
|
105
|
+
|
|
106
|
+
assert _ceiling_warnings(caplog) == []
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
async def test_server_ceiling_waits_exactly_as_long_as_the_server_allows(monkeypatch, caplog):
|
|
110
|
+
from matrx_orm import SERVER_CEILING
|
|
111
|
+
from matrx_orm.core.statement_ceiling import SERVER_CEILING_MARGIN_SECONDS
|
|
112
|
+
|
|
113
|
+
seen: list[float | None] = []
|
|
114
|
+
|
|
115
|
+
class _TimedConn(_Conn):
|
|
116
|
+
async def fetchval(self, sql, *args, timeout=None):
|
|
117
|
+
if "pg_settings" not in sql:
|
|
118
|
+
seen.append(timeout)
|
|
119
|
+
return await super().fetchval(sql, *args, timeout=timeout)
|
|
120
|
+
|
|
121
|
+
conn = _TimedConn(ceiling_ms=30_000)
|
|
122
|
+
_patch(monkeypatch, conn)
|
|
123
|
+
caplog.set_level(logging.WARNING, logger="matrx_orm.statement_ceiling")
|
|
124
|
+
|
|
125
|
+
await db_function.call_function("db", "seo", "bounded_fn", timeout=SERVER_CEILING)
|
|
126
|
+
|
|
127
|
+
assert seen == [30 + SERVER_CEILING_MARGIN_SECONDS]
|
|
128
|
+
assert _ceiling_warnings(caplog) == []
|
|
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
|