en-US/about_PSql_Deploy.help.txt
about_PSql_Deploy ================= SHORT DESCRIPTION PSql.Deploy is a migration and seeding system for SQL Server and Azure SQL Database. LONG DESCRIPTION The PSql.Deploy module provides cmdlets to deal with two core concepts: migrations and seeds. A migration is a SQL script that modifies the schema of a target database. A seed is a SQL script that populates a target database with data. Together, migrations and seeds provide a mechanism to perform database deployments in a controlled and repeatable manner. Source Directory Structure PSql.Deploy expects migrations and seeds to have a specific filesystem layout. src\ The source directory: a set of migrations and │ seeds for one database design. The name can vary. │ ├─ Migrations\ Migrations. Required only if there are any migrations. │ │ │ ├─ 0001\ One migration. The name can vary. │ │ │ │ │ ├─ _Main.sql Top-level script for the migration. It can │ │ │ include other files with the :r directive. │ │ ├─ FileA.sql Example file included by _Main.sql. │ │ ├─ FileB.sql Example file included by _Main.sql. │ │ └─ ... More files, subdirectories, etc. │ │ │ └─ ... More migrations. │ ├─ Seeds\ Seeds. Required only if there are any seeds. │ │ │ ├─ TestData\ One seed. The name can vary. │ │ │ │ │ ├─ _Main.sql Top-level script for the migration. It can │ │ │ include other files with the :r directive. │ │ ├─ FileA.sql Example file included by _Main.sql. │ │ ├─ FileB.sql Example file included by _Main.sql. │ │ └─ ... More files, subdirectories, etc. │ │ │ └─ ... More migrations. │ └─ ... PSql.Deploy does not care about other files or directories present in the source directory. See the sections below for more details about migrations and seeds. TODO: SQLCMD mode support. GO Breaks the script into batches. $(name) Replaced by the value of the variable 'name' in the migration or seed. :r <file> Include the contents of the specified file in the migration. The variable $(Path) is available in the included file and expands to the path of the migration or seed file that includes the file. :setvar <name> <value> Set a variable for the duration of the migration or seed. The variable $(name) is available in the included file and expands to the value set by this directive. TODO: General magic comments description. Target Parameter ---------------- Most PSql.Deploy cmdlets accept a -Target parameter to specify one or more target databases on which to operate. TODO: Expand Migrations ---------- A migration is a SQL script that evolves a target database's schema from its current version to the next version. Each subdirectory of {source-directory}\Migrations containing a _Main.sql file is a migration. The name of the subdirectory is the name of the migration. The _Main.sql file is the entry point for the migration. PSql.Deploy's Invoke-SqlMigrations cmdlet applies (runs) each migration exactly once per target database. After applying a migration, the cmdlet records the migration's state in a table in the target database. Future invocations of the cmdlet for the same target database will read the table and skip any migrations that have already been applied. Thus the Invoke-SqlMigrations cmdlet is idempotent: invoked multiple times against the same target database, the cmdlet applies only new migrations and does nothing to a fully-migrated database. The Get-SqlMigrations cmdlet lists the migrations found in a source directory or applied to a target database. When multiple migrations are present in the source directory but not yet applied to a target database, The Invoke-SqlMigrations cmdlet applies them in a specific order: from least to greatest by case-insensitive ordinal comparison of the migrations' names. It is therefore important to choose a migration naming scheme carefully to fit the development workflow of the database. See below for examples of migration naming schemes. Once applied to a target database, a migration should be considered immutable. The Invoke-SqlMigrations cmdlet computes a hash of all .sql files within the migration directory and subdirectories, recursively. The cmdlet stores this hash as part of the migration state in the target database. If a subsequent invocation finds that the computed hash is different from the stored hash, the cmdlet detects that the migration has been modified after application. The cmdlet then reports a validation error and aborts without applying any migrations. To resolve the error, either revert the changes to the migration, or update the hash value stored in all target databases (not recommended). Migrations named _Begin and _End, if present, are 'pseudo-migrations' that receive special treatment. Invoke-SqlMigrations executes _Begin before any other migrations and _End after all other migrations. Furthermore, the cmdlet does not record the state of these migrations in the target database. They execute every time the cmdlet runs against a target database and has at least one other migration to apply. _Begin is useful for connection setup tasks such as setting the transaction isolation level. _End is a good place to perform cleanup that must occur after every migration session. PSql.Deploy supports 'up', or forward, migrations only. It does not support 'down' migrations that would revert the database to some prior schema. Many migration activities are inherently destructive -- dropping a table, for example. The work to make those activities reversible is often much more complex than the work of the activities themselves, all to enable a feature that in practice is seldom used. Thus PSql.Deploy takes the position that 'down' migrations are not worth the effort. If a database schema must revert to a prior state, then author a new migration that makes the necessary changes, or restore the database from a backup. Use automated testing to find migration problems early, before they reach production. Phases To facilitate zero- and low-downtime deployments, PSql.Deploy splits deployment into three logical phases: Pre, Core, and Post. Each phase represents a different moment within a deployment process. Each SQL statement within a migration executes in exactly one of these phases. One migration can contain statements for multiple phases. The purposes of the phases are as follows: Pre This phase occurs before deployment of the workloads that use the target database, perhaps while previously deployed workloads are still running. Migration statements in this phase should make the database compatible with the upcoming workload deployment but should retain compatibility with any existing workloads. Core This phase occurs at some arbitrary point between the Pre and Post phases. PSql.Deploy interprets statements within this phase as requiring downtime, explicitly breaking a zero-downtime deployment schenario. Migration statements in this phase should make the database compatible with newly deployed workloads and need not retain compatibility with any previously deployed workloads. This phase is also suitable for deployment scenarios where downtime is not a concern. Post This phase occurs after deployment of the workloads that use the target database, while those workloads are running. Migration statements in this phase should finalize or clean up after the changes made in earlier phases while retaining compatibility with the deployed workloads. Dependencies PSql.Deploy enables one migration to declare a dependency on another migration. The Invoke-SqlMigrations cmdlet handles a dependency in one of two ways. If the required migration has been applied fully to a target database, then the dependency is satisfied already, and the cmdlet applies the depending migration normally to that target database. If neither the required migration nor the requiring migration has been applied yet to a target database, then the cmdlet moves Pre and Post statements into the Core phase as required to satisfy the dependency and preserve the guarantees that the cmdlet makes about the order of migration statements. Note that this makes dependency resolution incompatible with zero-downtime deployment scenarios, because Core entails downtime. Ordering Guarantees The Invoke-SqlMigrations cmdlet makes the following guarantees about the order of migration statement execution: - A migration's Pre-phase statements are guaranteed to execute after all previous migrations' Pre-phase statements. - A migration's Core-phase statements are guaranteed to execute after all previous migrations' Core-phase statements. - A migration's Post-phase statements are guaranteed to execute after all previous migrations' Post-phase statements. - If migration B depends on migration A, then A's Post-phase statements are guaranteed to execute before B's Pre-phase statements. Example 1: No dependencies These migrations: 1 │ Pre Core Post 2 │ Pre Core Post 3 │ Pre Core Post 4 │ Pre Core Post 5 │ Pre Core Post Yield this order operations: │ Pre │ Core │ Post │ ══╪════════════════════╪══════════════════════════╪══════════════════════════╡ 1 │ Pre │ Core │ Post │ 2 │ Pre │ Core │ Post │ 3 │ Pre │ Core │ Post │ 4 │ Pre │ Core │ Post │ 5 │ Pre │ Core │ Post │ Time──> Example 2: One dependency These migrations: 1 │ Pre Core Post 2 │ Pre Core Post <──╮ 3 │ Pre Core Post │ 4 │ Pre Core Post ───╯ Migration 4 depends on Migration 2 5 │ Pre Core Post Yield this order operations: │ Pre │ Core │ Post │ ══╪═════════════╪════════════════════════════════════════════╪════════════════╡ 1 │ Pre │ Core Post │ │ 2 │ Pre │ Core ^^^^ Post │ │ 3 │ Pre │ Core ^^^^ │ Post │ 4 │ │ Pre Core │ Post │ 5 │ │ ^^^ Pre Core │ Post │ Time──> ^^^ Magic Comments A migration may contain 'magic comments', which are special comments that the Invoke-SqlMigrations cmdlet interprets as directives. The cmdlet supports the following magic comments: --# PRE Causes subsequent SQL text to execute in the Pre phase. --# CORE Causes subsequent SQL text to execute in the Core phase. --# POST Causes subsequent SQL text to execute in the Post phase. --# REQUIRES: <migration-name> ... Declares a dependency on one or more migrations. Example Migration Naming Schemes Here are some example migration naming schemes: - Numeric, zero-padded: 0001, 0002, 0003, ... Pros: simple Cons: requires coordination if multiple people author migrations concurrently; requires new scheme if next number exceeds planned width Best for: solo projects or teams with a single migration author where the versioning scheme is unknown or subject to change - Version, zero-padded: v01.00.00, v01.00.01, v01.01.00, v02.00.00, ... Pros: descriptive; aligned with versioning Cons: requires coordination if multiple people author migrations concurrently; tricky if a version number exceeds planned width Best for: solo projects or teams with a single migration author where the versioning scheme is known and unlikely to change - Date and description: 2025-08-15-Initial, 2025-08-18-AddUsersTable, ... Pros: descriptive; requires less coordination for multiple authors Cons: need to keep migration name up-to-date during authoring Best for: teams with multiple migration authors - Date and work item number: 2025-08-15-00042, 2025-08-18-00123, ... Pros: requires less coordination for multiple authors Cons: need to keep migration name up-to-date during authoring; requires knowing work item numbers Best for: teams with multiple migration authors where it is useful to associate migrations with work items, pull requests, etc. Squashes A long-lived project with frequent schema changes will accumulate a large number of migrations over time. For such projects, it is sometimes useful to delete old migrations and instead use a create script that creates a database already migrated to that poitn in time. This is known as a 'squash'. Create scripts are outside the scope of PSql.Deploy, but PSql.Deploy supports squashes by not caring about completed migrations that are no longer present in the source directory. Nevertheless, there are two rules that must be oserved when performing a squash: 1. The oldest unsquashed migration must be same age or older than the oldest backup that is likely to be restored. 1. The oldest unsquashed migration must be same age or older than the create script. TODO: Describe this better when more brain cells are available. Seeds ----- A seed is a SQL script that populates a target database with data. Each subdirectory of {source-directory}\Seeds containing a _Main.sql file is a seed. The name of the subdirectory is the name of the seed. The _Main.sql file is the entry point for the seed. Seeds support several magic comments for organizing code into modules with dependencies: --# MODULE: name [topic ...] Starts a new module with the specified name, optionally declaring provided topics. --# PROVIDES: topic [topic ...] Indicates that the current module provides the specified topics. --# REQUIRES: topic [topic ...] Indicates that the current module requires the specified topics. --# WORKER: all|any Specifies worker execution mode. 'all' means the module executes on all workers; 'any' means it executes on any single worker. TODO: Describe seeds here. |