Lightweight 0.20260303.0
Loading...
Searching...
No Matches
SQLiteFormatter.hpp
1// SPDX-License-Identifier: Apache-2.0
2#pragma once
3
4#include "../SqlQueryFormatter.hpp"
5
6#include <reflection-cpp/reflection.hpp>
7
8#include <format>
9
10namespace Lightweight
11{
12
13class SQLiteQueryFormatter: public SqlQueryFormatter
14{
15 protected:
16 /// Formats a table name for use in a FROM clause.
17 /// If the table name is already quoted (starts with " or [), returns it as-is.
18 /// Otherwise, wraps it in double quotes.
19 [[nodiscard]] static std::string FormatFromTable(std::string_view table)
20 {
21 if (!table.empty() && (table.front() == '"' || table.front() == '['))
22 return std::string(table); // Already quoted/qualified
23 return std::format(R"("{}")", table);
24 }
25
26 public:
27 [[nodiscard]] bool RequiresTableRebuildForForeignKeyChange() const noexcept override
28 {
29 return true;
30 }
31
32 /// Builds the SQL query used to check whether a column exists on a SQLite table.
33 ///
34 /// The migration executor uses this to resolve the `-- LIGHTWEIGHT_SQLITE_GUARD:`
35 /// sentinels emitted by @ref AlterTable for `AddColumnIfNotExists` / `DropColumnIfExists`.
36 /// Keeping the pragma SQL here ensures the sentinel-emitting side and the
37 /// runtime-presence-check side share a single source of truth.
38 ///
39 /// @param tableName Name of the table to inspect.
40 /// @param columnName Name of the column whose existence to check.
41 /// @return SQL string returning a single integer column: non-zero iff the column exists.
42 [[nodiscard]] static std::string BuildColumnExistsQuery(std::string_view tableName, std::string_view columnName)
43 {
44 return std::format(R"(SELECT COUNT(*) FROM pragma_table_info('{}') WHERE name = '{}';)", tableName, columnName);
45 }
46
47 [[nodiscard]] std::string Insert(std::string_view intoTable,
48 std::string_view fields,
49 std::string_view values) const override
50 {
51 return std::format(R"(INSERT INTO "{}" ({}) VALUES ({}))", intoTable, fields, values);
52 }
53
54 [[nodiscard]] std::string Insert(std::string_view /*schema*/,
55 std::string_view intoTable,
56 std::string_view fields,
57 std::string_view values) const override
58 {
59 // SQLite doesn't support schemas - ignore schema parameter
60 return std::format(R"(INSERT INTO "{}" ({}) VALUES ({}))", intoTable, fields, values);
61 }
62
63 [[nodiscard]] std::string QueryLastInsertId(std::string_view /*tableName*/) const override
64 {
65 // This is SQLite syntax. We might want to provide aspecialized SQLite class instead.
66 return "SELECT LAST_INSERT_ROWID()";
67 }
68
69 [[nodiscard]] std::string_view BooleanLiteral(bool literalValue) const noexcept override
70 {
71 return literalValue ? "TRUE" : "FALSE";
72 }
73
74 [[nodiscard]] std::string_view DateFunction() const noexcept override
75 {
76 return "date()";
77 }
78
79 [[nodiscard]] std::string StringLiteral(std::string_view value) const noexcept override
80 {
81 if (value.empty())
82 return "''";
83
84 std::string escaped;
85 escaped.reserve(value.size() + 2);
86 escaped += '\'';
87 for (char const c: value)
88 {
89 if (c == '\'')
90 escaped += "''";
91 else
92 escaped += c;
93 }
94 escaped += '\'';
95 return escaped;
96 }
97
98 [[nodiscard]] std::string StringLiteral(char value) const noexcept override
99 {
100 if (value == '\'')
101 return "''''";
102 return std::format("'{}'", value);
103 }
104
105 [[nodiscard]] std::string BinaryLiteral(std::span<uint8_t const> data) const override
106 {
107 std::string result;
108 result.reserve((data.size() * 2) + 3);
109 result += "X'";
110 for (uint8_t byte: data)
111 result += std::format("{:02X}", byte);
112 result += "'";
113 return result;
114 }
115
116 [[nodiscard]] std::string QualifiedTableName(std::string_view schema, std::string_view table) const override
117 {
118 // SQLite doesn't use schemas in the same way - just return the quoted table name
119 if (schema.empty())
120 return std::format(R"("{}")", table);
121 // For SQLite attached databases, use database.table syntax
122 return std::format(R"("{}"."{}")", schema, table);
123 }
124
125 [[nodiscard]] std::string SelectCount(bool distinct,
126 std::string_view fromTable,
127 std::string_view fromTableAlias,
128 std::string_view tableJoins,
129 std::string_view whereCondition) const override
130 {
131 auto const formattedTable = FormatFromTable(fromTable);
132 if (fromTableAlias.empty())
133 return std::format(
134 "SELECT{} COUNT(*) FROM {}{}{}", distinct ? " DISTINCT" : "", formattedTable, tableJoins, whereCondition);
135 else
136 return std::format(R"(SELECT{} COUNT(*) FROM {} AS "{}"{}{})",
137 distinct ? " DISTINCT" : "",
138 formattedTable,
139 fromTableAlias,
140 tableJoins,
141 whereCondition);
142 }
143
144 [[nodiscard]] std::string SelectAll(bool distinct,
145 // NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
146 std::string_view fields,
147 std::string_view fromTable,
148 std::string_view fromTableAlias,
149 std::string_view tableJoins,
150 std::string_view whereCondition,
151 std::string_view orderBy,
152 std::string_view groupBy) const override
153 {
154 std::stringstream sqlQueryString;
155 sqlQueryString << "SELECT ";
156 if (distinct)
157 sqlQueryString << "DISTINCT ";
158 sqlQueryString << fields;
159 sqlQueryString << " FROM " << FormatFromTable(fromTable);
160 if (!fromTableAlias.empty())
161 sqlQueryString << " AS \"" << fromTableAlias << '"';
162 sqlQueryString << tableJoins;
163 sqlQueryString << whereCondition;
164 sqlQueryString << groupBy;
165 sqlQueryString << orderBy;
166
167 return sqlQueryString.str();
168 }
169
170 [[nodiscard]] std::string SelectFirst(bool distinct,
171 // NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
172 std::string_view fields,
173 std::string_view fromTable,
174 std::string_view fromTableAlias,
175 std::string_view tableJoins,
176 std::string_view whereCondition,
177 std::string_view orderBy,
178 size_t count) const override
179 {
180 std::stringstream sqlQueryString;
181 sqlQueryString << "SELECT " << fields;
182 if (distinct)
183 sqlQueryString << " DISTINCT";
184 sqlQueryString << " FROM " << FormatFromTable(fromTable);
185 if (!fromTableAlias.empty())
186 sqlQueryString << " AS \"" << fromTableAlias << "\"";
187 sqlQueryString << tableJoins;
188 sqlQueryString << whereCondition;
189 sqlQueryString << orderBy;
190 sqlQueryString << " LIMIT " << count;
191 return sqlQueryString.str();
192 }
193
194 [[nodiscard]] std::string SelectRange(bool distinct,
195 // NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
196 std::string_view fields,
197 std::string_view fromTable,
198 std::string_view fromTableAlias,
199 std::string_view tableJoins,
200 std::string_view whereCondition,
201 std::string_view orderBy,
202 std::string_view groupBy,
203 std::size_t offset,
204 std::size_t limit) const override
205 {
206 std::stringstream sqlQueryString;
207 sqlQueryString << "SELECT " << fields;
208 if (distinct)
209 sqlQueryString << " DISTINCT";
210 sqlQueryString << " FROM " << FormatFromTable(fromTable);
211 if (!fromTableAlias.empty())
212 sqlQueryString << " AS \"" << fromTableAlias << "\"";
213 sqlQueryString << tableJoins;
214 sqlQueryString << whereCondition;
215 sqlQueryString << groupBy;
216 sqlQueryString << orderBy;
217 sqlQueryString << " LIMIT " << limit << " OFFSET " << offset;
218 return sqlQueryString.str();
219 }
220
221 [[nodiscard]] std::string Update(std::string_view table,
222 std::string_view tableAlias,
223 std::string_view setFields,
224 std::string_view whereCondition) const override
225 {
226 auto const formattedTable = FormatFromTable(table);
227 if (tableAlias.empty())
228 return std::format("UPDATE {} SET {}{}", formattedTable, setFields, whereCondition);
229 else
230 return std::format(R"(UPDATE {} AS "{}" SET {}{})", formattedTable, tableAlias, setFields, whereCondition);
231 }
232
233 [[nodiscard]] std::string Delete(std::string_view fromTable,
234 std::string_view fromTableAlias,
235 std::string_view tableJoins,
236 std::string_view whereCondition) const override
237 {
238 auto const formattedTable = FormatFromTable(fromTable);
239 if (fromTableAlias.empty())
240 return std::format("DELETE FROM {}{}{}", formattedTable, tableJoins, whereCondition);
241 else
242 return std::format(R"(DELETE FROM {} AS "{}"{}{})", formattedTable, fromTableAlias, tableJoins, whereCondition);
243 }
244
245 [[nodiscard]] virtual std::string BuildColumnDefinition(SqlColumnDeclaration const& column) const
246 {
247 std::stringstream sqlQueryString;
248
249 sqlQueryString << '"' << column.name << "\" ";
250
251 if (column.primaryKey != SqlPrimaryKeyType::AUTO_INCREMENT)
252 sqlQueryString << ColumnType(column.type);
253 else
254 sqlQueryString << ColumnType(SqlColumnTypeDefinitions::Integer {});
255
256 if (column.required)
257 sqlQueryString << " NOT NULL";
258
259 if (column.primaryKey == SqlPrimaryKeyType::AUTO_INCREMENT)
260 sqlQueryString << " PRIMARY KEY AUTOINCREMENT";
261 else if (column.primaryKey == SqlPrimaryKeyType::NONE && !column.index && column.unique)
262 sqlQueryString << " UNIQUE";
263
264 if (!column.defaultValue.empty())
265 sqlQueryString << " DEFAULT " << column.defaultValue;
266
267 return sqlQueryString.str();
268 }
269
270 [[nodiscard]] static std::string BuildForeignKeyConstraint(std::string_view tableName,
271 std::string_view columnName,
272 SqlForeignKeyReferenceDefinition const& referencedColumn)
273 {
274 // Double-quote the constraint name so PostgreSQL preserves its case (otherwise
275 // PG would fold to lowercase, breaking the matching `DROP CONSTRAINT "FK_<…>"`
276 // emitted by `DropForeignKey`).
277 return std::format(R"(CONSTRAINT "{}" FOREIGN KEY ("{}") REFERENCES "{}"("{}"))",
278 BuildForeignKeyConstraintName(tableName, std::array { columnName }),
279 columnName,
280 referencedColumn.tableName,
281 referencedColumn.columnName);
282 }
283
284 // NOLINTNEXTLINE(readability-function-cognitive-complexity)
285 [[nodiscard]] StringList CreateTable(std::string_view schema,
286 std::string_view tableName,
287 std::vector<SqlColumnDeclaration> const& columns,
288 std::vector<SqlCompositeForeignKeyConstraint> const& foreignKeys,
289 bool ifNotExists = false) const override
290 {
291 auto sqlQueries = StringList {};
292
293 // NOLINTNEXTLINE(readability-function-cognitive-complexity)
294 sqlQueries.emplace_back([&]() {
295 std::stringstream sqlQueryString;
296 sqlQueryString << "CREATE TABLE ";
297 if (ifNotExists)
298 sqlQueryString << "IF NOT EXISTS ";
299 // SQLite doesn't support schemas - ignore schema parameter
300 (void) schema;
301 sqlQueryString << "\"" << tableName << "\" (";
302 std::vector<SqlColumnDeclaration const*> pks;
303 size_t currentColumn = 0;
304 std::string foreignKeyConstraints;
305 for (SqlColumnDeclaration const& column: columns)
306 {
307 if (currentColumn > 0)
308 sqlQueryString << ",";
309 ++currentColumn;
310 sqlQueryString << "\n ";
311 sqlQueryString << BuildColumnDefinition(column);
312
313 if (column.primaryKey != SqlPrimaryKeyType::NONE)
314 pks.push_back(&column);
315
316 if (column.foreignKey)
317 {
318 foreignKeyConstraints += ",\n ";
319 foreignKeyConstraints += BuildForeignKeyConstraint(tableName, column.name, *column.foreignKey);
320 }
321 }
322
323 for (SqlCompositeForeignKeyConstraint const& fk: foreignKeys)
324 {
325 // Emit a deterministic CONSTRAINT name (`FK_<table>_<col1>_<col2>...`) so that
326 // a later `DropForeignKey` / SQLite-rebuild can locate the constraint by name
327 // instead of relying on the backend's auto-generated identifier (PostgreSQL
328 // would otherwise pick `<table>_<col>_fkey`).
329 foreignKeyConstraints += ",\n CONSTRAINT \"";
330 foreignKeyConstraints += BuildForeignKeyConstraintName(tableName, fk.columns);
331 foreignKeyConstraints += "\" FOREIGN KEY (";
332 for (size_t i = 0; i < fk.columns.size(); ++i)
333 {
334 if (i > 0)
335 foreignKeyConstraints += ", ";
336 foreignKeyConstraints += '"' + fk.columns[i] + '"';
337 }
338 foreignKeyConstraints += ") REFERENCES \"";
339 foreignKeyConstraints += fk.referencedTableName;
340 foreignKeyConstraints += "\" (";
341 for (size_t i = 0; i < fk.referencedColumns.size(); ++i)
342 {
343 if (i > 0)
344 foreignKeyConstraints += ", ";
345 foreignKeyConstraints += '"' + fk.referencedColumns[i] + '"';
346 }
347 foreignKeyConstraints += ")";
348 }
349
350 // Filter out AUTO_INCREMENT from table-level PK constraint if it's the ONLY PK,
351 // because SQLite handles it inline. But for composite keys involving auto-inc (if that's even valid/used),
352 // or if we just want to be explicit.
353 // SQLite restriction: "INTEGER PRIMARY KEY" must be on the column definition for auto-increment.
354 // If we have AUTO_INCREMENT, it's already in BuildColumnDefinition.
355
356 std::erase_if(
357 pks, [](SqlColumnDeclaration const* col) { return col->primaryKey == SqlPrimaryKeyType::AUTO_INCREMENT; });
358
359 if (!pks.empty())
360 {
361 std::ranges::sort(pks, [](SqlColumnDeclaration const* a, SqlColumnDeclaration const* b) {
362 // If both have index, use it. If one has 0 (no index), treat it as "after" indexed ones?
363 // Or just rely on stable sort?
364 // Let's assume indices are properly set for composite keys.
365 // 1-based index vs 0. 0 means "unordered".
366 if (a->primaryKeyIndex != 0 && b->primaryKeyIndex != 0)
367 return a->primaryKeyIndex < b->primaryKeyIndex;
368 if (a->primaryKeyIndex != 0)
369 return true; // a comes first
370 if (b->primaryKeyIndex != 0)
371 return false; // b comes first
372 return false; // keep original order
373 });
374
375 sqlQueryString << ",\n PRIMARY KEY (";
376 for (size_t i = 0; i < pks.size(); ++i)
377 {
378 if (i > 0)
379 sqlQueryString << ", ";
380 sqlQueryString << '"' << pks[i]->name << '"';
381 }
382 sqlQueryString << ")";
383 }
384
385 sqlQueryString << foreignKeyConstraints;
386
387 sqlQueryString << "\n);";
388 return sqlQueryString.str();
389 }());
390
391 for (SqlColumnDeclaration const& column: columns)
392 {
393 if (column.index && column.primaryKey == SqlPrimaryKeyType::NONE)
394 {
395 // primary keys are always indexed
396 if (column.unique)
397 sqlQueries.emplace_back(std::format(R"(CREATE UNIQUE INDEX "{}_{}_index" ON "{}" ("{}");)",
398 tableName,
399 column.name,
400 tableName,
401 column.name));
402 else
403 sqlQueries.emplace_back(std::format(
404 R"(CREATE INDEX "{}_{}_index" ON "{}" ("{}");)", tableName, column.name, tableName, column.name));
405 }
406 }
407
408 return sqlQueries;
409 }
410
411 [[nodiscard]] StringList AlterTable(std::string_view /*schemaName*/,
412 std::string_view tableName,
413 std::vector<SqlAlterTableCommand> const& commands) const override
414 {
415 // SQLite doesn't support schemas - use table name only
416 auto const formatTable = [tableName]() {
417 return std::format(R"("{}")", tableName);
418 };
419
420 // SQLite has no native IF NOT EXISTS / IF EXISTS for columns. We emit each command
421 // as its own result entry; guarded commands are prefixed with a sentinel comment
422 // that the migration executor recognizes and uses to perform a pragma_table_info
423 // presence check at runtime.
424 StringList result;
425
426 using namespace SqlAlterTableCommands;
427 for (SqlAlterTableCommand const& command: commands)
428 {
429 std::string sql = std::visit(
430 detail::overloaded {
431 [&formatTable](RenameTable const& actualCommand) -> std::string {
432 return std::format(R"(ALTER TABLE {} RENAME TO "{}";)", formatTable(), actualCommand.newTableName);
433 },
434 [&formatTable, this](AddColumn const& actualCommand) -> std::string {
435 return std::format(R"(ALTER TABLE {} ADD COLUMN "{}" {} {};)",
436 formatTable(),
437 actualCommand.columnName,
438 ColumnType(actualCommand.columnType),
439 actualCommand.nullable == SqlNullable::NotNull ? "NOT NULL" : "NULL");
440 },
441 [&formatTable, this](AlterColumn const& actualCommand) -> std::string {
442 return std::format(R"(ALTER TABLE {} ALTER COLUMN "{}" {} {};)",
443 formatTable(),
444 actualCommand.columnName,
445 ColumnType(actualCommand.columnType),
446 actualCommand.nullable == SqlNullable::NotNull ? "NOT NULL" : "NULL");
447 },
448 [&formatTable](RenameColumn const& actualCommand) -> std::string {
449 return std::format(R"(ALTER TABLE {} RENAME COLUMN "{}" TO "{}";)",
450 formatTable(),
451 actualCommand.oldColumnName,
452 actualCommand.newColumnName);
453 },
454 [&formatTable](DropColumn const& actualCommand) -> std::string {
455 return std::format(R"(ALTER TABLE {} DROP COLUMN "{}";)", formatTable(), actualCommand.columnName);
456 },
457 [tableName](AddIndex const& actualCommand) -> std::string {
458 using namespace std::string_view_literals;
459 auto const uniqueStr = actualCommand.unique ? "UNIQUE "sv : ""sv;
460 return std::format(R"(CREATE {2}INDEX "{0}_{1}_index" ON "{0}" ("{1}");)",
461 tableName,
462 actualCommand.columnName,
463 uniqueStr);
464 },
465 [tableName](DropIndex const& actualCommand) -> std::string {
466 return std::format(R"(DROP INDEX "{0}_{1}_index";)", tableName, actualCommand.columnName);
467 },
468 [tableName](AddForeignKey const& actualCommand) -> std::string {
469 // SQLite cannot `ALTER TABLE … ADD CONSTRAINT`. The runtime executor
470 // recognizes this sentinel and rebuilds the table from sqlite_schema.
471 // All metadata the executor needs fits in the sentinel itself. We keep
472 // a commented-out equivalent MSSQL-style ALTER below so dry-run output
473 // stays readable.
474 return std::format(
475 R"(-- LIGHTWEIGHT_SQLITE_GUARD: ADD_FOREIGN_KEY "{0}" "{1}" "{2}" "{3}"
476-- ALTER TABLE "{0}" ADD {4};)",
477 tableName,
478 actualCommand.columnName,
479 actualCommand.referencedColumn.tableName,
480 actualCommand.referencedColumn.columnName,
481 BuildForeignKeyConstraint(tableName, actualCommand.columnName, actualCommand.referencedColumn));
482 },
483 [tableName](DropForeignKey const& actualCommand) -> std::string {
484 // SQLite has no `DROP CONSTRAINT`. Executor rebuilds the table without
485 // the matching `CONSTRAINT FK_<tbl>_<col>` clause.
486 return std::format(
487 R"(-- LIGHTWEIGHT_SQLITE_GUARD: DROP_FOREIGN_KEY "{0}" "{1}"
488-- ALTER TABLE "{0}" DROP CONSTRAINT "{2}";)",
489 tableName,
490 actualCommand.columnName,
492 std::array { std::string_view { actualCommand.columnName } }));
493 },
494 [tableName](AddCompositeForeignKey const& /*actualCommand*/) -> std::string {
495 // SQLite limitation: ALTER TABLE ADD CONSTRAINT not supported.
496 // We return empty string or comment to satisfy visitor.
497 return std::format(R"(-- AddCompositeForeignKey not supported for {};)", tableName);
498 },
499 [&formatTable, tableName, this](AddColumnIfNotExists const& actualCommand) -> std::string {
500 // SQLite doesn't support IF NOT EXISTS for ADD COLUMN.
501 // Emit a sentinel comment so the migration executor can presence-check
502 // via pragma_table_info() before running the ALTER TABLE.
503 return std::format(
504 R"(-- LIGHTWEIGHT_SQLITE_GUARD: ADD_COLUMN_IF_NOT_EXISTS "{0}" "{1}"
505ALTER TABLE {2} ADD COLUMN "{1}" {3} {4};)",
506 tableName,
507 actualCommand.columnName,
508 formatTable(),
509 ColumnType(actualCommand.columnType),
510 actualCommand.nullable == SqlNullable::NotNull ? "NOT NULL" : "NULL");
511 },
512 [&formatTable, tableName](DropColumnIfExists const& actualCommand) -> std::string {
513 // SQLite doesn't support IF EXISTS for DROP COLUMN; guarded like above.
514 return std::format(
515 R"(-- LIGHTWEIGHT_SQLITE_GUARD: DROP_COLUMN_IF_EXISTS "{0}" "{1}"
516ALTER TABLE {2} DROP COLUMN "{1}";)",
517 tableName,
518 actualCommand.columnName,
519 formatTable());
520 },
521 [tableName](DropIndexIfExists const& actualCommand) -> std::string {
522 return std::format(R"(DROP INDEX IF EXISTS "{0}_{1}_index";)", tableName, actualCommand.columnName);
523 },
524 },
525 command);
526
527 if (!sql.empty())
528 result.push_back(std::move(sql));
529 }
530
531 return result;
532 }
533
534 [[nodiscard]] std::string ColumnType(SqlColumnTypeDefinition const& type) const override
535 {
536 using namespace SqlColumnTypeDefinitions;
537 return std::visit(detail::overloaded {
538 [](Bigint const&) -> std::string { return "BIGINT"; },
539 [](Binary const&) -> std::string { return "BLOB"; },
540 [](Bool const&) -> std::string { return "BOOLEAN"; },
541 [](Char const& type) -> std::string { return std::format("CHAR({})", type.size); },
542 [](Date const&) -> std::string { return "DATE"; },
543 [](DateTime const&) -> std::string { return "DATETIME"; },
544 [](Decimal const& type) -> std::string {
545 return std::format("DECIMAL({}, {})", type.precision, type.scale);
546 },
547 [](Guid const&) -> std::string { return "GUID"; },
548 [](Integer const&) -> std::string { return "INTEGER"; },
549 [](NChar const& type) -> std::string { return std::format("NCHAR({})", type.size); },
550 [](NVarchar const& type) -> std::string { return std::format("NVARCHAR({})", type.size); },
551 [](Real const&) -> std::string { return "REAL"; },
552 [](Smallint const&) -> std::string { return "SMALLINT"; },
553 [](Text const&) -> std::string { return "TEXT"; },
554 [](Time const&) -> std::string { return "TIME"; },
555 [](Timestamp const&) -> std::string { return "TIMESTAMP"; },
556 [](Tinyint const&) -> std::string { return "TINYINT"; },
557 [](VarBinary const& type) -> std::string { return std::format("VARBINARY({})", type.size); },
558 [](Varchar const& type) -> std::string { return std::format("VARCHAR({})", type.size); },
559 },
560 type);
561 }
562
563 [[nodiscard]] StringList DropTable(std::string_view /*schemaName*/,
564 std::string_view const& tableName,
565 bool ifExists = false,
566 bool cascade = false) const override
567 {
568 // SQLite doesn't support CASCADE syntax, but if FK constraints are disabled
569 // (PRAGMA foreign_keys = OFF), dropping works. The cascade flag is ignored.
570 // SQLite doesn't support schemas - ignore schemaName parameter
571 (void) cascade;
572 if (ifExists)
573 return { std::format(R"(DROP TABLE IF EXISTS "{}";)", tableName) };
574 else
575 return { std::format(R"(DROP TABLE "{}";)", tableName) };
576 }
577
578 [[nodiscard]] std::string QueryServerVersion() const override
579 {
580 return "SELECT sqlite_version()";
581 }
582};
583
584} // namespace Lightweight
static std::string BuildForeignKeyConstraintName(std::string_view tableName, Range const &columns)
Builds the canonical foreign-key constraint name for a set of columns.
std::vector< std::string > StringList
Alias for a list of SQL statement strings.
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.