matrx-orm 3.1.232__tar.gz → 3.1.233__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.
Files changed (440) hide show
  1. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/.gitignore +1 -1
  2. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/CLAUDE.md +5 -1
  3. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/PKG-INFO +2 -2
  4. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/operations/catalog_select.py +99 -3
  5. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/state.py +60 -26
  6. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/pyproject.toml +2 -2
  7. matrx_orm-3.1.233/tests/test_a_bulk_cache_never_stalls_the_loop.py +113 -0
  8. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/test_dynamic_select_unregistered.py +67 -0
  9. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/AGENTS.md +0 -0
  10. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/BASE_CLASS_METHODS.md +0 -0
  11. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/CHANGELOG.md +0 -0
  12. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/MODEL_API.md +0 -0
  13. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/README.md +0 -0
  14. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/database/orm/extended/managers/ai_model_base.py +0 -0
  15. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/docs/migrations.md +0 -0
  16. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/FEATURE.md +0 -0
  17. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/MODULE_README.md +0 -0
  18. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/__init__.py +0 -0
  19. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/adapters/MODULE_README.md +0 -0
  20. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/adapters/__init__.py +0 -0
  21. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/adapters/async_postgresql.py +0 -0
  22. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/adapters/base_adapter.py +0 -0
  23. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/adapters/postgrest_client_adapter.py +0 -0
  24. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/adapters/supabase_adapter.py +0 -0
  25. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/admin/FEATURE.md +0 -0
  26. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/admin/__init__.py +0 -0
  27. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/admin/router.py +0 -0
  28. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/api/__init__.py +0 -0
  29. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/api/auth.py +0 -0
  30. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/api/config.py +0 -0
  31. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/api/handlers.py +0 -0
  32. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/api/protocol.py +0 -0
  33. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/api/server.py +0 -0
  34. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/associations.py +0 -0
  35. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/cache_debug.py +0 -0
  36. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/catalog.py +0 -0
  37. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/catalog_sql.py +0 -0
  38. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/client/MODULE_README.md +0 -0
  39. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/client/__init__.py +0 -0
  40. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/client/postgres_connection.py +0 -0
  41. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/client/postgrest.py +0 -0
  42. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/client/supabase_auth.py +0 -0
  43. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/client/supabase_config.py +0 -0
  44. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/client/supabase_manager.py +0 -0
  45. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/core/EXTENDED-TASK.md +0 -0
  46. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/core/FEATURE.md +0 -0
  47. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/core/MODULE_README.md +0 -0
  48. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/core/RELATIONS-TASKS.md +0 -0
  49. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/core/TASKS.md +0 -0
  50. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/core/__init__.py +0 -0
  51. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/core/admin_lane.py +0 -0
  52. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/core/async_db_manager.py +0 -0
  53. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/core/base.py +0 -0
  54. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/core/config.py +0 -0
  55. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/core/connections.py +0 -0
  56. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/core/db_function.py +0 -0
  57. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/core/diagnostics.py +0 -0
  58. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/core/expressions.py +0 -0
  59. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/core/extended.py +0 -0
  60. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/core/fields.py +0 -0
  61. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/core/foreign_session_state.py +0 -0
  62. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/core/introspection.py +0 -0
  63. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/core/listen.py +0 -0
  64. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/core/loop_filters.py +0 -0
  65. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/core/model_dto.py +0 -0
  66. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/core/model_view.py +0 -0
  67. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/core/one_database.py +0 -0
  68. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/core/paginator.py +0 -0
  69. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/core/pool_maintenance.py +0 -0
  70. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/core/pool_watch.py +0 -0
  71. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/core/pooler_role.py +0 -0
  72. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/core/pydantic_bridge.py +0 -0
  73. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/core/read_write.py +0 -0
  74. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/core/registry.py +0 -0
  75. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/core/relations.py +0 -0
  76. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/core/resilience.py +0 -0
  77. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/core/rls_session.py +0 -0
  78. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/core/session_context.py +0 -0
  79. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/core/signals.py +0 -0
  80. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/core/statement_ceiling.py +0 -0
  81. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/core/tls.py +0 -0
  82. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/core/transaction.py +0 -0
  83. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/core/types.py +0 -0
  84. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/core/write_queue.py +0 -0
  85. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/core/write_retry.py +0 -0
  86. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/entity.py +0 -0
  87. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/error_handling.py +0 -0
  88. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/exceptions.py +0 -0
  89. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/extended/__init__.py +0 -0
  90. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/extended/app_error_handler.py +0 -0
  91. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/identity_rule.py +0 -0
  92. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/local_sqlite.py +0 -0
  93. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/middleware/__init__.py +0 -0
  94. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/middleware/base.py +0 -0
  95. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/migrations/MODULE_README.md +0 -0
  96. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/migrations/__init__.py +0 -0
  97. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/migrations/cli.py +0 -0
  98. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/migrations/ddl.py +0 -0
  99. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/migrations/diff.py +0 -0
  100. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/migrations/executor.py +0 -0
  101. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/migrations/integration.py +0 -0
  102. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/migrations/loader.py +0 -0
  103. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/migrations/operations.py +0 -0
  104. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/migrations/state.py +0 -0
  105. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/migrations/table_filter.py +0 -0
  106. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/no_org_assignment.py +0 -0
  107. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/operations/FEATURE.md +0 -0
  108. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/operations/MODULE_README.md +0 -0
  109. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/operations/__init__.py +0 -0
  110. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/operations/bulk_update_values.py +0 -0
  111. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/operations/conflict.py +0 -0
  112. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/operations/conflict_writes.py +0 -0
  113. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/operations/create.py +0 -0
  114. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/operations/db_functions.py +0 -0
  115. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/operations/delete.py +0 -0
  116. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/operations/dynamic_admin.py +0 -0
  117. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/operations/dynamic_crud.py +0 -0
  118. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/operations/graph_walk.py +0 -0
  119. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/operations/latest_rows.py +0 -0
  120. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/operations/queue_claim.py +0 -0
  121. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/operations/read.py +0 -0
  122. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/operations/relational_backfill.py +0 -0
  123. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/operations/staging_load.py +0 -0
  124. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/operations/update.py +0 -0
  125. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/platform_access.py +0 -0
  126. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/platform_db.py +0 -0
  127. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/py.typed +0 -0
  128. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/pytest_lock_guard.py +0 -0
  129. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/pytest_plugin.py +0 -0
  130. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/python_sql/MODULE_README.md +0 -0
  131. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/python_sql/__init__.py +0 -0
  132. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/python_sql/db_objects.py +0 -0
  133. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/python_sql/table_detailed_relationships.py +0 -0
  134. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/python_sql/table_typescript_relationship.py +0 -0
  135. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/query/FEATURE.md +0 -0
  136. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/query/MODULE_README.md +0 -0
  137. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/query/__init__.py +0 -0
  138. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/query/builder.py +0 -0
  139. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/query/executor.py +0 -0
  140. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/resources/__init__.py +0 -0
  141. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/resources/additional-assigners-2026-09-12.json +0 -0
  142. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/resources/org_assignment_trigger_baseline.json +0 -0
  143. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/schema_builder/FEATURE.md +0 -0
  144. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/schema_builder/MODULE_README.md +0 -0
  145. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/schema_builder/__init__.py +0 -0
  146. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/schema_builder/code_handler.py +0 -0
  147. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/schema_builder/codegen_writer.py +0 -0
  148. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/schema_builder/columns.py +0 -0
  149. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/schema_builder/common.py +0 -0
  150. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/schema_builder/diff_preview.py +0 -0
  151. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/schema_builder/entity_capabilities.py +0 -0
  152. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/schema_builder/excluded_targets.py +0 -0
  153. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/schema_builder/generator.py +0 -0
  154. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/schema_builder/helpers/__init__.py +0 -0
  155. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/schema_builder/helpers/base_generators.py +0 -0
  156. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/schema_builder/helpers/entity_generators.py +0 -0
  157. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/schema_builder/helpers/git_checker.py +0 -0
  158. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/schema_builder/package_wiring.py +0 -0
  159. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/schema_builder/relationships.py +0 -0
  160. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/schema_builder/runner.py +0 -0
  161. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/schema_builder/schema.py +0 -0
  162. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/schema_builder/schema_manager.py +0 -0
  163. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/schema_builder/tables.py +0 -0
  164. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/schema_builder/views.py +0 -0
  165. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/secrets_battery/FEATURE.md +0 -0
  166. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/secrets_battery/__init__.py +0 -0
  167. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/secrets_battery/attachments.py +0 -0
  168. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/secrets_battery/backup_restore.py +0 -0
  169. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/secrets_battery/crypto.py +0 -0
  170. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/secrets_battery/host_authorization.py +0 -0
  171. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/secrets_battery/items.py +0 -0
  172. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/secrets_battery/model.py +0 -0
  173. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/secrets_battery/mutation_receipts.py +0 -0
  174. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/secrets_battery/native_enable.py +0 -0
  175. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/secrets_battery/native_lifecycle.py +0 -0
  176. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/secrets_battery/native_passkeys.py +0 -0
  177. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/secrets_battery/native_source.py +0 -0
  178. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/secrets_battery/org_service.py +0 -0
  179. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/secrets_battery/org_vault_gate.py +0 -0
  180. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/secrets_battery/service.py +0 -0
  181. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/secrets_battery/vault_history.py +0 -0
  182. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/session/README.md +0 -0
  183. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/session/__init__.py +0 -0
  184. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/session/coalesce.py +0 -0
  185. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/session/dag.py +0 -0
  186. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/session/errors.py +0 -0
  187. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/session/fallback.py +0 -0
  188. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/session/flush.py +0 -0
  189. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/session/lifecycle.py +0 -0
  190. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/session/managed.py +0 -0
  191. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/session/op.py +0 -0
  192. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/session/reads.py +0 -0
  193. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/session/session.py +0 -0
  194. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/session/telemetry.py +0 -0
  195. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/sql_executor/MODULE_README.md +0 -0
  196. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/sql_executor/__init__.py +0 -0
  197. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/sql_executor/executor.py +0 -0
  198. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/sql_executor/queries.py +0 -0
  199. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/sql_executor/registry.py +0 -0
  200. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/sql_executor/types.py +0 -0
  201. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/sql_executor/utils.py +0 -0
  202. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/utils/__init__.py +0 -0
  203. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/utils/sql_utils.py +0 -0
  204. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/utils/type_converters.py +0 -0
  205. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/matrx_orm/write_actor.py +0 -0
  206. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/scripts/git-branches.sh +0 -0
  207. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/scripts/publish.sh +0 -0
  208. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/scripts/release.sh +0 -0
  209. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/MODULE_README.md +0 -0
  210. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/__init__.py +0 -0
  211. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/conftest.py +0 -0
  212. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/MODULE_README.md +0 -0
  213. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/__init__.py +0 -0
  214. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_adapter_registry.py +0 -0
  215. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_admin_db_columns.py +0 -0
  216. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_admin_response_rendering.py +0 -0
  217. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_admin_search_negation.py +0 -0
  218. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_admin_search_sentinels.py +0 -0
  219. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_advisory_lock.py +0 -0
  220. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_agent_message.py +0 -0
  221. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_api_auth.py +0 -0
  222. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_api_config.py +0 -0
  223. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_api_handlers.py +0 -0
  224. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_api_protocol.py +0 -0
  225. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_array_agg_order_by.py +0 -0
  226. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_association_trigger_actor_naming.py +0 -0
  227. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_associations.py +0 -0
  228. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_associations_wiring.py +0 -0
  229. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_bulk_hydration_offload.py +0 -0
  230. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_bulk_update_by_pk.py +0 -0
  231. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_bulk_upsert_increment_set_fields.py +0 -0
  232. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_cache_debug.py +0 -0
  233. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_call_function.py +0 -0
  234. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_case_when.py +0 -0
  235. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_catalog.py +0 -0
  236. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_column_scoped_writes.py +0 -0
  237. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_committed_row_guard.py +0 -0
  238. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_composite_pk_filter.py +0 -0
  239. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_composite_pk_write_paths.py +0 -0
  240. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_config.py +0 -0
  241. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_conflict_target.py +0 -0
  242. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_conflict_writes.py +0 -0
  243. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_connection_codecs.py +0 -0
  244. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_connection_poison_guard.py +0 -0
  245. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_connection_primitives.py +0 -0
  246. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_count_composite_distinct.py +0 -0
  247. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_count_limit_param_leak.py +0 -0
  248. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_db_function_field.py +0 -0
  249. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_ddl_generator.py +0 -0
  250. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_deadlock_retry.py +0 -0
  251. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_derived_table.py +0 -0
  252. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_dirty_tracking.py +0 -0
  253. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_dynamic_crud.py +0 -0
  254. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_exceptions.py +0 -0
  255. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_execute_query_integrity_mapping.py +0 -0
  256. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_expression_primitives.py +0 -0
  257. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_fields.py +0 -0
  258. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_filter_jsonb_agg_array_index.py +0 -0
  259. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_fk_cycle_resolution.py +0 -0
  260. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_from_alias.py +0 -0
  261. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_fts.py +0 -0
  262. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_func_expression_args.py +0 -0
  263. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_graph_walk.py +0 -0
  264. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_having_annotate_alias.py +0 -0
  265. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_insert_ignore_and_admin_primitives.py +0 -0
  266. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_introspect_rls_policies.py +0 -0
  267. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_join_and_aggregate.py +0 -0
  268. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_latest_rows.py +0 -0
  269. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_listen_notify.py +0 -0
  270. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_local_sqlite.py +0 -0
  271. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_lock_contention_retry.py +0 -0
  272. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_managed_write_guard.py +0 -0
  273. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_matrx_entity.py +0 -0
  274. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_migration_diff_types.py +0 -0
  275. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_migration_loader.py +0 -0
  276. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_model_instance.py +0 -0
  277. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_model_meta.py +0 -0
  278. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_optimistic_concurrency.py +0 -0
  279. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_order_by_expression.py +0 -0
  280. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_org_assignment_inventory.py +0 -0
  281. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_orm_banners_are_client_safe.py +0 -0
  282. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_parameter_name_hydration.py +0 -0
  283. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_patch_jsonb_path.py +0 -0
  284. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_percentile_cont.py +0 -0
  285. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_persisted_count.py +0 -0
  286. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_person_scoped_session_bypasses_shared_cache.py +0 -0
  287. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_pool_create_never_wedges.py +0 -0
  288. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_pool_holder_census.py +0 -0
  289. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_pool_loop_recreation.py +0 -0
  290. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_pool_maintenance.py +0 -0
  291. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_postgrest_filters.py +0 -0
  292. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_pydantic_bridge.py +0 -0
  293. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_query_builder.py +0 -0
  294. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_query_error_detail.py +0 -0
  295. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_query_executor_sql.py +0 -0
  296. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_query_timeout.py +0 -0
  297. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_querybuilder_clone_semantics.py +0 -0
  298. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_queue_claim.py +0 -0
  299. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_read_connection_retry.py +0 -0
  300. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_read_write_declared.py +0 -0
  301. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_registry.py +0 -0
  302. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_registry_multi_database.py +0 -0
  303. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_registry_multi_schema.py +0 -0
  304. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_relational_backfill.py +0 -0
  305. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_relations.py +0 -0
  306. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_resilience.py +0 -0
  307. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_rls_session_scope.py +0 -0
  308. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_schema_exists.py +0 -0
  309. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_session_advisory_lock.py +0 -0
  310. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_standalone_write_session_context.py +0 -0
  311. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_state_cache.py +0 -0
  312. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_subquery_filter_raw.py +0 -0
  313. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_subquery_in_filter.py +0 -0
  314. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_subquery_nested_expressions.py +0 -0
  315. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_supabase_auth.py +0 -0
  316. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_supabase_config.py +0 -0
  317. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_tls.py +0 -0
  318. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_transaction_database_identity.py +0 -0
  319. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_transaction_gucs_one_round_trip.py +0 -0
  320. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_transaction_pool_release_sends_nothing.py +0 -0
  321. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_typed_join_projection.py +0 -0
  322. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_typed_predicates.py +0 -0
  323. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_update_case_expression.py +0 -0
  324. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_update_subquery.py +0 -0
  325. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_update_with_rebase.py +0 -0
  326. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_upsert_default_leak.py +0 -0
  327. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_upsert_with_conflict.py +0 -0
  328. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_values_jsonb_decode.py +0 -0
  329. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_watched_lifecycle.py +0 -0
  330. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_write_queue_deadline.py +0 -0
  331. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level1/test_write_retry_semantics.py +0 -0
  332. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level2/MODULE_README.md +0 -0
  333. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level2/__init__.py +0 -0
  334. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level2/conftest.py +0 -0
  335. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level2/test_bulk_ops.py +0 -0
  336. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level2/test_cache_integration.py +0 -0
  337. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level2/test_crud.py +0 -0
  338. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level2/test_foreign_keys.py +0 -0
  339. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level2/test_m2m.py +0 -0
  340. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level2/test_manager.py +0 -0
  341. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level2/test_migrations_live.py +0 -0
  342. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level2/test_query_execution.py +0 -0
  343. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level2/test_schema_diff.py +0 -0
  344. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/level2/verify_committed_row_guard_pg17.py +0 -0
  345. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/live_pooler_role.py +0 -0
  346. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/native_source_cases.py +0 -0
  347. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/native_source_oracle.rs +0 -0
  348. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/sample_project/.env.example +0 -0
  349. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/sample_project/README.md +0 -0
  350. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/sample_project/__init__.py +0 -0
  351. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/sample_project/generate.py +0 -0
  352. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/sample_project/generated/.gitkeep +0 -0
  353. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/sample_project/matrx_orm.yaml +0 -0
  354. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/sample_project/test_schema_generation.py +0 -0
  355. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/sample_project_desktop/.env.example +0 -0
  356. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/sample_project_desktop/README.md +0 -0
  357. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/sample_project_desktop/__init__.py +0 -0
  358. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/sample_project_desktop/client_example.py +0 -0
  359. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/sample_project_desktop/client_example.ts +0 -0
  360. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/sample_project_desktop/client_supabase_example.py +0 -0
  361. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/sample_project_desktop/server.py +0 -0
  362. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/schema/entity_tests.py +0 -0
  363. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/schema/test_base_generation.py +0 -0
  364. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/schema/test_composite_pk_fk_generation.py +0 -0
  365. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/schema/test_composite_unique_generation.py +0 -0
  366. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/schema/test_dto_identity_generation.py +0 -0
  367. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/schema/test_entity_capability_generation.py +0 -0
  368. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/schema/test_enum_ordering.py +0 -0
  369. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/schema/test_excluded_fk_targets.py +0 -0
  370. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/schema/test_generate_schema.py +0 -0
  371. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/schema/test_junction_analysis.py +0 -0
  372. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/schema/test_multi_schema_output.py +0 -0
  373. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/schema/test_schema_analysis.py +0 -0
  374. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/schema/test_sql_expression_defaults.py +0 -0
  375. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/schema/test_table_relationships_query.py +0 -0
  376. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/schema/test_view_generation.py +0 -0
  377. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/schema_builder/test_array_default_parsing.py +0 -0
  378. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/schema_builder/test_enum_array_column.py +0 -0
  379. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/schema_builder/test_focused_generation_plan.py +0 -0
  380. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/schema_builder/test_host_models_aggregator.py +0 -0
  381. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/schema_builder/test_never_filter_columns.py +0 -0
  382. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/session/__init__.py +0 -0
  383. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/session/conftest.py +0 -0
  384. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/session/test_a_failed_capture_still_names_the_failure.py +0 -0
  385. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/session/test_cache_no_downgrade.py +0 -0
  386. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/session/test_capture_poison_proof.py +0 -0
  387. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/session/test_coalesce.py +0 -0
  388. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/session/test_dag.py +0 -0
  389. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/session/test_disk_spill_fallback.py +0 -0
  390. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/session/test_flush_individual_fallback.py +0 -0
  391. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/session/test_governed_write.py +0 -0
  392. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/session/test_managed.py +0 -0
  393. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/session/test_op_enqueue_site.py +0 -0
  394. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/session/test_reads.py +0 -0
  395. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/session/test_record_error_bounded_insert.py +0 -0
  396. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/session/test_record_error_storm.py +0 -0
  397. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/session/test_session.py +0 -0
  398. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/session/test_spill_guard_plugin.py +0 -0
  399. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/test_a_backend_handed_over_mid_transaction_never_takes_our_writes.py +0 -0
  400. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/test_a_keyword_column_refuses_generation.py +0 -0
  401. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/test_a_list_of_uuids_binds_as_uuid_array.py +0 -0
  402. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/test_a_read_only_backend_never_refuses_our_writes.py +0 -0
  403. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/test_a_sealed_field_shows_no_character_of_its_value.py +0 -0
  404. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/test_array_arg_element_type.py +0 -0
  405. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/test_attachment_mutation_serialization.py +0 -0
  406. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/test_backup_alias_conflict.py +0 -0
  407. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/test_backup_history_restore.py +0 -0
  408. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/test_backup_restore_live.py +0 -0
  409. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/test_call_function_as.py +0 -0
  410. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/test_call_function_none_mode.py +0 -0
  411. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/test_composite_fk_is_not_a_column_fk.py +0 -0
  412. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/test_database_name_alias.py +0 -0
  413. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/test_db_functions.py +0 -0
  414. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/test_dsn_password_masking.py +0 -0
  415. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/test_foreign_session_state.py +0 -0
  416. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/test_immutable_insert_or_exact_verify.py +0 -0
  417. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/test_keyed_digest.py +0 -0
  418. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/test_model_cls_refactor.py +0 -0
  419. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/test_native_enable.py +0 -0
  420. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/test_native_lifecycle.py +0 -0
  421. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/test_native_lifecycle_fences.py +0 -0
  422. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/test_native_passkey_receipts.py +0 -0
  423. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/test_native_source.py +0 -0
  424. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/test_no_blocking_asyncpg_tls.py +0 -0
  425. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/test_one_database.py +0 -0
  426. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/test_order_by_desc_nulls_last.py +0 -0
  427. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/test_org_vault_gate.py +0 -0
  428. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/test_package_wiring_views.py +0 -0
  429. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/test_pooler_role_guard.py +0 -0
  430. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/test_secret_audit_names_its_organization.py +0 -0
  431. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/test_secrets_battery.py +0 -0
  432. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/test_secrets_minting_keys.py +0 -0
  433. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/test_shared_lock_guard.py +0 -0
  434. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/test_sql_param_casts.py +0 -0
  435. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/test_state_cache_sweeps_stale.py +0 -0
  436. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/test_statement_ceiling_warning.py +0 -0
  437. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/test_sync_twins_guard.py +0 -0
  438. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/test_update_result_truthiness.py +0 -0
  439. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/test_user_visible_lookup.py +0 -0
  440. {matrx_orm-3.1.232 → matrx_orm-3.1.233}/tests/test_vault_aggregate_recovery_stateful.py +0 -0
@@ -97,7 +97,7 @@ db.sqlite3
97
97
  db.sqlite3-journal
98
98
 
99
99
  # Flask stuff:
100
- instance/
100
+ /instance/
101
101
  .webassets-cache
102
102
 
103
103
  # Scrapy stuff:
@@ -164,7 +164,11 @@ enforced in code:
164
164
  `bulk_create` (or uses `insert_returning`) so its rows are neither read back
165
165
  nor cached. Before this an unread entry lived for the whole process, and a
166
166
  workflow worker's checkpoints/outcomes/events grew it until ECS killed the
167
- task for memory (MKT-111, 2026-10-15). Guard: `tests/test_state_cache_sweeps_stale.py`.
167
+ task for memory (MKT-111, 2026-10-15). The sweep pops oldest-first and stops at
168
+ the first fresh entry, and `cache_bulk` yields the loop once per sweep window, so
169
+ caching a big read never stalls the server (a full-scan sweep froze it 2-6 s on a
170
+ 21k-row read, 2026-10-17). Guards: `tests/test_state_cache_sweeps_stale.py`,
171
+ `tests/test_a_bulk_cache_never_stalls_the_loop.py`.
168
172
 
169
173
  This is not theoretical hygiene. On 2026-07-25 rule 1 was missing: `session/flush.py`
170
174
  builds a `{pk + payload}` stub per queued UPDATE and `bulk_update` cached it, so a
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: matrx-orm
3
- Version: 3.1.232
3
+ Version: 3.1.233
4
4
  Summary: Async-first PostgreSQL ORM with bidirectional migrations, schema introspection, many-to-many relationships, and built-in state caching
5
5
  Project-URL: Homepage, https://github.com/AI-Matrix-Engine/aidream-current
6
6
  Project-URL: Repository, https://github.com/AI-Matrix-Engine/aidream-current
@@ -26,7 +26,7 @@ Requires-Dist: asyncpg>=0.31.0
26
26
  Requires-Dist: cbor2<7,>=6.1.3
27
27
  Requires-Dist: cryptography>=43.0
28
28
  Requires-Dist: gitpython>=3.1.40
29
- Requires-Dist: matrx-utils>=2.0.74
29
+ Requires-Dist: matrx-utils>=2.0.75
30
30
  Requires-Dist: psycopg-pool>=3.2.5
31
31
  Requires-Dist: psycopg[binary]>=3.2.5
32
32
  Requires-Dist: python-dotenv>=1.0
@@ -58,6 +58,65 @@ class UnregisteredRelationError(ValueError):
58
58
  """
59
59
 
60
60
 
61
+ # JSON path selections on a json/jsonb column, e.g. ``provider_entry->>version``.
62
+ #
63
+ # Accepted forms (the ONLY ones; anything else is refused naming this list):
64
+ # col->key jsonb value col->>key text value
65
+ # col->k1->k2->>k3 chained steps col#>{k1,k2} jsonb at path
66
+ # col#>>{k1,k2} text at path (keys may be quoted: col->>'a-b')
67
+ # A key is letters, digits, ``_``, ``-`` or ``.``; an all-digit key is an array
68
+ # index. Keys are NEVER interpolated: they travel as one bound ``text[]`` parameter
69
+ # to ``#>`` / ``#>>``, and the column itself must be a live json/jsonb column.
70
+ JSON_PATH_FORMS = (
71
+ "col->key, col->>key, col->k1->k2->>k3, col#>{k1,k2}, col#>>{k1,k2} "
72
+ "(col must be a json/jsonb column; keys are letters, digits, _ - .)"
73
+ )
74
+ _BARE_KEY = r"[A-Za-z0-9_.]+(?:-(?!>)[A-Za-z0-9_.]+)*" # a "-" inside a key, never the "->" arrow
75
+ _JSON_KEY = rf"(?:'{_BARE_KEY}'|{_BARE_KEY})"
76
+ _JSON_ARROWS = re.compile(
77
+ rf"^(?P<col>[A-Za-z_][A-Za-z0-9_$]*)(?P<steps>(?:->>?{_JSON_KEY})+)$"
78
+ )
79
+ _JSON_HASH = re.compile(
80
+ rf"^(?P<col>[A-Za-z_][A-Za-z0-9_$]*)(?P<op>#>>?)\{{(?P<keys>{_JSON_KEY}(?:,{_JSON_KEY})*)\}}$"
81
+ )
82
+ _JSON_STEP = re.compile(rf"(->>?)({_JSON_KEY})")
83
+ _JSON_TYPES = {"json", "jsonb"}
84
+
85
+
86
+ def looks_like_json_path(spec: str) -> bool:
87
+ return "->" in spec or "#>" in spec
88
+
89
+
90
+ def parse_json_path(spec: str) -> tuple[str, bool, list[str]]:
91
+ """``spec`` -> ``(column, as_text, keys)`` or raise naming the accepted forms."""
92
+ text = spec.strip()
93
+ m = _JSON_HASH.match(text)
94
+ if m:
95
+ keys = [k.strip("'") for k in m.group("keys").split(",")]
96
+ return m.group("col"), m.group("op") == "#>>", keys
97
+ m = _JSON_ARROWS.match(text)
98
+ if m:
99
+ steps = _JSON_STEP.findall(m.group("steps"))
100
+ # ``->>`` returns text, so it can only be the LAST step.
101
+ if any(op == "->>" for op, _ in steps[:-1]):
102
+ raise UnregisteredRelationError(
103
+ f"Field {spec!r}: ->> returns text, so it can only be the last step. "
104
+ f"Accepted JSON path forms: {JSON_PATH_FORMS}."
105
+ )
106
+ return m.group("col"), steps[-1][0] == "->>", [k.strip("'") for _, k in steps]
107
+ raise UnregisteredRelationError(
108
+ f"Field {spec!r} is not a column or a supported JSON path. "
109
+ f"Accepted JSON path forms: {JSON_PATH_FORMS}."
110
+ )
111
+
112
+
113
+ def _quote_alias(spec: str) -> str:
114
+ """Quote a validated JSON-path spec as the result column's name."""
115
+ if '"' in spec or "\x00" in spec:
116
+ raise UnregisteredRelationError(f"Unsafe field name: {spec!r}")
117
+ return f'"{spec}"'
118
+
119
+
61
120
  def _quote(ident: str) -> str:
62
121
  if not _SAFE_IDENT.match(ident):
63
122
  raise UnregisteredRelationError(f"Unsafe SQL identifier: {ident!r}")
@@ -98,6 +157,13 @@ async def resolve_relation(table: str, *, database: str | None = None) -> tuple[
98
157
  )
99
158
 
100
159
 
160
+ async def _describe(schema: str, name: str, database: str) -> list[dict[str, Any]]:
161
+ from matrx_orm.catalog import describe_relation_columns
162
+
163
+ rows = await describe_relation_columns(database, schema=schema, table=name)
164
+ return [r for r in rows if isinstance(r, dict) and r.get("column_name")]
165
+
166
+
101
167
  async def live_columns(schema: str, name: str, *, database: str | None = None) -> list[str]:
102
168
  """Ordered live column names for a relation, straight from pg_catalog."""
103
169
  from matrx_orm.catalog import describe_relation_columns
@@ -202,7 +268,9 @@ async def catalog_select(
202
268
  raise UnregisteredRelationError(f'relation "{schema}.{name}" does not exist')
203
269
  live_set = set(live)
204
270
 
205
- requested = [c for c in columns if c != "*"]
271
+ requested_all = [c for c in columns if c != "*"]
272
+ json_specs = {c: parse_json_path(c) for c in requested_all if looks_like_json_path(c)}
273
+ requested = [c for c in requested_all if c not in json_specs]
206
274
  order_fields = [f.removeprefix("-") for f in order_by]
207
275
  filter_fields = [getattr(f, "field", "") for f in filters]
208
276
  unknown = [
@@ -211,7 +279,27 @@ async def catalog_select(
211
279
  if c and c not in live_set
212
280
  ]
213
281
  if unknown:
214
- raise _unknown_columns_error(schema, name, unknown, live, registered_fields)
282
+ err = _unknown_columns_error(schema, name, unknown, live, registered_fields)
283
+ if any(looks_like_json_path(u) for u in unknown):
284
+ raise UnregisteredRelationError(
285
+ f"{err}\nJSON paths are accepted in `fields` only (not match/order_by). "
286
+ f"Accepted forms: {JSON_PATH_FORMS}."
287
+ )
288
+ raise err
289
+ if json_specs:
290
+ type_by_col = {
291
+ str(r["column_name"]): str(r.get("data_type") or "").lower()
292
+ for r in await _describe(schema, name, db)
293
+ }
294
+ for spec, (col, _as_text, _keys) in json_specs.items():
295
+ if col not in live_set:
296
+ raise _unknown_columns_error(schema, name, [col], live, registered_fields)
297
+ dtype = type_by_col.get(col)
298
+ if dtype and dtype not in _JSON_TYPES:
299
+ raise UnregisteredRelationError(
300
+ f"Field {spec!r}: column {col!r} is {dtype}, not json/jsonb, so a JSON "
301
+ f"path cannot be taken from it. Accepted JSON path forms: {JSON_PATH_FORMS}."
302
+ )
215
303
 
216
304
  params: list[Any] = []
217
305
 
@@ -219,7 +307,15 @@ async def catalog_select(
219
307
  params.append(value)
220
308
  return f"${len(params)}"
221
309
 
222
- select_list = ", ".join(_quote(c) for c in requested) if requested else "*"
310
+ parts_select: list[str] = []
311
+ for c in requested_all:
312
+ if c in json_specs:
313
+ col, as_text, keys = json_specs[c]
314
+ op = "#>>" if as_text else "#>"
315
+ parts_select.append(f"{_quote(col)} {op} {next_param(keys)}::text[] AS {_quote_alias(c)}")
316
+ else:
317
+ parts_select.append(_quote(c))
318
+ select_list = ", ".join(parts_select) if parts_select else "*"
223
319
  sql = f"SELECT {select_list} FROM {_quote(schema)}.{_quote(name)}"
224
320
 
225
321
  predicates = [
@@ -269,25 +269,33 @@ class ModelState:
269
269
  )
270
270
  return matches[0] if matches else None
271
271
 
272
+ def _stale_before(self, now: datetime | None = None) -> datetime | None:
273
+ """The cache time an entry must be newer than to be fresh; None = never stale.
274
+
275
+ One model has ONE age rule, so staleness is a pure function of the
276
+ entry's cache time — which is what lets :meth:`sweep_stale` stop at the
277
+ first fresh entry instead of re-checking every row.
278
+ """
279
+ now = now or datetime.now()
280
+ if self.timeout is not None:
281
+ return now - timedelta(seconds=self.timeout)
282
+ if self.policy == CachePolicy.PERMANENT:
283
+ return None
284
+ if self.policy == CachePolicy.LONG_TERM:
285
+ return now - timedelta(hours=4)
286
+ if self.policy == CachePolicy.SHORT_TERM:
287
+ return now - timedelta(minutes=10)
288
+ if self.policy == CachePolicy.INSTANT:
289
+ return now - timedelta(minutes=1)
290
+ return datetime.max
291
+
272
292
  def _is_stale(self, record):
273
293
  cache_key = self._get_record_cache_key(record)
274
294
  cache_time = self._cache_times.get(cache_key)
275
295
  if not cache_time:
276
296
  return True
277
-
278
- if self.timeout is not None:
279
- return (datetime.now() - cache_time).total_seconds() > self.timeout
280
-
281
- now = datetime.now()
282
- if self.policy == CachePolicy.PERMANENT:
283
- return False
284
- elif self.policy == CachePolicy.LONG_TERM:
285
- return now - cache_time > timedelta(hours=4)
286
- elif self.policy == CachePolicy.SHORT_TERM:
287
- return now - cache_time > timedelta(minutes=10)
288
- elif self.policy == CachePolicy.INSTANT:
289
- return now - cache_time > timedelta(minutes=1)
290
- return True
297
+ cutoff = self._stale_before()
298
+ return cutoff is not None and cache_time < cutoff
291
299
 
292
300
  async def cache(self, record):
293
301
  """Cache a record with proper error handling.
@@ -336,7 +344,7 @@ class ModelState:
336
344
  return
337
345
 
338
346
  self._cache[cache_key] = record
339
- self._cache_times[cache_key] = datetime.now()
347
+ self._stamp(cache_key)
340
348
  self._writes_since_sweep = getattr(self, "_writes_since_sweep", 0) + 1
341
349
  if self._writes_since_sweep >= STALE_SWEEP_EVERY_WRITES:
342
350
  self.sweep_stale()
@@ -356,6 +364,15 @@ class ModelState:
356
364
  original_error=e,
357
365
  ) from e
358
366
 
367
+ def _stamp(self, cache_key) -> None:
368
+ """Record ``cache_key`` as cached NOW, at the END of ``_cache_times``.
369
+
370
+ ``_cache_times`` is kept in cache-time order (a re-cache moves the key to
371
+ the end), so the oldest entries are always at the front.
372
+ """
373
+ self._cache_times.pop(cache_key, None)
374
+ self._cache_times[cache_key] = datetime.now()
375
+
359
376
  def sweep_stale(self) -> int:
360
377
  """Drop every entry its policy already treats as stale. Returns how many.
361
378
 
@@ -366,18 +383,30 @@ class ModelState:
366
383
  cache) accumulated for the life of the process, multi-megabyte JSON
367
384
  included, until ECS killed the task for memory. A stale entry is
368
385
  already a miss, so dropping it changes no read.
386
+
387
+ THE STALL THIS CLOSES (2026-10-17): the first version re-checked EVERY
388
+ entry on every sweep. A sweep runs every ``STALE_SWEEP_EVERY_WRITES``
389
+ writes, so one bulk read of N fresh rows did N/200 full scans — the AI
390
+ catalog's 21,677-row rule-cell reload held the event loop 2-6 s and
391
+ froze every request on the server. ``_cache_times`` is in cache-time
392
+ order and one model has one age rule, so the sweep pops from the front
393
+ and stops at the first fresh entry: its cost is what it drops, never
394
+ what is cached.
369
395
  """
370
396
  self._writes_since_sweep = 0
397
+ cutoff = self._stale_before()
398
+ times = self._cache_times
371
399
  dropped = 0
372
- for key in list(self._cache_times):
373
- record = self._cache.get(key)
374
- if record is None or self._is_stale(record):
375
- self._cache.pop(key, None)
376
- self._cache_times.pop(key, None)
377
- lock = self._locks.get(key)
378
- if lock is not None and not lock.locked():
379
- self._locks.pop(key, None)
380
- dropped += 1
400
+ while times:
401
+ key = next(iter(times))
402
+ if key in self._cache and (cutoff is None or times[key] >= cutoff):
403
+ break
404
+ times.pop(key, None)
405
+ self._cache.pop(key, None)
406
+ lock = self._locks.get(key)
407
+ if lock is not None and not lock.locked():
408
+ self._locks.pop(key, None)
409
+ dropped += 1
381
410
  return dropped
382
411
 
383
412
  def _downgraded_fields(self, existing, incoming) -> list[str]:
@@ -419,7 +448,7 @@ class ModelState:
419
448
  record = self.model_class.from_db_result(data)
420
449
  cache_key = self._get_record_cache_key(record)
421
450
  self._cache[cache_key] = record
422
- self._cache_times[cache_key] = datetime.now()
451
+ self._stamp(cache_key)
423
452
 
424
453
  def get_all_cached(self):
425
454
  """Returns all cached records as a list."""
@@ -620,7 +649,7 @@ class StateManager:
620
649
  reason="Model not registered with StateManager",
621
650
  )
622
651
 
623
- for record in records:
652
+ for index, record in enumerate(records, start=1):
624
653
  try:
625
654
  pk_kwargs = {pk: getattr(record, pk) for pk in record._meta.primary_keys}
626
655
  if not await state.get(**pk_kwargs):
@@ -628,6 +657,11 @@ class StateManager:
628
657
  except Exception as e:
629
658
  vcprint(f"Failed to cache record: {str(e)}", color="yellow")
630
659
  continue
660
+ # An uncontended lock never yields, so a 20k-row read cached in
661
+ # one go held the loop for its whole length. Hand the loop back
662
+ # once per sweep window: other requests run between chunks.
663
+ if index % STALE_SWEEP_EVERY_WRITES == 0:
664
+ await asyncio.sleep(0)
631
665
  except StateError:
632
666
  raise
633
667
  except Exception as e:
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "matrx-orm"
3
- version = "3.1.232"
3
+ version = "3.1.233"
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.74",
44
+ "matrx-utils>=2.0.75",
45
45
  "pyyaml>=6.0.3",
46
46
  "python-dotenv>=1.0",
47
47
  "cryptography>=43.0",
@@ -0,0 +1,113 @@
1
+ """A bulk read never freezes the server while its rows are cached (2026-10-17).
2
+
3
+ The stale sweep (MKT-111) re-checked EVERY cached entry each time it ran, and it ran
4
+ every ``STALE_SWEEP_EVERY_WRITES`` writes — so caching N fresh rows did N/200 full
5
+ scans. The AI catalog's 21,677-row rule-cell reload held the event loop 2-6 s in
6
+ production (ops.system_error EventLoopStall, frame ``state.py:342 sweep_stale``) and
7
+ froze every request on the server. ``cache_bulk`` also never yielded, so even a
8
+ linear pass over 20k rows held the loop for its whole length.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import asyncio
14
+ import types
15
+ from datetime import datetime, timedelta
16
+
17
+ import pytest
18
+
19
+ from matrx_orm import state as state_module
20
+ from matrx_orm.state import CachePolicy, ModelState, StateManager
21
+
22
+
23
+ def _model(policy: CachePolicy, name: str):
24
+ meta = types.SimpleNamespace(primary_keys=["id"])
25
+ return type(
26
+ name,
27
+ (),
28
+ {
29
+ "_cache_policy": policy,
30
+ "_fields": {},
31
+ "_meta": meta,
32
+ "get_database_name": staticmethod(lambda: "bulk-cache-test"),
33
+ },
34
+ )
35
+
36
+
37
+ def _record(model, i: int) -> object:
38
+ rec = types.SimpleNamespace(id=f"row-{i}")
39
+ rec._meta = model._meta
40
+ return rec
41
+
42
+
43
+ @pytest.mark.asyncio
44
+ async def test_caching_fresh_rows_costs_one_key_per_row(monkeypatch: pytest.MonkeyPatch) -> None:
45
+ model = _model(CachePolicy.SHORT_TERM, "_LinearModel")
46
+ state = ModelState(model)
47
+ calls = 0
48
+ real = ModelState._get_record_cache_key
49
+
50
+ def counting(self, record):
51
+ nonlocal calls
52
+ calls += 1
53
+ return real(self, record)
54
+
55
+ monkeypatch.setattr(ModelState, "_get_record_cache_key", counting)
56
+ n = 4000
57
+ for i in range(n):
58
+ await state.cache(_record(model, i))
59
+ assert state.count() == n
60
+ # The quadratic sweep made this ~n + n*n/(2*200) = 44,000; the ordered sweep is n.
61
+ assert calls <= 2 * n, f"{calls} per-row checks to cache {n} fresh rows"
62
+
63
+
64
+ @pytest.mark.asyncio
65
+ async def test_bulk_cache_hands_the_loop_back() -> None:
66
+ model = _model(CachePolicy.SHORT_TERM, "_YieldModel")
67
+ StateManager.register_model(model)
68
+ turns = 0
69
+ done = False
70
+
71
+ async def other_request() -> None:
72
+ nonlocal turns
73
+ while not done:
74
+ turns += 1
75
+ await asyncio.sleep(0)
76
+
77
+ other = asyncio.create_task(other_request())
78
+ await asyncio.sleep(0)
79
+ start = turns
80
+ await StateManager.cache_bulk(model, [_record(model, i) for i in range(2000)])
81
+ ran_during = turns - start
82
+ done = True
83
+ await other
84
+ assert await StateManager.count(model) == 2000
85
+ assert ran_during >= 5, f"another request ran {ran_during} times during a 2000-row cache"
86
+
87
+
88
+ @pytest.mark.asyncio
89
+ async def test_the_sweep_drops_only_what_aged_out(monkeypatch: pytest.MonkeyPatch) -> None:
90
+ monkeypatch.setattr(state_module, "STALE_SWEEP_EVERY_WRITES", 10_000)
91
+ model = _model(CachePolicy.SHORT_TERM, "_AgeModel")
92
+ state = ModelState(model)
93
+ for i in range(6):
94
+ await state.cache(_record(model, i))
95
+ old = datetime.now() - timedelta(minutes=11) # SHORT_TERM is 10 minutes
96
+ for key in list(state._cache_times):
97
+ state._cache_times[key] = old
98
+ # row-2 is cached again now: it moves behind the old ones and must survive.
99
+ await state.cache(_record(model, 2))
100
+ await state.cache(_record(model, 9))
101
+ assert state.sweep_stale() == 5
102
+ assert sorted(state._cache) == ["row-2", "row-9"]
103
+ assert state.sweep_stale() == 0
104
+
105
+
106
+ @pytest.mark.asyncio
107
+ async def test_a_permanent_model_is_never_swept() -> None:
108
+ model = _model(CachePolicy.PERMANENT, "_PermanentModel")
109
+ state = ModelState(model)
110
+ await state.cache(_record(model, 1))
111
+ state._cache_times["row-1"] = datetime.now() - timedelta(days=30)
112
+ assert state.sweep_stale() == 0
113
+ assert "row-1" in state._cache
@@ -166,3 +166,70 @@ async def test_unsafe_identifier_never_reaches_postgres(monkeypatch) -> None:
166
166
 
167
167
  with pytest.raises(UnregisteredRelationError):
168
168
  await dynamic_select("ai.provider", columns=('evil"; drop table x; --',), database=DB)
169
+
170
+
171
+ def _wire_typed(monkeypatch, columns: dict[str, str], rows: list[dict[str, Any]]) -> dict[str, Any]:
172
+ seen = _wire_catalog(monkeypatch, columns=list(columns), rows=rows)
173
+
174
+ async def describe_relation_columns(database: str, *, schema: str, table: str):
175
+ return [{"column_name": c, "data_type": t} for c, t in columns.items()]
176
+
177
+ monkeypatch.setattr("matrx_orm.catalog.describe_relation_columns", describe_relation_columns)
178
+ return seen
179
+
180
+
181
+ @pytest.mark.asyncio
182
+ async def test_json_path_selection_is_built_with_bound_keys(monkeypatch) -> None:
183
+ """2026-10-17: an agent asked for ``provider_entry->>version`` and was told
184
+ the column did not exist. Paths are validated against the live json column and
185
+ the keys travel as a bound text[] — never interpolated."""
186
+ monkeypatch.setattr(
187
+ dynamic_crud,
188
+ "resolve_dynamic_model",
189
+ lambda table, *, database=None: (_ for _ in ()).throw(ValueError("none")),
190
+ )
191
+ seen = _wire_typed(
192
+ monkeypatch,
193
+ {"model_id": "text", "provider_entry": "jsonb"},
194
+ rows=[{"model_id": "m", "provider_entry->>version": "001"}],
195
+ )
196
+
197
+ rows = await dynamic_select(
198
+ "ai.provider_sync_candidates",
199
+ columns=("model_id", "provider_entry->>version", "provider_entry->a->>'b-c'", "provider_entry#>{x,0}"),
200
+ database=DB,
201
+ )
202
+
203
+ assert rows == [{"model_id": "m", "provider_entry->>version": "001"}]
204
+ assert seen["sql"] == (
205
+ 'SELECT "model_id", "provider_entry" #>> $1::text[] AS "provider_entry->>version", '
206
+ '"provider_entry" #>> $2::text[] AS "provider_entry->a->>\'b-c\'", '
207
+ '"provider_entry" #> $3::text[] AS "provider_entry#>{x,0}" '
208
+ 'FROM "ai"."provider_sync_candidates"'
209
+ )
210
+ assert seen["params"] == [["version"], ["a", "b-c"], ["x", "0"]]
211
+
212
+
213
+ @pytest.mark.asyncio
214
+ @pytest.mark.parametrize(
215
+ "spec, needle",
216
+ [
217
+ ("provider_entry->>ver sion", "Accepted JSON path forms"),
218
+ ("provider_entry->>a->b", "only be the last step"),
219
+ ("provider_entry->>x; drop table t", "Accepted JSON path forms"),
220
+ ("model_id->>version", "not json/jsonb"),
221
+ ("nope->>version", "does not exist"),
222
+ ],
223
+ )
224
+ async def test_bad_json_path_names_the_accepted_forms(monkeypatch, spec, needle) -> None:
225
+ monkeypatch.setattr(
226
+ dynamic_crud,
227
+ "resolve_dynamic_model",
228
+ lambda table, *, database=None: (_ for _ in ()).throw(ValueError("none")),
229
+ )
230
+ _wire_typed(monkeypatch, {"model_id": "text", "provider_entry": "jsonb"}, rows=[])
231
+
232
+ with pytest.raises(UnregisteredRelationError) as excinfo:
233
+ await dynamic_select("ai.provider_sync_candidates", columns=(spec,), database=DB)
234
+
235
+ assert needle in str(excinfo.value)
File without changes
File without changes
File without changes
File without changes