matrx-orm 3.1.30__tar.gz → 3.1.32__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 (314) hide show
  1. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/PKG-INFO +1 -1
  2. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/core/expressions.py +8 -8
  3. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/operations/conflict.py +6 -0
  4. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/python_sql/table_detailed_relationships.py +110 -133
  5. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/pyproject.toml +1 -1
  6. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_case_when.py +9 -8
  7. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_conflict_target.py +10 -0
  8. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_typed_predicates.py +2 -0
  9. matrx_orm-3.1.32/tests/schema/test_junction_analysis.py +55 -0
  10. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/.gitignore +0 -0
  11. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/AGENTS.md +0 -0
  12. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/BASE_CLASS_METHODS.md +0 -0
  13. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/CLAUDE.md +0 -0
  14. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/MODEL_API.md +0 -0
  15. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/README.md +0 -0
  16. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/database/orm/extended/managers/ai_model_base.py +0 -0
  17. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/docs/migrations.md +0 -0
  18. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/MODULE_README.md +0 -0
  19. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/__init__.py +0 -0
  20. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/adapters/MODULE_README.md +0 -0
  21. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/adapters/__init__.py +0 -0
  22. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/adapters/async_postgresql.py +0 -0
  23. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/adapters/base_adapter.py +0 -0
  24. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/adapters/postgresql.py +0 -0
  25. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/adapters/postgrest_client_adapter.py +0 -0
  26. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/adapters/supabase_adapter.py +0 -0
  27. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/admin/__init__.py +0 -0
  28. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/admin/router.py +0 -0
  29. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/api/__init__.py +0 -0
  30. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/api/auth.py +0 -0
  31. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/api/config.py +0 -0
  32. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/api/handlers.py +0 -0
  33. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/api/protocol.py +0 -0
  34. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/api/server.py +0 -0
  35. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/associations.py +0 -0
  36. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/cache_debug.py +0 -0
  37. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/catalog.py +0 -0
  38. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/client/MODULE_README.md +0 -0
  39. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/client/__init__.py +0 -0
  40. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/client/postgres_connection.py +0 -0
  41. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/client/postgrest.py +0 -0
  42. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/client/supabase_auth.py +0 -0
  43. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/client/supabase_config.py +0 -0
  44. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/client/supabase_manager.py +0 -0
  45. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/core/EXTENDED-TASK.md +0 -0
  46. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/core/MODULE_README.md +0 -0
  47. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/core/RELATIONS-TASKS.md +0 -0
  48. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/core/TASKS.md +0 -0
  49. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/core/__init__.py +0 -0
  50. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/core/async_db_manager.py +0 -0
  51. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/core/base.py +0 -0
  52. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/core/config.py +0 -0
  53. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/core/connections.py +0 -0
  54. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/core/db_function.py +0 -0
  55. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/core/diagnostics.py +0 -0
  56. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/core/extended.py +0 -0
  57. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/core/fields.py +0 -0
  58. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/core/introspection.py +0 -0
  59. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/core/listen.py +0 -0
  60. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/core/loop_filters.py +0 -0
  61. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/core/model_dto.py +0 -0
  62. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/core/model_view.py +0 -0
  63. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/core/paginator.py +0 -0
  64. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/core/pool_maintenance.py +0 -0
  65. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/core/pydantic_bridge.py +0 -0
  66. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/core/registry.py +0 -0
  67. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/core/relations.py +0 -0
  68. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/core/resilience.py +0 -0
  69. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/core/rls_session.py +0 -0
  70. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/core/signals.py +0 -0
  71. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/core/tls.py +0 -0
  72. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/core/transaction.py +0 -0
  73. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/core/types.py +0 -0
  74. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/core/write_queue.py +0 -0
  75. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/entity.py +0 -0
  76. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/error_handling.py +0 -0
  77. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/exceptions.py +0 -0
  78. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/extended/__init__.py +0 -0
  79. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/extended/app_error_handler.py +0 -0
  80. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/identity_rule.py +0 -0
  81. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/middleware/__init__.py +0 -0
  82. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/middleware/base.py +0 -0
  83. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/migrations/MODULE_README.md +0 -0
  84. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/migrations/__init__.py +0 -0
  85. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/migrations/cli.py +0 -0
  86. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/migrations/ddl.py +0 -0
  87. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/migrations/diff.py +0 -0
  88. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/migrations/executor.py +0 -0
  89. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/migrations/integration.py +0 -0
  90. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/migrations/loader.py +0 -0
  91. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/migrations/operations.py +0 -0
  92. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/migrations/state.py +0 -0
  93. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/migrations/table_filter.py +0 -0
  94. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/operations/MODULE_README.md +0 -0
  95. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/operations/__init__.py +0 -0
  96. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/operations/bulk_update_values.py +0 -0
  97. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/operations/conflict_writes.py +0 -0
  98. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/operations/create.py +0 -0
  99. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/operations/db_functions.py +0 -0
  100. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/operations/delete.py +0 -0
  101. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/operations/dynamic_admin.py +0 -0
  102. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/operations/dynamic_crud.py +0 -0
  103. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/operations/graph_walk.py +0 -0
  104. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/operations/latest_rows.py +0 -0
  105. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/operations/queue_claim.py +0 -0
  106. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/operations/read.py +0 -0
  107. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/operations/relational_backfill.py +0 -0
  108. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/operations/staging_load.py +0 -0
  109. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/operations/update.py +0 -0
  110. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/py.typed +0 -0
  111. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/python_sql/MODULE_README.md +0 -0
  112. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/python_sql/__init__.py +0 -0
  113. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/python_sql/db_objects.py +0 -0
  114. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/python_sql/table_typescript_relationship.py +0 -0
  115. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/query/MODULE_README.md +0 -0
  116. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/query/__init__.py +0 -0
  117. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/query/builder.py +0 -0
  118. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/query/executor.py +0 -0
  119. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/schema_builder/MODULE_README.md +0 -0
  120. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/schema_builder/__init__.py +0 -0
  121. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/schema_builder/code_handler.py +0 -0
  122. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/schema_builder/codegen_writer.py +0 -0
  123. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/schema_builder/columns.py +0 -0
  124. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/schema_builder/common.py +0 -0
  125. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/schema_builder/diff_preview.py +0 -0
  126. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/schema_builder/entity_capabilities.py +0 -0
  127. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/schema_builder/generator.py +0 -0
  128. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/schema_builder/helpers/__init__.py +0 -0
  129. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/schema_builder/helpers/base_generators.py +0 -0
  130. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/schema_builder/helpers/entity_generators.py +0 -0
  131. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/schema_builder/helpers/git_checker.py +0 -0
  132. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/schema_builder/package_wiring.py +0 -0
  133. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/schema_builder/relationships.py +0 -0
  134. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/schema_builder/runner.py +0 -0
  135. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/schema_builder/schema.py +0 -0
  136. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/schema_builder/schema_manager.py +0 -0
  137. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/schema_builder/tables.py +0 -0
  138. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/schema_builder/views.py +0 -0
  139. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/secrets_battery/FEATURE.md +0 -0
  140. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/secrets_battery/__init__.py +0 -0
  141. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/secrets_battery/crypto.py +0 -0
  142. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/secrets_battery/items.py +0 -0
  143. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/secrets_battery/model.py +0 -0
  144. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/secrets_battery/org_service.py +0 -0
  145. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/secrets_battery/service.py +0 -0
  146. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/session/README.md +0 -0
  147. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/session/__init__.py +0 -0
  148. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/session/coalesce.py +0 -0
  149. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/session/dag.py +0 -0
  150. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/session/errors.py +0 -0
  151. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/session/fallback.py +0 -0
  152. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/session/flush.py +0 -0
  153. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/session/lifecycle.py +0 -0
  154. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/session/managed.py +0 -0
  155. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/session/op.py +0 -0
  156. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/session/reads.py +0 -0
  157. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/session/session.py +0 -0
  158. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/session/telemetry.py +0 -0
  159. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/sql_executor/MODULE_README.md +0 -0
  160. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/sql_executor/__init__.py +0 -0
  161. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/sql_executor/executor.py +0 -0
  162. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/sql_executor/queries.py +0 -0
  163. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/sql_executor/registry.py +0 -0
  164. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/sql_executor/types.py +0 -0
  165. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/sql_executor/utils.py +0 -0
  166. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/state.py +0 -0
  167. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/utils/__init__.py +0 -0
  168. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/utils/sql_utils.py +0 -0
  169. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/matrx_orm/utils/type_converters.py +0 -0
  170. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/scripts/git-branches.sh +0 -0
  171. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/scripts/publish.sh +0 -0
  172. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/scripts/release.sh +0 -0
  173. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/MODULE_README.md +0 -0
  174. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/__init__.py +0 -0
  175. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/conftest.py +0 -0
  176. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/MODULE_README.md +0 -0
  177. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/__init__.py +0 -0
  178. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_admin_db_columns.py +0 -0
  179. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_admin_search_negation.py +0 -0
  180. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_admin_search_sentinels.py +0 -0
  181. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_advisory_lock.py +0 -0
  182. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_agent_message.py +0 -0
  183. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_api_auth.py +0 -0
  184. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_api_config.py +0 -0
  185. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_api_handlers.py +0 -0
  186. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_api_protocol.py +0 -0
  187. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_array_agg_order_by.py +0 -0
  188. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_associations.py +0 -0
  189. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_bulk_update_by_pk.py +0 -0
  190. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_bulk_upsert_increment_set_fields.py +0 -0
  191. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_cache_debug.py +0 -0
  192. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_call_function.py +0 -0
  193. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_catalog.py +0 -0
  194. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_composite_pk_filter.py +0 -0
  195. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_composite_pk_write_paths.py +0 -0
  196. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_config.py +0 -0
  197. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_conflict_writes.py +0 -0
  198. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_connection_codecs.py +0 -0
  199. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_connection_poison_guard.py +0 -0
  200. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_connection_primitives.py +0 -0
  201. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_count_composite_distinct.py +0 -0
  202. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_db_function_field.py +0 -0
  203. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_ddl_generator.py +0 -0
  204. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_derived_table.py +0 -0
  205. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_dirty_tracking.py +0 -0
  206. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_dynamic_crud.py +0 -0
  207. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_exceptions.py +0 -0
  208. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_expression_primitives.py +0 -0
  209. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_fields.py +0 -0
  210. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_filter_jsonb_agg_array_index.py +0 -0
  211. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_from_alias.py +0 -0
  212. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_fts.py +0 -0
  213. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_func_expression_args.py +0 -0
  214. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_graph_walk.py +0 -0
  215. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_having_annotate_alias.py +0 -0
  216. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_insert_ignore_and_admin_primitives.py +0 -0
  217. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_introspect_rls_policies.py +0 -0
  218. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_join_and_aggregate.py +0 -0
  219. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_latest_rows.py +0 -0
  220. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_listen_notify.py +0 -0
  221. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_managed_write_guard.py +0 -0
  222. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_matrx_entity.py +0 -0
  223. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_migration_diff_types.py +0 -0
  224. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_migration_loader.py +0 -0
  225. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_model_instance.py +0 -0
  226. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_model_meta.py +0 -0
  227. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_order_by_expression.py +0 -0
  228. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_patch_jsonb_path.py +0 -0
  229. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_pool_maintenance.py +0 -0
  230. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_postgrest_filters.py +0 -0
  231. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_pydantic_bridge.py +0 -0
  232. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_query_builder.py +0 -0
  233. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_query_error_detail.py +0 -0
  234. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_query_executor_sql.py +0 -0
  235. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_query_timeout.py +0 -0
  236. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_querybuilder_clone_semantics.py +0 -0
  237. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_queue_claim.py +0 -0
  238. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_registry.py +0 -0
  239. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_registry_multi_database.py +0 -0
  240. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_registry_multi_schema.py +0 -0
  241. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_relational_backfill.py +0 -0
  242. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_relations.py +0 -0
  243. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_resilience.py +0 -0
  244. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_schema_exists.py +0 -0
  245. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_session_advisory_lock.py +0 -0
  246. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_state_cache.py +0 -0
  247. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_subquery_filter_raw.py +0 -0
  248. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_subquery_in_filter.py +0 -0
  249. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_supabase_auth.py +0 -0
  250. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_supabase_config.py +0 -0
  251. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_tls.py +0 -0
  252. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_typed_join_projection.py +0 -0
  253. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_update_case_expression.py +0 -0
  254. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_update_subquery.py +0 -0
  255. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_upsert_with_conflict.py +0 -0
  256. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_values_jsonb_decode.py +0 -0
  257. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_watched_lifecycle.py +0 -0
  258. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level1/test_write_retry_semantics.py +0 -0
  259. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level2/MODULE_README.md +0 -0
  260. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level2/__init__.py +0 -0
  261. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level2/conftest.py +0 -0
  262. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level2/test_bulk_ops.py +0 -0
  263. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level2/test_cache_integration.py +0 -0
  264. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level2/test_crud.py +0 -0
  265. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level2/test_foreign_keys.py +0 -0
  266. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level2/test_m2m.py +0 -0
  267. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level2/test_manager.py +0 -0
  268. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level2/test_migrations_live.py +0 -0
  269. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level2/test_query_execution.py +0 -0
  270. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/level2/test_schema_diff.py +0 -0
  271. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/sample_project/.env.example +0 -0
  272. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/sample_project/README.md +0 -0
  273. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/sample_project/__init__.py +0 -0
  274. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/sample_project/generate.py +0 -0
  275. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/sample_project/generated/.gitkeep +0 -0
  276. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/sample_project/matrx_orm.yaml +0 -0
  277. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/sample_project/test_schema_generation.py +0 -0
  278. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/sample_project_desktop/.env.example +0 -0
  279. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/sample_project_desktop/README.md +0 -0
  280. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/sample_project_desktop/__init__.py +0 -0
  281. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/sample_project_desktop/client_example.py +0 -0
  282. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/sample_project_desktop/client_example.ts +0 -0
  283. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/sample_project_desktop/client_supabase_example.py +0 -0
  284. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/sample_project_desktop/server.py +0 -0
  285. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/schema/entity_tests.py +0 -0
  286. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/schema/test_base_generation.py +0 -0
  287. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/schema/test_composite_pk_fk_generation.py +0 -0
  288. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/schema/test_composite_unique_generation.py +0 -0
  289. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/schema/test_dto_identity_generation.py +0 -0
  290. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/schema/test_entity_capability_generation.py +0 -0
  291. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/schema/test_generate_schema.py +0 -0
  292. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/schema/test_multi_schema_output.py +0 -0
  293. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/schema/test_sql_expression_defaults.py +0 -0
  294. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/schema/test_view_generation.py +0 -0
  295. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/schema_builder/test_focused_generation_plan.py +0 -0
  296. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/session/__init__.py +0 -0
  297. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/session/conftest.py +0 -0
  298. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/session/test_capture_poison_proof.py +0 -0
  299. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/session/test_coalesce.py +0 -0
  300. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/session/test_dag.py +0 -0
  301. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/session/test_disk_spill_fallback.py +0 -0
  302. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/session/test_flush_individual_fallback.py +0 -0
  303. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/session/test_governed_write.py +0 -0
  304. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/session/test_managed.py +0 -0
  305. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/session/test_op_enqueue_site.py +0 -0
  306. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/session/test_reads.py +0 -0
  307. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/session/test_session.py +0 -0
  308. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/test_database_name_alias.py +0 -0
  309. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/test_db_functions.py +0 -0
  310. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/test_model_cls_refactor.py +0 -0
  311. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/test_no_blocking_asyncpg_tls.py +0 -0
  312. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/test_secrets_battery.py +0 -0
  313. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/test_sql_param_casts.py +0 -0
  314. {matrx_orm-3.1.30 → matrx_orm-3.1.32}/tests/test_sync_twins_guard.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: matrx-orm
3
- Version: 3.1.30
3
+ Version: 3.1.32
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
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from datetime import date, datetime
3
+ from datetime import date, datetime, timedelta
4
4
  from decimal import Decimal
5
5
  from typing import TYPE_CHECKING, Any
6
6
  from uuid import UUID
@@ -174,7 +174,7 @@ class OrderBy:
174
174
 
175
175
  def __init__(
176
176
  self,
177
- expression: "str | F | Func",
177
+ expression: str | F | Func,
178
178
  *,
179
179
  descending: bool = False,
180
180
  nulls: str | None = None,
@@ -228,7 +228,7 @@ class Q:
228
228
  OR = "OR"
229
229
  NOT = "NOT"
230
230
 
231
- def __init__(self, *args: "Q", **kwargs: Any) -> None:
231
+ def __init__(self, *args: Q, **kwargs: Any) -> None:
232
232
  self.connector: str = self.AND
233
233
  self.negated: bool = False
234
234
  # Children is a list of (Q | None) for compound nodes, or kwargs for leaf nodes.
@@ -237,19 +237,19 @@ class Q:
237
237
 
238
238
  # -- Boolean operators --------------------------------------------------
239
239
 
240
- def __and__(self, other: "Q") -> "Q":
240
+ def __and__(self, other: Q) -> Q:
241
241
  node = Q()
242
242
  node.connector = self.AND
243
243
  node.children = [self, other]
244
244
  return node
245
245
 
246
- def __or__(self, other: "Q") -> "Q":
246
+ def __or__(self, other: Q) -> Q:
247
247
  node = Q()
248
248
  node.connector = self.OR
249
249
  node.children = [self, other]
250
250
  return node
251
251
 
252
- def __invert__(self) -> "Q":
252
+ def __invert__(self) -> Q:
253
253
  node = Q(**self.kwargs)
254
254
  node.connector = self.connector
255
255
  node.children = list(self.children)
@@ -697,7 +697,7 @@ class Interval:
697
697
  def __init__(self, **parts: int | float) -> None:
698
698
  if not parts or not set(parts).issubset(self._UNITS):
699
699
  raise ValueError("Interval received an unsupported or empty unit set")
700
- self.value = " ".join(f"{amount} {unit}" for unit, amount in parts.items())
700
+ self.value = timedelta(**parts)
701
701
 
702
702
  def as_sql(self, params: list[Any]) -> str:
703
703
  params.append(self.value)
@@ -1776,7 +1776,7 @@ class Window:
1776
1776
 
1777
1777
  def __init__(
1778
1778
  self,
1779
- expression: "Func | Aggregate",
1779
+ expression: Func | Aggregate,
1780
1780
  *,
1781
1781
  partition_by: str | list[str] | None = None,
1782
1782
  order_by: str | list[str] | None = None,
@@ -79,6 +79,7 @@ class ConflictTarget:
79
79
  *,
80
80
  coalesce: dict[str, Any] | None = None,
81
81
  lower: tuple[str, ...] = (),
82
+ md5: tuple[str, ...] = (),
82
83
  where_is_null: tuple[str, ...] = (),
83
84
  where_is_not_null: tuple[str, ...] = (),
84
85
  where_equals: dict[str, Any] | None = None,
@@ -93,6 +94,9 @@ class ConflictTarget:
93
94
  unknown_lower = set(lower) - set(columns)
94
95
  if unknown_lower:
95
96
  raise ValueError(f"lower columns are not in conflict target: {sorted(unknown_lower)}")
97
+ unknown_md5 = set(md5) - set(columns)
98
+ if unknown_md5:
99
+ raise ValueError(f"md5 columns are not in conflict target: {sorted(unknown_md5)}")
96
100
  expressions: list[str] = []
97
101
  for column in columns:
98
102
  rendered = _identifier(column)
@@ -100,6 +104,8 @@ class ConflictTarget:
100
104
  rendered = f"COALESCE({rendered}, {_literal(coalesce[column])})"
101
105
  if column in lower:
102
106
  rendered = f"lower({rendered})"
107
+ if column in md5:
108
+ rendered = f"md5({rendered})"
103
109
  expressions.append(rendered)
104
110
  predicates = [f"{_identifier(column)} IS NULL" for column in where_is_null]
105
111
  predicates.extend(f"{_identifier(column)} IS NOT NULL" for column in where_is_not_null)
@@ -147,150 +147,127 @@ def analyze_junction_tables(schema, database_project, additional_schemas=None):
147
147
  return cached
148
148
 
149
149
  query = """
150
- WITH junction_candidates AS (
151
- SELECT
152
- tc.table_schema,
153
- tc.table_name,
154
- COUNT(*) as fk_count
155
- FROM
156
- information_schema.table_constraints tc
157
- WHERE
158
- tc.table_schema = ANY(%s::text[])
159
- AND tc.constraint_type = 'FOREIGN KEY'
160
- GROUP BY
161
- tc.table_schema, tc.table_name
162
- HAVING
163
- COUNT(*) >= 2
150
+ WITH junction_candidates AS MATERIALIZED (
151
+ SELECT
152
+ constraint_row.conrelid AS table_oid
153
+ FROM pg_catalog.pg_constraint constraint_row
154
+ JOIN pg_catalog.pg_class table_row
155
+ ON table_row.oid = constraint_row.conrelid
156
+ JOIN pg_catalog.pg_namespace table_schema
157
+ ON table_schema.oid = table_row.relnamespace
158
+ WHERE
159
+ constraint_row.contype = 'f'
160
+ AND table_schema.nspname = ANY(%s::text[])
161
+ AND table_row.relkind IN ('r', 'p')
162
+ GROUP BY constraint_row.conrelid
163
+ HAVING COUNT(*) >= 2
164
164
  ),
165
- foreign_key_details AS (
166
- SELECT DISTINCT
167
- tc.table_schema,
168
- tc.table_name,
169
- kcu.column_name as fk_column,
170
- ccu.table_schema as referenced_schema,
171
- ccu.table_name as referenced_table,
172
- ccu.column_name as referenced_column,
173
- EXISTS (
174
- SELECT 1
175
- FROM information_schema.table_constraints uc
176
- JOIN information_schema.key_column_usage kcu2
177
- ON uc.constraint_name = kcu2.constraint_name
178
- WHERE uc.table_schema = tc.table_schema
179
- AND uc.table_name = tc.table_name
180
- AND uc.constraint_type IN ('UNIQUE', 'PRIMARY KEY')
181
- AND kcu2.column_name = kcu.column_name
182
- ) as has_unique_constraint,
183
- EXISTS (
184
- SELECT 1
185
- FROM information_schema.table_constraints uc
186
- JOIN information_schema.key_column_usage kcu2
187
- ON uc.constraint_name = kcu2.constraint_name
188
- WHERE uc.table_schema = ccu.table_schema
189
- AND uc.table_name = ccu.table_name
190
- AND uc.constraint_type IN ('UNIQUE', 'PRIMARY KEY')
191
- AND kcu2.column_name = ccu.column_name
192
- ) as referenced_has_unique_constraint
193
- FROM
194
- information_schema.table_constraints tc
195
- JOIN information_schema.key_column_usage kcu
196
- ON tc.constraint_name = kcu.constraint_name
197
- AND tc.table_schema = kcu.table_schema
198
- JOIN information_schema.constraint_column_usage ccu
199
- ON ccu.constraint_name = tc.constraint_name
200
- WHERE
201
- tc.table_schema = ANY(%s::text[])
202
- AND tc.constraint_type = 'FOREIGN KEY'
165
+ foreign_key_details AS MATERIALIZED (
166
+ SELECT
167
+ candidate.table_oid,
168
+ table_schema.nspname AS table_schema,
169
+ table_row.relname AS table_name,
170
+ constraint_row.oid AS constraint_oid,
171
+ key_pair.position,
172
+ local_column.attname AS fk_column,
173
+ referenced_table.oid AS referenced_table_oid,
174
+ referenced_schema.nspname AS referenced_schema,
175
+ referenced_table.relname AS referenced_table,
176
+ referenced_column.attname AS referenced_column
177
+ FROM junction_candidates candidate
178
+ JOIN pg_catalog.pg_constraint constraint_row
179
+ ON constraint_row.conrelid = candidate.table_oid
180
+ AND constraint_row.contype = 'f'
181
+ JOIN pg_catalog.pg_class table_row
182
+ ON table_row.oid = candidate.table_oid
183
+ JOIN pg_catalog.pg_namespace table_schema
184
+ ON table_schema.oid = table_row.relnamespace
185
+ JOIN pg_catalog.pg_class referenced_table
186
+ ON referenced_table.oid = constraint_row.confrelid
187
+ JOIN pg_catalog.pg_namespace referenced_schema
188
+ ON referenced_schema.oid = referenced_table.relnamespace
189
+ CROSS JOIN LATERAL unnest(
190
+ constraint_row.conkey,
191
+ constraint_row.confkey
192
+ ) WITH ORDINALITY AS key_pair(local_attnum, referenced_attnum, position)
193
+ JOIN pg_catalog.pg_attribute local_column
194
+ ON local_column.attrelid = candidate.table_oid
195
+ AND local_column.attnum = key_pair.local_attnum
196
+ JOIN pg_catalog.pg_attribute referenced_column
197
+ ON referenced_column.attrelid = referenced_table.oid
198
+ AND referenced_column.attnum = key_pair.referenced_attnum
203
199
  ),
204
- primary_key_columns AS (
205
- SELECT
206
- kcu.table_schema,
207
- kcu.table_name,
208
- kcu.column_name as pk_column
209
- FROM
210
- information_schema.table_constraints tc
211
- JOIN information_schema.key_column_usage kcu
212
- ON tc.constraint_name = kcu.constraint_name
213
- AND tc.table_schema = kcu.table_schema
214
- WHERE
215
- tc.table_schema = ANY(%s::text[])
216
- AND tc.constraint_type = 'PRIMARY KEY'
200
+ primary_key_columns AS MATERIALIZED (
201
+ SELECT
202
+ constraint_row.conrelid AS table_oid,
203
+ array_agg(column_row.attname ORDER BY key_column.position) AS primary_keys
204
+ FROM pg_catalog.pg_constraint constraint_row
205
+ CROSS JOIN LATERAL unnest(constraint_row.conkey)
206
+ WITH ORDINALITY AS key_column(attnum, position)
207
+ JOIN pg_catalog.pg_attribute column_row
208
+ ON column_row.attrelid = constraint_row.conrelid
209
+ AND column_row.attnum = key_column.attnum
210
+ WHERE constraint_row.contype = 'p'
211
+ GROUP BY constraint_row.conrelid
217
212
  ),
218
- additional_columns AS (
219
- SELECT
220
- c.table_schema,
221
- c.table_name,
222
- array_agg(DISTINCT c.column_name ORDER BY c.column_name) as additional_fields
223
- FROM
224
- information_schema.columns c
225
- JOIN junction_candidates jc
226
- ON c.table_name = jc.table_name
227
- AND c.table_schema = jc.table_schema
228
- LEFT JOIN foreign_key_details fkd
229
- ON c.table_name = fkd.table_name
230
- AND c.table_schema = fkd.table_schema
231
- AND c.column_name = fkd.fk_column
232
- LEFT JOIN primary_key_columns pkc
233
- ON c.table_name = pkc.table_name
234
- AND c.table_schema = pkc.table_schema
235
- AND c.column_name = pkc.pk_column
236
- WHERE
237
- c.table_schema = ANY(%s::text[])
238
- AND fkd.fk_column IS NULL
239
- AND pkc.pk_column IS NULL
240
- GROUP BY
241
- c.table_schema, c.table_name
213
+ additional_columns AS MATERIALIZED (
214
+ SELECT
215
+ candidate.table_oid,
216
+ array_agg(column_row.attname ORDER BY column_row.attnum) AS additional_fields
217
+ FROM junction_candidates candidate
218
+ JOIN pg_catalog.pg_attribute column_row
219
+ ON column_row.attrelid = candidate.table_oid
220
+ AND column_row.attnum > 0
221
+ AND NOT column_row.attisdropped
222
+ WHERE NOT EXISTS (
223
+ SELECT 1
224
+ FROM pg_catalog.pg_constraint key_constraint
225
+ WHERE
226
+ key_constraint.conrelid = candidate.table_oid
227
+ AND key_constraint.contype IN ('f', 'p')
228
+ AND column_row.attnum = ANY(key_constraint.conkey)
229
+ )
230
+ GROUP BY candidate.table_oid
242
231
  )
243
- SELECT
244
- jc.table_schema,
245
- jc.table_name,
232
+ SELECT
233
+ foreign_key.table_schema,
234
+ foreign_key.table_name,
246
235
  json_agg(
247
236
  json_build_object(
248
- 'table', fkd.table_name,
249
- 'table_pks', (
250
- SELECT array_agg(DISTINCT pkc_table.pk_column)
251
- FROM primary_key_columns pkc_table
252
- WHERE pkc_table.table_schema = fkd.table_schema
253
- AND pkc_table.table_name = fkd.table_name
254
- ),
255
- 'related_table', fkd.referenced_table,
256
- 'related_pks', (
257
- SELECT array_agg(DISTINCT pkc_related.pk_column)
258
- FROM primary_key_columns pkc_related
259
- WHERE pkc_related.table_schema = fkd.referenced_schema
260
- AND pkc_related.table_name = fkd.referenced_table
261
- ),
262
- 'related_schema', fkd.referenced_schema,
263
- 'connecting_column', fkd.fk_column,
264
- 'referenced_column', fkd.referenced_column,
237
+ 'table', foreign_key.table_name,
238
+ 'table_pks', local_primary_key.primary_keys,
239
+ 'related_table', foreign_key.referenced_table,
240
+ 'related_pks', referenced_primary_key.primary_keys,
241
+ 'related_schema', foreign_key.referenced_schema,
242
+ 'connecting_column', foreign_key.fk_column,
243
+ 'referenced_column', foreign_key.referenced_column,
265
244
  'relationship_type',
266
- CASE
267
- WHEN fkd.referenced_table = fkd.table_name THEN 'self-referential'
268
- WHEN jc.fk_count >= 2 THEN 'many-to-many'
269
- WHEN fkd.has_unique_constraint AND fkd.referenced_has_unique_constraint THEN 'one-to-one'
270
- WHEN NOT fkd.has_unique_constraint THEN 'many-to-one'
271
- WHEN fkd.has_unique_constraint THEN 'one-to-many'
272
- ELSE 'undefined'
245
+ CASE
246
+ WHEN foreign_key.referenced_table_oid = foreign_key.table_oid
247
+ THEN 'self-referential'
248
+ ELSE 'many-to-many'
273
249
  END
274
250
  )
275
- ) as connected_tables,
276
- COALESCE(ac.additional_fields, ARRAY[]::text[]) as additional_fields,
277
- array_agg(DISTINCT pkc.pk_column) FILTER (WHERE pkc.pk_column IS NOT NULL) as primary_keys
278
- FROM
279
- junction_candidates jc
280
- JOIN foreign_key_details fkd
281
- ON jc.table_name = fkd.table_name
282
- AND jc.table_schema = fkd.table_schema
283
- LEFT JOIN additional_columns ac
284
- ON jc.table_name = ac.table_name
285
- AND jc.table_schema = ac.table_schema
286
- LEFT JOIN primary_key_columns pkc
287
- ON jc.table_name = pkc.table_name
288
- AND jc.table_schema = pkc.table_schema
289
- GROUP BY
290
- jc.table_schema, jc.table_name, ac.additional_fields;
251
+ ORDER BY foreign_key.constraint_oid, foreign_key.position
252
+ ) AS connected_tables,
253
+ COALESCE(additional.additional_fields, ARRAY[]::text[]) AS additional_fields,
254
+ local_primary_key.primary_keys
255
+ FROM foreign_key_details foreign_key
256
+ LEFT JOIN primary_key_columns local_primary_key
257
+ ON local_primary_key.table_oid = foreign_key.table_oid
258
+ LEFT JOIN primary_key_columns referenced_primary_key
259
+ ON referenced_primary_key.table_oid = foreign_key.referenced_table_oid
260
+ LEFT JOIN additional_columns additional
261
+ ON additional.table_oid = foreign_key.table_oid
262
+ GROUP BY
263
+ foreign_key.table_oid,
264
+ foreign_key.table_schema,
265
+ foreign_key.table_name,
266
+ local_primary_key.primary_keys,
267
+ additional.additional_fields;
291
268
  """
292
269
 
293
- results = execute_sql_query(query, (schemas, schemas, schemas, schemas), database_project)
270
+ results = execute_sql_query(query, (schemas,), database_project)
294
271
 
295
272
  # Transform the results into a more readable format
296
273
  analyzed_junctions = {}
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "matrx-orm"
3
- version = "3.1.30"
3
+ version = "3.1.32"
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" }
@@ -13,8 +13,9 @@ style of test_expression_primitives.py / test_join_and_aggregate.py.
13
13
  """
14
14
  from __future__ import annotations
15
15
 
16
+ from datetime import timedelta
17
+
16
18
  import pytest
17
- from matrx_orm import ColumnComparison, F
18
19
  from matrx_orm.core.base import Model, ModelMeta
19
20
  from matrx_orm.core.expressions import (
20
21
  ArrayAny,
@@ -162,8 +163,8 @@ def _make_order_model():
162
163
 
163
164
 
164
165
  def test_case_in_annotate():
165
- O = _make_order_model()
166
- qb = O.filter().annotate(
166
+ Order = _make_order_model()
167
+ qb = Order.filter().annotate(
167
168
  tier=Case(When(Compare(F("amount"), ">=", 1000), then=Value("gold")), default=Value("standard"))
168
169
  )
169
170
  sql, params = QueryExecutor(qb._build_query())._build_query()
@@ -174,8 +175,8 @@ def test_case_in_annotate():
174
175
 
175
176
 
176
177
  def test_case_in_order_by():
177
- O = _make_order_model()
178
- qb = O.filter().order_by(
178
+ Order = _make_order_model()
179
+ qb = Order.filter().order_by(
179
180
  OrderBy(Case(When(Compare(F("status"), "=", "paid"), then=0), default=1)),
180
181
  "-amount",
181
182
  )
@@ -187,8 +188,8 @@ def test_case_in_order_by():
187
188
  def test_case_in_order_by_with_nulls_last():
188
189
  """Mirrors the real _entity_recall Step-1 ORDER BY: exact-match-first
189
190
  tiebreak, then confidence_avg DESC NULLS LAST."""
190
- O = _make_order_model()
191
- qb = O.filter().order_by(
191
+ Order = _make_order_model()
192
+ qb = Order.filter().order_by(
192
193
  OrderBy(Case(When(Compare(Lower(F("status")), "=", "paid"), then=0), default=1)),
193
194
  OrderBy("amount", descending=True, nulls="last"),
194
195
  )
@@ -405,7 +406,7 @@ def test_expression_branch_anchors_the_case_so_literals_are_not_cast():
405
406
  "CASE WHEN (w IS NULL OR w < (NOW() - $1::interval)) "
406
407
  "THEN $2 ELSE (daily_auto_rag_cost_used_usd + $3) END"
407
408
  )
408
- assert params == ["24 hours", 0.42, 0.42]
409
+ assert params == [timedelta(hours=24), 0.42, 0.42]
409
410
 
410
411
 
411
412
  def test_column_branch_anchors_the_case_so_literals_are_not_cast():
@@ -102,6 +102,16 @@ def test_typed_partial_target_rejects_non_identifier_columns() -> None:
102
102
  ConflictTarget.partial(["project_id; DROP TABLE x"])
103
103
 
104
104
 
105
+ def test_typed_partial_target_renders_md5_expression() -> None:
106
+ target = ConflictTarget.partial(["source_id", "url"], md5=("url",))
107
+ assert target.expressions == ("source_id", "md5(url)")
108
+
109
+
110
+ def test_typed_partial_target_rejects_unknown_md5_column() -> None:
111
+ with pytest.raises(ValueError, match="md5 columns are not in conflict target"):
112
+ ConflictTarget.partial(["source_id", "url"], md5=("missing",))
113
+
114
+
105
115
  # ── Executor SQL shape ───────────────────────────────────────────────────────
106
116
 
107
117
 
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from datetime import timedelta
3
4
  from typing import Any
4
5
 
5
6
  import pytest
@@ -193,6 +194,7 @@ def test_arrays_tuple_membership_constant_and_interval() -> None:
193
194
  ["a"],
194
195
  ["00000000-0000-0000-0000-000000000001"],
195
196
  ]
197
+ assert params[4] == timedelta(hours=24)
196
198
 
197
199
 
198
200
  def test_function_name_and_tuple_type_are_validated() -> None:
@@ -0,0 +1,55 @@
1
+ from importlib import import_module
2
+
3
+ import_module("matrx_orm.schema_builder")
4
+ table_detailed_relationships = import_module("matrx_orm.python_sql.table_detailed_relationships")
5
+
6
+
7
+ def test_junction_analysis_uses_oid_catalog_query(monkeypatch):
8
+ captured: dict[str, object] = {}
9
+ rows = [
10
+ {
11
+ "table_schema": "app",
12
+ "table_name": "membership",
13
+ "connected_tables": [
14
+ {
15
+ "table": "membership",
16
+ "table_pks": ["user_id", "team_id"],
17
+ "related_table": "users",
18
+ "related_pks": ["id"],
19
+ "related_schema": "auth",
20
+ "connecting_column": "user_id",
21
+ "referenced_column": "id",
22
+ "relationship_type": "many-to-many",
23
+ }
24
+ ],
25
+ "additional_fields": ["created_at"],
26
+ "primary_keys": ["user_id", "team_id"],
27
+ }
28
+ ]
29
+
30
+ def fake_execute_sql_query(query, params, database_project):
31
+ captured.update(query=query, params=params, database_project=database_project)
32
+ return rows
33
+
34
+ monkeypatch.setattr(
35
+ table_detailed_relationships,
36
+ "execute_sql_query",
37
+ fake_execute_sql_query,
38
+ )
39
+ table_detailed_relationships.clear_junction_analysis_cache()
40
+
41
+ junctions, relationships = table_detailed_relationships.analyze_junction_tables(
42
+ "app",
43
+ "main",
44
+ additional_schemas=["auth"],
45
+ )
46
+
47
+ query = captured["query"]
48
+ assert isinstance(query, str)
49
+ assert "pg_catalog.pg_constraint" in query
50
+ assert "WITH ORDINALITY" in query
51
+ assert "information_schema" not in query
52
+ assert captured["params"] == (["app", "auth"],)
53
+ assert captured["database_project"] == "main"
54
+ assert junctions["membership"]["primary_keys"] == ["user_id", "team_id"]
55
+ assert relationships[0]["table_additional_fields"] == ["created_at"]
File without changes
File without changes
File without changes
File without changes
File without changes