# SchemaQuench Reference Take your declared schema and harden it onto a live database -- that's what SchemaQuench does. It reads a schema package, connects to the target server, and transforms each database to match the desired state. No hand-written ALTER scripts, no guessing what changed. Run it against dev, staging, and production with the same package, the same confidence, and the same boring, predictable result every time. SchemaQuench compares current state against desired state, makes only the changes necessary, and tracks migration scripts so they execute only once. One executable, four platforms. The product's `Platform` value (`SqlServer`, `PostgreSQL`, `MySQL`, or `MariaDb`) tells SchemaQuench which adapter, which DDL flavor, and which set of helper procedures to use. Everything else looks the same. --- ## Invocation SchemaQuench ships as part of the SchemaSmith distribution — see the [Installation guide](../guide/installation.md) for how to get the binary on your PATH. Run it from the directory containing `SchemaQuench.settings.json`: ```bash SchemaQuench ``` Common switches: ```bash SchemaQuench --ConfigFile:path/to/alternate.settings.json SchemaQuench --LogPath:path/to/logs # Pre-flight diagnostics (no deployment) SchemaQuench --TestConnection # validate connections + MinimumVersion, then exit SchemaQuench --PreviewTargets # validate connections + MinimumVersion + show target report, then exit # SQL Server SchemaQuench --ConnectionString:"Data Source=myserver;Initial Catalog=master;User ID=sa;Password=secret;TrustServerCertificate=True;" # PostgreSQL SchemaQuench --ConnectionString:"Host=myserver;Port=5432;Database=postgres;Username=deploy;Password=secret;" # MySQL SchemaQuench --ConnectionString:"Server=myserver;Port=3306;Database=mysql;User=deploy;Password=secret;" ``` The `--ConnectionString` switch bypasses all `Target` settings and passes the value directly to the platform-appropriate driver. --- ## Configuration Reference SchemaQuench reads configuration from `SchemaQuench.settings.json` (or the file specified by `--ConfigFile`), environment variables with the `SmithySettings_` prefix, and command-line switches. Later sources override earlier ones. For the shared configuration system see the [Configuration Reference](configuration.md). ### Target Connection Settings | Key | Type | Default | Description | |-----|------|---------|-------------| | `Target:Server` | string | _(required)_ | Database server hostname or IP. | | `Target:Port` | string | platform default | TCP port. SQL Server `1433`, PostgreSQL `5432`, MySQL `3306`. | | `Target:User` | string | _(empty)_ | Login username. SQL Server allows blank for Windows auth. | | `Target:Password` | string | _(empty)_ | Login password. | | `Target:SecondaryServers` | string | _(empty)_ | **SQL Server only.** Comma-separated list of additional servers (Availability Group secondaries) to quench in parallel with the primary. See [Secondary Servers](#secondary-servers). | | `Target:ConnectionProperties` | object | `{}` | Arbitrary key-value pairs appended to the connection string. Platform-specific keys -- see the [Configuration Reference](configuration.md#connection-configuration). | ### Behavior Settings | Key | Type | Default | Description | |-----|------|---------|-------------| | `SchemaPackagePath` | string | _(required)_ | Path to the schema package directory or ZIP file. | | `WhatIfONLY` | bool | `false` | Dry-run mode. Generates SQL without executing. | | `KindleTheForge` | bool | `true` | Deploy SchemaSmith helper procedures and the migration tracking table to each target database before quenching. | | `UpdateTables` | bool | `true` | Apply table structure changes (columns, indexes, constraints, foreign keys) from the schema package. | | `DropTablesRemovedFromProduct` | bool | `true` | Drop tables that exist in the database but aren't defined in the schema package. Also settable as a `Product.json` property — see [DropTablesRemovedFromProduct](#droptablesremovedfromproduct). | | `PreventDrop` | bool | `false` | Environment-wide no-drop protection: when `true`, this environment never drops an object for being absent from the product (every `Drop…RemovedFromProduct` pass is suppressed) and the run completes normally, itemizing the withheld drops in the deployment summary's `preventDrop` manifest. Transient drop-then-recreate for a declared change is unaffected. See [PreventDrop](#preventdrop). | | `DropColumnsRemovedFromProduct` | bool | `true` | Drop columns that exist in the database but aren't defined in the schema package. Resolves across a four-tier cascade (env → product → template → table) with explicit-false-sticky semantics. See [DropColumnsRemovedFromProduct](#dropcolumnsremovedfromproduct). | | `DeliverData` | bool | `true` | Run the per-table `DataDelivery` step and the `TableData`-slot scripts. Set to `false` to ship a structure-only deployment that leaves reference data untouched -- pairs naturally with `UpdateTables: true` for "deploy schema, skip data" pipelines. | | `RunScriptsTwice` | bool | `false` | Run object scripts twice to verify idempotency. A CI/testing tool. | | `TrackRunOnceMigrations` | bool | `true` | Track run-once migration scripts. When `false`, all scripts run on every deployment. | | `PruneObsoleteMigrationTracking` | bool | `true` | Remove tracking entries for scripts no longer in the package. When `Target` filters are active, prune is restricted to the targeted scope. See [PruneObsoleteMigrationTracking](#pruneobsoletemigrationtracking). | | `CheckpointDirectory` | string | `""` | Directory for checkpoint files used by `--ResumeQuench`. When blank, defaults to a per-platform temp location. See [Checkpoint and Resume](#checkpoint-and-resume). | | `MaxThreads` | int | `10` | Maximum parallel work units. Covers both database-level and schema-level iterations. Range 1--20. See [MaxThreads](#maxthreads). | | `VerboseLogging` | bool | `false` | Include `PRINT` / `RAISE NOTICE` / equivalent informational output from user scripts in logs. | | `ScriptTokens` | object | `{}` | Config-level overrides for product script tokens. | ### Full settings file example ```json { "Target": { "Server": "localhost", "Port": "", "User": "", "Password": "", "SecondaryServers": "", "ConnectionProperties": { "TrustServerCertificate": "True" }, "Templates": [], "Databases": [], "Schemas": [] }, "WhatIfONLY": false, "SchemaPackagePath": "./MyProduct", "KindleTheForge": true, "UpdateTables": true, "DropTablesRemovedFromProduct": true, "DropColumnsRemovedFromProduct": true, "DeliverData": true, "RunScriptsTwice": false, "TrackRunOnceMigrations": true, "PruneObsoleteMigrationTracking": true, "CheckpointDirectory": "", "MaxThreads": 10, "VerboseLogging": false, "ScriptTokens": {} } ``` For environment variable mapping, see [Configuration Reference -- Environment Variables](configuration.md#environment-variables). --- ## Secondary Servers SQL Server deployments targeting Availability Groups can quench to a primary plus one or more secondary servers in parallel. Configure secondaries on `Target`: ```json { "Target": { "Server": "primary-replica", "SecondaryServers": "secondary-1,secondary-2" } } ``` When a secondary list is configured, SchemaQuench routes each product-level folder to the right server based on its `ServerToQuench` setting (`Primary`, `Secondary`, or `Both`). Templates target the primary; product-level scripts can target either side. See [Schema Packages -- Secondary Servers](schema-packages.md#secondary-servers) for the package side of the configuration. > **PostgreSQL, MySQL, and MariaDB** deployments use a single connection. Replication and read-only standbys are typically managed at the database engine level, not by the deployment tool. --- ## Target Selective execution scope narrows a deployment to a subset of the work the product would otherwise perform. The most common use is deploying to a single newly-onboarded tenant without re-running the full product, canary-deploying a hotfix to one tenant to verify it before rolling out, or re-running a single template after a configuration change. Without `Target`, every template runs against every discovered database and schema. ### Filter dimensions | Key | Type | Default | Description | |-----|------|---------|-------------| | `Target:Templates` | string array | `[]` | Run only these templates. Empty array means no filter -- all templates run. | | `Target:Databases` | string array | `[]` | Run only against these databases. Empty array means no filter -- all discovered databases run. | | `Target:Schemas` | string array | `[]` | Run only against these schema names. Empty array means no filter -- all discovered schemas run. Applies only to schema-template iterations; regular-template work units bypass this filter entirely. | The three dimensions filter AND together. Setting `Target:Templates: ["TenantWorkspace"]` and `Target:Schemas: ["tenant_newco"]` runs only the TenantWorkspace template, and within that template only the iteration where the schema is `tenant_newco`. Unmatched work units are skipped before any database connections open for them. SchemaQuench validates filter values against the discovered universe before dispatching any work. A value that doesn't match anything in the discovered set fails immediately with a diagnostic that lists the available options, so a typo surfaces as a clear error rather than a silent empty run. > **Warning:** When `Target` filters are active, `PruneObsoleteMigrationTracking` is restricted to the targeted scope. This is intentional -- pruning tracking rows outside the targeted scope would delete correct records of migrations applied against databases and schemas that you explicitly excluded from this run. See [PruneObsoleteMigrationTracking](#pruneobsoletemigrationtracking) for the full rule. ### Onboarding example Deploy TenantWorkspace to a newly-onboarded tenant without touching any existing tenants: ```json { "Target": { "Server": "production-db", "Templates": ["TenantWorkspace"], "Schemas": ["tenant_newco"] } } ``` With this configuration, SchemaQuench runs `TenantWorkspace` and skips every other template in `TemplateOrder`. Within `TenantWorkspace`, it runs only the `tenant_newco` iteration -- `tenant_acme`, `tenant_beta`, and all other tenants are untouched, and their tracking rows in `CompletedMigrationScripts` are preserved exactly. For a full narrative walkthrough of tenant onboarding, see [Onboarding a new tenant](../guide/10-multi-tenant-deployments.md#onboarding-a-new-tenant). --- ## TemplateTargets `Target.TemplateTargets` lets the deployment system OWN the universe a schema template fans out across, instead of asking the target server to enumerate it. A template's `DatabaseIdentificationScript` / `SchemaIdentificationScript` still defines the package's contract -- this block replaces the script's result at runtime for one named template, per environment. The pattern unlocks single-canonical-package deployments where each environment's settings file declares which tenants belong on that target, and SchemaQuench reconciles existence (optionally provisioning what's missing) before deploying. ```json { "Target": { "TemplateTargets": { "TenantBody": { "Databases": ["tenant_acme", "tenant_globex"], "Schemas": ["acme", "globex"], "CreateIfMissing": true }, "Shared": { "Databases": ["tenant_acme"] } } } } ``` Each key under `TemplateTargets` is a template name as declared in `Product.json.TemplateOrder`. The value is an object with three optional properties. ### Databases String array. Replaces the result of the named template's `DatabaseIdentificationScript` for this run. When set, the listed databases ARE the universe -- the discovery script does not run. The template must declare a `DatabaseIdentificationScript` in its `Template.json`; if you don't need real discovery, the recommended marker is `"SELECT 'CONFIG-DRIVEN' AS DatabaseName WHERE 1=0"` -- a placeholder that returns no rows and signals "this template is database-fan-out, the universe lives in settings." ### Schemas String array. Replaces the result of the named template's `SchemaIdentificationScript` for this run. Same shape, same recommended placeholder: `"SELECT 'CONFIG-DRIVEN' AS SchemaName WHERE 1=0"`. When both axes are overridden on a schema template, the cross-product becomes the work-unit set: two databases × two schemas = four iterations. > **MySQL and MariaDB:** The schema axis does not apply -- MySQL and MariaDB have no schema-inside-database concept. `TemplateTargets.