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.
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/PKG-INFO +1 -1
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/CHANGELOG.md +11 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/agents/.claude/rules/plain-postgres.md +4 -4
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/convergence/fixes.py +16 -6
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/pyproject.toml +1 -1
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_convergence_constraints.py +6 -19
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_convergence_timeouts.py +5 -2
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/.gitignore +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/CLAUDE.md +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/LICENSE +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/README.md +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/README.md +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/__init__.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/adapters.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/agents/.claude/skills/plain-postgres-doctor/SKILL.md +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/aggregates.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/base.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/cli/__init__.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/cli/converge.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/cli/core.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/cli/decorators.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/cli/diagnose.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/cli/migrations.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/cli/schema.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/cli/sync.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/config.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/connection.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/constants.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/constraints.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/convergence/__init__.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/convergence/analysis.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/convergence/planning.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/database_url.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/db.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/ddl.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/default_settings.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/deletion.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/dialect.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/entrypoints.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/enums.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/exceptions.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/expressions.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/fields/__init__.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/fields/base.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/fields/binary.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/fields/boolean.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/fields/duration.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/fields/encrypted.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/fields/json.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/fields/mixins.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/fields/network.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/fields/numeric.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/fields/primary_key.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/fields/related.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/fields/related_descriptors.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/fields/related_lookups.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/fields/related_managers.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/fields/reverse_descriptors.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/fields/reverse_related.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/fields/temporal.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/fields/text.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/fields/timezones.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/fields/uuid.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/forms.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/functions/__init__.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/functions/comparison.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/functions/datetime.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/functions/math.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/functions/mixins.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/functions/random.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/functions/text.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/functions/uuid.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/functions/window.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/indexes.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/introspection/__init__.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/introspection/health/__init__.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/introspection/health/checks_cumulative.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/introspection/health/checks_snapshot.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/introspection/health/checks_structural.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/introspection/health/context.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/introspection/health/helpers.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/introspection/health/ownership.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/introspection/health/runner.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/introspection/health/types.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/introspection/schema.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/lookups.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/meta.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/middleware.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/migrations/__init__.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/migrations/autodetector.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/migrations/exceptions.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/migrations/executor.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/migrations/graph.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/migrations/loader.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/migrations/migration.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/migrations/operations/__init__.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/migrations/operations/base.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/migrations/operations/fields.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/migrations/operations/models.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/migrations/operations/special.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/migrations/optimizer.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/migrations/questioner.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/migrations/recorder.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/migrations/serializer.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/migrations/state.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/migrations/utils.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/migrations/writer.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/options.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/otel.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/preflight.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/query.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/query_utils.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/registry.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/schema.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/sources.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/sql/__init__.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/sql/compiler.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/sql/constants.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/sql/datastructures.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/sql/query.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/sql/where.py +0 -0
- {plain_postgres-0.100.0/tests/app/examples/migrations → plain_postgres-0.101.0/plain/postgres/test}/__init__.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/test/database.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/test/pytest.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/transaction.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/types.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/types.pyi +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/utils.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/forms.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/migrations/0001_initial.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/migrations/0002_test_field_removed.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/migrations/0003_deleteparent_childsetnull_childsetdefault_and_more.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/migrations/0004_defaultquerysetmodel_mixintestmodel_and_more.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/migrations/0005_feature_carfeature_car_features.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/migrations/0006_secretstore.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/migrations/0007_treenode_unconstrainedchild.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/migrations/0008_setsentinelparent_diamondparenta_midparent_and_more.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/migrations/0009_circb_circa_circb_partner.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/migrations/0010_hideableitem.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/migrations/0011_defaultsexample.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/migrations/0012_iterationexample.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/migrations/0013_indexexample_constraintexample_nullabilityexample.py +0 -0
- {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
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/migrations/0015_dbdefaultsexample.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/migrations/0016_formsexample.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/migrations/0017_random_string_token.py +0 -0
- {plain_postgres-0.100.0/plain/postgres/test → plain_postgres-0.101.0/tests/app/examples/migrations}/__init__.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/models/__init__.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/models/constraints.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/models/defaults.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/models/delete.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/models/encrypted.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/models/forms.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/models/indexes.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/models/iteration.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/models/mixins.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/models/nullability.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/models/querysets.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/models/relationships.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/models/trees.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/models/unregistered.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/urls.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/views.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/settings.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/urls.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/conftest.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/conftest_convergence.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_autodetector_not_null_errors.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_autodetector_type_change.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_connection_isolation.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_connection_lifecycle.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_connection_pool.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_constraint_violation_error.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_convergence.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_convergence_defaults.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_convergence_fk.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_convergence_indexes.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_convergence_nullability.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_database_url.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_db_expression_defaults.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_delete_behaviors.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_diagnose.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_encrypted_fields.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_exceptions.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_executor_connection_hook.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_field_defaults.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_functions_uuid.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_health.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_introspection.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_iterator.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_literal_default_persistence.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_m2m.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_management_connection.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_manager_assignment.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_migration_executor.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_mixins.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_modelform_roundtrip.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_no_callable_defaults.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_otel_metrics.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_random_string_field.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_raw_query.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_read_only_transactions.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_related.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_schema_normalize_type.py +0 -0
- {plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_schema_timeouts.py +0 -0
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
248
|
-
|
|
249
|
-
|
|
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
|
|
|
@@ -338,8 +338,8 @@ class TestDetectConstraintFixes:
|
|
|
338
338
|
|
|
339
339
|
|
|
340
340
|
class TestApplyConstraintFixes:
|
|
341
|
-
def
|
|
342
|
-
"""AddConstraintFix for check constraints
|
|
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
|
|
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
|
-
"""
|
|
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
|
-
#
|
|
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
|
|
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) ==
|
|
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):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/fields/related_descriptors.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/fields/reverse_descriptors.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/introspection/health/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/introspection/health/context.py
RENAMED
|
File without changes
|
{plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/introspection/health/helpers.py
RENAMED
|
File without changes
|
{plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/introspection/health/ownership.py
RENAMED
|
File without changes
|
{plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/introspection/health/runner.py
RENAMED
|
File without changes
|
{plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/introspection/health/types.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/migrations/operations/__init__.py
RENAMED
|
File without changes
|
{plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/migrations/operations/base.py
RENAMED
|
File without changes
|
{plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/migrations/operations/fields.py
RENAMED
|
File without changes
|
{plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/migrations/operations/models.py
RENAMED
|
File without changes
|
{plain_postgres-0.100.0 → plain_postgres-0.101.0}/plain/postgres/migrations/operations/special.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/migrations/0001_initial.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/migrations/0006_secretstore.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/migrations/0010_hideableitem.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/migrations/0016_formsexample.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/app/examples/models/relationships.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plain_postgres-0.100.0 → plain_postgres-0.101.0}/tests/test_autodetector_not_null_errors.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|