plain.postgres 0.100.0__tar.gz → 0.101.0__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 (205) hide show
  1. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/PKG-INFO +1 -1
  2. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/CHANGELOG.md +11 -0
  3. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/agents/.claude/rules/plain-postgres.md +4 -4
  4. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/convergence/fixes.py +16 -6
  5. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/pyproject.toml +1 -1
  6. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_convergence_constraints.py +6 -19
  7. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_convergence_timeouts.py +5 -2
  8. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/.gitignore +0 -0
  9. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/CLAUDE.md +0 -0
  10. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/LICENSE +0 -0
  11. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/README.md +0 -0
  12. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/README.md +0 -0
  13. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/__init__.py +0 -0
  14. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/adapters.py +0 -0
  15. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/agents/.claude/skills/plain-postgres-doctor/SKILL.md +0 -0
  16. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/aggregates.py +0 -0
  17. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/base.py +0 -0
  18. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/cli/__init__.py +0 -0
  19. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/cli/converge.py +0 -0
  20. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/cli/core.py +0 -0
  21. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/cli/decorators.py +0 -0
  22. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/cli/diagnose.py +0 -0
  23. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/cli/migrations.py +0 -0
  24. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/cli/schema.py +0 -0
  25. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/cli/sync.py +0 -0
  26. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/config.py +0 -0
  27. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/connection.py +0 -0
  28. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/constants.py +0 -0
  29. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/constraints.py +0 -0
  30. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/convergence/__init__.py +0 -0
  31. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/convergence/analysis.py +0 -0
  32. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/convergence/planning.py +0 -0
  33. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/database_url.py +0 -0
  34. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/db.py +0 -0
  35. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/ddl.py +0 -0
  36. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/default_settings.py +0 -0
  37. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/deletion.py +0 -0
  38. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/dialect.py +0 -0
  39. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/entrypoints.py +0 -0
  40. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/enums.py +0 -0
  41. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/exceptions.py +0 -0
  42. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/expressions.py +0 -0
  43. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/fields/__init__.py +0 -0
  44. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/fields/base.py +0 -0
  45. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/fields/binary.py +0 -0
  46. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/fields/boolean.py +0 -0
  47. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/fields/duration.py +0 -0
  48. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/fields/encrypted.py +0 -0
  49. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/fields/json.py +0 -0
  50. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/fields/mixins.py +0 -0
  51. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/fields/network.py +0 -0
  52. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/fields/numeric.py +0 -0
  53. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/fields/primary_key.py +0 -0
  54. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/fields/related.py +0 -0
  55. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/fields/related_descriptors.py +0 -0
  56. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/fields/related_lookups.py +0 -0
  57. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/fields/related_managers.py +0 -0
  58. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/fields/reverse_descriptors.py +0 -0
  59. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/fields/reverse_related.py +0 -0
  60. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/fields/temporal.py +0 -0
  61. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/fields/text.py +0 -0
  62. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/fields/timezones.py +0 -0
  63. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/fields/uuid.py +0 -0
  64. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/forms.py +0 -0
  65. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/functions/__init__.py +0 -0
  66. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/functions/comparison.py +0 -0
  67. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/functions/datetime.py +0 -0
  68. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/functions/math.py +0 -0
  69. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/functions/mixins.py +0 -0
  70. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/functions/random.py +0 -0
  71. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/functions/text.py +0 -0
  72. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/functions/uuid.py +0 -0
  73. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/functions/window.py +0 -0
  74. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/indexes.py +0 -0
  75. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/introspection/__init__.py +0 -0
  76. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/introspection/health/__init__.py +0 -0
  77. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/introspection/health/checks_cumulative.py +0 -0
  78. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/introspection/health/checks_snapshot.py +0 -0
  79. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/introspection/health/checks_structural.py +0 -0
  80. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/introspection/health/context.py +0 -0
  81. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/introspection/health/helpers.py +0 -0
  82. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/introspection/health/ownership.py +0 -0
  83. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/introspection/health/runner.py +0 -0
  84. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/introspection/health/types.py +0 -0
  85. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/introspection/schema.py +0 -0
  86. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/lookups.py +0 -0
  87. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/meta.py +0 -0
  88. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/middleware.py +0 -0
  89. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/migrations/__init__.py +0 -0
  90. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/migrations/autodetector.py +0 -0
  91. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/migrations/exceptions.py +0 -0
  92. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/migrations/executor.py +0 -0
  93. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/migrations/graph.py +0 -0
  94. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/migrations/loader.py +0 -0
  95. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/migrations/migration.py +0 -0
  96. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/migrations/operations/__init__.py +0 -0
  97. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/migrations/operations/base.py +0 -0
  98. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/migrations/operations/fields.py +0 -0
  99. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/migrations/operations/models.py +0 -0
  100. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/migrations/operations/special.py +0 -0
  101. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/migrations/optimizer.py +0 -0
  102. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/migrations/questioner.py +0 -0
  103. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/migrations/recorder.py +0 -0
  104. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/migrations/serializer.py +0 -0
  105. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/migrations/state.py +0 -0
  106. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/migrations/utils.py +0 -0
  107. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/migrations/writer.py +0 -0
  108. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/options.py +0 -0
  109. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/otel.py +0 -0
  110. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/preflight.py +0 -0
  111. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/query.py +0 -0
  112. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/query_utils.py +0 -0
  113. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/registry.py +0 -0
  114. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/schema.py +0 -0
  115. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/sources.py +0 -0
  116. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/sql/__init__.py +0 -0
  117. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/sql/compiler.py +0 -0
  118. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/sql/constants.py +0 -0
  119. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/sql/datastructures.py +0 -0
  120. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/sql/query.py +0 -0
  121. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/sql/where.py +0 -0
  122. {plain_postgres-0.100.0/tests/app/examples/migrations → plain_postgres-0.101.0/plain/postgres/test}/__init__.py +0 -0
  123. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/test/database.py +0 -0
  124. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/test/pytest.py +0 -0
  125. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/transaction.py +0 -0
  126. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/types.py +0 -0
  127. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/types.pyi +0 -0
  128. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/utils.py +0 -0
  129. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/forms.py +0 -0
  130. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/migrations/0001_initial.py +0 -0
  131. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/migrations/0002_test_field_removed.py +0 -0
  132. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/migrations/0003_deleteparent_childsetnull_childsetdefault_and_more.py +0 -0
  133. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/migrations/0004_defaultquerysetmodel_mixintestmodel_and_more.py +0 -0
  134. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/migrations/0005_feature_carfeature_car_features.py +0 -0
  135. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/migrations/0006_secretstore.py +0 -0
  136. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/migrations/0007_treenode_unconstrainedchild.py +0 -0
  137. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/migrations/0008_setsentinelparent_diamondparenta_midparent_and_more.py +0 -0
  138. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/migrations/0009_circb_circa_circb_partner.py +0 -0
  139. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/migrations/0010_hideableitem.py +0 -0
  140. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/migrations/0011_defaultsexample.py +0 -0
  141. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/migrations/0012_iterationexample.py +0 -0
  142. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/migrations/0013_indexexample_constraintexample_nullabilityexample.py +0 -0
  143. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/migrations/0014_widget_rename_feature_tag_remove_carfeature_car_and_more.py +0 -0
  144. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/migrations/0015_dbdefaultsexample.py +0 -0
  145. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/migrations/0016_formsexample.py +0 -0
  146. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/migrations/0017_random_string_token.py +0 -0
  147. {plain_postgres-0.100.0/plain/postgres/test → plain_postgres-0.101.0/tests/app/examples/migrations}/__init__.py +0 -0
  148. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/models/__init__.py +0 -0
  149. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/models/constraints.py +0 -0
  150. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/models/defaults.py +0 -0
  151. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/models/delete.py +0 -0
  152. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/models/encrypted.py +0 -0
  153. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/models/forms.py +0 -0
  154. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/models/indexes.py +0 -0
  155. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/models/iteration.py +0 -0
  156. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/models/mixins.py +0 -0
  157. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/models/nullability.py +0 -0
  158. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/models/querysets.py +0 -0
  159. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/models/relationships.py +0 -0
  160. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/models/trees.py +0 -0
  161. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/models/unregistered.py +0 -0
  162. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/urls.py +0 -0
  163. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/views.py +0 -0
  164. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/settings.py +0 -0
  165. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/urls.py +0 -0
  166. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/conftest.py +0 -0
  167. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/conftest_convergence.py +0 -0
  168. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_autodetector_not_null_errors.py +0 -0
  169. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_autodetector_type_change.py +0 -0
  170. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_connection_isolation.py +0 -0
  171. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_connection_lifecycle.py +0 -0
  172. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_connection_pool.py +0 -0
  173. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_constraint_violation_error.py +0 -0
  174. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_convergence.py +0 -0
  175. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_convergence_defaults.py +0 -0
  176. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_convergence_fk.py +0 -0
  177. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_convergence_indexes.py +0 -0
  178. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_convergence_nullability.py +0 -0
  179. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_database_url.py +0 -0
  180. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_db_expression_defaults.py +0 -0
  181. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_delete_behaviors.py +0 -0
  182. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_diagnose.py +0 -0
  183. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_encrypted_fields.py +0 -0
  184. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_exceptions.py +0 -0
  185. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_executor_connection_hook.py +0 -0
  186. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_field_defaults.py +0 -0
  187. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_functions_uuid.py +0 -0
  188. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_health.py +0 -0
  189. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_introspection.py +0 -0
  190. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_iterator.py +0 -0
  191. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_literal_default_persistence.py +0 -0
  192. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_m2m.py +0 -0
  193. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_management_connection.py +0 -0
  194. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_manager_assignment.py +0 -0
  195. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_migration_executor.py +0 -0
  196. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_mixins.py +0 -0
  197. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_modelform_roundtrip.py +0 -0
  198. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_no_callable_defaults.py +0 -0
  199. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_otel_metrics.py +0 -0
  200. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_random_string_field.py +0 -0
  201. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_raw_query.py +0 -0
  202. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_read_only_transactions.py +0 -0
  203. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_related.py +0 -0
  204. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_schema_normalize_type.py +0 -0
  205. {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_schema_timeouts.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plain.postgres
3
- Version: 0.100.0
3
+ Version: 0.101.0
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,16 @@
1
1
  # plain-postgres changelog
2
2
 
3
+ ## [0.101.0](https://github.com/dropseed/plain/releases/plain-postgres@0.101.0) (2026-04-30)
4
+
5
+ ### What's changed
6
+
7
+ - **Validate CHECK constraints in the same converge run that adds them.** `AddConstraintFix` now runs `ALTER TABLE ... ADD CONSTRAINT ... NOT VALID` followed by `ALTER TABLE ... VALIDATE CONSTRAINT` in a single `apply()`. The add is catalog-only (brief lock) and validate uses `SHARE UPDATE EXCLUSIVE` (doesn't block writes), so there's no benefit to deferring validation to a later run. Existing rows are checked before convergence reports success — previously, a CHECK constraint could be added in `NOT VALID` state and the validation step was its own follow-up fix. ([dc7eb8d3c2b7](https://github.com/dropseed/plain/commit/dc7eb8d3c2b7))
8
+ - `plain-postgres` rule references updated for the simpler `plain docs` CLI (no more `--section`). ([e03c3bd8b6d3](https://github.com/dropseed/plain/commit/e03c3bd8b6d3))
9
+
10
+ ### Upgrade instructions
11
+
12
+ - No changes required. The next `plain postgres sync` (or scheduled converge run) on a database with pending CHECK constraints will now both add and validate them in one step instead of two.
13
+
3
14
  ## [0.100.0](https://github.com/dropseed/plain/releases/plain-postgres@0.100.0) (2026-04-28)
4
15
 
5
16
  ### What's changed
@@ -42,7 +42,7 @@ This means: when you add an `Index` or `UniqueConstraint` to a model, no migrati
42
42
 
43
43
  For custom data migrations, use `uv run plain migrations create --empty --name <name>` to scaffold the file.
44
44
 
45
- Run `uv run plain docs postgres --section migrations` for full workflow details.
45
+ Run `uv run plain docs postgres` for full workflow details.
46
46
 
47
47
  ## Querying
48
48
 
@@ -57,7 +57,7 @@ Use `Model.query` to build querysets (e.g., `User.query.filter(is_active=True)`)
57
57
  - Wrap multi-step writes in `transaction.atomic()`
58
58
  - Always paginate list queries — unbounded querysets get slower as data grows
59
59
 
60
- Run `uv run plain docs postgres --section querying` for full patterns with code examples.
60
+ Run `uv run plain docs postgres` for full patterns with code examples.
61
61
 
62
62
  ## Schema Design
63
63
 
@@ -68,13 +68,13 @@ Run `uv run plain docs postgres --section querying` for full patterns with code
68
68
  - Choose `on_delete` deliberately: CASCADE for owned children, RESTRICT for referenced data, SET_NULL for optional references
69
69
  - No `allow_null` on string fields — use `default=""`
70
70
 
71
- Run `uv run plain docs postgres --section constraints` for full patterns with code examples.
71
+ Run `uv run plain docs postgres` for full patterns with code examples.
72
72
 
73
73
  ## Database Doctor
74
74
 
75
75
  Use the `/plain-postgres-doctor` skill to check overall database health — migration sync, schema correctness, and operational health.
76
76
 
77
- Run `uv run plain docs postgres --section diagnostics` for check details, thresholds, and production usage.
77
+ Run `uv run plain docs postgres` for check details, thresholds, and production usage.
78
78
 
79
79
  ## Differences from Django
80
80
 
@@ -196,7 +196,10 @@ class CreateIndexFix(Fix):
196
196
  class AddConstraintFix(Fix):
197
197
  """Add a missing constraint.
198
198
 
199
- Check constraints use NOT VALID to avoid a table scan.
199
+ Check constraints use ADD CONSTRAINT ... NOT VALID + VALIDATE CONSTRAINT
200
+ in a single apply() — the add is catalog-only (brief lock) and the
201
+ validate uses SHARE UPDATE EXCLUSIVE which doesn't block writes, so
202
+ there's no benefit to deferring validation to a later run.
200
203
  Unique constraints use CREATE UNIQUE INDEX CONCURRENTLY + USING INDEX
201
204
  to avoid blocking writes.
202
205
  """
@@ -208,8 +211,6 @@ class AddConstraintFix(Fix):
208
211
  model: type[Model]
209
212
 
210
213
  def describe(self) -> str:
211
- if isinstance(self.constraint, CheckConstraint):
212
- return f"{self.table}: add constraint {self.constraint.name} (NOT VALID)"
213
214
  return f"{self.table}: add constraint {self.constraint.name}"
214
215
 
215
216
  def apply(self) -> str:
@@ -244,9 +245,18 @@ class AddConstraintFix(Fix):
244
245
 
245
246
  def _apply_other(self) -> str:
246
247
  if isinstance(self.constraint, CheckConstraint):
247
- sql = self.constraint.to_sql(self.model, not_valid=True)
248
- else:
249
- sql = self.constraint.to_sql(self.model)
248
+ add_sql = self.constraint.to_sql(self.model, not_valid=True)
249
+ _execute_and_commit(add_sql)
250
+
251
+ validate_sql = (
252
+ f"ALTER TABLE {quote_name(self.table)}"
253
+ f" VALIDATE CONSTRAINT {quote_name(self.constraint.name)}"
254
+ )
255
+ _execute_and_commit(validate_sql, blocking=False)
256
+
257
+ return f"{add_sql}; {validate_sql}"
258
+
259
+ sql = self.constraint.to_sql(self.model)
250
260
  _execute_and_commit(sql)
251
261
  return sql
252
262
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "plain.postgres"
3
- version = "0.100.0"
3
+ version = "0.101.0"
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"
@@ -338,8 +338,8 @@ class TestDetectConstraintFixes:
338
338
 
339
339
 
340
340
  class TestApplyConstraintFixes:
341
- def test_add_check_constraint_uses_not_valid(self, isolated_db):
342
- """AddConstraintFix for check constraints creates NOT VALID."""
341
+ def test_add_check_constraint_validates_immediately(self, isolated_db):
342
+ """AddConstraintFix for check constraints adds NOT VALID and validates in one apply."""
343
343
  check = CheckConstraint(
344
344
  check=Q(id__gte=0),
345
345
  name="examples_constraintexample_id_nonneg",
@@ -356,10 +356,11 @@ class TestApplyConstraintFixes:
356
356
  sql = fix.apply()
357
357
 
358
358
  assert "NOT VALID" in sql
359
+ assert "VALIDATE CONSTRAINT" in sql
359
360
  assert constraint_exists(
360
361
  "examples_constraintexample", "examples_constraintexample_id_nonneg"
361
362
  )
362
- assert not constraint_is_valid(
363
+ assert constraint_is_valid(
363
364
  "examples_constraintexample", "examples_constraintexample_id_nonneg"
364
365
  )
365
366
  finally:
@@ -385,7 +386,7 @@ class TestApplyConstraintFixes:
385
386
  )
386
387
 
387
388
  def test_full_check_constraint_lifecycle(self, isolated_db):
388
- """Add NOT VALID -> validate -> fully valid constraint."""
389
+ """A single converge pass adds and validates a check constraint."""
389
390
  check = CheckConstraint(
390
391
  check=Q(id__gte=0),
391
392
  name="examples_constraintexample_id_nonneg",
@@ -394,7 +395,6 @@ class TestApplyConstraintFixes:
394
395
  ConstraintExample.model_options.constraints = [*original_constraints, check]
395
396
 
396
397
  try:
397
- # First converge pass: adds NOT VALID
398
398
  conn = get_connection()
399
399
  with conn.cursor() as cursor:
400
400
  items = plan_model_convergence(
@@ -403,25 +403,12 @@ class TestApplyConstraintFixes:
403
403
  assert len(items) == 1
404
404
  assert isinstance(items[0].fix, AddConstraintFix)
405
405
 
406
- items[0].fix.apply()
407
- assert not constraint_is_valid(
408
- "examples_constraintexample", "examples_constraintexample_id_nonneg"
409
- )
410
-
411
- # Second converge pass: detects NOT VALID, validates
412
- with conn.cursor() as cursor:
413
- items = plan_model_convergence(
414
- conn, cursor, ConstraintExample
415
- ).executable()
416
- assert len(items) == 1
417
- assert isinstance(items[0].fix, ValidateConstraintFix)
418
-
419
406
  items[0].fix.apply()
420
407
  assert constraint_is_valid(
421
408
  "examples_constraintexample", "examples_constraintexample_id_nonneg"
422
409
  )
423
410
 
424
- # Third pass: fully converged
411
+ # Second pass: fully converged
425
412
  with conn.cursor() as cursor:
426
413
  items = plan_model_convergence(
427
414
  conn, cursor, ConstraintExample
@@ -173,13 +173,16 @@ def test_add_constraint_unique_mixes_autocommit_and_blocking_commit(spy: _Spy):
173
173
  assert spy.commit_calls[0][1] is True
174
174
 
175
175
 
176
- def test_add_constraint_check_is_blocking(spy: _Spy):
176
+ def test_add_constraint_check_blocking_add_then_nonblocking_validate(spy: _Spy):
177
177
  cc = CheckConstraint(check=Q(id__gte=0), name="check_widget_id")
178
178
  fix = AddConstraintFix(table="examples_widget", constraint=cc, model=Widget)
179
179
  fix.apply()
180
180
  assert spy.autocommit_calls == []
181
- assert len(spy.commit_calls) == 1
181
+ assert len(spy.commit_calls) == 2
182
+ # Step 1: ADD CONSTRAINT ... NOT VALID — blocking
182
183
  assert spy.commit_calls[0][1] is True
184
+ # Step 2: VALIDATE CONSTRAINT — non-blocking
185
+ assert spy.commit_calls[1][1] is False
183
186
 
184
187
 
185
188
  def test_add_foreign_key_fix_blocking_add_then_nonblocking_validate(spy: _Spy):