@@ -78,256 +78,3 @@ Most PSql.Deploy cmdlets accept a -Target parameter to specify one or more
7878target databases on which to operate.
7979
8080TODO: Expand
81-
82-
83- Migrations
84- ----------
85-
86- A migration is a SQL script that evolves a target database's schema from its
87- current version to the next version.
88-
89- Each subdirectory of {source-directory}\Migrations containing a _Main.sql file
90- is a migration. The name of the subdirectory is the name of the migration.
91- The _Main.sql file is the entry point for the migration.
92-
93- PSql.Deploy's Invoke-SqlMigrations cmdlet applies (runs) each migration exactly
94- once per target database. After applying a migration, the cmdlet records the
95- migration's state in a table in the target database. Future invocations of
96- the cmdlet for the same target database will read the table and skip any
97- migrations that have already been applied. Thus the Invoke-SqlMigrations
98- cmdlet is idempotent: invoked multiple times against the same target database,
99- the cmdlet applies only new migrations and does nothing to a fully-migrated
100- database.
101-
102- The Get-SqlMigrations cmdlet lists the migrations found in a source directory
103- or applied to a target database.
104-
105- When multiple migrations are present in the source directory but not yet
106- applied to a target database, The Invoke-SqlMigrations cmdlet applies them in a
107- specific order: from least to greatest by case-insensitive ordinal comparison
108- of the migrations' names. It is therefore important to choose a migration
109- naming scheme carefully to fit the development workflow of the database. See
110- below for examples of migration naming schemes.
111-
112- Once applied to a target database, a migration should be considered immutable.
113- The Invoke-SqlMigrations cmdlet computes a hash of all .sql files within the
114- migration directory and subdirectories, recursively. The cmdlet stores this
115- hash as part of the migration state in the target database. If a subsequent
116- invocation finds that the computed hash is different from the stored hash, the
117- cmdlet detects that the migration has been modified after application. The
118- cmdlet then reports a validation error and aborts without applying any
119- migrations. To resolve the error, either revert the changes to the migration,
120- or update the hash value stored in all target databases (not recommended).
121-
122- Migrations named _Begin and _End, if present, are 'pseudo-migrations' that
123- receive special treatment. Invoke-SqlMigrations executes _Begin before any
124- other migrations and _End after all other migrations. Furthermore, the cmdlet
125- does not record the state of these migrations in the target database. They
126- execute every time the cmdlet runs against a target database and has at least
127- one other migration to apply. _Begin is useful for connection setup tasks such
128- as setting the transaction isolation level. _End is a good place to perform
129- cleanup that must occur after every migration session.
130-
131- PSql.Deploy supports 'up', or forward, migrations only. It does not support
132- 'down' migrations that would revert the database to some prior schema. Many
133- migration activities are inherently destructive -- dropping a table, for
134- example. The work to make those activities reversible is often much more
135- complex than the work of the activities themselves, all to enable a feature
136- that in practice is seldom used. Thus PSql.Deploy takes the position that
137- 'down' migrations are not worth the effort. If a database schema must revert
138- to a prior state, then author a new migration that makes the necessary changes,
139- or restore the database from a backup. Use automated testing to find migration
140- problems early, before they reach production.
141-
142- Phases
143-
144- To facilitate zero- and low-downtime deployments, PSql.Deploy splits deployment
145- into three logical phases: Pre, Core, and Post. Each phase represents a
146- different moment within a deployment process. Each SQL statement within a
147- migration executes in exactly one of these phases. One migration can contain
148- statements for multiple phases. The purposes of the phases are as follows:
149-
150- Pre
151- This phase occurs before deployment of the workloads that use the target
152- database, perhaps while previously deployed workloads are still running.
153- Migration statements in this phase should make the database compatible with
154- the upcoming workload deployment but should retain compatibility with any
155- existing workloads.
156-
157- Core
158- This phase occurs at some arbitrary point between the Pre and Post phases.
159- PSql.Deploy interprets statements within this phase as requiring downtime,
160- explicitly breaking a zero-downtime deployment schenario. Migration
161- statements in this phase should make the database compatible with newly
162- deployed workloads and need not retain compatibility with any previously
163- deployed workloads. This phase is also suitable for deployment scenarios
164- where downtime is not a concern.
165-
166- Post
167- This phase occurs after deployment of the workloads that use the target
168- database, while those workloads are running. Migration statements in this
169- phase should finalize or clean up after the changes made in earlier phases
170- while retaining compatibility with the deployed workloads.
171-
172- Dependencies
173-
174- PSql.Deploy enables one migration to declare a dependency on another migration.
175- The Invoke-SqlMigrations cmdlet handles a dependency in one of two ways. If
176- the required migration has been applied fully to a target database, then the
177- dependency is satisfied already, and the cmdlet applies the depending migration
178- normally to that target database. If neither the required migration nor the
179- requiring migration has been applied yet to a target database, then the cmdlet
180- moves Pre and Post statements into the Core phase as required to satisfy the
181- dependency and preserve the guarantees that the cmdlet makes about the order of
182- migration statements. Note that this makes dependency resolution incompatible
183- with zero-downtime deployment scenarios, because Core entails downtime.
184-
185- Ordering Guarantees
186-
187- The Invoke-SqlMigrations cmdlet makes the following guarantees about the order
188- of migration statement execution:
189-
190- - A migration's Pre-phase statements are guaranteed to execute after all
191- previous migrations' Pre-phase statements.
192-
193- - A migration's Core-phase statements are guaranteed to execute after all
194- previous migrations' Core-phase statements.
195-
196- - A migration's Post-phase statements are guaranteed to execute after all
197- previous migrations' Post-phase statements.
198-
199- - If migration B depends on migration A, then A's Post-phase statements are
200- guaranteed to execute before B's Pre-phase statements.
201-
202- Example 1: No dependencies
203-
204- These migrations:
205- 1 │ Pre Core Post
206- 2 │ Pre Core Post
207- 3 │ Pre Core Post
208- 4 │ Pre Core Post
209- 5 │ Pre Core Post
210-
211- Yield this order operations:
212- │ Pre │ Core │ Post │
213- ══╪════════════════════╪══════════════════════════╪══════════════════════════╡
214- 1 │ Pre │ Core │ Post │
215- 2 │ Pre │ Core │ Post │
216- 3 │ Pre │ Core │ Post │
217- 4 │ Pre │ Core │ Post │
218- 5 │ Pre │ Core │ Post │
219- Time──>
220-
221- Example 2: One dependency
222-
223- These migrations:
224- 1 │ Pre Core Post
225- 2 │ Pre Core Post <──╮
226- 3 │ Pre Core Post │
227- 4 │ Pre Core Post ───╯ Migration 4 depends on Migration 2
228- 5 │ Pre Core Post
229-
230- Yield this order operations:
231- │ Pre │ Core │ Post │
232- ══╪═════════════╪════════════════════════════════════════════╪════════════════╡
233- 1 │ Pre │ Core Post │ │
234- 2 │ Pre │ Core ^^^^ Post │ │
235- 3 │ Pre │ Core ^^^^ │ Post │
236- 4 │ │ Pre Core │ Post │
237- 5 │ │ ^^^ Pre Core │ Post │
238- Time──> ^^^
239-
240- Magic Comments
241-
242- A migration may contain 'magic comments', which are special comments that the
243- Invoke-SqlMigrations cmdlet interprets as directives. The cmdlet supports the
244- following magic comments:
245-
246- --# PRE
247- Causes subsequent SQL text to execute in the Pre phase.
248-
249- --# CORE
250- Causes subsequent SQL text to execute in the Core phase.
251-
252- --# POST
253- Causes subsequent SQL text to execute in the Post phase.
254-
255- --# REQUIRES: <migration-name> ...
256- Declares a dependency on one or more migrations.
257-
258- Example Migration Naming Schemes
259-
260- Here are some example migration naming schemes:
261-
262- - Numeric, zero-padded: 0001, 0002, 0003, ...
263- Pros: simple
264- Cons: requires coordination if multiple people author migrations
265- concurrently; requires new scheme if next number exceeds planned width
266- Best for: solo projects or teams with a single migration author where the
267- versioning scheme is unknown or subject to change
268-
269- - Version, zero-padded: v01.00.00, v01.00.01, v01.01.00, v02.00.00, ...
270- Pros: descriptive; aligned with versioning
271- Cons: requires coordination if multiple people author migrations
272- concurrently; tricky if a version number exceeds planned width
273- Best for: solo projects or teams with a single migration author where the
274- versioning scheme is known and unlikely to change
275-
276- - Date and description: 2025-08-15-Initial, 2025-08-18-AddUsersTable, ...
277- Pros: descriptive; requires less coordination for multiple authors
278- Cons: need to keep migration name up-to-date during authoring
279- Best for: teams with multiple migration authors
280-
281- - Date and work item number: 2025-08-15-00042, 2025-08-18-00123, ...
282- Pros: requires less coordination for multiple authors
283- Cons: need to keep migration name up-to-date during authoring; requires
284- knowing work item numbers
285- Best for: teams with multiple migration authors where it is useful to
286- associate migrations with work items, pull requests, etc.
287-
288- Squashes
289-
290- A long-lived project with frequent schema changes will accumulate a large
291- number of migrations over time. For such projects, it is sometimes useful to
292- delete old migrations and instead use a create script that creates a database
293- already migrated to that poitn in time. This is known as a 'squash'. Create
294- scripts are outside the scope of PSql.Deploy, but PSql.Deploy supports squashes
295- by not caring about completed migrations that are no longer present in the
296- source directory. Nevertheless, there are two rules that must be oserved when
297- performing a squash:
298-
299- 1. The oldest unsquashed migration must be same age or older than the oldest
300- backup that is likely to be restored.
301-
302- 1. The oldest unsquashed migration must be same age or older than the create
303- script.
304-
305- TODO: Describe this better when more brain cells are available.
306-
307-
308- Seeds
309- -----
310-
311- A seed is a SQL script that populates a target database with data.
312-
313- Each subdirectory of {source-directory}\Seeds containing a _Main.sql file is a
314- seed. The name of the subdirectory is the name of the seed. The _Main.sql
315- file is the entry point for the seed.
316-
317- Seeds support several magic comments for organizing code into modules with dependencies:
318-
319- --# MODULE: name [topic ...]
320- Starts a new module with the specified name, optionally declaring provided
321- topics.
322-
323- --# PROVIDES: topic [topic ...]
324- Indicates that the current module provides the specified topics.
325-
326- --# REQUIRES: topic [topic ...]
327- Indicates that the current module requires the specified topics.
328-
329- --# WORKER: all|any
330- Specifies worker execution mode. 'all' means the module executes on all
331- workers; 'any' means it executes on any single worker.
332-
333- TODO: Describe seeds here.
0 commit comments