plain.postgres 0.103.3__tar.gz → 0.103.4__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 (211) hide show
  1. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/.gitignore +1 -0
  2. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/PKG-INFO +1 -1
  3. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/CHANGELOG.md +10 -0
  4. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/preflight.py +10 -3
  5. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/pyproject.toml +2 -2
  6. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/app/examples/views.py +1 -1
  7. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/app/settings.py +1 -0
  8. plain_postgres-0.103.4/tests/internal/test_preflight_duplicate_indexes.py +102 -0
  9. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/CLAUDE.md +0 -0
  10. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/LICENSE +0 -0
  11. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/README.md +0 -0
  12. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/README.md +0 -0
  13. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/__init__.py +0 -0
  14. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/adapters.py +0 -0
  15. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/agents/.claude/rules/plain-postgres.md +0 -0
  16. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/agents/.claude/skills/plain-postgres-doctor/SKILL.md +0 -0
  17. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/aggregates.py +0 -0
  18. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/base.py +0 -0
  19. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/cli/__init__.py +0 -0
  20. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/cli/converge.py +0 -0
  21. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/cli/core.py +0 -0
  22. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/cli/decorators.py +0 -0
  23. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/cli/diagnose.py +0 -0
  24. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/cli/migrations.py +0 -0
  25. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/cli/schema.py +0 -0
  26. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/cli/sync.py +0 -0
  27. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/config.py +0 -0
  28. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/connection.py +0 -0
  29. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/constants.py +0 -0
  30. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/constraints.py +0 -0
  31. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/convergence/__init__.py +0 -0
  32. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/convergence/analysis.py +0 -0
  33. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/convergence/fixes.py +0 -0
  34. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/convergence/planning.py +0 -0
  35. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/database_url.py +0 -0
  36. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/db.py +0 -0
  37. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/ddl.py +0 -0
  38. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/default_settings.py +0 -0
  39. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/deletion.py +0 -0
  40. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/dialect.py +0 -0
  41. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/entrypoints.py +0 -0
  42. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/enums.py +0 -0
  43. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/exceptions.py +0 -0
  44. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/expressions.py +0 -0
  45. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/fields/__init__.py +0 -0
  46. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/fields/base.py +0 -0
  47. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/fields/binary.py +0 -0
  48. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/fields/boolean.py +0 -0
  49. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/fields/duration.py +0 -0
  50. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/fields/encrypted.py +0 -0
  51. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/fields/json.py +0 -0
  52. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/fields/mixins.py +0 -0
  53. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/fields/network.py +0 -0
  54. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/fields/numeric.py +0 -0
  55. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/fields/primary_key.py +0 -0
  56. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/fields/related.py +0 -0
  57. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/fields/related_descriptors.py +0 -0
  58. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/fields/related_lookups.py +0 -0
  59. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/fields/related_managers.py +0 -0
  60. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/fields/reverse_descriptors.py +0 -0
  61. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/fields/reverse_related.py +0 -0
  62. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/fields/temporal.py +0 -0
  63. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/fields/text.py +0 -0
  64. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/fields/timezones.py +0 -0
  65. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/fields/uuid.py +0 -0
  66. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/forms.py +0 -0
  67. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/functions/__init__.py +0 -0
  68. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/functions/comparison.py +0 -0
  69. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/functions/datetime.py +0 -0
  70. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/functions/math.py +0 -0
  71. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/functions/mixins.py +0 -0
  72. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/functions/random.py +0 -0
  73. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/functions/text.py +0 -0
  74. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/functions/uuid.py +0 -0
  75. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/functions/window.py +0 -0
  76. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/indexes.py +0 -0
  77. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/introspection/__init__.py +0 -0
  78. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/introspection/health/__init__.py +0 -0
  79. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/introspection/health/checks_cumulative.py +0 -0
  80. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/introspection/health/checks_snapshot.py +0 -0
  81. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/introspection/health/checks_structural.py +0 -0
  82. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/introspection/health/context.py +0 -0
  83. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/introspection/health/helpers.py +0 -0
  84. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/introspection/health/ownership.py +0 -0
  85. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/introspection/health/runner.py +0 -0
  86. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/introspection/health/types.py +0 -0
  87. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/introspection/schema.py +0 -0
  88. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/lookups.py +0 -0
  89. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/meta.py +0 -0
  90. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/middleware.py +0 -0
  91. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/migrations/__init__.py +0 -0
  92. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/migrations/autodetector.py +0 -0
  93. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/migrations/exceptions.py +0 -0
  94. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/migrations/executor.py +0 -0
  95. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/migrations/graph.py +0 -0
  96. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/migrations/loader.py +0 -0
  97. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/migrations/migration.py +0 -0
  98. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/migrations/operations/__init__.py +0 -0
  99. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/migrations/operations/base.py +0 -0
  100. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/migrations/operations/fields.py +0 -0
  101. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/migrations/operations/models.py +0 -0
  102. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/migrations/operations/special.py +0 -0
  103. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/migrations/optimizer.py +0 -0
  104. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/migrations/questioner.py +0 -0
  105. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/migrations/recorder.py +0 -0
  106. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/migrations/serializer.py +0 -0
  107. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/migrations/state.py +0 -0
  108. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/migrations/utils.py +0 -0
  109. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/migrations/writer.py +0 -0
  110. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/options.py +0 -0
  111. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/otel.py +0 -0
  112. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/query.py +0 -0
  113. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/query_utils.py +0 -0
  114. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/registry.py +0 -0
  115. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/schema.py +0 -0
  116. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/sources.py +0 -0
  117. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/sql/__init__.py +0 -0
  118. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/sql/compiler.py +0 -0
  119. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/sql/constants.py +0 -0
  120. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/sql/datastructures.py +0 -0
  121. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/sql/query.py +0 -0
  122. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/sql/where.py +0 -0
  123. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/test/__init__.py +0 -0
  124. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/test/database.py +0 -0
  125. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/test/pytest.py +0 -0
  126. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/transaction.py +0 -0
  127. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/types.py +0 -0
  128. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/types.pyi +0 -0
  129. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/plain/postgres/utils.py +0 -0
  130. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/app/examples/forms.py +0 -0
  131. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/app/examples/migrations/0001_initial.py +0 -0
  132. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/app/examples/migrations/0002_test_field_removed.py +0 -0
  133. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/app/examples/migrations/0003_deleteparent_childsetnull_childsetdefault_and_more.py +0 -0
  134. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/app/examples/migrations/0004_defaultquerysetmodel_mixintestmodel_and_more.py +0 -0
  135. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/app/examples/migrations/0005_feature_carfeature_car_features.py +0 -0
  136. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/app/examples/migrations/0006_secretstore.py +0 -0
  137. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/app/examples/migrations/0007_treenode_unconstrainedchild.py +0 -0
  138. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/app/examples/migrations/0008_setsentinelparent_diamondparenta_midparent_and_more.py +0 -0
  139. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/app/examples/migrations/0009_circb_circa_circb_partner.py +0 -0
  140. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/app/examples/migrations/0010_hideableitem.py +0 -0
  141. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/app/examples/migrations/0011_defaultsexample.py +0 -0
  142. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/app/examples/migrations/0012_iterationexample.py +0 -0
  143. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/app/examples/migrations/0013_indexexample_constraintexample_nullabilityexample.py +0 -0
  144. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/app/examples/migrations/0014_widget_rename_feature_tag_remove_carfeature_car_and_more.py +0 -0
  145. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/app/examples/migrations/0015_dbdefaultsexample.py +0 -0
  146. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/app/examples/migrations/0016_formsexample.py +0 -0
  147. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/app/examples/migrations/0017_random_string_token.py +0 -0
  148. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/app/examples/migrations/0018_storageparametersexample.py +0 -0
  149. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/app/examples/migrations/__init__.py +0 -0
  150. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/app/examples/models/__init__.py +0 -0
  151. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/app/examples/models/constraints.py +0 -0
  152. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/app/examples/models/defaults.py +0 -0
  153. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/app/examples/models/delete.py +0 -0
  154. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/app/examples/models/encrypted.py +0 -0
  155. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/app/examples/models/forms.py +0 -0
  156. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/app/examples/models/indexes.py +0 -0
  157. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/app/examples/models/iteration.py +0 -0
  158. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/app/examples/models/mixins.py +0 -0
  159. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/app/examples/models/nullability.py +0 -0
  160. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/app/examples/models/querysets.py +0 -0
  161. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/app/examples/models/relationships.py +0 -0
  162. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/app/examples/models/storage_parameters.py +0 -0
  163. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/app/examples/models/trees.py +0 -0
  164. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/app/examples/models/unregistered.py +0 -0
  165. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/app/examples/urls.py +0 -0
  166. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/app/urls.py +0 -0
  167. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/conftest.py +0 -0
  168. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/conftest_convergence.py +0 -0
  169. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/internal/test_autodetector_not_null_errors.py +0 -0
  170. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/internal/test_autodetector_type_change.py +0 -0
  171. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/internal/test_connection_isolation.py +0 -0
  172. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/internal/test_connection_lifecycle.py +0 -0
  173. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/internal/test_connection_pool.py +0 -0
  174. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/internal/test_constraint_violation_error.py +0 -0
  175. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/internal/test_convergence.py +0 -0
  176. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/internal/test_convergence_constraints.py +0 -0
  177. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/internal/test_convergence_defaults.py +0 -0
  178. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/internal/test_convergence_fk.py +0 -0
  179. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/internal/test_convergence_indexes.py +0 -0
  180. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/internal/test_convergence_nullability.py +0 -0
  181. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/internal/test_convergence_storage_parameters.py +0 -0
  182. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/internal/test_convergence_timeouts.py +0 -0
  183. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/internal/test_db_expression_defaults.py +0 -0
  184. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/internal/test_diagnose.py +0 -0
  185. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/internal/test_executor_connection_hook.py +0 -0
  186. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/internal/test_health.py +0 -0
  187. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/internal/test_introspection.py +0 -0
  188. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/internal/test_literal_default_persistence.py +0 -0
  189. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/internal/test_management_connection.py +0 -0
  190. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/internal/test_migration_executor.py +0 -0
  191. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/internal/test_no_callable_defaults.py +0 -0
  192. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/internal/test_otel_metrics.py +0 -0
  193. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/internal/test_preflight_fk_coverage.py +0 -0
  194. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/internal/test_schema_normalize_type.py +0 -0
  195. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/internal/test_schema_timeouts.py +0 -0
  196. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/public/test_database_url.py +0 -0
  197. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/public/test_delete_behaviors.py +0 -0
  198. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/public/test_encrypted_fields.py +0 -0
  199. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/public/test_exceptions.py +0 -0
  200. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/public/test_field_defaults.py +0 -0
  201. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/public/test_functions_uuid.py +0 -0
  202. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/public/test_iterator.py +0 -0
  203. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/public/test_m2m.py +0 -0
  204. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/public/test_manager_assignment.py +0 -0
  205. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/public/test_mixins.py +0 -0
  206. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/public/test_modelform_roundtrip.py +0 -0
  207. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/public/test_queryset_repr.py +0 -0
  208. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/public/test_random_string_field.py +0 -0
  209. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/public/test_raw_query.py +0 -0
  210. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/public/test_read_only_transactions.py +0 -0
  211. {plain_postgres-0.103.3 → plain_postgres-0.103.4}/tests/public/test_related.py +0 -0
@@ -3,6 +3,7 @@
3
3
  *.egg-info
4
4
  *.py[co]
5
5
  __pycache__
6
+ .pytest_cache
6
7
  *.DS_Store
7
8
  *.swp
8
9
  *.swo
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plain.postgres
3
- Version: 0.103.3
3
+ Version: 0.103.4
4
4
  Summary: Model your data and store it in a database.
5
5
  Author-email: Dave Gaeddert <dave.gaeddert@dropseed.dev>
6
6
  License-Expression: BSD-3-Clause
@@ -1,5 +1,15 @@
1
1
  # plain-postgres changelog
2
2
 
3
+ ## [0.103.4](https://github.com/dropseed/plain/releases/plain-postgres@0.103.4) (2026-05-12)
4
+
5
+ ### What's changed
6
+
7
+ - **`postgres.duplicate_indexes` preflight check now skips partial indexes** (those with a `condition=`). Previously a bare `Index(fields=[fk])` carried for FK coverage was flagged as redundant with a partial composite `Index(fields=[fk, ...], condition=Q(...))`, contradicting the `postgres.missing_fk_index` check. The two warnings are now mutually consistent — partials don't cover full-column lookups, so they can't shadow a single-column index. ([1e8a3f72db](https://github.com/dropseed/plain/commit/1e8a3f72db))
8
+
9
+ ### Upgrade instructions
10
+
11
+ - No changes required. Apps that were silencing `postgres.duplicate_indexes` to work around the false positive can drop the silence.
12
+
3
13
  ## [0.103.3](https://github.com/dropseed/plain/releases/plain-postgres@0.103.3) (2026-05-08)
4
14
 
5
15
  ### What's changed
@@ -27,16 +27,23 @@ def _get_app_models() -> list[Any]:
27
27
 
28
28
 
29
29
  def _collect_model_indexes(model: Any) -> list[tuple[str, list[str], bool]]:
30
- """Collect all index field-lists for a model as (name, fields, is_unique) tuples."""
30
+ """Collect (name, fields, is_unique) for non-partial indexes/constraints.
31
+
32
+ Partials are skipped for the same reason as in ``_fk_covered_field_names``.
33
+ """
31
34
  all_indexes: list[tuple[str, list[str], bool]] = []
32
35
 
33
36
  for index in model.model_options.indexes:
34
- if index.fields:
37
+ if index.fields and not index.is_partial:
35
38
  fields = [f.lstrip("-") for f in index.fields]
36
39
  all_indexes.append((index.name, fields, False))
37
40
 
38
41
  for constraint in model.model_options.constraints:
39
- if isinstance(constraint, UniqueConstraint) and constraint.fields:
42
+ if (
43
+ isinstance(constraint, UniqueConstraint)
44
+ and constraint.fields
45
+ and not constraint.is_partial
46
+ ):
40
47
  all_indexes.append((constraint.name, list(constraint.fields), True))
41
48
 
42
49
  return all_indexes
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "plain.postgres"
3
- version = "0.103.3"
3
+ version = "0.103.4"
4
4
  description = "Model your data and store it in a database."
5
5
  authors = [{ name = "Dave Gaeddert", email = "dave.gaeddert@dropseed.dev" }]
6
6
  readme = "README.md"
@@ -16,7 +16,7 @@ dependencies = ["plain>=0.134.0,<1.0.0", "psycopg>=3.2", "psycopg-pool>=3.2"]
16
16
  "plain.postgres" = "plain.postgres.test.pytest"
17
17
 
18
18
  [dependency-groups]
19
- dev = ["plain.pytest<1.0.0", "opentelemetry-sdk>=1.34.1"]
19
+ dev = ["plain.pytest<1.0.0", "plain.templates>=0.1.0,<1.0.0", "opentelemetry-sdk>=1.34.1"]
20
20
 
21
21
  [tool.hatch.build.targets.wheel]
22
22
  packages = ["plain"]
@@ -5,7 +5,7 @@ from typing import Any
5
5
 
6
6
  from plain.forms import BaseForm
7
7
  from plain.http import Response
8
- from plain.views import CreateView, UpdateView
8
+ from plain.templates.views import CreateView, UpdateView
9
9
 
10
10
  from .forms import (
11
11
  ChildCascadeForm,
@@ -1,6 +1,7 @@
1
1
  SECRET_KEY = "test"
2
2
  URLS_ROUTER = "app.urls.AppRouter"
3
3
  INSTALLED_PACKAGES = [
4
+ "plain.templates",
4
5
  "plain.postgres",
5
6
  "app.examples",
6
7
  ]
@@ -0,0 +1,102 @@
1
+ """Unit tests for `_collect_model_indexes` — the helper that feeds
2
+ `postgres.duplicate_indexes`. Partial indexes/constraints must be
3
+ excluded so the check doesn't contradict `postgres.missing_fk_indexes`,
4
+ which already treats partials as non-covering.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from types import SimpleNamespace
10
+
11
+ from plain.postgres import Q
12
+ from plain.postgres.constraints import UniqueConstraint
13
+ from plain.postgres.indexes import Index
14
+ from plain.postgres.preflight import _collect_model_indexes
15
+
16
+
17
+ def _model(*, indexes=(), constraints=()) -> SimpleNamespace:
18
+ """Minimal model_options stand-in for the helper."""
19
+ return SimpleNamespace(
20
+ model_options=SimpleNamespace(
21
+ indexes=list(indexes), constraints=list(constraints)
22
+ )
23
+ )
24
+
25
+
26
+ def _names(collected) -> set[str]:
27
+ return {name for name, _fields, _unique in collected}
28
+
29
+
30
+ def test_non_partial_index_collected():
31
+ model = _model(indexes=[Index(name="t_team_idx", fields=["team"])])
32
+ assert _names(_collect_model_indexes(model)) == {"t_team_idx"}
33
+
34
+
35
+ def test_non_partial_unique_constraint_collected():
36
+ model = _model(constraints=[UniqueConstraint(fields=["team"], name="t_team_uniq")])
37
+ assert _names(_collect_model_indexes(model)) == {"t_team_uniq"}
38
+
39
+
40
+ def test_partial_index_excluded():
41
+ model = _model(
42
+ indexes=[
43
+ Index(
44
+ name="t_team_open_idx",
45
+ fields=["team", "created_at"],
46
+ condition=Q(resolved_at__isnull=True),
47
+ )
48
+ ]
49
+ )
50
+ assert _collect_model_indexes(model) == []
51
+
52
+
53
+ def test_partial_unique_constraint_excluded():
54
+ model = _model(
55
+ constraints=[
56
+ UniqueConstraint(
57
+ fields=["team"],
58
+ name="t_team_active_uniq",
59
+ condition=Q(deleted_at__isnull=True),
60
+ )
61
+ ]
62
+ )
63
+ assert _collect_model_indexes(model) == []
64
+
65
+
66
+ def test_bare_index_not_flagged_against_partial_prefix():
67
+ """A bare `Index(fields=[fk])` carried for FK coverage must survive
68
+ alongside a partial composite `Index(fields=[fk, other], condition=...)`
69
+ — otherwise the duplicate check fights the missing-FK check."""
70
+ model = _model(
71
+ indexes=[
72
+ Index(name="t_team_idx", fields=["team"]),
73
+ Index(
74
+ name="t_team_open_idx",
75
+ fields=["team", "created_at"],
76
+ condition=Q(resolved_at__isnull=True),
77
+ ),
78
+ ]
79
+ )
80
+ assert _names(_collect_model_indexes(model)) == {"t_team_idx"}
81
+
82
+
83
+ def test_mixed_keeps_only_non_partial():
84
+ model = _model(
85
+ indexes=[
86
+ Index(name="t_a_idx", fields=["a"]),
87
+ Index(
88
+ name="t_a_partial_idx",
89
+ fields=["a"],
90
+ condition=Q(deleted_at__isnull=True),
91
+ ),
92
+ ],
93
+ constraints=[
94
+ UniqueConstraint(fields=["b"], name="t_b_uniq"),
95
+ UniqueConstraint(
96
+ fields=["b"],
97
+ name="t_b_active_uniq",
98
+ condition=Q(deleted_at__isnull=True),
99
+ ),
100
+ ],
101
+ )
102
+ assert _names(_collect_model_indexes(model)) == {"t_a_idx", "t_b_uniq"}