4#include "../SqlAdvisoryLock.hpp"
5#include "../SqlQueryFormatter.hpp"
6#include "../SqlRetryClassifier.hpp"
8#include <reflection-cpp/reflection.hpp>
21class SQLiteQueryFormatter:
public SqlQueryFormatter
27 [[nodiscard]]
static std::string FormatFromTable(std::string_view table)
29 if (!table.empty() && (table.front() ==
'"' || table.front() ==
'['))
30 return std::string(table);
31 return std::format(R
"("{}")", table);
40 [[nodiscard]]
bool RequiresTableRebuildForSchemaChange() const noexcept
override
45 [[nodiscard]]
bool IdentifiersAreCaseInsensitive() const noexcept
override
50 [[nodiscard]]
bool ComparesFixedCharWithPadSemantics() const noexcept
override
55 [[nodiscard]]
bool BoundsBulkLoadTransactionSize() const noexcept
override
60 [[nodiscard]]
bool SupportsSchemas() const noexcept
override
75 [[nodiscard]]
static std::string BuildColumnExistsQuery(std::string_view tableName, std::string_view columnName)
77 return std::format(R
"(SELECT COUNT(*) FROM pragma_table_info('{}') WHERE name = '{}';)", tableName, columnName);
80 [[nodiscard]] std::string Insert(std::string_view intoTable,
81 std::string_view fields,
82 std::string_view values) const override
84 return std::format(R
"(INSERT INTO "{}" ({}) VALUES ({}))", intoTable, fields, values);
87 [[nodiscard]] std::string Insert(std::string_view schema,
88 std::string_view intoTable,
89 std::string_view fields,
90 std::string_view values) const override
93 return std::format(
"INSERT INTO {} ({}) VALUES ({})",
94 FormatTableName(SupportsSchemas() ? schema : std::string_view {}, intoTable),
99 [[nodiscard]] std::string QueryLastInsertId(std::string_view )
const override
102 return "SELECT LAST_INSERT_ROWID()";
105 [[nodiscard]] std::string_view BooleanLiteral(
bool literalValue)
const noexcept override
107 return literalValue ?
"TRUE" :
"FALSE";
110 [[nodiscard]] std::string_view DateFunction() const noexcept
override
115 [[nodiscard]] std::string StringLiteral(std::string_view value)
const noexcept override
121 escaped.reserve(value.size() + 2);
123 for (
char const c: value)
134 [[nodiscard]] std::string StringLiteral(
char value)
const noexcept override
138 return std::format(
"'{}'", value);
141 [[nodiscard]] std::string BinaryLiteral(std::span<uint8_t const> data)
const override
144 result.reserve((data.size() * 2) + 3);
146 for (uint8_t
byte: data)
147 result += std::format(
"{:02X}", byte);
152 [[nodiscard]] std::string QualifiedTableName(std::string_view schema, std::string_view table)
const override
156 return std::format(R
"("{}")", table);
158 return std::format(R
"("{}"."{}")", schema, table);
161 [[nodiscard]] std::string SelectCount(bool distinct,
162 std::string_view fromTable,
163 std::string_view fromTableAlias,
164 std::string_view tableJoins,
165 std::string_view whereCondition,
166 std::string_view groupBy)
const override
168 auto const formattedTable = FormatFromTable(fromTable);
169 if (fromTableAlias.empty())
170 return std::format(
"SELECT{} COUNT(*) FROM {}{}{}{}",
171 distinct ?
" DISTINCT" :
"",
177 return std::format(R
"(SELECT{} COUNT(*) FROM {} AS "{}"{}{}{})",
178 distinct ? " DISTINCT" :
"",
186 [[nodiscard]] std::string SelectAll(
bool distinct,
188 std::string_view fields,
189 std::string_view fromTable,
190 std::string_view fromTableAlias,
191 std::string_view tableJoins,
192 std::string_view whereCondition,
193 std::string_view orderBy,
194 std::string_view groupBy)
const override
196 std::stringstream sqlQueryString;
197 sqlQueryString <<
"SELECT ";
199 sqlQueryString <<
"DISTINCT ";
200 sqlQueryString << fields;
201 sqlQueryString <<
" FROM " << FormatFromTable(fromTable);
202 if (!fromTableAlias.empty())
203 sqlQueryString <<
" AS \"" << fromTableAlias <<
'"';
204 sqlQueryString << tableJoins;
205 sqlQueryString << whereCondition;
206 sqlQueryString << groupBy;
207 sqlQueryString << orderBy;
209 return sqlQueryString.str();
212 [[nodiscard]] std::string SelectFirst(
bool distinct,
214 std::string_view fields,
215 std::string_view fromTable,
216 std::string_view fromTableAlias,
217 std::string_view tableJoins,
218 std::string_view whereCondition,
219 std::string_view orderBy,
220 std::string_view groupBy,
221 size_t count)
const override
223 std::stringstream sqlQueryString;
224 sqlQueryString <<
"SELECT ";
226 sqlQueryString <<
"DISTINCT ";
227 sqlQueryString << fields;
228 sqlQueryString <<
" FROM " << FormatFromTable(fromTable);
229 if (!fromTableAlias.empty())
230 sqlQueryString <<
" AS \"" << fromTableAlias <<
"\"";
231 sqlQueryString << tableJoins;
232 sqlQueryString << whereCondition;
233 sqlQueryString << groupBy;
234 sqlQueryString << orderBy;
235 sqlQueryString <<
" LIMIT " << count;
236 return sqlQueryString.str();
239 [[nodiscard]] std::string SelectRange(
bool distinct,
241 std::string_view fields,
242 std::string_view fromTable,
243 std::string_view fromTableAlias,
244 std::string_view tableJoins,
245 std::string_view whereCondition,
246 std::string_view orderBy,
247 std::string_view groupBy,
249 std::size_t limit)
const override
251 std::stringstream sqlQueryString;
252 sqlQueryString <<
"SELECT ";
254 sqlQueryString <<
"DISTINCT ";
255 sqlQueryString << fields;
256 sqlQueryString <<
" FROM " << FormatFromTable(fromTable);
257 if (!fromTableAlias.empty())
258 sqlQueryString <<
" AS \"" << fromTableAlias <<
"\"";
259 sqlQueryString << tableJoins;
260 sqlQueryString << whereCondition;
261 sqlQueryString << groupBy;
262 sqlQueryString << orderBy;
263 sqlQueryString <<
" LIMIT " << limit <<
" OFFSET " << offset;
264 return sqlQueryString.str();
267 [[nodiscard]] std::string Update(std::string_view table,
268 std::string_view tableAlias,
269 std::string_view setFields,
270 std::string_view whereCondition)
const override
272 auto const formattedTable = FormatFromTable(table);
273 if (tableAlias.empty())
274 return std::format(
"UPDATE {} SET {}{}", formattedTable, setFields, whereCondition);
276 return std::format(R
"(UPDATE {} AS "{}" SET {}{})", formattedTable, tableAlias, setFields, whereCondition);
279 [[nodiscard]] std::string Delete(std::string_view fromTable,
280 std::string_view fromTableAlias,
281 std::string_view tableJoins,
282 std::string_view whereCondition) const override
284 auto const formattedTable = FormatFromTable(fromTable);
285 if (fromTableAlias.empty())
286 return std::format(
"DELETE FROM {}{}{}", formattedTable, tableJoins, whereCondition);
288 return std::format(R
"(DELETE FROM {} AS "{}"{}{})", formattedTable, fromTableAlias, tableJoins, whereCondition);
291 [[nodiscard]] virtual std::string BuildColumnDefinition(SqlColumnDeclaration
const& column)
const
293 std::stringstream sqlQueryString;
295 sqlQueryString <<
'"' << column.name <<
"\" ";
297 if (column.primaryKey != SqlPrimaryKeyType::AUTO_INCREMENT)
298 sqlQueryString << ColumnType(column.type);
300 sqlQueryString << ColumnType(SqlColumnTypeDefinitions::Integer {});
303 sqlQueryString <<
" NOT NULL";
305 if (column.primaryKey == SqlPrimaryKeyType::AUTO_INCREMENT)
306 sqlQueryString <<
" PRIMARY KEY AUTOINCREMENT";
307 else if (column.primaryKey == SqlPrimaryKeyType::NONE && !column.index && column.unique)
308 sqlQueryString <<
" UNIQUE";
310 if (!column.defaultValue.empty())
311 sqlQueryString <<
" DEFAULT " << column.defaultValue;
313 return sqlQueryString.str();
316 [[nodiscard]]
static std::string BuildForeignKeyConstraint(std::string_view tableName,
317 std::string_view columnName,
318 SqlForeignKeyReferenceDefinition
const& referencedColumn)
325 return std::format(R
"(CONSTRAINT "{}" FOREIGN KEY ("{}") REFERENCES "{}"("{}"))",
328 referencedColumn.tableName,
329 referencedColumn.columnName);
333 [[nodiscard]]
StringList CreateTable(std::string_view schema,
334 std::string_view tableName,
335 std::vector<SqlColumnDeclaration>
const& columns,
336 std::vector<SqlCompositeForeignKeyConstraint>
const& foreignKeys,
337 bool ifNotExists =
false)
const override
341 auto const qualifiedTableName =
FormatTableName(SupportsSchemas() ? schema : std::string_view {}, tableName);
344 sqlQueries.emplace_back([&]() {
345 std::stringstream sqlQueryString;
346 sqlQueryString <<
"CREATE TABLE ";
348 sqlQueryString <<
"IF NOT EXISTS ";
349 sqlQueryString << qualifiedTableName <<
" (";
350 std::vector<SqlColumnDeclaration const*> pks;
351 size_t currentColumn = 0;
352 std::string foreignKeyConstraints;
353 for (SqlColumnDeclaration
const& column: columns)
355 if (currentColumn > 0)
356 sqlQueryString <<
",";
358 sqlQueryString <<
"\n ";
359 sqlQueryString << BuildColumnDefinition(column);
361 if (column.primaryKey != SqlPrimaryKeyType::NONE)
362 pks.push_back(&column);
364 if (column.foreignKey)
366 foreignKeyConstraints +=
",\n ";
367 foreignKeyConstraints += BuildForeignKeyConstraint(tableName, column.name, *column.foreignKey);
371 for (SqlCompositeForeignKeyConstraint
const& fk: foreignKeys)
377 foreignKeyConstraints +=
",\n CONSTRAINT \"";
379 foreignKeyConstraints +=
"\" FOREIGN KEY (";
380 for (
size_t i = 0; i < fk.columns.size(); ++i)
383 foreignKeyConstraints +=
", ";
384 foreignKeyConstraints +=
'"' + fk.columns[i] +
'"';
386 foreignKeyConstraints +=
") REFERENCES \"";
387 foreignKeyConstraints += fk.referencedTableName;
388 foreignKeyConstraints +=
"\" (";
389 for (
size_t i = 0; i < fk.referencedColumns.size(); ++i)
392 foreignKeyConstraints +=
", ";
393 foreignKeyConstraints +=
'"' + fk.referencedColumns[i] +
'"';
395 foreignKeyConstraints +=
")";
405 pks, [](SqlColumnDeclaration
const* col) {
return col->primaryKey == SqlPrimaryKeyType::AUTO_INCREMENT; });
409 std::ranges::sort(pks, [](SqlColumnDeclaration
const* a, SqlColumnDeclaration
const* b) {
414 if (a->primaryKeyIndex != 0 && b->primaryKeyIndex != 0)
415 return a->primaryKeyIndex < b->primaryKeyIndex;
416 if (a->primaryKeyIndex != 0)
418 if (b->primaryKeyIndex != 0)
423 sqlQueryString <<
",\n PRIMARY KEY (";
424 for (
size_t i = 0; i < pks.size(); ++i)
427 sqlQueryString <<
", ";
428 sqlQueryString <<
'"' << pks[i]->name <<
'"';
430 sqlQueryString <<
")";
433 sqlQueryString << foreignKeyConstraints;
435 sqlQueryString <<
"\n);";
436 return sqlQueryString.str();
439 for (SqlColumnDeclaration
const& column: columns)
441 if (column.index && column.primaryKey == SqlPrimaryKeyType::NONE)
445 sqlQueries.emplace_back(std::format(R
"(CREATE UNIQUE INDEX "{}_{}_index" ON {} ("{}");)",
451 sqlQueries.emplace_back(std::format(R
"(CREATE INDEX "{}_{}_index" ON {} ("{}");)",
468 [[nodiscard]]
static std::string EscapeSentinelField(std::string_view identifier)
471 out.reserve(identifier.size());
472 for (
auto const ch: identifier)
481 [[nodiscard]] std::string FormatAlterTableCommand(std::string_view tableName,
SqlAlterTableCommand const& command)
const
483 auto const formatTable = [tableName]() {
484 return std::format(R
"("{}")", tableName);
487 using namespace SqlAlterTableCommands;
490 [&formatTable](RenameTable
const& actualCommand) -> std::string {
491 return std::format(R
"(ALTER TABLE {} RENAME TO "{}";)", formatTable(), actualCommand.newTableName);
493 [&formatTable, this](AddColumn
const& actualCommand) -> std::string {
494 return std::format(R
"(ALTER TABLE {} ADD COLUMN "{}" {} {};)",
496 actualCommand.columnName,
497 ColumnType(actualCommand.columnType),
498 actualCommand.nullable == SqlNullable::NotNull ? "NOT NULL" :
"NULL");
500 [&formatTable, tableName,
this](AlterColumn
const& actualCommand) -> std::string {
508 R
"(-- LIGHTWEIGHT_SQLITE_GUARD: ALTER_COLUMN "{0}" "{1}" "{2}" "{3}"
509-- ALTER TABLE {4} ALTER COLUMN "{1}" {2} {3};)",
510 EscapeSentinelField(tableName),
511 EscapeSentinelField(actualCommand.columnName),
512 ColumnType(actualCommand.columnType),
513 actualCommand.nullable == SqlNullable::NotNull ? "NOT NULL" :
"NULL",
516 [&formatTable](RenameColumn
const& actualCommand) -> std::string {
517 return std::format(R
"(ALTER TABLE {} RENAME COLUMN "{}" TO "{}";)",
519 actualCommand.oldColumnName,
520 actualCommand.newColumnName);
522 [&formatTable](DropColumn const& actualCommand) -> std::string {
523 return std::format(R
"(ALTER TABLE {} DROP COLUMN "{}";)", formatTable(), actualCommand.columnName);
525 [tableName](AddIndex const& actualCommand) -> std::string {
526 using namespace std::string_view_literals;
527 auto const uniqueStr = actualCommand.unique ?
"UNIQUE "sv :
""sv;
528 return std::format(R
"(CREATE {2}INDEX "{0}_{1}_index" ON "{0}" ("{1}");)",
530 actualCommand.columnName,
533 [tableName](DropIndex const& actualCommand) -> std::string {
534 return std::format(R
"(DROP INDEX "{0}_{1}_index";)", tableName, actualCommand.columnName);
536 [tableName](AddForeignKey const& actualCommand) -> std::string {
543 R
"(-- LIGHTWEIGHT_SQLITE_GUARD: ADD_FOREIGN_KEY "{0}" "{1}" "{2}" "{3}"
544-- ALTER TABLE "{0}" ADD {4};)",
546 actualCommand.columnName,
547 actualCommand.referencedColumn.tableName,
548 actualCommand.referencedColumn.columnName,
549 BuildForeignKeyConstraint(tableName, actualCommand.columnName, actualCommand.referencedColumn));
551 [tableName](DropForeignKey const& actualCommand) -> std::string {
555 R
"(-- LIGHTWEIGHT_SQLITE_GUARD: DROP_FOREIGN_KEY "{0}" "{1}"
556-- ALTER TABLE "{0}" DROP CONSTRAINT "{2}";)",
558 actualCommand.columnName,
560 std::array { std::string_view { actualCommand.columnName } }));
562 [tableName](AddCompositeForeignKey const& actualCommand) -> std::string {
569 auto const joinComma = [](std::vector<std::string>
const& v) {
571 for (
size_t i = 0; i < v.size(); ++i)
579 auto const joinQuoted = [](std::vector<std::string>
const& v) {
581 for (
size_t i = 0; i < v.size(); ++i)
593 R
"(-- LIGHTWEIGHT_SQLITE_GUARD: ADD_COMPOSITE_FOREIGN_KEY "{0}" "{1}" "{2}" "{3}"
594-- ALTER TABLE "{0}" ADD CONSTRAINT "{4}" FOREIGN KEY ({5}) REFERENCES "{2}"({6});)",
596 joinComma(actualCommand.columns),
597 actualCommand.referencedTableName,
598 joinComma(actualCommand.referencedColumns),
600 joinQuoted(actualCommand.columns),
601 joinQuoted(actualCommand.referencedColumns));
603 [&formatTable, tableName, this](AddColumnIfNotExists
const& actualCommand) -> std::string {
608 R
"(-- LIGHTWEIGHT_SQLITE_GUARD: ADD_COLUMN_IF_NOT_EXISTS "{0}" "{1}"
609ALTER TABLE {2} ADD COLUMN "{1}" {3} {4};)",
611 actualCommand.columnName,
613 ColumnType(actualCommand.columnType),
614 actualCommand.nullable == SqlNullable::NotNull ? "NOT NULL" :
"NULL");
616 [&formatTable, tableName](DropColumnIfExists
const& actualCommand) -> std::string {
619 R
"(-- LIGHTWEIGHT_SQLITE_GUARD: DROP_COLUMN_IF_EXISTS "{0}" "{1}"
620ALTER TABLE {2} DROP COLUMN "{1}";)",
622 actualCommand.columnName,
625 [tableName](DropIndexIfExists const& actualCommand) -> std::string {
626 return std::format(R
"(DROP INDEX IF EXISTS "{0}_{1}_index";)", tableName, actualCommand.columnName);
633 [[nodiscard]]
StringList AlterTable(std::string_view ,
634 std::string_view tableName,
635 std::vector<SqlAlterTableCommand>
const& commands)
const override
644 auto sql = FormatAlterTableCommand(tableName, command);
646 result.push_back(std::move(sql));
651 [[nodiscard]] std::string ColumnType(SqlColumnTypeDefinition
const& type)
const override
653 using namespace SqlColumnTypeDefinitions;
654 return std::visit(detail::overloaded {
655 [](Bigint
const&) -> std::string {
return "BIGINT"; },
656 [](Binary
const&) -> std::string {
return "BLOB"; },
657 [](Bool
const&) -> std::string {
return "BOOLEAN"; },
658 [](Char
const& type) -> std::string {
return std::format(
"CHAR({})", type.size); },
659 [](Date
const&) -> std::string {
return "DATE"; },
660 [](DateTime
const&) -> std::string {
return "DATETIME"; },
661 [](Decimal
const& type) -> std::string {
662 return std::format(
"DECIMAL({}, {})", type.precision, type.scale);
664 [](Guid
const&) -> std::string {
return "GUID"; },
665 [](Integer
const&) -> std::string {
return "INTEGER"; },
666 [](NChar
const& type) -> std::string {
return std::format(
"NCHAR({})", type.size); },
667 [](NVarchar
const& type) -> std::string {
return std::format(
"NVARCHAR({})", type.size); },
668 [](Real
const&) -> std::string {
return "REAL"; },
669 [](Smallint
const&) -> std::string {
return "SMALLINT"; },
670 [](Text
const&) -> std::string {
return "TEXT"; },
671 [](Time
const&) -> std::string {
return "TIME"; },
672 [](Timestamp
const&) -> std::string {
return "TIMESTAMP"; },
673 [](Tinyint
const&) -> std::string {
return "TINYINT"; },
674 [](VarBinary
const& type) -> std::string {
return std::format(
"VARBINARY({})", type.size); },
675 [](Varchar
const& type) -> std::string {
return std::format(
"VARCHAR({})", type.size); },
680 [[nodiscard]]
StringList DropTable(std::string_view ,
681 std::string_view
const& tableName,
682 bool ifExists =
false,
683 bool cascade =
false)
const override
690 return { std::format(R
"(DROP TABLE IF EXISTS "{}";)", tableName) };
692 return { std::format(R
"(DROP TABLE "{}";)", tableName) };
695 [[nodiscard]] std::string QueryServerVersion() const override
697 return "SELECT sqlite_version()";
703 [[nodiscard]]
StringList SqliteSettingsStatements(SqliteConnectionSettings
const& settings)
const override
706 auto const busyTimeoutMs =
707 std::clamp<std::chrono::milliseconds::rep>(settings.busyTimeout.count(), 0, std::numeric_limits<int>::max());
709 auto statements =
StringList { std::format(
"PRAGMA busy_timeout = {}", busyTimeoutMs) };
710 if (
auto const mode = JournalModeKeyword(settings.journalMode); !mode.empty())
711 statements.emplace_back(std::format(
"PRAGMA journal_mode = {}", mode));
718 [[nodiscard]]
static constexpr std::string_view JournalModeKeyword(
SqliteJournalMode mode)
noexcept
721 constexpr auto keywords = std::array<std::string_view, 7> {
722 "",
"DELETE",
"TRUNCATE",
"PERSIST",
"MEMORY",
"WAL",
"OFF",
724 auto const index =
static_cast<std::size_t
>(mode);
725 return index < keywords.size() ? keywords[index] : std::string_view {};
736 [[nodiscard]] SqlAdvisoryLockHandler
const& AdvisoryLockOps()
const override
738 return SqliteAdvisoryLockOps();
743 [[nodiscard]] SqlRetryClassifier
const& RetryOps() const noexcept
override
SqliteJournalMode
The SQLite journal mode a connection requests when it is established.
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.