Skip to content
PtahDocs
latest

Graphic preview

100%
Page type: reference

Drift findings

Every category a drift report counts a change under, with the severity it carries.

ptah schema drift counts every difference between the desired schema and the database under a finding category, and ptah schema serve counts the same categories in its drift panel. A finding is named for the kind of object it counts and for what applying the desired schema would do to it: <kind>_added counts the objects the database lacks, <kind>_removed the objects the desired schema does not declare, and <kind>_modified the objects both hold that differ. Every difference the comparison reports is counted under a category, so a report whose drift is true has at least one finding.

An object of a kind Ptah does not model, such as a MySQL event, is not a difference and has no category. The report names it under not_compared; see Events.

The categories are not disjoint: a change that has a category of its own, such as a removal that takes a UNIQUE constraint’s enforcement with it, is counted under its kind as well. migration/safety/categories_doc_test.go fails when the classifier reports a category this page does not list, when a row here names one it does not report, or when the two give a category different severities.

Kind _added _removed _modified
columns warning destructive warning
constraints warning destructive
enum_values warning destructive
enums safe destructive
functions safe destructive warning
grants safe warning
indexes warning warning
materialized_views safe destructive destructive
rls_enabled_tables safe destructive
rls_policies safe destructive warning
role_memberships safe warning
roles safe destructive warning
schemas safe destructive warning
sequences safe destructive warning
tables safe destructive
triggers safe destructive warning
views safe destructive warning

A blank cell is a direction the kind has no category for. A table or an enum type that differs is counted by what differs in it: its columns, its values or its comment.

Removing an object is destructive when something is lost that the report cannot give back: a table’s or a materialized view’s rows, a sequence’s position, the definition of a view, a function or a trigger that readers and writers rely on. Removing an index, a privilege or a membership is a warning: nothing stored goes with it, and a reader or a writer that relied on it slows down or is refused until it is restored. A modified materialized view is destructive because the plan drops it and creates it again; one whose only change is a setting its owner changes in place keeps its rows and is a warning.

The plan check rates the statements a plan writes for a change as this page rates the change, and tests plan each removal and each replacement on the engines that have it and fail when the two ratings differ:

  • ptah migrations plan --check-destructive refuses a plan that drops a sequence, a view, a materialized view or a trigger, and rates a REVOKE a warning.
  • A drop of a view or a trigger that the same plan creates again, under the same name and for a trigger on the same table, replaces the object rather than removing it. It is a warning, as views_modified and triggers_modified are, and so is a CREATE OR REPLACE of either.
  • A drop with CASCADE stays destructive even when the object is created again: CASCADE also drops every object that depends on it, and creating it again does not bring them back. A PostgreSQL plan writes CASCADE only where it relies on it, so a view whose column list changes is dropped without it, and the plan check rates the drop a warning, as the drift report counts a modified view.
  • A sequence and a materialized view lose their position or their rows when they are dropped, so a drop of either is destructive whatever follows it.

These categories count a setting or a part of an object rather than the object:

Category Severity What it counts
column_identities_removed destructive Columns that lose an identity and take none. The server drops the identity’s sequence, and an insert that leaves the column out fails. Each is one of the columns_modified as well.
column_types_narrowed destructive Column modifications whose new type cannot hold every value of the old one: a shorter string, a smaller text or blob type, fewer bytes or bits, a smaller integer, decimal or float, fewer enum members, or a temporal type with a lower precision, a smaller range or fewer fields. Each is one of the columns_modified as well.
comments_changed safe Comments the plan writes in place on tables, indexes, constraints and the other objects that take a comment through a statement of their own. A column’s comment is counted under columns_modified.
constraints_validated warning Constraints the database holds NOT VALID and the plan validates. Validation reads every row and fails on one the constraint refuses.
grant_options_added safe Privileges the database holds without the WITH GRANT OPTION the desired schema gives them.
grant_options_removed warning Privileges the database holds WITH GRANT OPTION and the desired schema grants without it.
index_visibility_changed warning Indexes whose visibility to the optimizer changes: MySQL’s VISIBLE and INVISIBLE, MariaDB’s IGNORED.
indexes_renamed warning Indexes the plan renames in place, on YDB. A query that names the index stops finding it.
rls_force_added safe Tables whose FORCE ROW LEVEL SECURITY the plan turns on.
rls_force_removed destructive Tables whose FORCE ROW LEVEL SECURITY the plan turns off, which exempts the owner from the table’s policies.
unique_protections_removed destructive Index removals that take a UNIQUE constraint’s enforcement with them. Each is one of the indexes_removed as well.
vector_dimension_changed destructive Column modifications that change a pgvector dimension, which the server refuses while a row holds a vector. Each is one of the columns_modified as well.

Feature owners, undecided objects and rows

Section titled “Feature owners, undecided objects and rows”

A change a feature owner compares, such as a PostgreSQL extension, policy or domain, is named feature_changes:<change kind> with the severity the owner gives it. An owner that assesses the change’s effect on access adds feature_access_widened:<change kind>, feature_access_narrowed:<change kind>, feature_access_unchanged:<change kind> or feature_access_unknown:<change kind>. The JSON report’s inventory counts an owner’s objects under the same change kind, and a provider’s where the provider counts them; see How much each side holds. A declared object the read could not check is counted under one undecided finding at warning severity; see Drift: the difference as a check. Reference rows have findings of their own.

docs/drift-findings.json in the Ptah repository lists every category a drift report can carry: the categories above, undecided, and the reference rows’ data_rows_inserted, data_rows_updated and data_rows_deleted. The file is generated, and a test fails when it falls behind the code, so a tool that keeps its own list of categories can read it at the commit it pins rather than read Ptah’s source:

Terminal window
git show <commit>:docs/drift-findings.json

Each entry names a category, the severities a finding of it can carry from the lowest to the highest, and the forms it is written in: bare for the category alone, and qualified for the category followed by a colon and a feature owner’s change kind.

{
"category": "feature_access_unknown",
"severities": ["destructive"],
"bare": true,
"qualified": true
}

In Go, migration/safety.FindingCategories returns the schema comparison’s share of the list in the same shape.