SchemaSmith Concept

Drop Control

SchemaQuench removes database objects when they're deleted from your product definition — unless drop control tells it to preserve them. Eight configurable flags let you decide what stays and what goes.

By the SchemaSmith Team · Last reviewed

A cascade of configuration tiers deciding whether a database object removed from a product definition is dropped or preserved

Multiple settings in SchemaSmith share the same story: start conservative, align in stages, enforce once you're ready.

Overview

When you remove a table, column, foreign key, constraint, statistics object, or index from your product definition, SchemaQuench has to decide what to do with the copy that's still in the database. The drop-control flags are that decision. Each one governs a single object type's removal by absence — the case where an object exists in the target but no longer appears anywhere in the schema package.

Seven of the eight flags default to true, so the database stays in sync as objects leave the product over time. The eighth, DropUnknownIndexes, defaults to false. Every flag resolves through the same tiered cascade, and a false set anywhere in that cascade is a hard guardrail that no lower tier can override.

The eight flags

Each flag targets one object type and gates only removal by absence. Seven default to true; DropUnknownIndexes is the lone default-false flag, because it removes indexes SchemaSmith never created.

Flag Governs removal-by-absence of… Default Engines
DropTablesRemovedFromProduct Tables the product previously managed, absent from every table JSON true All
DropColumnsRemovedFromProduct Columns absent from the table JSON true All
DropForeignKeysRemovedFromProduct Foreign keys absent from the table JSON true All
DropCheckConstraintsRemovedFromProduct Table-level CHECK constraints absent from the JSON true All
DropExcludeConstraintsRemovedFromProduct EXCLUDE constraints absent from the JSON true PostgreSQL
DropStatisticsRemovedFromProduct User-created statistics absent from the JSON true SQL Server, PostgreSQL
DropIndexesRemovedFromProduct Product-owned indexes absent from the JSON true All
DropUnknownIndexes Out-of-band indexes SchemaSmith never managed, on managed tables false All

Only removal by absence is gated

An object whose definition merely changed — a foreign key with a new referenced column, a check constraint with a new expression, an index with different columns — is always dropped and recreated so the new definition takes effect, regardless of these flags. And some objects are never in scope at all: a primary key is never dropped by a drop-control flag, and auto-created statistics are left alone — only the named objects your product defines can be dropped by absence.

The four-tier cascade

Every drop-control flag resolves the same way: SchemaQuench composes an effective value from a stack of tiers, evaluated from the broadest scope to the narrowest.

The tiers

  • Environment — the flag in SchemaQuench.settings.json, or the SmithySettings_<FlagName> environment variable. Controls every product deployed in that environment.
  • Product — the flag in Product.json. Controls a single product wherever it deploys.
  • Template — the flag in Template.json. Controls a single template within a product.
  • Table — the flag in a table's .json file. Protects that one table's objects. Six of the eight flags reach this tier; DropTablesRemovedFromProduct and DropUnknownIndexes have no table tier and stop at the template level.
// SchemaQuench.settings.json — suppress column drops for every product in this environment
{ "DropColumnsRemovedFromProduct": false }

Explicit false is sticky

A false set at any tier locks the effective value to false for all lower tiers. A true at a lower tier overrides an inherited true but can never override an ancestor's false. Absent (not set) inherits from the tier above. This makes a higher-tier false a hard guardrail: an environment that sets false suppresses the drop regardless of what any product or template declares.

Per-table tightening

The table tier can only tighten. A table can set its own false to protect its objects even when higher tiers permit the drop — but it cannot set true to re-enable a drop a higher tier has suppressed.

// Tables/dbo.AuditLog.json — protect this table's columns regardless of higher-tier settings
{ "Name": "AuditLog", "DropColumnsRemovedFromProduct": false }

Turning enforcement on safely

DropUnknownIndexes defaults to false for good reason: most teams adopting SchemaSmith inherit environments with years of index drift. Turn it on after capturing every needed index in your repository — not before. The same staged approach applies to any enforcement flag you're switching on, DropTablesRemovedFromProduct included.

  1. Extract and inventory. Use SchemaTongs to capture your current schema, then review what comes out — you may find objects in production that don't exist in dev, duplicates, or contradictions.
  2. Build up the repository in stages. Add objects to your schema package in controlled stages, maybe one schema area at a time. Deploy each stage, verify performance, confirm the deployment window is acceptable.
  3. Keep enforcement off during alignment. Leave DropUnknownIndexes and DropTablesRemovedFromProduct off until you're confident the repository represents the complete desired state — otherwise you risk dropping objects you haven't captured yet.
  4. Turn on enforcement to prevent drift from returning. Once alignment is verified across environments, enable the flags. Now SchemaSmith stops drift the moment you turn your back.

Why big-bang alignment fails

Bringing everything into alignment in a single deployment is one of the most common mistakes teams make: hours-long deployment windows when every index gets rebuilt, unexpected performance degradation when indexes are dropped that weren't captured yet, or accidental data loss when a table is dropped that still had dependencies. Add what's safe, deploy, verify, repeat.

Two rollback-friendly postures

Turning a drop off is one way to protect data. Keeping the drop on but recoverable is another.

Never drop. Set DropTablesRemovedFromProduct: false in production. A table that leaves the product is left in place; SchemaQuench touches nothing. The trade-off is accumulation — the database collects orphaned tables until you retire them by hand.

Drop but recoverable. Keep auto-drops on and install the recyclebin hooks. A removed table moves into a recyclebin instead of being destroyed, auto-restores if it returns to the product, and is purged after a retention window.

See Recyclebin for the hooks contract, registry, and cleanup scheduling. The recyclebin protects tables only — a dropped column is never caught.

Drop control in patch packages

A patch package is a subset of a full product, so its drops need special handling. When SchemaQuench deploys a subset against a target that already has the full product installed, it would normally read every object absent from the patch as "should be dropped."

SchemaShears prevents that: it stamps the drop-suppression flags to false in the emitted patch's Product.json, so objects outside the patch are preserved rather than reconciled away. As of v2.2.0, all seven stamped flags are enforced.

Need a patch to drop something on purpose? The --AllowDrops switch re-enables specific categories. See SchemaShears for the manifest workflow and the full category list.

Platform notes

Two flags are engine-specific; the rest apply to all three.

Flag SQL Server PostgreSQL MySQL
DropExcludeConstraintsRemovedFromProduct
DropStatisticsRemovedFromProduct
All other drop-control flags

EXCLUDE constraints are a PostgreSQL feature, so DropExcludeConstraintsRemovedFromProduct is accepted but has no effect on SQL Server or MySQL. MySQL has no separate statistics objects, so DropStatisticsRemovedFromProduct does not apply there. And on MySQL, DropForeignKeysRemovedFromProduct now governs foreign-key cleanup on its own — that cleanup previously required enabling DropUnknownIndexes, matching how SQL Server and PostgreSQL have always behaved.