ferro-orm 0.16.3__tar.gz → 0.17.1__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 (384) hide show
  1. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/CHANGELOG.md +38 -0
  2. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/CONTEXT.md +20 -0
  3. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/Cargo.lock +79 -68
  4. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/Cargo.toml +1 -1
  5. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/PKG-INFO +1 -1
  6. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/crates/ferro-ddl-lowering/src/lib.rs +7 -0
  7. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/crates/ferro-migrate/src/emit.rs +89 -10
  8. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/crates/ferro-migrate/src/lib.rs +103 -1
  9. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/crates/ferro-migrate/src/tests.rs +343 -0
  10. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/crates/ferro-schema-ir/src/lib.rs +263 -16
  11. ferro_orm-0.17.1/docs/adr/0007-reverse-relations-tested-not-traversed.md +62 -0
  12. ferro_orm-0.17.1/docs/adr/0008-predicate-negation-uniform-not.md +30 -0
  13. ferro_orm-0.17.1/docs/adr/0010-create-pass-owns-only-missing-tables.md +39 -0
  14. ferro_orm-0.17.1/docs/examples/existence_tests.py +195 -0
  15. ferro_orm-0.17.1/docs/examples/existence_tests_annotated.py +101 -0
  16. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/examples/predicates.py +42 -0
  17. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/examples/predicates_annotated.py +22 -0
  18. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/pages/api/queries.md +4 -0
  19. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/pages/concepts/architecture.md +1 -1
  20. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/pages/faq.md +1 -1
  21. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/pages/guide/migrations.md +3 -0
  22. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/pages/guide/queries.md +172 -4
  23. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/pages/roadmap.md +2 -2
  24. ferro_orm-0.17.1/docs/plans/2026-07-18-016-existence-tests-design.md +161 -0
  25. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/pyproject.toml +1 -1
  26. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/src/ferro/__init__.py +16 -4
  27. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/src/ferro/columns.py +94 -0
  28. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/src/ferro/ir/compiler.py +5 -0
  29. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/src/ferro/models.py +4 -1
  30. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/src/ferro/query/builder.py +48 -9
  31. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/src/ferro/query/nodes.py +484 -24
  32. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/src/ferro/query/wire.py +14 -7
  33. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/src/introspect.rs +231 -0
  34. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/src/migrate.rs +51 -6
  35. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/src/operations.rs +147 -29
  36. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/src/query.rs +572 -4
  37. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/src/schema.rs +13 -0
  38. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/fixtures/ir_vectors/README.md +7 -5
  39. ferro_orm-0.17.1/tests/fixtures/ir_vectors/query_account_exists_v7.json +40 -0
  40. ferro_orm-0.17.1/tests/fixtures/ir_vectors/query_account_scoped_exists_v7.json +74 -0
  41. ferro_orm-0.17.1/tests/fixtures/ir_vectors/query_owner_nested_exists_v7.json +47 -0
  42. ferro_orm-0.17.1/tests/fixtures/ir_vectors/query_owner_not_exists_v7.json +37 -0
  43. ferro_orm-0.16.3/tests/fixtures/ir_vectors/query_transaction_aggregate_v5.json → ferro_orm-0.17.1/tests/fixtures/ir_vectors/query_transaction_aggregate_v7.json +2 -2
  44. ferro_orm-0.16.3/tests/fixtures/ir_vectors/query_transaction_global_aggregate_v5.json → ferro_orm-0.17.1/tests/fixtures/ir_vectors/query_transaction_global_aggregate_v7.json +2 -2
  45. ferro_orm-0.16.3/tests/fixtures/ir_vectors/query_transaction_include_v5.json → ferro_orm-0.17.1/tests/fixtures/ir_vectors/query_transaction_include_v7.json +2 -2
  46. ferro_orm-0.16.3/tests/fixtures/ir_vectors/query_transaction_left_join_v5.json → ferro_orm-0.17.1/tests/fixtures/ir_vectors/query_transaction_left_join_v7.json +2 -2
  47. ferro_orm-0.16.3/tests/fixtures/ir_vectors/query_transaction_record_v5.json → ferro_orm-0.17.1/tests/fixtures/ir_vectors/query_transaction_record_v7.json +2 -2
  48. ferro_orm-0.16.3/tests/fixtures/ir_vectors/query_transaction_traversal_v5.json → ferro_orm-0.17.1/tests/fixtures/ir_vectors/query_transaction_traversal_v7.json +2 -2
  49. ferro_orm-0.16.3/tests/fixtures/ir_vectors/query_transaction_traversed_record_v5.json → ferro_orm-0.17.1/tests/fixtures/ir_vectors/query_transaction_traversed_record_v7.json +2 -2
  50. ferro_orm-0.16.3/tests/fixtures/ir_vectors/query_user_compound_v5.json → ferro_orm-0.17.1/tests/fixtures/ir_vectors/query_user_compound_v7.json +2 -2
  51. ferro_orm-0.17.1/tests/fixtures/ir_vectors/query_user_m2m_exists_v7.json +51 -0
  52. ferro_orm-0.17.1/tests/fixtures/ir_vectors/query_user_not_compound_v7.json +49 -0
  53. ferro_orm-0.17.1/tests/fixtures/ir_vectors/query_user_not_leaf_v7.json +44 -0
  54. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_auto_migrate.py +169 -0
  55. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_ir_vectors_contract.py +58 -7
  56. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_migrate_plan.py +84 -0
  57. ferro_orm-0.17.1/tests/test_negation.py +226 -0
  58. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_query_builder.py +1 -1
  59. ferro_orm-0.17.1/tests/test_query_exists.py +707 -0
  60. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_query_joins.py +13 -1
  61. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_query_wire_vectors.py +82 -10
  62. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  63. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  64. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/.github/PERMISSIONS.md +0 -0
  65. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/.github/PYPI_CHECKLIST.md +0 -0
  66. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/.github/PYPI_SETUP.md +0 -0
  67. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/.github/generated/wheels.generated.yml +0 -0
  68. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/.github/pull_request_template.md +0 -0
  69. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/.github/workflows/ci.yml +0 -0
  70. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/.github/workflows/packaging-smoke.yml +0 -0
  71. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/.github/workflows/publish-docs.yml +0 -0
  72. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/.github/workflows/publish.yml +0 -0
  73. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/.github/workflows/release.yml +0 -0
  74. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/.gitignore +0 -0
  75. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/.pre-commit-config.yaml +0 -0
  76. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/.python-version +0 -0
  77. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/AGENTS.md +0 -0
  78. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/CONTRIBUTING.md +0 -0
  79. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/LICENSE +0 -0
  80. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/README.md +0 -0
  81. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/benchmarks/README.md +0 -0
  82. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/benchmarks/__init__.py +0 -0
  83. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/benchmarks/__main__.py +0 -0
  84. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/benchmarks/backends.py +0 -0
  85. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/benchmarks/baselines/postgres.json +0 -0
  86. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/benchmarks/baselines/sqlite.json +0 -0
  87. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/benchmarks/compare.py +0 -0
  88. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/benchmarks/harness.py +0 -0
  89. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/benchmarks/model.py +0 -0
  90. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/benchmarks/run.py +0 -0
  91. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/crates/ferro-ddl-lowering/Cargo.toml +0 -0
  92. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/crates/ferro-migrate/Cargo.toml +0 -0
  93. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/crates/ferro-migrate/src/order.rs +0 -0
  94. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/crates/ferro-schema-ir/Cargo.toml +0 -0
  95. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/adr/0001-resolved-epoch-registration.md +0 -0
  96. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/adr/0002-unified-column-fact-derivation.md +0 -0
  97. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/adr/0003-path-blind-db-type-validation.md +0 -0
  98. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/adr/0004-jsonb-postgres-only-canonical.md +0 -0
  99. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/adr/0005-jsonb-default-on-postgres.md +0 -0
  100. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/adr/0006-relation-traversal-inner-joins.md +0 -0
  101. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/adr/0007-materialization-plan-complete-instances.md +0 -0
  102. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/adr/0008-populated-relations-include.md +0 -0
  103. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/adr/0009-aggregate-projections-derived-grouping.md +0 -0
  104. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/agents/domain.md +0 -0
  105. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/agents/issue-tracker.md +0 -0
  106. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/agents/triage-labels.md +0 -0
  107. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/architecture-review-2026-07-08.html +0 -0
  108. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/brainstorms/2026-04-29-named-connections-role-routing-requirements.md +0 -0
  109. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/brainstorms/2026-05-08-typed-query-predicates-requirements.md +0 -0
  110. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/brainstorms/2026-05-13-configurable-column-storage-types-requirements.md +0 -0
  111. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/brainstorms/2026-05-14-autogenerate-support-for-db-type-requirements.md +0 -0
  112. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/brainstorms/2026-05-25-annotated-strenum-cold-hydration-requirements.md +0 -0
  113. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/brainstorms/2026-06-24-ir-p8-119-wire-automigrate-requirements.md +0 -0
  114. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/brainstorms/2026-06-25-ir-p8-120-parity-gate-requirements.md +0 -0
  115. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/brainstorms/2026-06-26-ir-p8.5-140-shared-lowering-requirements.md +0 -0
  116. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/brainstorms/2026-06-26-ir-p8.5-143-db-type-drop-requirements.md +0 -0
  117. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/brainstorms/2026-06-26-ir-p8.5-144-reconcile-indexes-requirements.md +0 -0
  118. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/brainstorms/2026-06-28-ir-p8.5-141-single-schema-ir-producer-requirements.md +0 -0
  119. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/brainstorms/2026-06-29-ir-p8.6-146-dialect-enum-unification-requirements.md +0 -0
  120. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/brainstorms/2026-06-29-ir-p8.6-153-create-path-ir-requirements.md +0 -0
  121. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/brainstorms/2026-06-30-ir-p8.6-155-deferred-annotations-requirements.md +0 -0
  122. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/brainstorms/2026-06-30-ir-p8.6-158-check-renderer-requirements.md +0 -0
  123. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/brainstorms/2026-07-01-ir-p8.6-154-datetime-tz-coarseness-requirements.md +0 -0
  124. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/brainstorms/2026-07-01-ir-p8.6-162-typed-save-bind-requirements.md +0 -0
  125. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/brainstorms/2026-07-01-ir-p8.6-165-blob-introspection-false-positive-requirements.md +0 -0
  126. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/brainstorms/2026-07-02-ff-c1-codec-plan-design.md +0 -0
  127. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/brainstorms/2026-07-06-upgrade-guide-consolidation-requirements.md +0 -0
  128. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/examples/aggregations.py +0 -0
  129. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/examples/aggregations_annotated.py +0 -0
  130. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/examples/multiple_databases.py +0 -0
  131. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/examples/mutations.py +0 -0
  132. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/examples/pagination.py +0 -0
  133. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/examples/partial_selects.py +0 -0
  134. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/examples/partial_selects_annotated.py +0 -0
  135. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/examples/populated_relations.py +0 -0
  136. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/examples/populated_relations_annotated.py +0 -0
  137. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/examples/quickstart.py +0 -0
  138. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/examples/quickstart_annotated.py +0 -0
  139. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/examples/raw_sql.py +0 -0
  140. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/examples/relationships.py +0 -0
  141. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/examples/relationships_annotated.py +0 -0
  142. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/examples/soft_deletes.py +0 -0
  143. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/examples/soft_deletes_annotated.py +0 -0
  144. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/examples/testing_conftest.py +0 -0
  145. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/examples/timestamps.py +0 -0
  146. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/examples/timestamps_annotated.py +0 -0
  147. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/examples/transactions.py +0 -0
  148. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/examples/traversal.py +0 -0
  149. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/examples/traversal_annotated.py +0 -0
  150. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/pages/api/connection.md +0 -0
  151. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/pages/api/exceptions.md +0 -0
  152. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/pages/api/fields.md +0 -0
  153. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/pages/api/migrations.md +0 -0
  154. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/pages/api/model.md +0 -0
  155. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/pages/api/raw-sql.md +0 -0
  156. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/pages/api/relationships.md +0 -0
  157. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/pages/api/transactions.md +0 -0
  158. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/pages/changelog.md +0 -0
  159. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/pages/concepts/backends.md +0 -0
  160. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/pages/concepts/identity-map.md +0 -0
  161. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/pages/concepts/performance.md +0 -0
  162. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/pages/concepts/query-typing.md +0 -0
  163. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/pages/concepts/type-safety.md +0 -0
  164. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/pages/contributing.md +0 -0
  165. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/pages/getting-started/installation.md +0 -0
  166. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/pages/getting-started/next-steps.md +0 -0
  167. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/pages/getting-started/quickstart.md +0 -0
  168. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/pages/guide/aggregations.md +0 -0
  169. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/pages/guide/connections.md +0 -0
  170. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/pages/guide/models-and-fields.md +0 -0
  171. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/pages/guide/mutations.md +0 -0
  172. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/pages/guide/raw-sql.md +0 -0
  173. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/pages/guide/relationships.md +0 -0
  174. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/pages/guide/transactions.md +0 -0
  175. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/pages/howto/migrate-from-sqlalchemy.md +0 -0
  176. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/pages/howto/multiple-databases.md +0 -0
  177. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/pages/howto/pagination.md +0 -0
  178. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/pages/howto/soft-deletes.md +0 -0
  179. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/pages/howto/testing.md +0 -0
  180. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/pages/howto/timestamps.md +0 -0
  181. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/pages/howto/upgrade-guide.md +0 -0
  182. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/pages/index.md +0 -0
  183. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/pages/stylesheets/extra.css +0 -0
  184. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/pages/why-ferro.md +0 -0
  185. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/plans/2026-04-24-001-refactor-multi-db-backend-architecture-plan.md +0 -0
  186. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/plans/2026-04-29-001-typed-null-binds-plan.md +0 -0
  187. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/plans/2026-04-29-002-feat-named-connections-plan.md +0 -0
  188. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/plans/2026-05-07-001-refactor-generic-model-connection-plan.md +0 -0
  189. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/plans/2026-05-08-001-feat-typed-query-predicates-plan.md +0 -0
  190. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/plans/2026-05-13-001-feat-configurable-column-storage-types-plan.md +0 -0
  191. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/plans/2026-05-25-001-fix-annotated-strenum-cold-hydration-plan.md +0 -0
  192. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/plans/2026-06-19-001-ir-first-roadmap.md +0 -0
  193. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/plans/2026-06-24-001-feat-ir-p8-119-wire-automigrate-plan.md +0 -0
  194. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/plans/2026-06-25-001-feat-ir-p8-120-parity-gate-plan.md +0 -0
  195. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/plans/2026-06-26-001-feat-ir-p8.5-140-shared-lowering-plan.md +0 -0
  196. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/plans/2026-06-26-002-feat-ir-p8.5-143-db-type-drop-plan.md +0 -0
  197. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/plans/2026-06-26-003-feat-ir-p8.5-144-reconcile-indexes-plan.md +0 -0
  198. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/plans/2026-06-28-001-feat-ir-p8.5-141-single-schema-ir-producer-plan.md +0 -0
  199. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/plans/2026-06-29-001-refactor-ir-p8.6-153-create-path-unification-plan.md +0 -0
  200. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/plans/2026-06-29-002-refactor-ir-p8.6-146-dialect-enum-unification-plan.md +0 -0
  201. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/plans/2026-06-30-001-refactor-ir-p8.6-158-check-renderer-plan.md +0 -0
  202. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/plans/2026-06-30-002-fix-ir-p8.6-155-deferred-annotations-plan.md +0 -0
  203. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/plans/2026-07-01-001-feat-ir-p8.6-162-typed-save-bind-plan.md +0 -0
  204. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/plans/2026-07-01-002-fix-ir-p8.6-154-datetime-tz-coarseness-plan.md +0 -0
  205. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/plans/2026-07-01-003-fix-ir-p8.6-165-blob-introspection-plan.md +0 -0
  206. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/plans/2026-07-02-001-fable-fixes-roadmap.md +0 -0
  207. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/plans/2026-07-02-002-feat-ff-a-172-typed-exceptions-plan.md +0 -0
  208. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/plans/2026-07-02-003-ff-c-native-decode-design.md +0 -0
  209. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/plans/2026-07-02-004-ff-c-c2-catalog-cache-design.md +0 -0
  210. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/plans/2026-07-02-005-ff-d-identity-routing-design.md +0 -0
  211. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/plans/2026-07-02-006-ff-d-identity-routing-plan.md +0 -0
  212. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/plans/2026-07-03-007-ff-e-registry-identity-design.md +0 -0
  213. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/plans/2026-07-03-008-ff-e-registry-identity-plan.md +0 -0
  214. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/plans/2026-07-06-009-ff-f-query-builder-1.0-design.md +0 -0
  215. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/plans/2026-07-06-010-ff-f-query-builder-1.0-plan.md +0 -0
  216. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/plans/2026-07-06-011-ff-g-hardening-design.md +0 -0
  217. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/plans/2026-07-06-012-ff-g-hardening-plan.md +0 -0
  218. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/plans/2026-07-06-013-ff-g2-operations-dedup-design.md +0 -0
  219. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/plans/2026-07-06-014-ff-g2-operations-dedup-plan.md +0 -0
  220. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/plans/2026-07-06-015-docs-upgrade-guide-consolidation-plan.md +0 -0
  221. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/plans/ir-first-migration-guide.md +0 -0
  222. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/plans/ir-first-release-checklist.md +0 -0
  223. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/rfc/ir-contracts-v1.md +0 -0
  224. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/solutions/README.md +0 -0
  225. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/solutions/architecture-patterns/ir-first-lowering-consolidation-audit.md +0 -0
  226. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/solutions/architecture-patterns/ir-first-merge-readiness-review.md +0 -0
  227. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/solutions/issues/pydantic-slots-missing-after-ferro-hydration.md +0 -0
  228. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/solutions/issues/python-3.14-deferred-annotation-typeerror-swallow.md +0 -0
  229. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/solutions/issues/sa-pk-column-nullable-divergence.md +0 -0
  230. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/solutions/issues/sa-vs-rust-unique-constraint-shape.md +0 -0
  231. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/solutions/issues/self-fk-evicts-component-from-create-order.md +0 -0
  232. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/solutions/issues/sqlite-integer-decimal-hydrates-as-none.md +0 -0
  233. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/solutions/issues/sqlite-null-hydrates-as-int-zero.md +0 -0
  234. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/solutions/issues/typed-where-null-panics-is-null.md +0 -0
  235. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/solutions/patterns/configurable-column-storage-types.md +0 -0
  236. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/solutions/patterns/cross-emitter-ddl-parity.md +0 -0
  237. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/solutions/patterns/ddl-on-live-engine.md +0 -0
  238. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/solutions/patterns/derived-type-and-naming-decision-table.md +0 -0
  239. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/solutions/patterns/foreign-key-index.md +0 -0
  240. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/solutions/patterns/index-unique-redundancy.md +0 -0
  241. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/solutions/patterns/ir-invariants.md +0 -0
  242. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/solutions/patterns/persistence-state.md +0 -0
  243. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/solutions/patterns/shadow-fk-columns.md +0 -0
  244. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/solutions/patterns/sqlite-alembic-reconnect-hydration-tests.md +0 -0
  245. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/docs/solutions/patterns/typed-null-binds.md +0 -0
  246. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/justfile +0 -0
  247. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/scripts/demo_queries.py +0 -0
  248. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/scripts/demo_queries_pre_v012.py +0 -0
  249. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/src/backend.rs +0 -0
  250. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/src/codec.rs +0 -0
  251. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/src/codec_plan.rs +0 -0
  252. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/src/connection.rs +0 -0
  253. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/src/errors.rs +0 -0
  254. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/src/ferro/_annotation_utils.py +0 -0
  255. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/src/ferro/_bind_payload.py +0 -0
  256. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/src/ferro/_core.pyi +0 -0
  257. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/src/ferro/_deprecations.py +0 -0
  258. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/src/ferro/_shadow_fk_types.py +0 -0
  259. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/src/ferro/base.py +0 -0
  260. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/src/ferro/composite_indexes.py +0 -0
  261. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/src/ferro/composite_uniques.py +0 -0
  262. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/src/ferro/exceptions.py +0 -0
  263. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/src/ferro/fields.py +0 -0
  264. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/src/ferro/ir/__init__.py +0 -0
  265. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/src/ferro/metaclass.py +0 -0
  266. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/src/ferro/migrations/__init__.py +0 -0
  267. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/src/ferro/migrations/alembic.py +0 -0
  268. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/src/ferro/py.typed +0 -0
  269. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/src/ferro/query/__init__.py +0 -0
  270. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/src/ferro/query/rows.py +0 -0
  271. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/src/ferro/raw.py +0 -0
  272. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/src/ferro/registry.py +0 -0
  273. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/src/ferro/relations/__init__.py +0 -0
  274. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/src/ferro/relations/descriptors.py +0 -0
  275. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/src/ferro/session.py +0 -0
  276. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/src/ferro/state.py +0 -0
  277. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/src/hydration.rs +0 -0
  278. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/src/lib.rs +0 -0
  279. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/src/naming_ffi.rs +0 -0
  280. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/src/schema_bind.rs +0 -0
  281. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/src/state.rs +0 -0
  282. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/__init__.py +0 -0
  283. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/conftest.py +0 -0
  284. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/db_backends.py +0 -0
  285. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/fixtures/ir_vectors/codec_registry_core_v1.json +0 -0
  286. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/fixtures/ir_vectors/schema_invoice_baseline_v1.json +0 -0
  287. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/fixtures/ir_vectors/schema_phase1_fixture_models_v1.json +0 -0
  288. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/fixtures/ir_vectors/schema_raw_str_pk_autoincrement_v1.json +0 -0
  289. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/static_fixtures/bad_includes.py +0 -0
  290. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/static_fixtures/bad_predicates.py +0 -0
  291. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/static_fixtures/bad_projections.py +0 -0
  292. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/static_fixtures/good_includes.py +0 -0
  293. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/static_fixtures/good_predicates.py +0 -0
  294. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/static_fixtures/good_projections.py +0 -0
  295. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_aggregation.py +0 -0
  296. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_alembic_autogenerate.py +0 -0
  297. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_alembic_bridge.py +0 -0
  298. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_alembic_db_type.py +0 -0
  299. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_alembic_nullability.py +0 -0
  300. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_alembic_type_mapping.py +0 -0
  301. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_annotation_utils.py +0 -0
  302. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_bulk_create_chunking.py +0 -0
  303. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_bulk_install.py +0 -0
  304. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_bulk_update.py +0 -0
  305. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_catalog_cache.py +0 -0
  306. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_codec_plan.py +0 -0
  307. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_column_specs.py +0 -0
  308. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_compiled_query.py +0 -0
  309. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_composite_index.py +0 -0
  310. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_composite_unique.py +0 -0
  311. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_connection.py +0 -0
  312. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_connection_redaction.py +0 -0
  313. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_constraints.py +0 -0
  314. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_cross_emitter_parity.py +0 -0
  315. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_crud.py +0 -0
  316. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_db_backends.py +0 -0
  317. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_db_type_cross_emitter_parity.py +0 -0
  318. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_db_type_integration.py +0 -0
  319. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_db_type_typing.py +0 -0
  320. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_db_type_validation.py +0 -0
  321. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_deletion.py +0 -0
  322. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_deprecations.py +0 -0
  323. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_docs_examples.py +0 -0
  324. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_documentation_features.py +0 -0
  325. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_enum_cold_hydration.py +0 -0
  326. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_exception_mapping.py +0 -0
  327. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_exceptions.py +0 -0
  328. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_field_wrapper.py +0 -0
  329. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_fk_target_pk.py +0 -0
  330. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_generation_counter_dirty_tracking.py +0 -0
  331. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_global_aggregates.py +0 -0
  332. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_grouped_aggregates.py +0 -0
  333. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_helpers.py +0 -0
  334. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_hydration.py +0 -0
  335. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_hydration_equivalence.py +0 -0
  336. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_identity_memory.py +0 -0
  337. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_identity_refresh.py +0 -0
  338. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_identity_scoped_invalidation.py +0 -0
  339. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_identity_weakref.py +0 -0
  340. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_import_budget.py +0 -0
  341. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_include.py +0 -0
  342. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_metaclass_internals.py +0 -0
  343. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_metadata.py +0 -0
  344. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_model_identity.py +0 -0
  345. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_models.py +0 -0
  346. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_mutation_pagination_guard.py +0 -0
  347. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_named_connections_integration.py +0 -0
  348. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_naming_single_source.py +0 -0
  349. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_one_to_one.py +0 -0
  350. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_operation_seam_sync.py +0 -0
  351. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_partial_selects.py +0 -0
  352. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_primary_key_facts.py +0 -0
  353. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_provisional_import.py +0 -0
  354. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_query_column_validation.py +0 -0
  355. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_query_immutability.py +0 -0
  356. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_query_typing.py +0 -0
  357. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_raw_sql.py +0 -0
  358. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_refresh.py +0 -0
  359. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_registry_entrypoints.py +0 -0
  360. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_registry_owner.py +0 -0
  361. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_relation_specs.py +0 -0
  362. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_relationship_engine.py +0 -0
  363. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_relationship_resolution_errors.py +0 -0
  364. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_route_single_site.py +0 -0
  365. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_routing_errors.py +0 -0
  366. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_save_pk_edges.py +0 -0
  367. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_save_semantics.py +0 -0
  368. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_schema.py +0 -0
  369. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_schema_constraints.py +0 -0
  370. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_schema_db_type_metadata.py +0 -0
  371. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_schema_enum_annotations.py +0 -0
  372. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_session.py +0 -0
  373. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_shadow_fk_types.py +0 -0
  374. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_sqlite_alembic_reconnect_hydration.py +0 -0
  375. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_static_contracts.py +0 -0
  376. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_string_search.py +0 -0
  377. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_structural_types.py +0 -0
  378. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_temporal_types.py +0 -0
  379. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_transactions.py +0 -0
  380. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_traversed_projection.py +0 -0
  381. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_typed_null_binds.py +0 -0
  382. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/tests/test_typed_save_bind.py +0 -0
  383. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/uv.lock +0 -0
  384. {ferro_orm-0.16.3 → ferro_orm-0.17.1}/zensical.toml +0 -0
@@ -1,6 +1,44 @@
1
1
  # CHANGELOG
2
2
 
3
3
 
4
+ ## v0.17.1 (2026-07-19)
5
+
6
+ ### Bug Fixes
7
+
8
+ - **migrate**: Auto-migrate pass ownership — column-before-index sequencing and FK on_delete
9
+ reconciliation ([#326](https://github.com/syn54x/ferro-orm/pull/326),
10
+ [`3451b1d`](https://github.com/syn54x/ferro-orm/commit/3451b1dad37d8ac39709d986aa20b1de4c12542a))
11
+
12
+
13
+ ## v0.17.0 (2026-07-18)
14
+
15
+ ### Documentation
16
+
17
+ - Design existence tests (.exists()) and uniform ~ negation
18
+ ([`c68a477`](https://github.com/syn54x/ferro-orm/commit/c68a47780cd5fe6d8ecfcb4f7e0f9c8ec2b798fe))
19
+
20
+ - **query**: Document universal ~ negation and three-valued logic
21
+ ([`6061d66`](https://github.com/syn54x/ferro-orm/commit/6061d66e96c99f089339f7a4a83b9424101da8be))
22
+
23
+ ### Features
24
+
25
+ - **query**: Bare .exists() on reverse FK — existence-test tracer bullet
26
+ ([#319](https://github.com/syn54x/ferro-orm/pull/319),
27
+ [`2538e06`](https://github.com/syn54x/ferro-orm/commit/2538e06b217e449d10dd564687f5ca3581d5d08d))
28
+
29
+ - **query**: Existence-test error surfaces and docs — the feature's edges
30
+ ([`be88aed`](https://github.com/syn54x/ferro-orm/commit/be88aed35f0975f9333bf0dabacaffc8a82f8f34))
31
+
32
+ - **query**: M2M existence tests — the two-hop correlation path
33
+ ([`336b529`](https://github.com/syn54x/ferro-orm/commit/336b5298d0e7beca8231452655544fcc7a15ad6a))
34
+
35
+ - **query**: Scoped existence tests — inner predicates, nesting, cross-scope guard
36
+ ([`df11625`](https://github.com/syn54x/ferro-orm/commit/df11625b90e01d293f4863bec8afafb9110a1ac3))
37
+
38
+ - **query**: Uniform predicate negation — prefix ~ as a NOT wire node
39
+ ([`bfa3126`](https://github.com/syn54x/ferro-orm/commit/bfa3126914b5a1b5d93a65ee3fe2f066bb314b1f))
40
+
41
+
4
42
  ## v0.16.3 (2026-07-16)
5
43
 
6
44
  ### Bug Fixes
@@ -56,6 +56,10 @@ _Avoid_: Join inference, nested filter, path lookup, string path
56
56
  The ordered sequence of forward-FK hops a traversal walks (`account`, or `account → owner`). A path is the identity of a join: the same path referenced anywhere in a query is one join, and distinct paths to the same model are distinct joins. Left-join requests apply to a whole path.
57
57
  _Avoid_: Join alias, lookup chain, dotted path string
58
58
 
59
+ **Existence test**:
60
+ The only predicate form on a reverse or many-to-many relation — `t.lines.exists(...)` — asking whether at least one related row exists, optionally scoped by a full inner predicate over the related model. Renders as a correlated EXISTS; the result stays root-shaped (shape-preserving query), so it composes with any other predicate, ordering, and paging. Reverse relations are *tested*, never *traversed*: traversal remains a forward-FK concept.
61
+ _Avoid_: membership filter, semi-join filter, reverse traversal, subquery filter, `.any()`
62
+
59
63
  **Shape-preserving query**:
60
64
  The invariant that filtering and ordering never change what a query returns — a query over Transaction yields Transaction instances regardless of which relations its predicates traverse. Only an explicit projection operation may change the result shape.
61
65
  _Avoid_: Implicit projection, row narrowing
@@ -119,3 +123,19 @@ _Avoid_: Import-time registration, partial registry
119
123
  **Resolved registration**:
120
124
  The registry epoch after relationship resolution completes — join tables exist, shadow FK columns are wired, and the SchemaIR modelset is authoritative for DDL and auto-migrate.
121
125
  _Avoid_: Final registration, committed registry
126
+
127
+ **Create pass**:
128
+ The auto-migrate step that brings missing tables into existence — the table, its columns, its indexes and constraints, together. A table that already exists is left completely untouched by this pass, whatever its shape.
129
+ _Avoid_: Bootstrap, ensure-tables, table sync
130
+
131
+ **Reconciliation pass**:
132
+ The `migrate_updates` step that alters existing tables to match the registered models — the only authority for DDL against a table that already exists. Within one table, column changes land before the indexes and constraints that reference them.
133
+ _Avoid_: Update pass, schema sync, drift repair
134
+
135
+ **Ferro-owned artifact**:
136
+ An index or constraint whose name follows ferro's canonical naming (`idx_`, `uq_`, `fk_`, `ck_`), marking it as reconcilable: auto-migrate may create, rebuild, or drop it to match the declared model. Artifacts named any other way belong to the user and are never altered or dropped.
137
+ _Avoid_: Managed index, system constraint, internal index
138
+
139
+ **Constraint rebuild**:
140
+ Drop-and-recreate of a ferro-owned constraint whose live definition no longer matches the declared model — a foreign key's `on_delete`, its target, its columns. Metadata-only: rows are never touched. On a backend that cannot alter constraints, ferro warns loudly and skips; it never diverges silently.
141
+ _Avoid_: Constraint alter, FK patch, in-place constraint update
@@ -95,9 +95,9 @@ checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04"
95
95
 
96
96
  [[package]]
97
97
  name = "cc"
98
- version = "1.2.67"
98
+ version = "1.3.0"
99
99
  source = "registry+https://github.com/rust-lang/crates.io-index"
100
- checksum = "e17dd265a7d0f31ef544e1b20e03add05d3b45b491b633b10d67145d2acc1a38"
100
+ checksum = "c89588d05638b5b4594a3348a2d6c20277e43a7f5c5202b05cc56888475a47b8"
101
101
  dependencies = [
102
102
  "find-msvc-tools",
103
103
  "shlex",
@@ -210,7 +210,7 @@ dependencies = [
210
210
  "ident_case",
211
211
  "proc-macro2",
212
212
  "quote",
213
- "syn",
213
+ "syn 2.0.119",
214
214
  ]
215
215
 
216
216
  [[package]]
@@ -221,7 +221,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
221
221
  dependencies = [
222
222
  "darling_core",
223
223
  "quote",
224
- "syn",
224
+ "syn 2.0.119",
225
225
  ]
226
226
 
227
227
  [[package]]
@@ -269,7 +269,7 @@ checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f"
269
269
  dependencies = [
270
270
  "proc-macro2",
271
271
  "quote",
272
- "syn",
272
+ "syn 2.0.119",
273
273
  ]
274
274
 
275
275
  [[package]]
@@ -327,7 +327,7 @@ dependencies = [
327
327
 
328
328
  [[package]]
329
329
  name = "ferro"
330
- version = "0.16.3"
330
+ version = "0.17.1"
331
331
  dependencies = [
332
332
  "chrono",
333
333
  "dashmap",
@@ -412,9 +412,9 @@ dependencies = [
412
412
 
413
413
  [[package]]
414
414
  name = "futures"
415
- version = "0.3.32"
415
+ version = "0.3.33"
416
416
  source = "registry+https://github.com/rust-lang/crates.io-index"
417
- checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d"
417
+ checksum = "a88cf1f829d945f548cf8fec32c61b1f202b6d93b45848602fc02af4b12ad218"
418
418
  dependencies = [
419
419
  "futures-channel",
420
420
  "futures-core",
@@ -427,9 +427,9 @@ dependencies = [
427
427
 
428
428
  [[package]]
429
429
  name = "futures-channel"
430
- version = "0.3.32"
430
+ version = "0.3.33"
431
431
  source = "registry+https://github.com/rust-lang/crates.io-index"
432
- checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d"
432
+ checksum = "262590f4fe6afeb0bc83be1daa64e52657fe185690a958af7f3ad0e92085c5ae"
433
433
  dependencies = [
434
434
  "futures-core",
435
435
  "futures-sink",
@@ -437,15 +437,15 @@ dependencies = [
437
437
 
438
438
  [[package]]
439
439
  name = "futures-core"
440
- version = "0.3.32"
440
+ version = "0.3.33"
441
441
  source = "registry+https://github.com/rust-lang/crates.io-index"
442
- checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
442
+ checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7"
443
443
 
444
444
  [[package]]
445
445
  name = "futures-executor"
446
- version = "0.3.32"
446
+ version = "0.3.33"
447
447
  source = "registry+https://github.com/rust-lang/crates.io-index"
448
- checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d"
448
+ checksum = "6754879cc9f2c66f88c6e5c35344bb0bdb0708b0352b1201815667c7eabc7458"
449
449
  dependencies = [
450
450
  "futures-core",
451
451
  "futures-task",
@@ -465,38 +465,38 @@ dependencies = [
465
465
 
466
466
  [[package]]
467
467
  name = "futures-io"
468
- version = "0.3.32"
468
+ version = "0.3.33"
469
469
  source = "registry+https://github.com/rust-lang/crates.io-index"
470
- checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718"
470
+ checksum = "4577ecaa3c4f96589d473f679a71b596316f6641bc350038b962a5daf0085d7a"
471
471
 
472
472
  [[package]]
473
473
  name = "futures-macro"
474
- version = "0.3.32"
474
+ version = "0.3.33"
475
475
  source = "registry+https://github.com/rust-lang/crates.io-index"
476
- checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b"
476
+ checksum = "2d6d3cde68c518367be28956066ddfef33813991b77a55005a69dae04bf3b10b"
477
477
  dependencies = [
478
478
  "proc-macro2",
479
479
  "quote",
480
- "syn",
480
+ "syn 2.0.119",
481
481
  ]
482
482
 
483
483
  [[package]]
484
484
  name = "futures-sink"
485
- version = "0.3.32"
485
+ version = "0.3.33"
486
486
  source = "registry+https://github.com/rust-lang/crates.io-index"
487
- checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893"
487
+ checksum = "e34418ac499d6305c2fb5ad0ed2f6ac998c5f8ca209b4510f7f94242c647e307"
488
488
 
489
489
  [[package]]
490
490
  name = "futures-task"
491
- version = "0.3.32"
491
+ version = "0.3.33"
492
492
  source = "registry+https://github.com/rust-lang/crates.io-index"
493
- checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393"
493
+ checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109"
494
494
 
495
495
  [[package]]
496
496
  name = "futures-util"
497
- version = "0.3.32"
497
+ version = "0.3.33"
498
498
  source = "registry+https://github.com/rust-lang/crates.io-index"
499
- checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
499
+ checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa"
500
500
  dependencies = [
501
501
  "futures-channel",
502
502
  "futures-core",
@@ -772,13 +772,13 @@ dependencies = [
772
772
 
773
773
  [[package]]
774
774
  name = "inherent"
775
- version = "1.0.13"
775
+ version = "1.0.14"
776
776
  source = "registry+https://github.com/rust-lang/crates.io-index"
777
- checksum = "c727f80bfa4a6c6e2508d2f05b6f4bfce242030bd88ed15ae5331c5b5d30fba7"
777
+ checksum = "bee2c455ca60511a054699102d40ce7153621cb2429558f9eaa600e4499b5984"
778
778
  dependencies = [
779
779
  "proc-macro2",
780
780
  "quote",
781
- "syn",
781
+ "syn 3.0.0",
782
782
  ]
783
783
 
784
784
  [[package]]
@@ -1045,9 +1045,9 @@ checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
1045
1045
 
1046
1046
  [[package]]
1047
1047
  name = "portable-atomic"
1048
- version = "1.13.1"
1048
+ version = "1.14.0"
1049
1049
  source = "registry+https://github.com/rust-lang/crates.io-index"
1050
- checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
1050
+ checksum = "3d20d5497ef88037a52ff98267d066e7f11fcc5e99bbfbd58a42336193aacec3"
1051
1051
 
1052
1052
  [[package]]
1053
1053
  name = "potential_utf"
@@ -1069,9 +1069,9 @@ dependencies = [
1069
1069
 
1070
1070
  [[package]]
1071
1071
  name = "proc-macro2"
1072
- version = "1.0.106"
1072
+ version = "1.0.107"
1073
1073
  source = "registry+https://github.com/rust-lang/crates.io-index"
1074
- checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
1074
+ checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
1075
1075
  dependencies = [
1076
1076
  "unicode-ident",
1077
1077
  ]
@@ -1134,7 +1134,7 @@ dependencies = [
1134
1134
  "proc-macro2",
1135
1135
  "pyo3-macros-backend",
1136
1136
  "quote",
1137
- "syn",
1137
+ "syn 2.0.119",
1138
1138
  ]
1139
1139
 
1140
1140
  [[package]]
@@ -1147,14 +1147,14 @@ dependencies = [
1147
1147
  "proc-macro2",
1148
1148
  "pyo3-build-config",
1149
1149
  "quote",
1150
- "syn",
1150
+ "syn 2.0.119",
1151
1151
  ]
1152
1152
 
1153
1153
  [[package]]
1154
1154
  name = "quote"
1155
- version = "1.0.46"
1155
+ version = "1.0.47"
1156
1156
  source = "registry+https://github.com/rust-lang/crates.io-index"
1157
- checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368"
1157
+ checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
1158
1158
  dependencies = [
1159
1159
  "proc-macro2",
1160
1160
  ]
@@ -1320,15 +1320,15 @@ dependencies = [
1320
1320
  "heck 0.4.1",
1321
1321
  "proc-macro2",
1322
1322
  "quote",
1323
- "syn",
1323
+ "syn 2.0.119",
1324
1324
  "thiserror",
1325
1325
  ]
1326
1326
 
1327
1327
  [[package]]
1328
1328
  name = "serde"
1329
- version = "1.0.228"
1329
+ version = "1.0.229"
1330
1330
  source = "registry+https://github.com/rust-lang/crates.io-index"
1331
- checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
1331
+ checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
1332
1332
  dependencies = [
1333
1333
  "serde_core",
1334
1334
  "serde_derive",
@@ -1336,22 +1336,22 @@ dependencies = [
1336
1336
 
1337
1337
  [[package]]
1338
1338
  name = "serde_core"
1339
- version = "1.0.228"
1339
+ version = "1.0.229"
1340
1340
  source = "registry+https://github.com/rust-lang/crates.io-index"
1341
- checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
1341
+ checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
1342
1342
  dependencies = [
1343
1343
  "serde_derive",
1344
1344
  ]
1345
1345
 
1346
1346
  [[package]]
1347
1347
  name = "serde_derive"
1348
- version = "1.0.228"
1348
+ version = "1.0.229"
1349
1349
  source = "registry+https://github.com/rust-lang/crates.io-index"
1350
- checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
1350
+ checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
1351
1351
  dependencies = [
1352
1352
  "proc-macro2",
1353
1353
  "quote",
1354
- "syn",
1354
+ "syn 3.0.0",
1355
1355
  ]
1356
1356
 
1357
1357
  [[package]]
@@ -1533,7 +1533,7 @@ dependencies = [
1533
1533
  "quote",
1534
1534
  "sqlx-core",
1535
1535
  "sqlx-macros-core",
1536
- "syn",
1536
+ "syn 2.0.119",
1537
1537
  ]
1538
1538
 
1539
1539
  [[package]]
@@ -1556,7 +1556,7 @@ dependencies = [
1556
1556
  "sqlx-mysql",
1557
1557
  "sqlx-postgres",
1558
1558
  "sqlx-sqlite",
1559
- "syn",
1559
+ "syn 2.0.119",
1560
1560
  "tokio",
1561
1561
  "url",
1562
1562
  ]
@@ -1707,6 +1707,17 @@ dependencies = [
1707
1707
  "unicode-ident",
1708
1708
  ]
1709
1709
 
1710
+ [[package]]
1711
+ name = "syn"
1712
+ version = "3.0.0"
1713
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1714
+ checksum = "f2fac314a64dc9a36e61a9eb4261a5e9bbfbc922b27e518af97bc32b926cf967"
1715
+ dependencies = [
1716
+ "proc-macro2",
1717
+ "quote",
1718
+ "unicode-ident",
1719
+ ]
1720
+
1710
1721
  [[package]]
1711
1722
  name = "synstructure"
1712
1723
  version = "0.13.2"
@@ -1715,7 +1726,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
1715
1726
  dependencies = [
1716
1727
  "proc-macro2",
1717
1728
  "quote",
1718
- "syn",
1729
+ "syn 2.0.119",
1719
1730
  ]
1720
1731
 
1721
1732
  [[package]]
@@ -1726,22 +1737,22 @@ checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
1726
1737
 
1727
1738
  [[package]]
1728
1739
  name = "thiserror"
1729
- version = "2.0.18"
1740
+ version = "2.0.19"
1730
1741
  source = "registry+https://github.com/rust-lang/crates.io-index"
1731
- checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
1742
+ checksum = "09a43598840e33d5b0331f38c5e30d13bb11c11210a4b58f0d9b18a5a5eefcd9"
1732
1743
  dependencies = [
1733
1744
  "thiserror-impl",
1734
1745
  ]
1735
1746
 
1736
1747
  [[package]]
1737
1748
  name = "thiserror-impl"
1738
- version = "2.0.18"
1749
+ version = "2.0.19"
1739
1750
  source = "registry+https://github.com/rust-lang/crates.io-index"
1740
- checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
1751
+ checksum = "43cbfe0cf76104d42a574802844187e84a305e531ed54455f11fbde0f10541cd"
1741
1752
  dependencies = [
1742
1753
  "proc-macro2",
1743
1754
  "quote",
1744
- "syn",
1755
+ "syn 3.0.0",
1745
1756
  ]
1746
1757
 
1747
1758
  [[package]]
@@ -1771,9 +1782,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
1771
1782
 
1772
1783
  [[package]]
1773
1784
  name = "tokio"
1774
- version = "1.52.3"
1785
+ version = "1.53.0"
1775
1786
  source = "registry+https://github.com/rust-lang/crates.io-index"
1776
- checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe"
1787
+ checksum = "d988bcd52dbe076d3d46903332f58c912b87a2c49b1428419a5845154762ffee"
1777
1788
  dependencies = [
1778
1789
  "bytes",
1779
1790
  "libc",
@@ -1788,13 +1799,13 @@ dependencies = [
1788
1799
 
1789
1800
  [[package]]
1790
1801
  name = "tokio-macros"
1791
- version = "2.7.0"
1802
+ version = "2.7.1"
1792
1803
  source = "registry+https://github.com/rust-lang/crates.io-index"
1793
- checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496"
1804
+ checksum = "6328af13490e73a9b4694030fafd93f8c8c6a9dede33e821c3fc63eddf8042ba"
1794
1805
  dependencies = [
1795
1806
  "proc-macro2",
1796
1807
  "quote",
1797
- "syn",
1808
+ "syn 2.0.119",
1798
1809
  ]
1799
1810
 
1800
1811
  [[package]]
@@ -1828,7 +1839,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
1828
1839
  dependencies = [
1829
1840
  "proc-macro2",
1830
1841
  "quote",
1831
- "syn",
1842
+ "syn 2.0.119",
1832
1843
  ]
1833
1844
 
1834
1845
  [[package]]
@@ -1970,7 +1981,7 @@ dependencies = [
1970
1981
  "bumpalo",
1971
1982
  "proc-macro2",
1972
1983
  "quote",
1973
- "syn",
1984
+ "syn 2.0.119",
1974
1985
  "wasm-bindgen-shared",
1975
1986
  ]
1976
1987
 
@@ -1989,14 +2000,14 @@ version = "0.26.11"
1989
2000
  source = "registry+https://github.com/rust-lang/crates.io-index"
1990
2001
  checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9"
1991
2002
  dependencies = [
1992
- "webpki-roots 1.0.8",
2003
+ "webpki-roots 1.0.9",
1993
2004
  ]
1994
2005
 
1995
2006
  [[package]]
1996
2007
  name = "webpki-roots"
1997
- version = "1.0.8"
2008
+ version = "1.0.9"
1998
2009
  source = "registry+https://github.com/rust-lang/crates.io-index"
1999
- checksum = "bf85cb06032201fa7c6f829d7db5a7e5aa45bcc0655327713065f6f0576731bf"
2010
+ checksum = "7dcd9d09a39985f5344844e66b0c530a33843579125f23e21e9f0f220850f22a"
2000
2011
  dependencies = [
2001
2012
  "rustls-pki-types",
2002
2013
  ]
@@ -2032,7 +2043,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
2032
2043
  dependencies = [
2033
2044
  "proc-macro2",
2034
2045
  "quote",
2035
- "syn",
2046
+ "syn 2.0.119",
2036
2047
  ]
2037
2048
 
2038
2049
  [[package]]
@@ -2043,7 +2054,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
2043
2054
  dependencies = [
2044
2055
  "proc-macro2",
2045
2056
  "quote",
2046
- "syn",
2057
+ "syn 2.0.119",
2047
2058
  ]
2048
2059
 
2049
2060
  [[package]]
@@ -2243,7 +2254,7 @@ checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e"
2243
2254
  dependencies = [
2244
2255
  "proc-macro2",
2245
2256
  "quote",
2246
- "syn",
2257
+ "syn 2.0.119",
2247
2258
  "synstructure",
2248
2259
  ]
2249
2260
 
@@ -2264,7 +2275,7 @@ checksum = "e2e817b7b52d0c7358d3246da9d69935ebb18116b2b102b4230dac079b4862f5"
2264
2275
  dependencies = [
2265
2276
  "proc-macro2",
2266
2277
  "quote",
2267
- "syn",
2278
+ "syn 2.0.119",
2268
2279
  ]
2269
2280
 
2270
2281
  [[package]]
@@ -2284,7 +2295,7 @@ checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1"
2284
2295
  dependencies = [
2285
2296
  "proc-macro2",
2286
2297
  "quote",
2287
- "syn",
2298
+ "syn 2.0.119",
2288
2299
  "synstructure",
2289
2300
  ]
2290
2301
 
@@ -2324,7 +2335,7 @@ checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555"
2324
2335
  dependencies = [
2325
2336
  "proc-macro2",
2326
2337
  "quote",
2327
- "syn",
2338
+ "syn 2.0.119",
2328
2339
  ]
2329
2340
 
2330
2341
  [[package]]
@@ -3,7 +3,7 @@ members = ["crates/ferro-schema-ir", "crates/ferro-ddl-lowering", "crates/ferro-
3
3
 
4
4
  [package]
5
5
  name = "ferro"
6
- version = "0.16.3"
6
+ version = "0.17.1"
7
7
  edition = "2024"
8
8
  readme = "README.md"
9
9
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ferro-orm
3
- Version: 0.16.3
3
+ Version: 0.17.1
4
4
  Requires-Dist: pydantic>=2.0
5
5
  Requires-Dist: alembic>=1.18.1 ; extra == 'alembic'
6
6
  Requires-Dist: sqlalchemy>=2.0.46 ; extra == 'alembic'
@@ -542,6 +542,13 @@ pub fn fk_name(table_lower: &str, col_name: &str, to_table: &str) -> String {
542
542
  raw
543
543
  }
544
544
 
545
+ /// Whether a live constraint name follows the ferro FK convention above — the
546
+ /// ownership test: reconciliation only ever rebuilds names ferro emits;
547
+ /// constraints named any other way belong to the user and are never altered.
548
+ pub fn is_ferro_fk_name(name: &str) -> bool {
549
+ name.starts_with("fk_")
550
+ }
551
+
545
552
  /// Single-column unique name with 63-char guard.
546
553
  pub fn single_unique_index_name(table_lower: &str, col_name: &str) -> String {
547
554
  let raw = format!("uq_{table_lower}_{col_name}");
@@ -408,16 +408,7 @@ fn emit_add_column(
408
408
 
409
409
  if let Some(fk) = model.foreign_keys.iter().find(|fk| fk.column == column) {
410
410
  if dialect == Dialect::Postgres {
411
- let on_delete = fk_action_from_str(fk.on_delete.as_deref());
412
- result.statements.push(format!(
413
- "ALTER TABLE {} ADD CONSTRAINT {} FOREIGN KEY ({}) REFERENCES {} ({}) ON DELETE {}",
414
- quote_ident(table),
415
- quote_ident(&fk_constraint_name(table, fk)),
416
- quote_ident(column),
417
- quote_ident(&fk.to_table),
418
- quote_ident(&fk.to_column),
419
- fk_action_sql(on_delete),
420
- ));
411
+ result.statements.push(render_add_fk_sql(table, fk));
421
412
  } else {
422
413
  result.warnings.push(format!(
423
414
  "Added foreign-key column '{}.{}' without its FOREIGN KEY constraint \
@@ -432,6 +423,39 @@ fn emit_add_column(
432
423
  Ok(result)
433
424
  }
434
425
 
426
+ /// `ALTER TABLE ... ADD CONSTRAINT ... FOREIGN KEY ... ON DELETE ...` for one
427
+ /// IR foreign key. Postgres-only — SQLite cannot add table constraints to an
428
+ /// existing table; its callers warn instead.
429
+ fn render_add_fk_sql(table: &str, fk: &ferro_schema_ir::SchemaForeignKey) -> String {
430
+ format!(
431
+ "ALTER TABLE {} ADD CONSTRAINT {} FOREIGN KEY ({}) REFERENCES {} ({}) ON DELETE {}",
432
+ quote_ident(table),
433
+ quote_ident(&fk_constraint_name(table, fk)),
434
+ quote_ident(&fk.column),
435
+ quote_ident(&fk.to_table),
436
+ quote_ident(&fk.to_column),
437
+ fk_action_sql(fk_action_from_str(fk.on_delete.as_deref())),
438
+ )
439
+ }
440
+
441
+ /// Find the declared FK for `column` on `table` in the new-IR model.
442
+ fn find_foreign_key<'a>(
443
+ model: &'a SchemaModel,
444
+ table: &str,
445
+ column: &str,
446
+ ) -> Result<&'a ferro_schema_ir::SchemaForeignKey, EmissionError> {
447
+ model
448
+ .foreign_keys
449
+ .iter()
450
+ .find(|fk| fk.column == column)
451
+ .ok_or_else(|| EmissionError {
452
+ message: format!(
453
+ "Foreign-key operation for '{}.{}' has no matching FK in the declared IR",
454
+ table, column
455
+ ),
456
+ })
457
+ }
458
+
435
459
  fn emit_alter_column_type(
436
460
  table: &str,
437
461
  column: &str,
@@ -673,6 +697,61 @@ pub fn emit_sql_with_ir(
673
697
  MigrationOp::DropIndex { table: _, name } => {
674
698
  result.statements.push(format!("DROP INDEX IF EXISTS \"{}\"", name));
675
699
  }
700
+ MigrationOp::AddForeignKey { table, column } => {
701
+ let model = find_model(&new_models, table)?;
702
+ let fk = find_foreign_key(model, table, column)?;
703
+ match dialect {
704
+ Dialect::Postgres => result.statements.push(render_add_fk_sql(table, fk)),
705
+ Dialect::Sqlite => result.warnings.push(format!(
706
+ "Declared FOREIGN KEY on '{}.{}' (on_delete {}) has no live \
707
+ constraint, and SQLite cannot add table constraints to an \
708
+ existing table. Referential integrity for this column is not \
709
+ database-enforced; use Alembic if you need the constraint.",
710
+ table,
711
+ column,
712
+ fk_action_sql(fk_action_from_str(fk.on_delete.as_deref())),
713
+ )),
714
+ }
715
+ }
716
+ MigrationOp::RebuildForeignKey {
717
+ table,
718
+ column,
719
+ old_name,
720
+ } => {
721
+ let model = find_model(&new_models, table)?;
722
+ let fk = find_foreign_key(model, table, column)?;
723
+ match dialect {
724
+ Dialect::Postgres => {
725
+ result.statements.push(format!(
726
+ "ALTER TABLE {} DROP CONSTRAINT {}",
727
+ quote_ident(table),
728
+ quote_ident(old_name),
729
+ ));
730
+ result.statements.push(render_add_fk_sql(table, fk));
731
+ }
732
+ Dialect::Sqlite => {
733
+ let live_action = find_model(&old_models, table)
734
+ .ok()
735
+ .and_then(|old| {
736
+ old.foreign_keys.iter().find(|live| live.column == *column)
737
+ })
738
+ .map(|live| {
739
+ fk_action_sql(fk_action_from_str(live.on_delete.as_deref()))
740
+ })
741
+ .unwrap_or("<unknown>");
742
+ result.warnings.push(format!(
743
+ "Foreign key on '{}.{}' declares on_delete {} but the live \
744
+ constraint enforces {}; SQLite cannot alter constraints in \
745
+ place, so the live behavior remains. Migrate with Alembic to \
746
+ apply the declared action.",
747
+ table,
748
+ column,
749
+ fk_action_sql(fk_action_from_str(fk.on_delete.as_deref())),
750
+ live_action,
751
+ ));
752
+ }
753
+ }
754
+ }
676
755
  }
677
756
  }
678
757