4#include "../SqlAdvisoryLock.hpp"
5#include "../SqlQueryFormatter.hpp"
6#include "../SqlRetryClassifier.hpp"
8#include <reflection-cpp/reflection.hpp>
15class SQLiteQueryFormatter:
public SqlQueryFormatter
21 [[nodiscard]]
static std::string FormatFromTable(std::string_view table)
23 if (!table.empty() && (table.front() ==
'"' || table.front() ==
'['))
24 return std::string(table);
25 return std::format(R
"("{}")", table);
34 [[nodiscard]]
bool RequiresTableRebuildForSchemaChange() const noexcept
override
49 [[nodiscard]]
static std::string BuildColumnExistsQuery(std::string_view tableName, std::string_view columnName)
51 return std::format(R
"(SELECT COUNT(*) FROM pragma_table_info('{}') WHERE name = '{}';)", tableName, columnName);
54 [[nodiscard]] std::string Insert(std::string_view intoTable,
55 std::string_view fields,
56 std::string_view values) const override
58 return std::format(R
"(INSERT INTO "{}" ({}) VALUES ({}))", intoTable, fields, values);
61 [[nodiscard]] std::string Insert(std::string_view ,
62 std::string_view intoTable,
63 std::string_view fields,
64 std::string_view values)
const override
67 return std::format(R
"(INSERT INTO "{}" ({}) VALUES ({}))", intoTable, fields, values);
70 [[nodiscard]] std::string QueryLastInsertId(std::string_view )
const override
73 return "SELECT LAST_INSERT_ROWID()";
76 [[nodiscard]] std::string_view BooleanLiteral(
bool literalValue)
const noexcept override
78 return literalValue ?
"TRUE" :
"FALSE";
81 [[nodiscard]] std::string_view DateFunction() const noexcept
override
86 [[nodiscard]] std::string StringLiteral(std::string_view value)
const noexcept override
92 escaped.reserve(value.size() + 2);
94 for (
char const c: value)
105 [[nodiscard]] std::string StringLiteral(
char value)
const noexcept override
109 return std::format(
"'{}'", value);
112 [[nodiscard]] std::string BinaryLiteral(std::span<uint8_t const> data)
const override
115 result.reserve((data.size() * 2) + 3);
117 for (uint8_t
byte: data)
118 result += std::format(
"{:02X}", byte);
123 [[nodiscard]] std::string QualifiedTableName(std::string_view schema, std::string_view table)
const override
127 return std::format(R
"("{}")", table);
129 return std::format(R
"("{}"."{}")", schema, table);
132 [[nodiscard]] std::string SelectCount(bool distinct,
133 std::string_view fromTable,
134 std::string_view fromTableAlias,
135 std::string_view tableJoins,
136 std::string_view whereCondition,
137 std::string_view groupBy)
const override
139 auto const formattedTable = FormatFromTable(fromTable);
140 if (fromTableAlias.empty())
141 return std::format(
"SELECT{} COUNT(*) FROM {}{}{}{}",
142 distinct ?
" DISTINCT" :
"",
148 return std::format(R
"(SELECT{} COUNT(*) FROM {} AS "{}"{}{}{})",
149 distinct ? " DISTINCT" :
"",
157 [[nodiscard]] std::string SelectAll(
bool distinct,
159 std::string_view fields,
160 std::string_view fromTable,
161 std::string_view fromTableAlias,
162 std::string_view tableJoins,
163 std::string_view whereCondition,
164 std::string_view orderBy,
165 std::string_view groupBy)
const override
167 std::stringstream sqlQueryString;
168 sqlQueryString <<
"SELECT ";
170 sqlQueryString <<
"DISTINCT ";
171 sqlQueryString << fields;
172 sqlQueryString <<
" FROM " << FormatFromTable(fromTable);
173 if (!fromTableAlias.empty())
174 sqlQueryString <<
" AS \"" << fromTableAlias <<
'"';
175 sqlQueryString << tableJoins;
176 sqlQueryString << whereCondition;
177 sqlQueryString << groupBy;
178 sqlQueryString << orderBy;
180 return sqlQueryString.str();
183 [[nodiscard]] std::string SelectFirst(
bool distinct,
185 std::string_view fields,
186 std::string_view fromTable,
187 std::string_view fromTableAlias,
188 std::string_view tableJoins,
189 std::string_view whereCondition,
190 std::string_view orderBy,
191 std::string_view groupBy,
192 size_t count)
const override
194 std::stringstream sqlQueryString;
195 sqlQueryString <<
"SELECT ";
197 sqlQueryString <<
"DISTINCT ";
198 sqlQueryString << fields;
199 sqlQueryString <<
" FROM " << FormatFromTable(fromTable);
200 if (!fromTableAlias.empty())
201 sqlQueryString <<
" AS \"" << fromTableAlias <<
"\"";
202 sqlQueryString << tableJoins;
203 sqlQueryString << whereCondition;
204 sqlQueryString << groupBy;
205 sqlQueryString << orderBy;
206 sqlQueryString <<
" LIMIT " << count;
207 return sqlQueryString.str();
210 [[nodiscard]] std::string SelectRange(
bool distinct,
212 std::string_view fields,
213 std::string_view fromTable,
214 std::string_view fromTableAlias,
215 std::string_view tableJoins,
216 std::string_view whereCondition,
217 std::string_view orderBy,
218 std::string_view groupBy,
220 std::size_t limit)
const override
222 std::stringstream sqlQueryString;
223 sqlQueryString <<
"SELECT ";
225 sqlQueryString <<
"DISTINCT ";
226 sqlQueryString << fields;
227 sqlQueryString <<
" FROM " << FormatFromTable(fromTable);
228 if (!fromTableAlias.empty())
229 sqlQueryString <<
" AS \"" << fromTableAlias <<
"\"";
230 sqlQueryString << tableJoins;
231 sqlQueryString << whereCondition;
232 sqlQueryString << groupBy;
233 sqlQueryString << orderBy;
234 sqlQueryString <<
" LIMIT " << limit <<
" OFFSET " << offset;
235 return sqlQueryString.str();
238 [[nodiscard]] std::string Update(std::string_view table,
239 std::string_view tableAlias,
240 std::string_view setFields,
241 std::string_view whereCondition)
const override
243 auto const formattedTable = FormatFromTable(table);
244 if (tableAlias.empty())
245 return std::format(
"UPDATE {} SET {}{}", formattedTable, setFields, whereCondition);
247 return std::format(R
"(UPDATE {} AS "{}" SET {}{})", formattedTable, tableAlias, setFields, whereCondition);
250 [[nodiscard]] std::string Delete(std::string_view fromTable,
251 std::string_view fromTableAlias,
252 std::string_view tableJoins,
253 std::string_view whereCondition) const override
255 auto const formattedTable = FormatFromTable(fromTable);
256 if (fromTableAlias.empty())
257 return std::format(
"DELETE FROM {}{}{}", formattedTable, tableJoins, whereCondition);
259 return std::format(R
"(DELETE FROM {} AS "{}"{}{})", formattedTable, fromTableAlias, tableJoins, whereCondition);
262 [[nodiscard]] virtual std::string BuildColumnDefinition(SqlColumnDeclaration
const& column)
const
264 std::stringstream sqlQueryString;
266 sqlQueryString <<
'"' << column.name <<
"\" ";
268 if (column.primaryKey != SqlPrimaryKeyType::AUTO_INCREMENT)
269 sqlQueryString << ColumnType(column.type);
271 sqlQueryString << ColumnType(SqlColumnTypeDefinitions::Integer {});
274 sqlQueryString <<
" NOT NULL";
276 if (column.primaryKey == SqlPrimaryKeyType::AUTO_INCREMENT)
277 sqlQueryString <<
" PRIMARY KEY AUTOINCREMENT";
278 else if (column.primaryKey == SqlPrimaryKeyType::NONE && !column.index && column.unique)
279 sqlQueryString <<
" UNIQUE";
281 if (!column.defaultValue.empty())
282 sqlQueryString <<
" DEFAULT " << column.defaultValue;
284 return sqlQueryString.str();
287 [[nodiscard]]
static std::string BuildForeignKeyConstraint(std::string_view tableName,
288 std::string_view columnName,
289 SqlForeignKeyReferenceDefinition
const& referencedColumn)
296 return std::format(R
"(CONSTRAINT "{}" FOREIGN KEY ("{}") REFERENCES "{}"("{}"))",
299 referencedColumn.tableName,
300 referencedColumn.columnName);
304 [[nodiscard]]
StringList CreateTable(std::string_view schema,
305 std::string_view tableName,
306 std::vector<SqlColumnDeclaration>
const& columns,
307 std::vector<SqlCompositeForeignKeyConstraint>
const& foreignKeys,
308 bool ifNotExists =
false)
const override
313 sqlQueries.emplace_back([&]() {
314 std::stringstream sqlQueryString;
315 sqlQueryString <<
"CREATE TABLE ";
317 sqlQueryString <<
"IF NOT EXISTS ";
320 sqlQueryString <<
"\"" << tableName <<
"\" (";
321 std::vector<SqlColumnDeclaration const*> pks;
322 size_t currentColumn = 0;
323 std::string foreignKeyConstraints;
324 for (SqlColumnDeclaration
const& column: columns)
326 if (currentColumn > 0)
327 sqlQueryString <<
",";
329 sqlQueryString <<
"\n ";
330 sqlQueryString << BuildColumnDefinition(column);
332 if (column.primaryKey != SqlPrimaryKeyType::NONE)
333 pks.push_back(&column);
335 if (column.foreignKey)
337 foreignKeyConstraints +=
",\n ";
338 foreignKeyConstraints += BuildForeignKeyConstraint(tableName, column.name, *column.foreignKey);
342 for (SqlCompositeForeignKeyConstraint
const& fk: foreignKeys)
348 foreignKeyConstraints +=
",\n CONSTRAINT \"";
350 foreignKeyConstraints +=
"\" FOREIGN KEY (";
351 for (
size_t i = 0; i < fk.columns.size(); ++i)
354 foreignKeyConstraints +=
", ";
355 foreignKeyConstraints +=
'"' + fk.columns[i] +
'"';
357 foreignKeyConstraints +=
") REFERENCES \"";
358 foreignKeyConstraints += fk.referencedTableName;
359 foreignKeyConstraints +=
"\" (";
360 for (
size_t i = 0; i < fk.referencedColumns.size(); ++i)
363 foreignKeyConstraints +=
", ";
364 foreignKeyConstraints +=
'"' + fk.referencedColumns[i] +
'"';
366 foreignKeyConstraints +=
")";
376 pks, [](SqlColumnDeclaration
const* col) {
return col->primaryKey == SqlPrimaryKeyType::AUTO_INCREMENT; });
380 std::ranges::sort(pks, [](SqlColumnDeclaration
const* a, SqlColumnDeclaration
const* b) {
385 if (a->primaryKeyIndex != 0 && b->primaryKeyIndex != 0)
386 return a->primaryKeyIndex < b->primaryKeyIndex;
387 if (a->primaryKeyIndex != 0)
389 if (b->primaryKeyIndex != 0)
394 sqlQueryString <<
",\n PRIMARY KEY (";
395 for (
size_t i = 0; i < pks.size(); ++i)
398 sqlQueryString <<
", ";
399 sqlQueryString <<
'"' << pks[i]->name <<
'"';
401 sqlQueryString <<
")";
404 sqlQueryString << foreignKeyConstraints;
406 sqlQueryString <<
"\n);";
407 return sqlQueryString.str();
410 for (SqlColumnDeclaration
const& column: columns)
412 if (column.index && column.primaryKey == SqlPrimaryKeyType::NONE)
416 sqlQueries.emplace_back(std::format(R
"(CREATE UNIQUE INDEX "{}_{}_index" ON "{}" ("{}");)",
422 sqlQueries.emplace_back(std::format(
423 R
"(CREATE INDEX "{}_{}_index" ON "{}" ("{}");)", tableName, column.name, tableName, column.name));
436 [[nodiscard]]
static std::string EscapeSentinelField(std::string_view identifier)
439 out.reserve(identifier.size());
440 for (
auto const ch: identifier)
449 [[nodiscard]] std::string FormatAlterTableCommand(std::string_view tableName,
SqlAlterTableCommand const& command)
const
451 auto const formatTable = [tableName]() {
452 return std::format(R
"("{}")", tableName);
455 using namespace SqlAlterTableCommands;
458 [&formatTable](RenameTable
const& actualCommand) -> std::string {
459 return std::format(R
"(ALTER TABLE {} RENAME TO "{}";)", formatTable(), actualCommand.newTableName);
461 [&formatTable, this](AddColumn
const& actualCommand) -> std::string {
462 return std::format(R
"(ALTER TABLE {} ADD COLUMN "{}" {} {};)",
464 actualCommand.columnName,
465 ColumnType(actualCommand.columnType),
466 actualCommand.nullable == SqlNullable::NotNull ? "NOT NULL" :
"NULL");
468 [&formatTable, tableName,
this](AlterColumn
const& actualCommand) -> std::string {
476 R
"(-- LIGHTWEIGHT_SQLITE_GUARD: ALTER_COLUMN "{0}" "{1}" "{2}" "{3}"
477-- ALTER TABLE {4} ALTER COLUMN "{1}" {2} {3};)",
478 EscapeSentinelField(tableName),
479 EscapeSentinelField(actualCommand.columnName),
480 ColumnType(actualCommand.columnType),
481 actualCommand.nullable == SqlNullable::NotNull ? "NOT NULL" :
"NULL",
484 [&formatTable](RenameColumn
const& actualCommand) -> std::string {
485 return std::format(R
"(ALTER TABLE {} RENAME COLUMN "{}" TO "{}";)",
487 actualCommand.oldColumnName,
488 actualCommand.newColumnName);
490 [&formatTable](DropColumn const& actualCommand) -> std::string {
491 return std::format(R
"(ALTER TABLE {} DROP COLUMN "{}";)", formatTable(), actualCommand.columnName);
493 [tableName](AddIndex const& actualCommand) -> std::string {
494 using namespace std::string_view_literals;
495 auto const uniqueStr = actualCommand.unique ?
"UNIQUE "sv :
""sv;
496 return std::format(R
"(CREATE {2}INDEX "{0}_{1}_index" ON "{0}" ("{1}");)",
498 actualCommand.columnName,
501 [tableName](DropIndex const& actualCommand) -> std::string {
502 return std::format(R
"(DROP INDEX "{0}_{1}_index";)", tableName, actualCommand.columnName);
504 [tableName](AddForeignKey const& actualCommand) -> std::string {
511 R
"(-- LIGHTWEIGHT_SQLITE_GUARD: ADD_FOREIGN_KEY "{0}" "{1}" "{2}" "{3}"
512-- ALTER TABLE "{0}" ADD {4};)",
514 actualCommand.columnName,
515 actualCommand.referencedColumn.tableName,
516 actualCommand.referencedColumn.columnName,
517 BuildForeignKeyConstraint(tableName, actualCommand.columnName, actualCommand.referencedColumn));
519 [tableName](DropForeignKey const& actualCommand) -> std::string {
523 R
"(-- LIGHTWEIGHT_SQLITE_GUARD: DROP_FOREIGN_KEY "{0}" "{1}"
524-- ALTER TABLE "{0}" DROP CONSTRAINT "{2}";)",
526 actualCommand.columnName,
528 std::array { std::string_view { actualCommand.columnName } }));
530 [tableName](AddCompositeForeignKey const& actualCommand) -> std::string {
537 auto const joinComma = [](std::vector<std::string>
const& v) {
539 for (
size_t i = 0; i < v.size(); ++i)
547 auto const joinQuoted = [](std::vector<std::string>
const& v) {
549 for (
size_t i = 0; i < v.size(); ++i)
561 R
"(-- LIGHTWEIGHT_SQLITE_GUARD: ADD_COMPOSITE_FOREIGN_KEY "{0}" "{1}" "{2}" "{3}"
562-- ALTER TABLE "{0}" ADD CONSTRAINT "{4}" FOREIGN KEY ({5}) REFERENCES "{2}"({6});)",
564 joinComma(actualCommand.columns),
565 actualCommand.referencedTableName,
566 joinComma(actualCommand.referencedColumns),
568 joinQuoted(actualCommand.columns),
569 joinQuoted(actualCommand.referencedColumns));
571 [&formatTable, tableName, this](AddColumnIfNotExists
const& actualCommand) -> std::string {
576 R
"(-- LIGHTWEIGHT_SQLITE_GUARD: ADD_COLUMN_IF_NOT_EXISTS "{0}" "{1}"
577ALTER TABLE {2} ADD COLUMN "{1}" {3} {4};)",
579 actualCommand.columnName,
581 ColumnType(actualCommand.columnType),
582 actualCommand.nullable == SqlNullable::NotNull ? "NOT NULL" :
"NULL");
584 [&formatTable, tableName](DropColumnIfExists
const& actualCommand) -> std::string {
587 R
"(-- LIGHTWEIGHT_SQLITE_GUARD: DROP_COLUMN_IF_EXISTS "{0}" "{1}"
588ALTER TABLE {2} DROP COLUMN "{1}";)",
590 actualCommand.columnName,
593 [tableName](DropIndexIfExists const& actualCommand) -> std::string {
594 return std::format(R
"(DROP INDEX IF EXISTS "{0}_{1}_index";)", tableName, actualCommand.columnName);
601 [[nodiscard]]
StringList AlterTable(std::string_view ,
602 std::string_view tableName,
603 std::vector<SqlAlterTableCommand>
const& commands)
const override
612 auto sql = FormatAlterTableCommand(tableName, command);
614 result.push_back(std::move(sql));
619 [[nodiscard]] std::string ColumnType(SqlColumnTypeDefinition
const& type)
const override
621 using namespace SqlColumnTypeDefinitions;
622 return std::visit(detail::overloaded {
623 [](Bigint
const&) -> std::string {
return "BIGINT"; },
624 [](Binary
const&) -> std::string {
return "BLOB"; },
625 [](Bool
const&) -> std::string {
return "BOOLEAN"; },
626 [](Char
const& type) -> std::string {
return std::format(
"CHAR({})", type.size); },
627 [](Date
const&) -> std::string {
return "DATE"; },
628 [](DateTime
const&) -> std::string {
return "DATETIME"; },
629 [](Decimal
const& type) -> std::string {
630 return std::format(
"DECIMAL({}, {})", type.precision, type.scale);
632 [](Guid
const&) -> std::string {
return "GUID"; },
633 [](Integer
const&) -> std::string {
return "INTEGER"; },
634 [](NChar
const& type) -> std::string {
return std::format(
"NCHAR({})", type.size); },
635 [](NVarchar
const& type) -> std::string {
return std::format(
"NVARCHAR({})", type.size); },
636 [](Real
const&) -> std::string {
return "REAL"; },
637 [](Smallint
const&) -> std::string {
return "SMALLINT"; },
638 [](Text
const&) -> std::string {
return "TEXT"; },
639 [](Time
const&) -> std::string {
return "TIME"; },
640 [](Timestamp
const&) -> std::string {
return "TIMESTAMP"; },
641 [](Tinyint
const&) -> std::string {
return "TINYINT"; },
642 [](VarBinary
const& type) -> std::string {
return std::format(
"VARBINARY({})", type.size); },
643 [](Varchar
const& type) -> std::string {
return std::format(
"VARCHAR({})", type.size); },
648 [[nodiscard]]
StringList DropTable(std::string_view ,
649 std::string_view
const& tableName,
650 bool ifExists =
false,
651 bool cascade =
false)
const override
658 return { std::format(R
"(DROP TABLE IF EXISTS "{}";)", tableName) };
660 return { std::format(R
"(DROP TABLE "{}";)", tableName) };
663 [[nodiscard]] std::string QueryServerVersion() const override
665 return "SELECT sqlite_version()";
676 [[nodiscard]] SqlAdvisoryLockHandler
const& AdvisoryLockOps()
const override
678 return SqliteAdvisoryLockOps();
683 [[nodiscard]] SqlRetryClassifier
const& RetryOps() const noexcept
override
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 SqlRetryClassifier const & SqliteRetryOps() noexcept
Returns the SQLite-specific singleton classifier.