Configuration

One config spine across SchemaQuench, SchemaTongs, and DataTongs. File, environment, CLI — layered so CI can override a single value without touching the rest. Same contract for every supported engine.

By the SchemaSmith Team · Last reviewed

Configuration

Every SchemaSmith CLI tool shares the same configuration spine — one consistent system for settings files, environment variables, and command-line switches.

CLI Switch Format

SchemaSmith is flexible about how you pass switches — pick whichever style feels natural. All switches accept either a double-dash (--) or forward-slash (/) prefix. Separate the switch name from its value with : or =. A single leading dash (-) also works.

--switch:value
--switch=value
/switch:value
-switch:value

Switch names are case-insensitive--logpath, --LogPath, and --LOGPATH all do the same thing.

Values that contain spaces must be quoted:

--ConfigFile:"C:\configs\my config.json"
/LogPath:"C:\My Logs\SchemaSmith"

Flags that take no value (like --version or --help) are specified without a separator.

Common Switches

Every SchemaSmith CLI tool recognizes these switches. They're processed before any configuration is loaded.

Switch Aliases Description
--version -v, --ver Print the tool name and version number, then exit.
--help -h, -? Print the available command-line switches, then exit.
--ConfigFile:<path> Path to the settings file. Overrides the default <ToolName>.settings.json.
--LogPath:<path> Directory for log files and backup subdirectories. Defaults to the tool's executable directory. See Log File Location for operational detail.
--ConnectionString:<connstr> Full ADO.NET / Npgsql / MySqlConnector connection string appropriate to the target platform. When provided, this bypasses all individual connection settings (Server, Port, User, Password, ConnectionProperties).
--Encrypt / --NoEncrypt Force transport encryption on or off, applying the correct property for the target engine (Encrypt on SQL Server, SSL Mode on PostgreSQL, SslMode on MySQL and MariaDB). Wins over ConnectionProperties. Applies to SchemaQuench, SchemaTongs, and DataTongs.

Examples

# Deploy a schema package with a custom config and log directory
SchemaQuench --ConfigFile:production.json --LogPath:C:\Logs

# Extract a schema using a specific config
SchemaTongs --ConfigFile:extract-config.json

# Check which version is installed
SchemaQuench --version

# See available switches
SchemaTongs --help

For full --ConnectionString examples per platform, see Connection Configuration below.

Configuration Hierarchy

SchemaSmith layers configuration so you can set sensible defaults in a file and override just the pieces that change per environment. Later sources override earlier ones. The full chain, from lowest to highest priority:

  1. Settings file<ToolName>.settings.json
  2. User secrets — .NET user secrets (debug builds only, not present in release builds)
  3. Environment variables — prefixed with SmithySettings_
  4. CLI switches — the named switches (--ConnectionString, --ConfigFile, --LogPath) and any --Key=value override (see below)

This means a value set in the settings file can be overridden by an environment variable, and a CLI switch always wins.

Override example

Suppose your SchemaQuench.settings.json sets the server:

{
    "Target": {
        "Server": "dev-server"
    }
}

You can override just the server for a single run using an environment variable:

$env:SmithySettings_Target__Server = "staging-server"
SchemaQuench
export SmithySettings_Target__Server=staging-server
SchemaQuench

Or override the entire connection from the command line:

SchemaQuench --ConnectionString:"Host=prod-server;\
    Database=mydb;Username=deploy;Password=s3cret;"

The --ConnectionString switch bypasses all individual connection settings — Server, Port, User, Password, and ConnectionProperties are all ignored when a full connection string is provided.

Overriding any setting

Beyond the named switches, any configuration option can be set or overridden from the command line with a --Key=value switch. This is the same override reach the SmithySettings_ environment variables give you, without touching a file or exporting a variable — ideal for CI and one-off runs.

The rule mirrors the environment-variable grammar exactly: an = separates the key from the value, and a double underscore (__) nests into the configuration hierarchy. Whatever you'd write after SmithySettings_ you write after -- — so every row in the environment variables table has a command-line twin:

Command-line switchMaps to config key
--MinimumVersion=16MinimumVersion
--Target__Server=prod-dbTarget:Server
--Source__Password=s3cretSource:Password
--Target__ConnectionProperties__Encrypt=trueTarget:ConnectionProperties:Encrypt
# Override just the server and skip cert validation for a single run
SchemaQuench --Target__Server=staging-db `
             --Target__ConnectionProperties__TrustServerCertificate=true
# Override just the server and skip cert validation for a single run
SchemaQuench --Target__Server=staging-db \
             --Target__ConnectionProperties__TrustServerCertificate=true

A --Key=value override sits at the top of the hierarchy above — it wins over the settings file, user secrets, and environment variables.

Two things to keep in mind:

  • The = is required for an override. The named switches (--LogPath, --ConfigFile, --ConnectionString) accept a : separator, but nesting into arbitrary settings needs the __/= form so a value containing a colon (a Windows path, a host:port) is never mistaken for a key boundary.
  • --ConnectionString still bypasses the individual connection settings. If you pass both --ConnectionString and a --Source__Server= / --Target__Server= override, the full connection string wins for connecting.

Unrecognized settings are reported

A mistyped setting is otherwise invisible. Target:Sever binds nothing, so the run proceeds exactly as though you had never set it — and a deployment that silently ignores half your configuration is worse than one that refuses to start.

Each tool checks the settings it was given against the settings it actually reads, and warns about anything it does not recognize:

WARN  Configuration key 'Target:Sever' is not read by SchemaQuench and
will have no effect. Check for a typo.

This is the same treatment --NoSuchSwitch already gets on the command line, and it covers every configuration source — the settings file, SmithySettings_ environment variables, and CLI overrides all land in the same configuration and are all checked.

Three things are deliberately not reported:

  • Sections the tool does not own. SchemaQuench says nothing about a Source: section, and neither Tongs tool comments on Target:. A file may serve more than one tool, or carry settings for a version you have not installed yet.
  • Open sections, where you choose the namesScriptTokens, Target:ConnectionProperties, Source:ConnectionProperties, Target:TemplateTargets, and FolderMapping. Anything beneath these is your value, not a setting name.
  • Array elements such as Target:Databases:0.

It is a warning, not an error — the run continues. Treat one as a typo until proven otherwise.

Settings Files

Each tool looks for its own settings file by name:

Tool Default settings file
SchemaQuench SchemaQuench.settings.json
SchemaTongs SchemaTongs.settings.json
DataTongs DataTongs.settings.json

The tool searches for the file in two locations, in order:

  1. The current working directory (where you run the command)
  2. The tool's executable directory (where the binary lives)

If the file is found in the current directory, that copy is used. If not, the tool falls back to the executable directory. If neither location has the file, the tool starts with an empty configuration (any required values must come from environment variables or CLI switches).

To use a different file entirely, pass the --ConfigFile switch:

SchemaQuench --ConfigFile:C:\configs\production.json

The path can be absolute or relative to the current working directory.

Environment Variables

Environment variables give you a clean way to inject configuration without touching files on disk — exactly what you need in CI/CD pipelines and containers. All three tools read environment variables prefixed with SmithySettings_. The prefix is stripped, and double underscores (__) map to hierarchy separators in the configuration structure.

Mapping rules

The prefix SmithySettings_ maps to the root of the config tree; each __ becomes a : hierarchy separator. Example: SmithySettings_Target__ServerTarget:Server{ "Target": { "Server": "..." } }.

Environment variable Maps to config key
SmithySettings_Target__ServerTarget:Server
SmithySettings_Target__PortTarget:Port
SmithySettings_Target__UserTarget:User
SmithySettings_Target__PasswordTarget:Password
SmithySettings_Target__ConnectionProperties__TrustServerCertificateTarget:ConnectionProperties:TrustServerCertificate
SmithySettings_SchemaPackagePathSchemaPackagePath
SmithySettings_WhatIfONLYWhatIfONLY

SchemaTongs and DataTongs use Source instead of Target for their connection section, so the equivalent variables start with SmithySettings_Source__:

# SchemaQuench connection
$env:SmithySettings_Target__Server = "myserver"
$env:SmithySettings_Target__Password = "s3cret"

# SchemaTongs / DataTongs connection
$env:SmithySettings_Source__Server = "myserver"
$env:SmithySettings_Source__Password = "s3cret"
# SchemaQuench connection
export SmithySettings_Target__Server=myserver
export SmithySettings_Target__Password=s3cret

# SchemaTongs / DataTongs connection
export SmithySettings_Source__Server=myserver
export SmithySettings_Source__Password=s3cret

Environment variables are especially useful in CI/CD pipelines and containers where you don't want secrets in files on disk.

Connection Configuration

Each tool has one connection section. SchemaQuench uses a Target section (it writes to the server), while SchemaTongs and DataTongs use a Source section (they read from the server). The structure is the same either way, and the same keys work for every supported platform — the adapter under the hood routes the call to the right client library based on the product's declared platform.

Individual connection settings

{
    "Target": {
        "Server": "myserver",
        "Port": "",
        "User": "deploy",
        "Password": "s3cret",
        "ConnectionProperties": {
            "TrustServerCertificate": "True"
        }
    }
}
Key Purpose
Server Database server hostname or IP address.
Port TCP port. Leave blank for the platform default (SQL Server 1433, PostgreSQL 5432, MySQL 3306, MariaDB 3306).
User Login username.
Password Login password. Masked in log output — see Startup Configuration Dump.
IntegratedSecurity SQL Server only. Set to true to force Windows Authentication, superseding any configured User and Password.
Database Database name. Used by SchemaTongs and DataTongs. SchemaQuench reads its target databases from the schema package instead.
ConnectionProperties Dictionary of additional connection string properties. Each key-value pair is appended to the built connection string.
UnsupportedFeaturePolicy What happens when the package declares a feature the detected target version cannot support — NULLS NOT DISTINCT on PostgreSQL below 15, Always Encrypted on SQL Server below 2016, a CHECK constraint on MySQL below 8.0.16. The default warn emits the object without the unsupported aspect and records it under Unsupported Feature Downgrades in the deployment summary; fail aborts the run with a message naming the feature and the version it requires. Applies on SQL Server, PostgreSQL, MySQL, and MariaDB.

Platform-specific connection properties:

  • SQL ServerTrustServerCertificate, Encrypt, ApplicationIntent, etc.
  • PostgreSQLSslMode, Pooling, Timeout, etc. (Npgsql keys)
  • MySQLSslMode, ConnectionTimeout, AllowPublicKeyRetrieval, etc. (MySqlConnector keys)
  • MariaDBSslMode, ConnectionTimeout, AllowPublicKeyRetrieval, etc. (MySqlConnector keys)

--Encrypt and --NoEncrypt are the shorter path for transport encryption specifically: each sets the correct property for the target engine and wins over ConnectionProperties. The practical use for --NoEncrypt is an older or hardened SQL Server instance that classic sqlcmd reaches unencrypted but whose TLS handshake the modern driver cannot complete. The equivalent long form is --Target__ConnectionProperties__Encrypt=false.

Whatever you put in ConnectionProperties is appended to the connection string for that platform's client library. Consult the corresponding driver documentation for the exhaustive list.

Full connection string override

Instead of individual settings, you can provide a complete connection string for the target platform:

SchemaQuench --ConnectionString:"Data Source=myserver;\
    Initial Catalog=mydb;User ID=sa;Password=s3cret;\
    TrustServerCertificate=True;"
SchemaQuench --ConnectionString:"Host=myserver;Port=5432;\
    Database=mydb;Username=deploy;Password=s3cret;"
SchemaQuench --ConnectionString:"Server=myserver;Port=3306;\
    Database=mydb;User=deploy;Password=s3cret;"
SchemaQuench --ConnectionString:"Server=myserver;Port=3306;\
    Database=mydb;User=deploy;Password=s3cret;"

When --ConnectionString is provided, all individual connection settings (Server, Port, User, Password, ConnectionProperties) are bypassed.

Windows authentication

SQL Server only. Leave both User and Password blank, or set IntegratedSecurity to true, and the tool connects using the identity of the process. PostgreSQL, MySQL, and MariaDB require explicit credentials.

Prefer IntegratedSecurity=true when you are layering environment-variable or command-line overrides over a settings file that already carries a User and Password: an override can add or change a value but cannot clear one — on Windows, setting an environment variable to empty deletes the variable rather than blanking it, so the file's credential would otherwise remain in force. The key lives in whichever connection section the tool owns — Target:IntegratedSecurity for SchemaQuench, Source:IntegratedSecurity for SchemaTongs and DataTongs — and is settable from any shell, for example SmithySettings_Target__IntegratedSecurity=true.

Under the default, a downgraded deploy still reports success

With UnsupportedFeaturePolicy left at warn, a deployment that dropped an unsupported aspect still completes and reports success. The receipt is the Unsupported Feature Downgrades section of the deployment summary — that is the place to check what was relaxed. Set fail when you would rather the run stop than deploy a reduced object. This is intended behavior, not a defect: it exists so you can see what happened and decide whether to retry against a newer target or accept the downgrade.

Sensitive value masking

Your credentials stay out of the logs. When a tool logs its resolved command-line switches and its active configuration at startup — and when SchemaQuench logs its product and template script tokens — it scrubs any value whose name matches a built-in sensitive-name set, so a log is safe to attach to a support ticket, paste into a CI artifact, or drop into a screenshot.

Switches are scrubbed on the same rules as configuration values: a --Target__Password=... or --ConnectionString=... renders its value as *** while the switch name still prints, so you can confirm what was passed without exposing it. See Startup configuration dump for the full startup output.

The default sensitive-name patterns (case-insensitive, substring match) are Password, Pwd, Secret, ApiKey, Token, ConnectionString, and Credential. A matched value renders as *** while its name still prints, so you can confirm the setting exists without exposing it. An embedded Password= / Pwd= inside a connection-string value is stripped even when the surrounding setting or token is not sensitively named — one leaked connection string is one too many.

A credential inside a URL is also masked. A value shaped like scheme://user:secret@host/path has its password replaced with ***, regardless of the setting name. The username, host, port, and path remain visible, so the log stays diagnosable.

All other values are logged as-is, so you can audit the active configuration from the log. Both exceptions match on the shape of the value rather than its name, so they apply even where you have not told SchemaSmith the value is sensitive.

Configuration:
    Server: myserver
    Port: 5432
    User: deploy
    Password: ***
    ConnectionProperties:
      SslMode: Prefer

Tuning the scrubbing

An optional LogHygiene block in any tool's *.settings.json tunes the behavior. With no block present, the defaults above apply.

"LogHygiene": {
  // Suppress the token-logging section entirely -- one notice line, no token
  // names and no values. For products with hundreds of tokens. Default: true.
  "LogTokens": true,

  // Scrub these exact token names too, beyond the default patterns.
  "ScrubTokens": [ "Handshake", "TenantSeed" ],

  // Extra contains-match patterns (the * is optional).
  "ScrubPatterns": [ "*Salt*", "*PrivateKey*" ],

  // Opt a false positive back out -- log this name verbatim even though it
  // matches a default pattern (e.g. a column literally named "Token").
  "AllowTokens": [ "PublicToken" ]
}

When a token name appears in both AllowTokens and a scrub rule, AllowTokens wins and the value is logged verbatim — but an embedded connection-string password is still stripped.

Failure artifacts and debug SQL

When any script fails during a SchemaQuench deployment — a user or migration script, a generated table-quench procedure, a product-level Before/After script, a validation script (BaselineValidationScript, VersionStampScript), or a data-delivery merge — SchemaQuench writes the exact token-expanded SQL the server rejected to a re-runnable .sql artifact file. Every surface reports it the same way in the progress log:

Unable to quench 'Before/01-seed.sql': Invalid column name 'Region'.
    Resolved SQL written to: C:\logs\SchemaQuench - Failed 01-seed prod.App.sql

Every artifact is a .sql file with a comment header (server/database/schema, the failing script name, the error message), every batch the engine received, separated by GO, with the last-attempted batch marked -- >>> FAILING BATCH (#N) >>>. The failing-batch marker is a best-effort hint — the engine marks the last batch it attempted, which is usually the one that caused the error.

Artifacts are raw by default — all token values are already expanded to their real values, so you can open the file, connect to the target, and reproduce the failure immediately without any further substitution.

Note

Artifacts land in the ArtifactPath directory (default: current working directory), not the log directory. This is intentional: raw artifacts may contain expanded sensitive values and should not be automatically swept into log archives or CI artifacts.

ArtifactPath

Directory where SchemaQuench writes resolved-SQL failure artifacts and generated-SQL debug files.

{ "ArtifactPath": "C:\\deploy\\debug" }
DefaultBehavior
(not set)Artifacts land in the current working directory (where SchemaQuench was launched).
A directory pathArtifacts land in the specified directory. Relative paths are resolved from the current working directory.

Setting ArtifactPath is useful when you want artifacts in a consistent location regardless of where SchemaQuench is invoked — a CI agent's workspace directory, for example, or a dedicated debug folder outside the log path. The directory is created automatically if it does not exist.

ScrubArtifacts

Controls whether sensitive values are redacted in failure artifacts before writing.

{ "ScrubArtifacts": true }
ValueBehavior
false (default)Artifacts contain real expanded values. Re-runnable immediately — open in a query tool and reproduce the failure without restoring secrets.
trueSensitive token values (names matching *Password*, *Secret*, *ApiKey*, *Token*, etc., per LogHygiene rules) and inline connection-string passwords are redacted to ***. Safe to attach to a CI artifact or support ticket. To reproduce, restore the real values first.

Leave ScrubArtifacts off for local debugging — raw artifacts are immediately re-runnable. Turn it on for CI environments or when attaching an artifact to a support ticket. For a step-by-step walkthrough of working a failed deployment from artifact to fix, see Logging.

Debug SQL files

When SchemaQuench runs one of its generated procedures against your target database, it dumps the exact SQL it sent to a companion .sql file — honoring ScrubArtifacts the same as any other failure artifact. If the procedure throws, the progress log surfaces the file path via the same Resolved SQL written to: line described above. Open it in your query tool of choice, re-run the SQL by hand, and reproduce or narrow the problem without guessing what SchemaSmith actually executed.

Generated procedures cover missing tables and columns, modified tables, indexes, foreign keys, materialized views, indexed views, and the table-JSON parse step. Debug files follow the pattern SchemaQuench - <operation> <server>.<database>.sql:

SchemaQuench - Quench Missing Tables And Columns prod-db.NorthwindClone.sql
SchemaQuench - Quench Modified Tables prod-db.NorthwindClone.sql
SchemaQuench - Quench Indexes prod-db.NorthwindClone.sql
SchemaQuench - Quench Foreign Keys prod-db.NorthwindClone.sql
SchemaQuench - Quench Materialized Views prod-db.NorthwindClone.sql
SchemaQuench - Quench Indexed Views prod-db.NorthwindClone.sql
SchemaQuench - Parse Table Json prod-db.NorthwindClone.sql

Each run overwrites the debug files for the operations it actually performed. Operations that don't apply to your platform (for example, indexed views on PostgreSQL or materialized views on MySQL) produce no file. Debug files land in the ArtifactPath directory (default: current working directory); --LogPath controls the progress and error logs, not artifact or debug SQL.

Drop-control settings

SchemaSmith removes database objects that no longer appear in the schema package — drop-by-absence — under a family of flags. Most of the Drop…RemovedFromProduct flags compose across tiers — environment → product → template — with the environment tier living here in SchemaQuench.settings.json as a deployment-wide guardrail, independent of any package. The column, foreign-key, check-constraint, exclude-constraint, statistics, index and period flags add a per-table tier; DropTablesRemovedFromProduct and DropUnknownIndexes stop at the template tier. Two sit outside that shape entirely and are covered in their own paragraphs below: DropPeriodsRemovedFromProduct skips the product and template tiers, and DropEventsRemovedFromProduct is environment-only. The PreventDrop row is not part of that cascade: this environment-level form has no Product.json or Template.json tier. The per-table PreventDrop, set in a table's own .json, is a separate control — see below.

SettingDefaultApplies to
DropTablesRemovedFromProducttrueAll platforms
DropColumnsRemovedFromProducttrueAll platforms
DropForeignKeysRemovedFromProducttrueAll platforms
DropCheckConstraintsRemovedFromProducttrueTable-level checks, all platforms
DropExcludeConstraintsRemovedFromProducttruePostgreSQL only
DropStatisticsRemovedFromProducttrueSQL Server + PostgreSQL
DropIndexesRemovedFromProducttrueProduct-owned indexes, all platforms
DropEventsRemovedFromProductfalseScheduled events, MySQL and MariaDB only
DropPeriodsRemovedFromProductfalseApplication-time periods, MariaDB only
DropUnknownIndexesfalseOut-of-band indexes, all platforms
PreventDropfalseSuppresses every by-absence drop for the run, all platforms

Each flag is also settable as an environment variable using the standard prefix — SmithySettings_<FlagName>, for example SmithySettings_DropUnknownIndexes.

DropPeriodsRemovedFromProduct departs from that pattern on both counts. It resolves environment → table — there is no product or template tier — and it defaults to false. That off default is deliberate: a package with no Periods entry is not necessarily asserting that the table has no period. Packages written before periods were supported have no such entry, and neither does one extracted from a MariaDB version whose catalog cannot report periods at all. Defaulting to drop would delete a period on the strength of a silence that means nothing. Turn the flag on when your package is genuinely the authority on the table's periods, or set it on a single table to override the environment setting for that table alone. Dropping a period does not touch your data — the columns it spanned keep everything in them.

DropEventsRemovedFromProduct is the other Drop…RemovedFromProduct flag that defaults to false, and it departs further still: it is a single environment tier. Events live in a template’s Events/ folder rather than under a table, so there is no per-table level to cascade from. It is not a package property and appears in no generated .json-schemas file, so leaving it in a Product.json or Template.json has no effect on a deploy but makes --Validate report an unexpected property (SS-JSON-001, Error) and exit 2. Event removal reaches only events SchemaSmith created — one made by hand, or by a scripted Events/ file, is never touched.

A false at any tier is sticky: it locks the effective value to false for all lower tiers and cannot be re-enabled by a more-specific setting. Absent inherits from the tier above; a true at a lower tier overrides an inherited true but never an ancestor's explicit false. For the flags that expose a per-table tier, a table's own .json can only tighten (set its own false to protect its objects); it can never re-enable a drop a higher tier suppressed.

The environment tier for DropUnknownIndexes is new in this release — previously it was settable only in Product.json and Template.json. Its default is false at every tier, so index-drop-by-absence stays off unless you opt in.

PreventDrop is the blanket. Set it true here (or via SmithySettings_PreventDrop) and the environment never drops an object for being absent from the product — every pass above is suppressed for the whole run, regardless of what any package, template, or table declares. The run still completes normally with exit code 0: SchemaQuench applies every additive and modifying change, logs each drop it withheld, and itemizes them in the deployment summary's preventDrop manifest. Only removal by absence is held back — an object that is still declared but must be dropped and recreated to apply a change reconciles as usual.

A separate per-table PreventDrop boolean, set in a table's .json, protects one named table. Its persistence is a different mechanism from the cascade's sticky false above: SchemaSmith stores the marker in its ownership tracking inside the database, so the protection survives the table leaving the package entirely. For what each flag governs, both PreventDrop layers, the recyclebin-friendly removal pattern, and per-platform behavior, see Drop control and the SchemaQuench reference.

SchemaQuench runtime controls

Forcing a re-kindle

SchemaSmith records a content-hash stamp of the helper procedures and tables it installs in each target database, and skips the re-install on later runs when nothing has changed — so a normal deployment pays the install cost only when the tooling actually moves. ForceReKindle (default false) overrides that skip and re-installs the helper objects unconditionally — handy after a manual edit to the helpers, when diagnosing a deploy problem, or any time you want a known-good baseline regardless of stamp state.

Set it in SchemaQuench.settings.json, or pass --ForceReKindle on the command line (presence enables it, no value needed); when both are present the CLI switch wins. If you can't change the configuration or CLI invocation, dropping the SchemaSmith.KindleStamp marker table (SchemaSmith_KindleStamp on MySQL and MariaDB) has the same effect — the gate sees the missing stamp on the next run and re-installs.

Selective deployment scope

SchemaQuench can narrow a run to a subset of its targets without editing the package. The Target block takes Templates, Databases, and Schemas arrays that filter the discovered universe (the three dimensions AND together); TemplateTargets overrides discovery per template and can provision missing databases or schemas via CreateIfMissing. Both live in SchemaQuench.settings.json — see the SchemaQuench reference for the full behavior.

Dropping a schema-bound blocker SQL Server

SQL Server refuses to alter a column while a schema-bound module — a view or function created WITH SCHEMABINDING — references it, failing with error 4922, which names neither the module nor what to do about it. DropSchemaBoundDependents decides what happens next: it is SQL Server only, defaults to false, and composes environment → product → template. Left off, SchemaSmith reports the same refusal but names the blocking module, the column it blocks, and the remedy. Turned on, it drops the module, applies the column change, and recreates the module afterwards.

The recreate comes from your package, not from a copy of what was on the server — SchemaSmith does not save and replay the definition it found there. Your package is the authority on what the module should be; the copy on the server is only whatever happens to be deployed. That is why the script has to run after the table work: put schema-bound modules in a folder assigned to the AfterTablesObjects slot. One consequence to plan for: a dropped view or function loses every GRANT on it, and SchemaSmith does not put them back — it does not manage permissions on any object, so it has nothing to restore them from. Re-grant on the recreated module.

Rebuilding a table instead of altering it

RebuildPolicy controls when SchemaQuench replaces a table wholesale — building it fresh, copying the rows across, swapping it in — rather than altering in place. It is off by default, so a table that does not ask for a rebuild never gets one. Two situations make a rebuild the better move. Cost: a wide table with several pending column-type changes pays one table rewrite per change, and a rebuild collapses them into a single copy. Column order: reordering existing columns is not something any supported engine can do in place, so a rebuild is the only mechanism that can make a deployed table match the order its package declares. The environment tier sets it with three flat keys in SchemaQuench.settings.jsonRebuildPolicyMode, RebuildPolicyThreshold and RebuildPolicyOnOrderMismatch; Product.json, Template.json and a table's own .json each take a RebuildPolicy object instead. These levels replace rather than blend — deliberately unlike the drop-control cascade above. The most specific level that declares a policy defines it whole, so a table declaring { "Mode": "ALWAYS" } does not pick up a product-level threshold; a table that declares nothing inherits the nearest level that does, and only when no level declares one at all is the effective policy { "Mode": "NEVER" }.

Altering a column on a system-versioned table MariaDB

On MariaDB this is not an ordinary alter. The engine refuses it outright unless @@system_versioning_alter_history is KEEP — and KEEP does not merely permit the change, it applies the change to the stored history as well. Rows recorded years ago are rewritten into the new shape, so the history stops being a record of what the table actually looked like at the time. That is a decision about data retention rather than a detail of syntax, so SchemaSmith will not make it for you: SystemVersioningAlterHistory — an environment setting in SchemaQuench.settings.json, not a package property, so it does not belong in Product.json, Template.json or a table's .json, where the generated schemas now reject it — left unset keeps the engine's refusal, and KEEP lets the change proceed with the history rewritten. Leaving it unset costs nothing on a healthy deploy — the refusal fires only when a change genuinely requires rewriting history, never on a re-deploy where the table already matches its definition. If you would rather not rewrite it, drop system versioning, make the change, and re-enable it, accepting the gap deliberately instead of discovering it later.

Quick Reference

Common invocation patterns for the scenarios above. Copy, paste, adjust the server and credential values for your target.

# Run with defaults (settings file in current directory or tool directory)
SchemaQuench

# Custom config file
SchemaQuench --ConfigFile:staging.json

# Custom log directory
SchemaQuench --LogPath:C:\Logs\quench

# Override connection via environment
$env:SmithySettings_Target__Server = "prod-db"
$env:SmithySettings_Target__User = "deploy"
$env:SmithySettings_Target__Password = "s3cret"
SchemaQuench

# Override connection via CLI (platform-appropriate connection string)
SchemaQuench --ConnectionString:"Host=prod-db;`
    Database=mydb;Username=deploy;Password=s3cret;"

# Force a full helper re-kindle (re-install SchemaSmith objects)
SchemaQuench --ForceReKindle

# Environment guardrail: never drop unknown indexes
$env:SmithySettings_DropUnknownIndexes = "false"
# Run with defaults (settings file in current directory or tool directory)
SchemaQuench

# Custom config file
SchemaQuench --ConfigFile:staging.json

# Custom log directory
SchemaQuench --LogPath:/var/log/quench

# Override connection via environment
export SmithySettings_Target__Server=prod-db
export SmithySettings_Target__User=deploy
export SmithySettings_Target__Password=s3cret
SchemaQuench

# Override connection via CLI (platform-appropriate connection string)
SchemaQuench --ConnectionString:"Host=prod-db;\
    Database=mydb;Username=deploy;Password=s3cret;"

# Force a full helper re-kindle (re-install SchemaSmith objects)
SchemaQuench --ForceReKindle

# Environment guardrail: never drop unknown indexes
export SmithySettings_DropUnknownIndexes=false

Hands-on lab

Wire SchemaSmith into a CI/CD pipeline, with environment variables for credentials and settings files for defaults.

Start the CI/CD lab

Shape. Strengthen. Succeed.

Free schema-as-code for SQL Server, PostgreSQL, MySQL, and MariaDB.

The source is on GitHub for anyone to read.

Get Started on GitHub