Lightweight 0.20261030.0
Loading...
Searching...
No Matches
MigrationPlan.hpp
1// SPDX-License-Identifier: Apache-2.0
2
3#pragma once
4
5#include "../Api.hpp"
6#include "../SqlColumnTypeDefinitions.hpp"
7#include "../SqlDataBinder.hpp"
8#include "../Utils.hpp"
9
10#include <reflection-cpp/reflection.hpp>
11
12#include <cstddef>
13#include <cstdint>
14#include <functional>
15#include <map>
16#include <optional>
17#include <set>
18#include <string>
19#include <string_view>
20#include <variant>
21#include <vector>
22
23namespace Lightweight
24{
25
26class SqlQueryFormatter;
27
28namespace detail
29{
30
31 template <typename T>
33 {
34 static_assert(AlwaysFalse<T>, "Unsupported type for SQL column definition.");
35 };
36
37 template <>
38 struct SqlColumnTypeDefinitionOf<std::string>
39 {
40 static constexpr auto value = SqlColumnTypeDefinitions::Text {};
41 };
42
43 template <>
44 struct SqlColumnTypeDefinitionOf<bool>
45 {
46 static constexpr auto value = SqlColumnTypeDefinitions::Bool {};
47 };
48
49 template <>
50 struct SqlColumnTypeDefinitionOf<char>
51 {
52 static constexpr auto value = SqlColumnTypeDefinitions::Char { 1 };
53 };
54
55 template <>
56 struct SqlColumnTypeDefinitionOf<SqlDate>
57 {
58 static constexpr auto value = SqlColumnTypeDefinitions::Date {};
59 };
60
61 template <>
62 struct SqlColumnTypeDefinitionOf<SqlDateTime>
63 {
64 static constexpr auto value = SqlColumnTypeDefinitions::DateTime {};
65 };
66
67 template <>
68 struct SqlColumnTypeDefinitionOf<SqlTime>
69 {
70 static constexpr auto value = SqlColumnTypeDefinitions::Time {};
71 };
72
73 template <size_t Precision, size_t Scale>
74 struct SqlColumnTypeDefinitionOf<SqlNumeric<Precision, Scale>>
75 {
76 static constexpr auto value = SqlColumnTypeDefinitions::Decimal { .precision = Precision, .scale = Scale };
77 };
78
79 template <>
80 struct SqlColumnTypeDefinitionOf<SqlGuid>
81 {
82 static constexpr auto value = SqlColumnTypeDefinitions::Guid {};
83 };
84
85 template <typename T>
86 requires(detail::OneOf<T, int16_t, uint16_t>)
87 struct SqlColumnTypeDefinitionOf<T>
88 {
89 static constexpr auto value = SqlColumnTypeDefinitions::Smallint {};
90 };
91
92 template <typename T>
93 requires(detail::OneOf<T, int32_t, uint32_t>)
94 struct SqlColumnTypeDefinitionOf<T>
95 {
96 static constexpr auto value = SqlColumnTypeDefinitions::Integer {};
97 };
98
99 template <typename T>
100 requires(detail::OneOf<T, int64_t, uint64_t>)
101 struct SqlColumnTypeDefinitionOf<T>
102 {
103 static constexpr auto value = SqlColumnTypeDefinitions::Bigint {};
104 };
105
106 template <>
107 struct SqlColumnTypeDefinitionOf<float>
108 {
109 static constexpr auto value = SqlColumnTypeDefinitions::Real { .precision = 24 };
110 };
111
112 template <>
113 struct SqlColumnTypeDefinitionOf<double>
114 {
115 static constexpr auto value = SqlColumnTypeDefinitions::Real { .precision = 53 };
116 };
117
118 template <size_t N, typename CharT>
119 requires(detail::OneOf<CharT, char>)
120 struct SqlColumnTypeDefinitionOf<SqlFixedString<N, CharT, SqlFixedStringMode::VARIABLE_SIZE>>
121 {
122 static constexpr auto value = SqlColumnTypeDefinitions::Varchar { N };
123 };
124
125 template <size_t N, typename CharT>
126 requires(detail::OneOf<CharT, char16_t, char32_t, wchar_t>)
127 struct SqlColumnTypeDefinitionOf<SqlFixedString<N, CharT, SqlFixedStringMode::VARIABLE_SIZE>>
128 {
129 static constexpr auto value = SqlColumnTypeDefinitions::NVarchar { N };
130 };
131
132 template <size_t N, typename CharT>
133 requires(detail::OneOf<CharT, char>)
134 struct SqlColumnTypeDefinitionOf<SqlFixedString<N, CharT, SqlFixedStringMode::FIXED_SIZE>>
135 {
136 static constexpr auto value = SqlColumnTypeDefinitions::Char { N };
137 };
138
139 template <size_t N, typename CharT>
140 requires(detail::OneOf<CharT, char16_t, char32_t, wchar_t>)
141 struct SqlColumnTypeDefinitionOf<SqlFixedString<N, CharT, SqlFixedStringMode::FIXED_SIZE>>
142 {
143 static constexpr auto value = SqlColumnTypeDefinitions::NChar { N };
144 };
145
146 template <size_t N, typename CharT>
147 struct SqlColumnTypeDefinitionOf<SqlFixedString<N, CharT, SqlFixedStringMode::FIXED_SIZE_RIGHT_TRIMMED>>
148 {
149 static constexpr auto value = SqlColumnTypeDefinitions::Char { N };
150 };
151
152 template <size_t N, typename CharT>
153 requires(detail::OneOf<CharT, char16_t, char32_t, wchar_t>)
154 struct SqlColumnTypeDefinitionOf<SqlFixedString<N, CharT, SqlFixedStringMode::FIXED_SIZE_RIGHT_TRIMMED>>
155 {
156 static constexpr auto value = SqlColumnTypeDefinitions::NChar { N };
157 };
158
159 template <size_t N, typename CharT>
160 requires(detail::OneOf<CharT, char>)
161 struct SqlColumnTypeDefinitionOf<SqlDynamicString<N, CharT>>
162 {
163 static constexpr auto value = SqlColumnTypeDefinitions::Varchar { N };
164 };
165
166 template <size_t N, typename CharT>
167 requires(detail::OneOf<CharT, char8_t, char16_t, char32_t, wchar_t>)
168 struct SqlColumnTypeDefinitionOf<SqlDynamicString<N, CharT>>
169 {
170 static constexpr auto value = SqlColumnTypeDefinitions::NVarchar { N };
171 };
172
173 template <typename T>
174 struct SqlColumnTypeDefinitionOf<std::optional<T>>
175 {
176 static constexpr auto value = SqlColumnTypeDefinitionOf<T>::value;
177 };
178
179 template <>
180 struct SqlColumnTypeDefinitionOf<SqlText>
181 {
182 static constexpr auto value = SqlColumnTypeDefinitions::Text {};
183 };
184
185 template <size_t N>
186 struct SqlColumnTypeDefinitionOf<SqlDynamicBinary<N>>
187 {
188 static constexpr auto value = SqlColumnTypeDefinitions::VarBinary { N };
189 };
190
191} // namespace detail
192
193/// @brief Represents a SQL column type definition of T.
194///
195/// @ingroup QueryBuilder
196template <typename T>
197constexpr auto SqlColumnTypeDefinitionOf = detail::SqlColumnTypeDefinitionOf<T>::value;
198
199/// @brief Represents a primary key type.
200///
201/// This enumeration represents the primary key type of a column.
202///
203/// @ingroup QueryBuilder
204enum class SqlPrimaryKeyType : uint8_t
205{
206 NONE,
207 MANUAL,
208 AUTO_INCREMENT,
209 GUID,
210};
211
212/// @brief Represents a foreign key reference definition.
213///
214/// @ingroup QueryBuilder
216{
217 /// The table name that the foreign key references.
218 std::string tableName;
219
220 /// The column name that the foreign key references.
221 std::string columnName;
222};
223
224/// @brief Represents a SQL column declaration.
225///
226/// @ingroup QueryBuilder
228{
229 /// The name of the column.
230 std::string name;
231
232 /// The type of the column.
233 SqlColumnTypeDefinition type;
234
235 /// The primary key type of the column.
236 SqlPrimaryKeyType primaryKey { SqlPrimaryKeyType::NONE };
237
238 /// The foreign key reference definition of the column.
239 std::optional<SqlForeignKeyReferenceDefinition> foreignKey {};
240
241 /// Indicates if the column is required (non-nullable).
242 bool required { false };
243
244 /// Indicates if the column is unique.
245 bool unique { false };
246
247 /// The default value of the column.
248 std::string defaultValue {};
249
250 /// Indicates if the column is indexed.
251 bool index { false };
252
253 /// The 1-based index in the primary key (0 if not part of a specific order).
254 uint16_t primaryKeyIndex { 0 };
255};
256
257/// @brief Represents a composite foreign key constraint.
258///
259/// @ingroup QueryBuilder
261{
262 /// The columns in the current table.
263 std::vector<std::string> columns;
264
265 /// The referenced table name.
267
268 /// The referenced columns in the referenced table.
269 std::vector<std::string> referencedColumns;
270};
271
272struct SqlCreateTablePlan
273{
274 std::string schemaName;
275 std::string tableName;
276 std::vector<SqlColumnDeclaration> columns;
277 std::vector<SqlCompositeForeignKeyConstraint> foreignKeys;
278 bool ifNotExists { false }; ///< If true, generates CREATE TABLE IF NOT EXISTS.
279};
280
281namespace SqlAlterTableCommands
282{
283
284 struct RenameTable
285 {
286 std::string_view newTableName;
287 };
288
289 struct AddColumn
290 {
291 std::string columnName;
292 SqlColumnTypeDefinition columnType;
293 SqlNullable nullable = SqlNullable::Null;
294 };
295
296 struct AlterColumn
297 {
298 std::string columnName;
299 SqlColumnTypeDefinition columnType;
300 SqlNullable nullable = SqlNullable::Null;
301 };
302
303 struct AddIndex
304 {
305 std::string_view columnName;
306 bool unique = false;
307 };
308
309 struct RenameColumn
310 {
311 std::string_view oldColumnName;
312 std::string_view newColumnName;
313 };
314
315 struct DropColumn
316 {
317 std::string_view columnName;
318 };
319
320 struct DropIndex
321 {
322 std::string_view columnName;
323 };
324
325 struct AddForeignKey
326 {
327 std::string columnName;
328 SqlForeignKeyReferenceDefinition referencedColumn;
329 };
330
331 struct AddCompositeForeignKey
332 {
333 std::vector<std::string> columns;
334 std::string referencedTableName;
335 std::vector<std::string> referencedColumns;
336 /// The schema of the referenced table; empty means the schema of the altered table.
337 std::string referencedSchemaName {};
338 };
339
340 struct DropForeignKey
341 {
342 std::string columnName;
343 };
344
345 /// Adds a column only if it does not already exist.
347 {
348 /// The name of the column to add.
349 std::string columnName;
350 /// The type of the column to add.
351 SqlColumnTypeDefinition columnType;
352 /// Whether the column is nullable.
353 SqlNullable nullable = SqlNullable::Null;
354 };
355
356 /// Drops a column only if it exists.
358 {
359 /// The name of the column to drop.
360 std::string columnName;
361 };
362
363 /// Drops an index only if it exists.
365 {
366 /// The name of the column whose index to drop.
367 std::string columnName;
368 };
369
370} // namespace SqlAlterTableCommands
371
372/// @brief Represents a single SQL ALTER TABLE command.
373///
374/// @ingroup QueryBuilder
375using SqlAlterTableCommand = std::variant<SqlAlterTableCommands::RenameTable,
376 SqlAlterTableCommands::AddColumn,
378 SqlAlterTableCommands::AlterColumn,
379 SqlAlterTableCommands::AddIndex,
380 SqlAlterTableCommands::RenameColumn,
381 SqlAlterTableCommands::DropColumn,
383 SqlAlterTableCommands::DropIndex,
385 SqlAlterTableCommands::AddForeignKey,
386 SqlAlterTableCommands::AddCompositeForeignKey,
387 SqlAlterTableCommands::DropForeignKey>;
388
389/// @brief Represents a SQL ALTER TABLE plan on a given table.
390///
391/// @ingroup QueryBuilder
393{
394 /// The schema name of the table to alter.
395 std::string_view schemaName;
396
397 /// The name of the table to alter.
398 std::string_view tableName;
399
400 /// The list of commands to execute on the table.
401 std::vector<SqlAlterTableCommand> commands;
402};
403
404/// @brief Represents a SQL DROP TABLE plan.
405///
406/// @ingroup QueryBuilder
408{
409 /// The schema name of the table to drop.
410 std::string_view schemaName;
411
412 /// The name of the table to drop.
413 std::string_view tableName;
414
415 /// If true, generates DROP TABLE IF EXISTS instead of DROP TABLE.
416 bool ifExists { false };
417
418 /// If true, drops all foreign key constraints referencing this table first.
419 /// On PostgreSQL, uses CASCADE. On MS SQL, drops FK constraints explicitly.
420 bool cascade { false };
421};
422
423/// @brief Represents a raw SQL plan.
424///
425/// @ingroup QueryBuilder
427{
428 /// The raw SQL to execute.
429 std::string_view sql;
430};
431
432/// @brief Represents a SQL INSERT data plan for migrations.
433///
434/// This structure represents an INSERT statement for a migration plan.
435///
436/// @ingroup QueryBuilder
438{
439 /// The schema name of the table to insert into.
440 std::string schemaName;
441
442 /// The name of the table to insert into.
443 std::string tableName;
444
445 /// The columns and their values to insert.
446 std::vector<std::pair<std::string, SqlVariant>> columns;
447
448 /// @brief Key columns that make the insert conditional, empty for a plain insert.
449 ///
450 /// When non-empty, the row is inserted only if no row with the same values in these
451 /// columns exists yet (`INSERT ... SELECT ... WHERE NOT EXISTS (...)`). Each key column
452 /// must be one of @ref columns and must not hold NULL; rendering throws otherwise.
453 std::vector<std::string> keyColumns;
454};
455
456/// @brief Represents a SQL UPDATE data plan for migrations.
457///
458/// This structure represents an UPDATE statement for a migration plan.
459///
460/// @ingroup QueryBuilder
462{
463 /// The schema name of the table to update.
464 std::string schemaName;
465
466 /// The name of the table to update.
467 std::string tableName;
468
469 /// The columns and their values to set.
470 std::vector<std::pair<std::string, SqlVariant>> setColumns;
471
472 /// @brief Column-to-expression assignments that cannot be represented as a literal value.
473 ///
474 /// Each entry is `(column name, raw SQL expression)`. The expression is emitted verbatim
475 /// after `"col" = ` at SQL-rendering time — used for things like `set TARGET = SOURCE` or
476 /// `set CTR = CTR + 1` where the RHS references another column or contains arithmetic.
477 /// Entries here are appended to the SET clause after `setColumns`.
478 std::vector<std::pair<std::string, std::string>> setExpressions;
479
480 /// The column name for the WHERE clause.
481 std::string whereColumn;
482
483 /// The comparison operator for the WHERE clause (e.g., "=", "<>", etc.).
484 std::string whereOp;
485
486 /// The value for the WHERE clause.
488
489 /// @brief Pre-rendered WHERE-clause body (the text after `WHERE`), used when the
490 /// condition cannot be expressed with the simple `(whereColumn op whereValue)`
491 /// triple — e.g. composite `AND`/`OR`/`NOT`, `IS NULL`, `IN (subquery)`, or
492 /// `EXISTS (subquery)`. When non-empty, this takes precedence over the
493 /// structured triple at SQL-emission time.
494 std::string whereExpression;
495};
496
497/// @brief Represents a SQL DELETE data plan for migrations.
498///
499/// This structure represents a DELETE statement for a migration plan.
500///
501/// @ingroup QueryBuilder
503{
504 /// The schema name of the table to delete from.
505 std::string schemaName;
506
507 /// The name of the table to delete from.
508 std::string tableName;
509
510 /// The column name for the WHERE clause.
511 std::string whereColumn;
512
513 /// The comparison operator for the WHERE clause (e.g., "=", "<>", etc.).
514 std::string whereOp;
515
516 /// The value for the WHERE clause.
518
519 /// Pre-rendered WHERE-clause body. See `SqlUpdateDataPlan::whereExpression`.
520 std::string whereExpression;
521};
522
523/// @brief Represents a SQL CREATE INDEX plan for migrations.
524///
525/// This structure represents a CREATE INDEX statement for a migration plan.
526///
527/// @ingroup QueryBuilder
529{
530 /// The schema name of the table to create the index on.
531 std::string schemaName;
532
533 /// The name of the index to create.
534 std::string indexName;
535
536 /// The name of the table to create the index on.
537 std::string tableName;
538
539 /// The columns to include in the index.
540 std::vector<std::string> columns;
541
542 /// If true, creates a UNIQUE index.
543 bool unique { false };
544
545 /// If true, generates CREATE INDEX IF NOT EXISTS.
546 bool ifNotExists { false };
547
548 /// Pre-rendered predicate body of a partial (PostgreSQL / SQLite) or filtered
549 /// (SQL Server) index, without the leading `WHERE`. Empty means the index covers
550 /// every row. Emitted verbatim after the column list as `WHERE <expression>`; the
551 /// caller is responsible for dialect-safe quoting. Mirrors
552 /// `SqlUpdateDataPlan::whereExpression`.
553 std::string whereExpression {};
554};
555
556// clang-format off
557
558/// @brief Represents a single SQL migration plan element.
559///
560/// This variant represents a single SQL migration plan element.
561///
562/// @ingroup QueryBuilder
563using SqlMigrationPlanElement = std::variant<
564 SqlCreateTablePlan,
572>;
573
574// clang-format on
575
576/// @brief Canonical compat-flag name requesting LUpd-compatible client-side string
577/// truncation. Kept with the consumer (`MigrationRenderContext`) so there is a single
578/// source of truth; `Lightweight::Config::CompatFlagLupTruncate` aliases this value.
579inline constexpr std::string_view CompatFlagLupTruncateName = "lup-truncate";
580
581/// @brief Compat-flag name that lets a foreign-key column follow the narrow/wide text
582/// kind of the column it references (`CHAR`<->`NCHAR`, `VARCHAR`<->`NVARCHAR`, same
583/// length). Lets one migration apply to legacy databases whose referenced columns are
584/// narrow and to fresh ones where they are wide. Only takes effect on DBMSes whose
585/// formatter reports `DistinguishesNarrowAndWideText()` (SQL Server).
586inline constexpr std::string_view CompatFlagFkMatchReferencedTextName = "fk-match-referenced-text";
587
588/// @brief Opt-in behavioural knobs that the `ToSql` migration-plan renderer honours.
589///
590/// When a profile declares a `compat:` flag (see `Lightweight::Config::CompatFlags`), dbtool
591/// / dbtool-gui construct one of these and pass it alongside the formatter so migration
592/// rendering can diverge from strict behaviour per-run.
593///
594/// The context is mutable across successive `ToSql` calls within the same migration run:
595/// it accumulates a column-width cache populated from `CreateTable` / `AlterTable` steps as
596/// they are rendered, so later `Insert` / `Update` steps can truncate oversize values
597/// without a server round-trip.
598///
599/// Default-constructed instances are strict (no truncation, no logging); this is the shape
600/// used by code paths that don't care about compat, so existing callers keep their
601/// behaviour.
602struct [[nodiscard]] MigrationRenderContext
603{
604 /// When true, string values in `Insert` / `Update` steps are truncated to the
605 /// destination column's declared character width and the truncation is logged via
606 /// `SqlLogger::OnWarning`. Mirrors LUpd's client-side clipping behaviour; see
607 /// `Lightweight::Config::CompatFlagLupTruncate`.
608 bool lupTruncate = false;
609
610 /// When true (and the formatter distinguishes narrow and wide text), a foreign-key
611 /// column declared by a `CreateTable` / `AlterTable` step is rendered with the
612 /// narrow/wide variant of the referenced column's actual type when the two differ
613 /// only in that respect. See `CompatFlagFkMatchReferencedTextName`.
614 bool fkMatchReferencedText = false;
615
616 /// Per-(schema, table, column) cache of declared character widths. Populated lazily
617 /// from `SqlCreateTablePlan` / `SqlAlterTablePlan` as they are rendered; the key is
618 /// (`schema`, `table`, `column`). A zero/absent entry means "no known width" and the
619 /// truncation layer leaves the value alone.
621 {
622 std::string schema; ///< Schema label (empty for engines without schemas, e.g. SQLite).
623 std::string table; ///< Table name.
624 std::string column; ///< Column name.
625 /// Total ordering on (schema, table, column) — defaulted three-way comparison.
626 auto operator<=>(ColumnKey const&) const = default;
627 };
628
629 /// @brief How a column's declared width is counted. The truncation layer must match
630 /// the server's interpretation: a VARCHAR(100) on MSSQL holds 100 *bytes*, but an
631 /// NVARCHAR(100) holds 100 *characters*. Truncating to 100 characters when the
632 /// server budget is 100 bytes still overflows on multi-byte source data.
633 enum class WidthUnit : uint8_t
634 {
635 Characters,
636 Bytes,
637 };
638
639 struct ColumnWidth
640 {
641 std::size_t value { 0 };
642 WidthUnit unit { WidthUnit::Characters };
643 };
644
645 /// Cache of declared character widths, populated lazily as plan elements render.
646 std::map<ColumnKey, ColumnWidth> columnWidths;
647
648 /// Per-column text types (`Char` / `NChar` / `Varchar` / `NVarchar` only), from steps
649 /// rendered so far and from `widthLookup`. Consulted by `fkMatchReferencedText`.
650 std::map<ColumnKey, SqlColumnTypeDefinition> textColumnTypes;
651
652 /// @brief Optional fallback that fetches column widths for a `(schema, table)` from
653 /// the live database when the cache has no entry for it.
654 ///
655 /// The renderer calls this once per `(schema, table)` it can't satisfy from
656 /// `columnWidths`. Implementations should issue a single `INFORMATION_SCHEMA.COLUMNS`
657 /// (or equivalent) query and write every char/varchar column's declared width back
658 /// into `columnWidths`. They may also write a sentinel (e.g. an empty entry) when the
659 /// table doesn't exist, so the renderer doesn't keep retrying. Default-empty means
660 /// "no fallback" — i.e. only widths declared by the same migration plan are
661 /// considered.
662 std::function<void(MigrationRenderContext&, std::string_view /*schema*/, std::string_view /*table*/)> widthLookup;
663
664 /// @brief Tables for which the lookup has already been attempted, so we don't
665 /// re-query for every INSERT against the same table.
666 struct TableKey
667 {
668 std::string schema; ///< Schema label (empty when the engine reports none).
669 std::string table; ///< Table name.
670 /// Total ordering on (schema, table) — defaulted three-way comparison.
671 auto operator<=>(TableKey const&) const = default;
672 };
673 /// Tables for which `widthLookup` has already been called; prevents repeat queries.
674 std::set<TableKey> lookupAttempted;
675
676 /// @brief Identity of the migration currently being rendered. Set by
677 /// `MigrationManager::ApplySingleMigration` before `ToSql` is called for each
678 /// step so warnings (e.g. `lup-truncate`) can attribute themselves to the
679 /// offending migration. Empty / zero when rendering happens outside a migration
680 /// run (e.g. unit tests calling `ToSql` directly).
682 /// Timestamp of the migration currently being rendered (0 outside a migration run).
683 std::uint64_t activeMigrationTimestamp = 0;
684};
685
686/// Formats the given SQL migration plan element as a list of SQL statements.
687///
688/// @param formatter The SQL query formatter to use.
689/// @param element The SQL migration plan element to format.
690///
691/// @return A list of SQL statements.
692///
693/// @ingroup QueryBuilder
694[[nodiscard]] LIGHTWEIGHT_API std::vector<std::string> ToSql(SqlQueryFormatter const& formatter,
695 SqlMigrationPlanElement const& element);
696
697/// @overload
698/// Rendering variant that consults a `MigrationRenderContext` for compat flags
699/// and column-width tracking. Updates `context` in place when the rendered step declares
700/// new columns.
701[[nodiscard]] LIGHTWEIGHT_API std::vector<std::string> ToSql(SqlQueryFormatter const& formatter,
702 SqlMigrationPlanElement const& element,
703 MigrationRenderContext& context);
704
705/// @brief A statement whose values are bound as parameters when it runs.
706///
707/// @ingroup QueryBuilder
709{
710 /// The statement text, with one `?` placeholder per parameter.
711 std::string sql;
712
713 /// The values bound to the placeholders, in order.
714 std::vector<SqlVariant> parameters;
715};
716
717/// @brief Renders an `Insert(...).IfNotExists(...)` step for execution with bound parameters.
718///
719/// `MigrationManager` executes this form, binding the values, while checksums and previews
720/// (`ToSql`) keep showing them as literals, so the checksum of a migration does not depend on how
721/// its values reach the database. Callers that run `ToSql` output themselves (e.g.
722/// `SqlStatement::MigrateDirect`) execute the literal form.
723///
724/// UTF-8 text is bound as UTF-16, so it is stored exactly as its literal rendering would store it.
725/// A GUID, date, time or timestamp is bound as the text its literal quotes, so the server parses the
726/// same text either way: a native time or timestamp would keep the fraction the literal drops, and
727/// SQLite would store a timestamp in another text form that a key comparison never matches.
728/// Other values are bound natively. Exact decimals (also at high scale), binary values and `bool`
729/// store exactly as their literals; a `double` reaches the column as its binary value. That stores
730/// the same as the literal for FLOAT/REAL columns and for DECIMAL columns of low scale, but it can
731/// differ where the literal is parsed as a decimal: a DECIMAL column with more fractional digits
732/// than the double holds exactly (e.g. DECIMAL(28,12) receives 123456.789011999994 rather than
733/// 123456.789012000000), SQLite parsing some literals 1-2 ULP off the correctly rounded double the
734/// bound value carries, and a double into a text column (SQLite stores 3.0 as "3.0" bound, "3" as a
735/// literal). Exact decimals belong in `SqlNumeric` / `SqlDynamicNumeric`.
736///
737/// @param element The step to render.
738/// @return The bound statement, or nothing when @p element is not an `IfNotExists` insert.
739/// @throws std::invalid_argument A key column is not one of the insert's columns or holds NULL.
740///
741/// @ingroup QueryBuilder
742[[nodiscard]] LIGHTWEIGHT_API std::optional<SqlBoundStatement> ToBoundSql(SqlMigrationPlanElement const& element);
743
744/// @overload
745/// Applies the value-level compat knobs of @p context (string truncation) like `ToSql` does. Callers
746/// render the step with `ToSql(formatter, element, context)` first, which reports the truncation.
747[[nodiscard]] LIGHTWEIGHT_API std::optional<SqlBoundStatement> ToBoundSql(SqlMigrationPlanElement const& element,
748 MigrationRenderContext& context);
749
750/// @brief Represents a SQL migration plan.
751///
752/// This structure represents a SQL migration plan that can be executed on a database.
753///
754/// @ingroup QueryBuilder
755struct [[nodiscard]] SqlMigrationPlan
756{
757 /// The SQL query formatter to use.
759 /// The migration plan steps.
760 std::vector<SqlMigrationPlanElement> steps {};
761
762 /// Converts the migration plan to a list of SQL statements.
763 [[nodiscard]] LIGHTWEIGHT_API std::vector<std::string> ToSql() const;
764};
765
766/// @brief Formats the given SQL migration plan as a list of SQL statements.
767///
768/// @param formatter The SQL query formatter to use.
769/// @param plans The SQL migration plans to format.
770///
771/// @return A list of SQL statements.
772///
773/// @ingroup QueryBuilder
774[[nodiscard]] LIGHTWEIGHT_API std::vector<std::string> ToSql(SqlQueryFormatter const& formatter,
775 std::vector<SqlMigrationPlan> const& plans);
776
777} // namespace Lightweight
API to format SQL queries for different SQL dialects.
std::variant< SqlAlterTableCommands::RenameTable, SqlAlterTableCommands::AddColumn, SqlAlterTableCommands::AddColumnIfNotExists, SqlAlterTableCommands::AlterColumn, SqlAlterTableCommands::AddIndex, SqlAlterTableCommands::RenameColumn, SqlAlterTableCommands::DropColumn, SqlAlterTableCommands::DropColumnIfExists, SqlAlterTableCommands::DropIndex, SqlAlterTableCommands::DropIndexIfExists, SqlAlterTableCommands::AddForeignKey, SqlAlterTableCommands::AddCompositeForeignKey, SqlAlterTableCommands::DropForeignKey > SqlAlterTableCommand
Represents a single SQL ALTER TABLE command.
LIGHTWEIGHT_API std::vector< std::string > ToSql(SqlQueryFormatter const &formatter, SqlMigrationPlanElement const &element)
LIGHTWEIGHT_API std::optional< SqlBoundStatement > ToBoundSql(SqlMigrationPlanElement const &element)
Renders an Insert(...).IfNotExists(...) step for execution with bound parameters.
SqlPrimaryKeyType
Represents a primary key type.
std::variant< SqlCreateTablePlan, SqlAlterTablePlan, SqlDropTablePlan, SqlCreateIndexPlan, SqlRawSqlPlan, SqlInsertDataPlan, SqlUpdateDataPlan, SqlDeleteDataPlan > SqlMigrationPlanElement
Represents a single SQL migration plan element.
constexpr auto SqlColumnTypeDefinitionOf
Represents a SQL column type definition of T.
std::string schema
Schema label (empty for engines without schemas, e.g. SQLite).
auto operator<=>(ColumnKey const &) const =default
Total ordering on (schema, table, column) — defaulted three-way comparison.
Tables for which the lookup has already been attempted, so we don't re-query for every INSERT against...
std::string schema
Schema label (empty when the engine reports none).
auto operator<=>(TableKey const &) const =default
Total ordering on (schema, table) — defaulted three-way comparison.
Opt-in behavioural knobs that the ToSql migration-plan renderer honours.
std::string activeMigrationTitle
Identity of the migration currently being rendered. Set by MigrationManager::ApplySingleMigration bef...
std::map< ColumnKey, ColumnWidth > columnWidths
Cache of declared character widths, populated lazily as plan elements render.
WidthUnit
How a column's declared width is counted. The truncation layer must match the server's interpretation...
std::map< ColumnKey, SqlColumnTypeDefinition > textColumnTypes
std::function< void(MigrationRenderContext &, std::string_view, std::string_view)> widthLookup
Optional fallback that fetches column widths for a (schema, table) from the live database when the ca...
std::set< TableKey > lookupAttempted
Tables for which widthLookup has already been called; prevents repeat queries.
Adds a column only if it does not already exist.
SqlNullable nullable
Whether the column is nullable.
SqlColumnTypeDefinition columnType
The type of the column to add.
std::string columnName
The name of the column to add.
std::string columnName
The name of the column to drop.
std::string columnName
The name of the column whose index to drop.
Represents a SQL ALTER TABLE plan on a given table.
std::string_view schemaName
The schema name of the table to alter.
std::string_view tableName
The name of the table to alter.
std::vector< SqlAlterTableCommand > commands
The list of commands to execute on the table.
A statement whose values are bound as parameters when it runs.
std::vector< SqlVariant > parameters
The values bound to the placeholders, in order.
std::string sql
The statement text, with one ? placeholder per parameter.
Represents a SQL column declaration.
bool required
Indicates if the column is required (non-nullable).
bool index
Indicates if the column is indexed.
SqlColumnTypeDefinition type
The type of the column.
std::string name
The name of the column.
std::optional< SqlForeignKeyReferenceDefinition > foreignKey
The foreign key reference definition of the column.
SqlPrimaryKeyType primaryKey
The primary key type of the column.
std::string defaultValue
The default value of the column.
bool unique
Indicates if the column is unique.
uint16_t primaryKeyIndex
The 1-based index in the primary key (0 if not part of a specific order).
Represents a composite foreign key constraint.
std::vector< std::string > columns
The columns in the current table.
std::string referencedTableName
The referenced table name.
std::vector< std::string > referencedColumns
The referenced columns in the referenced table.
Represents a SQL CREATE INDEX plan for migrations.
std::vector< std::string > columns
The columns to include in the index.
std::string schemaName
The schema name of the table to create the index on.
bool ifNotExists
If true, generates CREATE INDEX IF NOT EXISTS.
std::string indexName
The name of the index to create.
std::string tableName
The name of the table to create the index on.
bool unique
If true, creates a UNIQUE index.
Represents a SQL DELETE data plan for migrations.
std::string whereExpression
Pre-rendered WHERE-clause body. See SqlUpdateDataPlan::whereExpression.
std::string whereOp
The comparison operator for the WHERE clause (e.g., "=", "<>", etc.).
std::string tableName
The name of the table to delete from.
std::string whereColumn
The column name for the WHERE clause.
std::string schemaName
The schema name of the table to delete from.
SqlVariant whereValue
The value for the WHERE clause.
Represents a SQL DROP TABLE plan.
std::string_view schemaName
The schema name of the table to drop.
std::string_view tableName
The name of the table to drop.
bool ifExists
If true, generates DROP TABLE IF EXISTS instead of DROP TABLE.
Represents a foreign key reference definition.
std::string tableName
The table name that the foreign key references.
std::string columnName
The column name that the foreign key references.
Represents a SQL INSERT data plan for migrations.
std::vector< std::pair< std::string, SqlVariant > > columns
The columns and their values to insert.
std::string schemaName
The schema name of the table to insert into.
std::vector< std::string > keyColumns
Key columns that make the insert conditional, empty for a plain insert.
std::string tableName
The name of the table to insert into.
Represents a SQL migration plan.
SqlQueryFormatter const & formatter
The SQL query formatter to use.
LIGHTWEIGHT_API std::vector< std::string > ToSql() const
Converts the migration plan to a list of SQL statements.
Represents a raw SQL plan.
std::string_view sql
The raw SQL to execute.
Represents a SQL UPDATE data plan for migrations.
std::string schemaName
The schema name of the table to update.
SqlVariant whereValue
The value for the WHERE clause.
std::string whereExpression
Pre-rendered WHERE-clause body (the text after WHERE), used when the condition cannot be expressed wi...
std::vector< std::pair< std::string, SqlVariant > > setColumns
The columns and their values to set.
std::string tableName
The name of the table to update.
std::string whereOp
The comparison operator for the WHERE clause (e.g., "=", "<>", etc.).
std::vector< std::pair< std::string, std::string > > setExpressions
Column-to-expression assignments that cannot be represented as a literal value.
std::string whereColumn
The column name for the WHERE clause.
Represents a value that can be any of the supported SQL data types.