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