Lightweight 0.20260303.0
Loading...
Searching...
No Matches
SqlMigration.hpp
1// SPDX-License-Identifier: Apache-2.0
2
3#pragma once
4
5#include "Api.hpp"
6#include "DataMapper/DataMapper.hpp"
7#include "SqlQuery/Migrate.hpp"
8#include "SqlSchema.hpp"
9#include "SqlTransaction.hpp"
10
11#include <functional>
12#include <list>
13#include <map>
14#include <optional>
15#include <vector>
16
17namespace Lightweight
18{
19
20class SqlConnection;
21
22/// @defgroup SqlMigration SQL Migration
23/// @brief Classes and functions for SQL schema migrations.
24
25namespace SqlMigration
26{
27 class MigrationBase;
28
29 /// Represents a unique timestamp of a migration.
30 ///
31 /// This struct is used to identify migrations and is used as a key in the migration history table.
32 ///
33 /// Note, a recommended format for the timestamp is a human readable format like YYYYMMDDHHMMSS
34 ///
35 /// @code
36 /// MigrationTimestamp { 2026'01'17'00'31'20 };
37 /// @endcode
38 ///
39 /// @ingroup SqlMigration
41 {
42 /// The numeric timestamp value identifying the migration.
43 uint64_t value {};
44
45 /// Three-way comparison operator.
46 constexpr std::weak_ordering operator<=>(MigrationTimestamp const& other) const noexcept = default;
47 };
48
49 /// Result of verifying a migration's checksum.
50 ///
51 /// @ingroup SqlMigration
53 {
54 MigrationTimestamp timestamp; ///< The timestamp of the verified migration.
55 std::string_view title; ///< The title of the verified migration.
56 std::string storedChecksum; ///< The checksum stored in the database.
57 std::string computedChecksum; ///< The checksum computed from the current migration definition.
58 bool matches; ///< Whether the stored and computed checksums match.
59 };
60
61 /// Result of reverting multiple migrations.
62 ///
63 /// @ingroup SqlMigration
65 {
66 std::vector<MigrationTimestamp> revertedTimestamps; ///< Successfully reverted migrations
67 std::optional<MigrationTimestamp> failedAt; ///< Migration that failed, if any
68 std::string errorMessage; ///< Error message if failed
69 };
70
71 /// Status summary of migrations.
72 ///
73 /// @ingroup SqlMigration
75 {
76 size_t appliedCount {}; ///< Number of migrations that have been applied
77 size_t pendingCount {}; ///< Number of migrations waiting to be applied
78 size_t mismatchCount {}; ///< Number of applied migrations with checksum mismatches
79 size_t unknownAppliedCount {}; ///< Number of applied migrations not found in registered list
80 size_t totalRegistered {}; ///< Total number of registered migrations
81 };
82
83 /// Associates a software release with the highest migration timestamp present at release time.
84 ///
85 /// Releases are declared in source (typically a migration plugin) via
86 /// `LIGHTWEIGHT_SQL_RELEASE(version, highestTimestamp)`. They let tools answer questions like
87 /// "which migrations belong to release 6.7.0?" or "roll back everything applied after 6.7.0".
88 ///
89 /// The `highestTimestamp` is an inclusive upper bound: a migration `M` belongs to the release
90 /// iff `prev_release_ts < M.timestamp <= highestTimestamp`, where `prev_release_ts` is the
91 /// previous release's timestamp (or 0 if there is none).
92 ///
93 /// @ingroup SqlMigration
95 {
96 /// Human-readable version string, e.g. "6.7.0".
97 std::string version;
98 /// Highest migration timestamp contained in this release (inclusive).
100 };
101
102 /// Main API to use for managing SQL migrations
103 ///
104 /// This class is a singleton and can be accessed using the GetInstance() method.
105 ///
106 /// @ingroup SqlMigration
108 {
109 public:
110 /// Type alias for a list of migration pointers.
111 using MigrationList = std::list<MigrationBase const*>;
112
113 /// Get the singleton instance of the migration manager.
114 ///
115 /// @return Reference to the migration manager.
116 /// Get the singleton instance of the migration manager.
117 ///
118 /// @return Reference to the migration manager.
119 LIGHTWEIGHT_API static MigrationManager& GetInstance();
120
121 /// Add a migration to the manager.
122 ///
123 /// @param migration Pointer to the migration to add.
124 LIGHTWEIGHT_API void AddMigration(MigrationBase const* migration);
125
126 /// Get all migrations that have been added to the manager.
127 ///
128 /// @return List of migrations.
129 [[nodiscard]] LIGHTWEIGHT_API MigrationList const& GetAllMigrations() const noexcept;
130
131 /// Get a migration by timestamp.
132 ///
133 /// @param timestamp Timestamp of the migration to get.
134 /// @return Pointer to the migration if found, nullptr otherwise.
135 [[nodiscard]] LIGHTWEIGHT_API MigrationBase const* GetMigration(MigrationTimestamp timestamp) const noexcept;
136
137 /// Remove all migrations from the manager.
138 ///
139 /// This function is useful if the migration manager should be reset.
140 LIGHTWEIGHT_API void RemoveAllMigrations();
141
142 /// Get all migrations that have not been applied yet.
143 ///
144 /// @return List of pending migrations.
145 [[nodiscard]] LIGHTWEIGHT_API std::list<MigrationBase const*> GetPending() const noexcept;
146
147 /// Callback type invoked during migration execution to report progress.
149 std::function<void(MigrationBase const& /*migration*/, size_t /*current*/, size_t /*total*/)>;
150
151 /// Apply a single migration from a migration object.
152 ///
153 /// @param migration Pointer to the migration to apply.
154 LIGHTWEIGHT_API void ApplySingleMigration(MigrationBase const& migration);
155
156 /// Revert a single migration from a migration object.
157 ///
158 /// @param migration Pointer to the migration to revert.
159 LIGHTWEIGHT_API void RevertSingleMigration(MigrationBase const& migration);
160
161 /// Apply all migrations that have not been applied yet.
162 ///
163 /// @param feedbackCallback Callback to be called for each migration.
164 /// @return Number of applied migrations.
165 LIGHTWEIGHT_API size_t ApplyPendingMigrations(ExecuteCallback const& feedbackCallback = {});
166
167 /// Create the migration history table if it does not exist.
168 LIGHTWEIGHT_API void CreateMigrationHistory();
169
170 /// Get all applied migration IDs.
171 ///
172 /// @return Vector of applied migration IDs.
173 [[nodiscard]] LIGHTWEIGHT_API std::vector<MigrationTimestamp> GetAppliedMigrationIds() const;
174
175 /// Get the data mapper used for migrations.
176 [[nodiscard]] LIGHTWEIGHT_API DataMapper& GetDataMapper();
177
178 /// Get the data mapper used for migrations.
179 [[nodiscard]] LIGHTWEIGHT_API DataMapper& GetDataMapper() const
180 {
181 return const_cast<MigrationManager*>(this)->GetDataMapper();
182 }
183
184 /// Close the data mapper.
185 ///
186 /// This function is useful if explicitly closing the connection is desired before
187 /// the migration manager is destroyed.
188 LIGHTWEIGHT_API void CloseDataMapper();
189
190 /// Get a transaction for the data mapper.
191 ///
192 /// @return Transaction.
193 LIGHTWEIGHT_API SqlTransaction Transaction();
194
195 /// Preview SQL statements for a single migration without executing.
196 ///
197 /// This is useful for dry-run mode to see what SQL would be executed.
198 ///
199 /// @param migration The migration to preview.
200 /// @return Vector of SQL statements that would be executed.
201 [[nodiscard]] LIGHTWEIGHT_API std::vector<std::string> PreviewMigration(MigrationBase const& migration) const;
202
203 /// Preview SQL statements for all pending migrations without executing.
204 ///
205 /// This is useful for dry-run mode to see what SQL would be executed.
206 ///
207 /// @param feedbackCallback Optional callback to be called for each migration.
208 /// @return Vector of all SQL statements that would be executed.
209 [[nodiscard]] LIGHTWEIGHT_API std::vector<std::string> PreviewPendingMigrations(
210 ExecuteCallback const& feedbackCallback = {}) const;
211
212 /// Verify checksums of all applied migrations.
213 ///
214 /// Compares the stored checksums in the database with the computed checksums
215 /// of the current migration definitions. This helps detect if migrations
216 /// have been modified after they were applied.
217 ///
218 /// @return Vector of verification results for migrations with mismatched or missing checksums.
219 [[nodiscard]] LIGHTWEIGHT_API std::vector<ChecksumVerificationResult> VerifyChecksums() const;
220
221 /// Mark a migration as applied without executing its Up() method.
222 ///
223 /// This is useful for:
224 /// - Baseline migrations when setting up an existing database
225 /// - Marking migrations that were applied manually or through other means
226 /// - Skipping migrations that are not applicable to a specific environment
227 ///
228 /// @param migration The migration to mark as applied.
229 /// @throws std::runtime_error if the migration is already applied.
230 LIGHTWEIGHT_API void MarkMigrationAsApplied(MigrationBase const& migration);
231
232 /// Revert all migrations applied after the target timestamp.
233 ///
234 /// This method reverts migrations in reverse chronological order,
235 /// rolling back from the most recent to just after the target.
236 /// The target migration itself is NOT reverted.
237 ///
238 /// @param target Target timestamp to revert to. Migrations > target are reverted.
239 /// @param feedbackCallback Optional callback for progress updates.
240 /// @return Result containing list of reverted timestamps or error information.
241 /// @note Stops on first error. Previously reverted migrations in this call are NOT rolled back.
242 [[nodiscard]] LIGHTWEIGHT_API RevertResult RevertToMigration(MigrationTimestamp target,
243 ExecuteCallback const& feedbackCallback = {});
244
245 /// Get a summary status of all migrations.
246 ///
247 /// This method provides a quick overview of the migration state without
248 /// needing to iterate through individual migrations.
249 ///
250 /// @return Status struct with counts of applied, pending, and mismatched migrations.
251 [[nodiscard]] LIGHTWEIGHT_API MigrationStatus GetMigrationStatus() const;
252
253 /// Validate that all registered migrations have satisfiable dependencies.
254 ///
255 /// For each pending migration, verifies that every declared dependency is either
256 /// already applied or itself pending (so it will be applied first due to topological
257 /// ordering). Also detects cycles among pending migrations.
258 ///
259 /// @throws std::runtime_error if any dependency is unresolved or a cycle is found.
260 LIGHTWEIGHT_API void ValidateDependencies() const;
261
262 /// Register a release marker for a software version.
263 ///
264 /// Typically called from `LIGHTWEIGHT_SQL_RELEASE(version, timestamp)` at static init time.
265 /// Releases are ordered by `highestTimestamp`; two releases may not share the same
266 /// `highestTimestamp`, and the same version string may not be registered twice.
267 ///
268 /// @param version Human-readable version, e.g. "6.7.0".
269 /// @param highestTimestamp Highest migration timestamp (inclusive) that belongs to this release.
270 /// @throws std::runtime_error on duplicate version or duplicate timestamp.
271 LIGHTWEIGHT_API void RegisterRelease(std::string version, MigrationTimestamp highestTimestamp);
272
273 /// Remove all registered releases. Useful for resetting state in tests.
274 LIGHTWEIGHT_API void RemoveAllReleases();
275
276 /// Get all registered releases, sorted ascending by `highestTimestamp`.
277 [[nodiscard]] LIGHTWEIGHT_API std::vector<MigrationRelease> const& GetAllReleases() const noexcept;
278
279 /// Find a release by exact version string match.
280 ///
281 /// @return Pointer to the matching release, or nullptr if no release with that version is registered.
282 [[nodiscard]] LIGHTWEIGHT_API MigrationRelease const* FindReleaseByVersion(std::string_view version) const noexcept;
283
284 /// Find the release whose timestamp range contains `timestamp`.
285 ///
286 /// Returns the release with the smallest `highestTimestamp` that is `>= timestamp`.
287 /// Returns nullptr if `timestamp` is greater than every registered release's highestTimestamp
288 /// (i.e., the migration is post-all-releases / unreleased).
289 [[nodiscard]] LIGHTWEIGHT_API MigrationRelease const* FindReleaseForTimestamp(
290 MigrationTimestamp timestamp) const noexcept;
291
292 /// Get all registered migrations belonging to a given release.
293 ///
294 /// A migration `M` belongs to release `R` iff
295 /// `prev_release_ts < M.timestamp <= R.highestTimestamp`, where `prev_release_ts` is the
296 /// previous release's timestamp (or 0 if `R` is the first release).
297 ///
298 /// @param version The version string to look up.
299 /// @return Migrations in the release, ordered by timestamp. Empty if the version is unknown.
300 [[nodiscard]] LIGHTWEIGHT_API MigrationList GetMigrationsForRelease(std::string_view version) const;
301
302 /// @brief Snapshot of the schema the registered migrations *intend* to produce.
303 ///
304 /// Pure plan-walk — never executes SQL, never opens a connection. Folds the
305 /// effects of every registered migration (up to an optional cut-off timestamp)
306 /// into a per-table view of "the final shape" plus a chronological list of
307 /// data steps and surviving indexes/releases. Used by `dbtool fold` to emit a
308 /// self-contained baseline (`.cpp` plugin or `.sql`).
310 {
311 /// Per-table state: ordered column declarations + per-table FK list.
313 {
314 /// Ordered column declarations as they should appear in the emitted CREATE TABLE.
315 std::vector<SqlColumnDeclaration> columns;
316
317 /// Composite (multi-column) foreign keys declared on this table.
318 std::vector<SqlCompositeForeignKeyConstraint> compositeForeignKeys;
319
320 /// True when the original migration created the table with `IF NOT EXISTS`.
321 bool ifNotExists = false;
322 };
323
324 /// (schema, table) → folded `TableState`. Insertion order is *not* preserved by
325 /// `std::map` — for emission order use `creationOrder` below.
326 std::map<SqlSchema::FullyQualifiedTableName, TableState> tables;
327
328 /// Tables in *creation* order (first-time-seen). Reverse for safe DROP ordering
329 /// when tearing the schema down.
330 std::vector<SqlSchema::FullyQualifiedTableName> creationOrder;
331
332 /// Indexes that survive folding (created on tables still present at end).
333 std::vector<SqlCreateIndexPlan> indexes;
334
335 /// One data step (INSERT/UPDATE/DELETE/RawSql) tagged with its source migration.
336 struct DataStep
337 {
338 /// Timestamp of the migration that contributed this data step.
340
341 /// Title of the migration that contributed this data step.
342 std::string sourceTitle;
343
344 /// The plan element to replay (INSERT/UPDATE/DELETE/RawSql).
346 };
347
348 /// Data steps in chronological order. **No coalescing** — the fold replays
349 /// every data step verbatim, exactly as if migrations were applied in order.
350 std::vector<DataStep> dataSteps;
351
352 /// Releases declared via `LIGHTWEIGHT_SQL_RELEASE` that fall within the fold range.
353 std::vector<MigrationRelease> releases;
354
355 /// Migrations that contributed to the fold (timestamp + title). Used by emitters
356 /// to write a header comment explaining what was collapsed.
357 std::vector<std::pair<MigrationTimestamp, std::string>> foldedMigrations;
358 };
359
360 /// @brief Pure plan-walk that folds the effect of every registered migration.
361 ///
362 /// Visits each migration's `Up()` plan in timestamp order (or up to
363 /// `upToInclusive` if provided) and accumulates the cumulative end-state into a
364 /// `PlanFoldingResult`. **Never** executes SQL or touches a database connection
365 /// — the supplied formatter is only used to build the in-memory plan elements.
366 ///
367 /// @param formatter Formatter used by the migration query builder while walking
368 /// each migration's `Up()`. Any standard formatter works; the
369 /// walk inspects plan element shapes, not rendered SQL.
370 /// @param upToInclusive If set, fold only migrations with `timestamp <= upToInclusive`.
371 /// If unset, fold all registered migrations.
372 ///
373 /// @return The folded snapshot. Safe to call without a `MigrationManager`-attached
374 /// data mapper.
375 [[nodiscard]] LIGHTWEIGHT_API PlanFoldingResult FoldRegisteredMigrations(
376 SqlQueryFormatter const& formatter, std::optional<MigrationTimestamp> upToInclusive = std::nullopt) const;
377
378 private:
379 /// Return the pending list in dependency-respecting order.
380 ///
381 /// Dependencies among pending migrations are resolved via topological sort.
382 /// Migrations with no dependency between them keep timestamp order.
383 /// Throws on missing deps or cycles.
384 [[nodiscard]] MigrationList TopoSortPending(MigrationList pending,
385 std::vector<MigrationTimestamp> const& applied) const;
386
387 MigrationList _migrations;
388 std::vector<MigrationRelease> _releases;
389 mutable DataMapper* _dataMapper { nullptr };
390 };
391
392/// Requires the user to call LIGHTWEIGHT_MIGRATION_PLUGIN() in exactly one CPP file of the migration plugin.
393///
394/// @ingroup SqlMigration
395#define LIGHTWEIGHT_MIGRATION_PLUGIN() \
396 /* NOLINTNEXTLINE(bugprone-macro-parentheses) */ \
397 extern "C" LIGHTWEIGHT_EXPORT Lightweight::SqlMigration::MigrationManager* AcquireMigrationManager() \
398 { \
399 return &Lightweight::SqlMigration::MigrationManager::GetInstance(); \
400 }
401
402 /// Represents a single unique SQL migration.
403 ///
404 /// @ingroup SqlMigration
406 {
407 public:
408 /// Default copy constructor.
409 MigrationBase(MigrationBase const&) = default;
410 MigrationBase(MigrationBase&&) = delete;
411 /// Default copy assignment operator.
414 /// Constructs a migration with the given timestamp and title.
415 MigrationBase(MigrationTimestamp timestamp, std::string_view title):
416 _timestamp { timestamp },
417 _title { title }
418 {
420 }
421
422 virtual ~MigrationBase() = default;
423
424 /// Apply the migration.
425 ///
426 /// @param plan Query builder to use for building the migration plan.
427 virtual void Up(SqlMigrationQueryBuilder& plan) const = 0;
428
429 /// Revert the migration.
430 ///
431 /// @param plan Query builder to use for building the migration plan.
432 virtual void Down(SqlMigrationQueryBuilder& plan) const
433 {
434 (void) plan;
435 }
436
437 /// Check if this migration has a Down() implementation for rollback.
438 ///
439 /// This method determines whether the migration can be safely reverted.
440 /// The default implementation returns false. Derived classes that implement
441 /// Down() should override this to return true.
442 ///
443 /// @return true if Down() is implemented and can revert this migration.
444 [[nodiscard]] virtual bool HasDownImplementation() const noexcept
445 {
446 return false;
447 }
448
449 /// Get the timestamps of migrations that this migration depends on.
450 ///
451 /// Dependencies are enforced at apply time: each declared dependency must be
452 /// registered and applied (or pending in the same run, in dependency order)
453 /// before this migration will run. The default implementation returns no
454 /// dependencies, preserving timestamp-ordered apply behavior.
455 ///
456 /// @return Vector of dependency timestamps. Empty if this migration has none.
457 [[nodiscard]] virtual std::vector<MigrationTimestamp> GetDependencies() const
458 {
459 return {};
460 }
461
462 /// Get the author of the migration, if any.
463 ///
464 /// @return Author string, empty if not set.
465 [[nodiscard]] virtual std::string_view GetAuthor() const noexcept
466 {
467 return {};
468 }
469
470 /// Get the long-form description of the migration, if any.
471 ///
472 /// This is a multi-line description in addition to the short title.
473 ///
474 /// @return Description string, empty if not set.
475 [[nodiscard]] virtual std::string_view GetDescription() const noexcept
476 {
477 return {};
478 }
479
480 /// Get the timestamp of the migration.
481 ///
482 /// @return Timestamp of the migration.
483 [[nodiscard]] MigrationTimestamp GetTimestamp() const noexcept
484 {
485 return _timestamp;
486 }
487
488 /// Get the title of the migration.
489 ///
490 /// @return Title of the migration.
491 [[nodiscard]] std::string_view GetTitle() const noexcept
492 {
493 return _title;
494 }
495
496 /// Compute SHA-256 checksum of migration's Up() SQL statements.
497 ///
498 /// The checksum is computed from the SQL statements that would be executed
499 /// by this migration. This allows detecting if a migration has been modified
500 /// after it was applied.
501 ///
502 /// @param formatter The SQL query formatter to use for generating SQL.
503 /// @return SHA-256 hex string (64 characters).
504 [[nodiscard]] LIGHTWEIGHT_API std::string ComputeChecksum(SqlQueryFormatter const& formatter) const;
505
506 private:
507 MigrationTimestamp _timestamp;
508 std::string_view _title;
509 };
510
511 /// Represents a single unique SQL migration.
512 ///
513 /// This class is a convenience class that can be used to create a migration.
514 ///
515 /// @ingroup SqlMigration
516 /// Optional metadata attached to a Migration at construction time.
517 ///
518 /// All fields are optional. Unset fields behave as if the feature
519 /// were not used (e.g. empty dependencies preserves timestamp order).
520 ///
521 /// @ingroup SqlMigration
523 {
524 /// Migrations that must be applied before this one.
525 std::vector<MigrationTimestamp> dependencies {};
526 /// Author of the migration. Recorded in schema_migrations when the migration is applied.
527 std::string_view author {};
528 /// Long-form description. Recorded in schema_migrations when the migration is applied.
529 std::string_view description {};
530 };
531
532 template <uint64_t TsValue>
533 class Migration: public MigrationBase
534 {
535 public:
536 /// The migration's timestamp, available at compile time from the type itself.
537 ///
538 /// Exposed so @ref TimestampOf can extract the timestamp without
539 /// having to repeat the literal at every use site (e.g. in dependency lists).
540 static constexpr MigrationTimestamp TimeStamp { TsValue };
541
542 /// Create a new migration.
543 ///
544 /// @param title Title of the migration.
545 /// @param up Function to apply the migration.
546 /// @param down Function to revert the migration (optional).
547 Migration(std::string_view title,
548 std::function<void(SqlMigrationQueryBuilder&)> const& up,
549 std::function<void(SqlMigrationQueryBuilder&)> const& down = {}):
550 MigrationBase(TimeStamp, title),
551 _up { up },
552 _down { down }
553 {
554 }
555
556 /// Create a new migration with optional metadata (dependencies, author, description).
557 ///
558 /// @param title Title of the migration.
559 /// @param metadata Optional metadata including dependencies, author, and description.
560 /// @param up Function to apply the migration.
561 /// @param down Function to revert the migration (optional).
562 Migration(std::string_view title,
563 MigrationMetadata metadata,
564 std::function<void(SqlMigrationQueryBuilder&)> const& up,
565 std::function<void(SqlMigrationQueryBuilder&)> const& down = {}):
566 MigrationBase(TimeStamp, title),
567 _up { up },
568 _down { down },
569 _metadata { std::move(metadata) }
570 {
571 }
572
573 /// Apply the migration.
574 ///
575 /// @param builder Query builder to use for building the migration plan.
576 void Up(SqlMigrationQueryBuilder& builder) const override
577 {
578 _up(builder);
579 }
580
581 /// Revert the migration.
582 ///
583 /// @param builder Query builder to use for building the migration plan.
584 void Down(SqlMigrationQueryBuilder& builder) const override
585 {
586 if (_down)
587 _down(builder);
588 }
589
590 /// Check if this migration has a Down() implementation.
591 ///
592 /// @return true if a down function was provided.
593 [[nodiscard]] bool HasDownImplementation() const noexcept override
594 {
595 return static_cast<bool>(_down);
596 }
597
598 /// Get the declared dependencies for this migration.
599 [[nodiscard]] std::vector<MigrationTimestamp> GetDependencies() const override
600 {
601 return _metadata.dependencies;
602 }
603
604 /// Get the author of this migration.
605 [[nodiscard]] std::string_view GetAuthor() const noexcept override
606 {
607 return _metadata.author;
608 }
609
610 /// Get the long-form description of this migration.
611 [[nodiscard]] std::string_view GetDescription() const noexcept override
612 {
613 return _metadata.description;
614 }
615
616 private:
617 std::function<void(SqlMigrationQueryBuilder&)> _up;
618 std::function<void(SqlMigrationQueryBuilder&)> _down;
619 MigrationMetadata _metadata {};
620 };
621
622 /// Extracts the @ref MigrationTimestamp from a migration type that exposes a static
623 /// constexpr `TimeStamp` member.
624 ///
625 /// Works with both the class template @ref Migration and the struct generated by the
626 /// @ref LIGHTWEIGHT_SQL_MIGRATION macro. Use via `decltype`:
627 ///
628 /// @code
629 /// auto migrationA = SqlMigration::Migration<202601010001>("dep base", [](auto&){ ... });
630 /// auto tsA = TimestampOf<decltype(migrationA)>; // MigrationTimestamp { 202601010001 }
631 /// @endcode
632 ///
633 /// @tparam T A migration type exposing `static constexpr MigrationTimestamp TimeStamp`.
634 template <typename T>
635 inline constexpr MigrationTimestamp TimestampOf = T::TimeStamp;
636
637 namespace detail
638 {
639 /// RAII registrar used by `LIGHTWEIGHT_SQL_RELEASE` to register a release with the
640 /// migration manager at static-initialization time. Not intended for direct use.
641 ///
642 /// @ingroup SqlMigration
643 struct ReleaseRegistrar
644 {
645 /// Registers `{version, highestTimestamp}` with the singleton manager.
646 ReleaseRegistrar(std::string version, MigrationTimestamp highestTimestamp)
647 {
648 MigrationManager::GetInstance().RegisterRelease(std::move(version), highestTimestamp);
649 }
650 };
651 } // namespace detail
652
653} // namespace SqlMigration
654
655} // namespace Lightweight
656
657#define _LIGHTWEIGHT_CONCATENATE(s1, s2) s1##s2
658#define _LIGHTWEIGHT_CONCATENATE_INNER(s1, s2) _LIGHTWEIGHT_CONCATENATE(s1, s2)
659
660/// @brief Represents the C++ migration object for a given timestamped migration.
661/// @param timestamp Timestamp of the migration.
662///
663/// @ingroup SqlMigration
664#define LIGHTWEIGHT_MIGRATION_INSTANCE(timestamp) migration_##timestamp
665
666/// @brief Creates a new migration.
667/// @param timestamp Timestamp of the migration.
668/// @param description Description of the migration.
669///
670/// @note The migration will be registered with the migration manager automatically.
671///
672/// @code
673/// #include <Lightweight/SqlMigration.hpp>
674///
675/// LIGHTWEIGHT_SQL_MIGRATION(20260117234120, "Create table 'MyTable'")
676/// {
677/// // Use 'plan' to define the migration steps, for example creating tables.
678/// }
679/// @endcode
680///
681/// @see Lightweight::SqlMigrationQueryBuilder
682///
683/// @ingroup SqlMigration
684#define LIGHTWEIGHT_SQL_MIGRATION(timestamp, description) \
685 struct Migration_##timestamp: public Lightweight::SqlMigration::MigrationBase \
686 { \
687 /** The migration's timestamp, exposed so @ref TimestampOf can extract it from the type. */ \
688 static constexpr Lightweight::SqlMigration::MigrationTimestamp TimeStamp { static_cast<uint64_t>(timestamp) }; \
689 explicit Migration_##timestamp(): \
690 Lightweight::SqlMigration::MigrationBase(TimeStamp, description) \
691 { \
692 } \
693 \
694 void Up(Lightweight::SqlMigrationQueryBuilder& plan) const override; \
695 void Down(Lightweight::SqlMigrationQueryBuilder& /*plan*/) const override {} \
696 }; \
697 \
698 static Migration_##timestamp _LIGHTWEIGHT_CONCATENATE(migration_, timestamp); \
699 \
700 void Migration_##timestamp::Up(Lightweight::SqlMigrationQueryBuilder& plan) const
701
702/// @brief Associates a software release (version string) with the highest migration timestamp
703/// present at the time of that release.
704///
705/// Declare one `LIGHTWEIGHT_SQL_RELEASE` per cut release, alongside the migrations that belong to it.
706/// The macro registers with the migration manager at static-initialization time. Multiple releases
707/// may coexist in the same translation unit.
708///
709/// @param version A string literal, e.g. `"6.7.0"`.
710/// @param highestTimestamp An unsigned integer literal matching the timestamp format used by migrations.
711///
712/// @code
713/// LIGHTWEIGHT_SQL_MIGRATION(20260101120000, "Initial schema") { ... }
714/// LIGHTWEIGHT_SQL_RELEASE("6.6.0", 20260101120000);
715///
716/// LIGHTWEIGHT_SQL_MIGRATION(20260501120000, "Add orders table") { ... }
717/// LIGHTWEIGHT_SQL_RELEASE("6.7.0", 20260501120000);
718/// @endcode
719///
720/// @ingroup SqlMigration
721#define LIGHTWEIGHT_SQL_RELEASE(version, highestTimestamp) \
722 static ::Lightweight::SqlMigration::detail::ReleaseRegistrar _LIGHTWEIGHT_CONCATENATE_INNER(_lw_release_, __COUNTER__) \
723 { \
724 (version), ::Lightweight::SqlMigration::MigrationTimestamp \
725 { \
726 (highestTimestamp) \
727 } \
728 }
Main API for mapping records to and from the database using high level C++ syntax.
Query builder for building SQL migration queries.
Definition Migrate.hpp:469
virtual void Up(SqlMigrationQueryBuilder &plan) const =0
MigrationTimestamp GetTimestamp() const noexcept
std::string_view GetTitle() const noexcept
virtual std::vector< MigrationTimestamp > GetDependencies() const
virtual std::string_view GetAuthor() const noexcept
virtual std::string_view GetDescription() const noexcept
MigrationBase(MigrationBase const &)=default
Default copy constructor.
LIGHTWEIGHT_API std::string ComputeChecksum(SqlQueryFormatter const &formatter) const
MigrationBase(MigrationTimestamp timestamp, std::string_view title)
Constructs a migration with the given timestamp and title.
virtual void Down(SqlMigrationQueryBuilder &plan) const
virtual bool HasDownImplementation() const noexcept
MigrationBase & operator=(MigrationBase const &)=default
Default copy assignment operator.
LIGHTWEIGHT_API void MarkMigrationAsApplied(MigrationBase const &migration)
std::function< void(MigrationBase const &, size_t, size_t)> ExecuteCallback
Callback type invoked during migration execution to report progress.
LIGHTWEIGHT_API DataMapper & GetDataMapper()
Get the data mapper used for migrations.
LIGHTWEIGHT_API RevertResult RevertToMigration(MigrationTimestamp target, ExecuteCallback const &feedbackCallback={})
LIGHTWEIGHT_API void AddMigration(MigrationBase const *migration)
std::list< MigrationBase const * > MigrationList
Type alias for a list of migration pointers.
LIGHTWEIGHT_API MigrationList const & GetAllMigrations() const noexcept
LIGHTWEIGHT_API std::vector< std::string > PreviewPendingMigrations(ExecuteCallback const &feedbackCallback={}) const
LIGHTWEIGHT_API void RevertSingleMigration(MigrationBase const &migration)
LIGHTWEIGHT_API DataMapper & GetDataMapper() const
Get the data mapper used for migrations.
LIGHTWEIGHT_API void RemoveAllReleases()
Remove all registered releases. Useful for resetting state in tests.
LIGHTWEIGHT_API PlanFoldingResult FoldRegisteredMigrations(SqlQueryFormatter const &formatter, std::optional< MigrationTimestamp > upToInclusive=std::nullopt) const
Pure plan-walk that folds the effect of every registered migration.
LIGHTWEIGHT_API SqlTransaction Transaction()
LIGHTWEIGHT_API void RegisterRelease(std::string version, MigrationTimestamp highestTimestamp)
LIGHTWEIGHT_API MigrationBase const * GetMigration(MigrationTimestamp timestamp) const noexcept
LIGHTWEIGHT_API std::list< MigrationBase const * > GetPending() const noexcept
LIGHTWEIGHT_API std::vector< ChecksumVerificationResult > VerifyChecksums() const
LIGHTWEIGHT_API MigrationRelease const * FindReleaseByVersion(std::string_view version) const noexcept
LIGHTWEIGHT_API MigrationRelease const * FindReleaseForTimestamp(MigrationTimestamp timestamp) const noexcept
LIGHTWEIGHT_API MigrationList GetMigrationsForRelease(std::string_view version) const
LIGHTWEIGHT_API std::vector< std::string > PreviewMigration(MigrationBase const &migration) const
LIGHTWEIGHT_API size_t ApplyPendingMigrations(ExecuteCallback const &feedbackCallback={})
LIGHTWEIGHT_API void CreateMigrationHistory()
Create the migration history table if it does not exist.
LIGHTWEIGHT_API MigrationStatus GetMigrationStatus() const
LIGHTWEIGHT_API void ValidateDependencies() const
LIGHTWEIGHT_API std::vector< MigrationRelease > const & GetAllReleases() const noexcept
Get all registered releases, sorted ascending by highestTimestamp.
LIGHTWEIGHT_API void ApplySingleMigration(MigrationBase const &migration)
static LIGHTWEIGHT_API MigrationManager & GetInstance()
LIGHTWEIGHT_API std::vector< MigrationTimestamp > GetAppliedMigrationIds() const
API to format SQL queries for different SQL dialects.
std::variant< SqlCreateTablePlan, SqlAlterTablePlan, SqlDropTablePlan, SqlCreateIndexPlan, SqlRawSqlPlan, SqlInsertDataPlan, SqlUpdateDataPlan, SqlDeleteDataPlan > SqlMigrationPlanElement
Represents a single SQL migration plan element.
MigrationTimestamp timestamp
The timestamp of the verified migration.
std::string computedChecksum
The checksum computed from the current migration definition.
std::string storedChecksum
The checksum stored in the database.
bool matches
Whether the stored and computed checksums match.
std::string_view title
The title of the verified migration.
One data step (INSERT/UPDATE/DELETE/RawSql) tagged with its source migration.
std::string sourceTitle
Title of the migration that contributed this data step.
SqlMigrationPlanElement element
The plan element to replay (INSERT/UPDATE/DELETE/RawSql).
MigrationTimestamp sourceTimestamp
Timestamp of the migration that contributed this data step.
Per-table state: ordered column declarations + per-table FK list.
std::vector< SqlColumnDeclaration > columns
Ordered column declarations as they should appear in the emitted CREATE TABLE.
std::vector< SqlCompositeForeignKeyConstraint > compositeForeignKeys
Composite (multi-column) foreign keys declared on this table.
Snapshot of the schema the registered migrations intend to produce.
std::vector< SqlCreateIndexPlan > indexes
Indexes that survive folding (created on tables still present at end).
std::map< SqlSchema::FullyQualifiedTableName, TableState > tables
std::vector< MigrationRelease > releases
Releases declared via LIGHTWEIGHT_SQL_RELEASE that fall within the fold range.
std::vector< SqlSchema::FullyQualifiedTableName > creationOrder
std::vector< std::pair< MigrationTimestamp, std::string > > foldedMigrations
std::vector< MigrationTimestamp > dependencies
Migrations that must be applied before this one.
std::string_view author
Author of the migration. Recorded in schema_migrations when the migration is applied.
std::string_view description
Long-form description. Recorded in schema_migrations when the migration is applied.
MigrationTimestamp highestTimestamp
Highest migration timestamp contained in this release (inclusive).
std::string version
Human-readable version string, e.g. "6.7.0".
size_t mismatchCount
Number of applied migrations with checksum mismatches.
size_t appliedCount
Number of migrations that have been applied.
size_t totalRegistered
Total number of registered migrations.
size_t pendingCount
Number of migrations waiting to be applied.
size_t unknownAppliedCount
Number of applied migrations not found in registered list.
constexpr std::weak_ordering operator<=>(MigrationTimestamp const &other) const noexcept=default
Three-way comparison operator.
uint64_t value
The numeric timestamp value identifying the migration.
std::vector< MigrationTimestamp > revertedTimestamps
Successfully reverted migrations.
std::optional< MigrationTimestamp > failedAt
Migration that failed, if any.
std::string errorMessage
Error message if failed.