Lightweight 0.20250904.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 public:
16 [[nodiscard]] std::string Insert(std::string_view intoTable,
17 std::string_view fields,
18 std::string_view values) const override
19 {
20 return std::format(R"(INSERT INTO "{}" ({}) VALUES ({}))", intoTable, fields, values);
21 }
22
23 [[nodiscard]] std::string QueryLastInsertId(std::string_view /*tableName*/) const override
24 {
25 // This is SQLite syntax. We might want to provide aspecialized SQLite class instead.
26 return "SELECT LAST_INSERT_ROWID()";
27 }
28
29 [[nodiscard]] std::string_view BooleanLiteral(bool literalValue) const noexcept override
30 {
31 return literalValue ? "TRUE" : "FALSE";
32 }
33
34 [[nodiscard]] std::string StringLiteral(std::string_view value) const noexcept override
35 {
36 // TODO: Implement escaping of special characters.
37 return std::format("'{}'", value);
38 }
39
40 [[nodiscard]] std::string StringLiteral(char value) const noexcept override
41 {
42 // TODO: Implement escaping of special characters.
43 return std::format("'{}'", value);
44 }
45
46 [[nodiscard]] std::string SelectCount(bool distinct,
47 std::string_view fromTable,
48 std::string_view fromTableAlias,
49 std::string_view tableJoins,
50 std::string_view whereCondition) const override
51 {
52 if (fromTableAlias.empty())
53 return std::format(
54 R"(SELECT{} COUNT(*) FROM "{}"{}{})", distinct ? " DISTINCT" : "", fromTable, tableJoins, whereCondition);
55 else
56 return std::format(R"(SELECT{} COUNT(*) FROM "{}" AS "{}"{}{})",
57 distinct ? " DISTINCT" : "",
58 fromTable,
59 fromTableAlias,
60 tableJoins,
61 whereCondition);
62 }
63
64 [[nodiscard]] std::string SelectAll(bool distinct,
65 // NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
66 std::string_view fields,
67 std::string_view fromTable,
68 std::string_view fromTableAlias,
69 std::string_view tableJoins,
70 std::string_view whereCondition,
71 std::string_view orderBy,
72 std::string_view groupBy) const override
73 {
74 std::stringstream sqlQueryString;
75 sqlQueryString << "SELECT ";
76 if (distinct)
77 sqlQueryString << "DISTINCT ";
78 sqlQueryString << fields;
79 sqlQueryString << " FROM \"" << fromTable << '"';
80 if (!fromTableAlias.empty())
81 sqlQueryString << " AS \"" << fromTableAlias << '"';
82 sqlQueryString << tableJoins;
83 sqlQueryString << whereCondition;
84 sqlQueryString << groupBy;
85 sqlQueryString << orderBy;
86
87 return sqlQueryString.str();
88 }
89
90 [[nodiscard]] std::string SelectFirst(bool distinct,
91 // NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
92 std::string_view fields,
93 std::string_view fromTable,
94 std::string_view fromTableAlias,
95 std::string_view tableJoins,
96 std::string_view whereCondition,
97 std::string_view orderBy,
98 size_t count) const override
99 {
100 std::stringstream sqlQueryString;
101 sqlQueryString << "SELECT " << fields;
102 if (distinct)
103 sqlQueryString << " DISTINCT";
104 sqlQueryString << " FROM \"" << fromTable << "\"";
105 if (!fromTableAlias.empty())
106 sqlQueryString << " AS \"" << fromTableAlias << "\"";
107 sqlQueryString << tableJoins;
108 sqlQueryString << whereCondition;
109 sqlQueryString << orderBy;
110 sqlQueryString << " LIMIT " << count;
111 return sqlQueryString.str();
112 }
113
114 [[nodiscard]] std::string SelectRange(bool distinct,
115 // NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
116 std::string_view fields,
117 std::string_view fromTable,
118 std::string_view fromTableAlias,
119 std::string_view tableJoins,
120 std::string_view whereCondition,
121 std::string_view orderBy,
122 std::string_view groupBy,
123 std::size_t offset,
124 std::size_t limit) const override
125 {
126 std::stringstream sqlQueryString;
127 sqlQueryString << "SELECT " << fields;
128 if (distinct)
129 sqlQueryString << " DISTINCT";
130 sqlQueryString << " FROM \"" << fromTable << "\"";
131 if (!fromTableAlias.empty())
132 sqlQueryString << " AS \"" << fromTableAlias << "\"";
133 sqlQueryString << tableJoins;
134 sqlQueryString << whereCondition;
135 sqlQueryString << groupBy;
136 sqlQueryString << orderBy;
137 sqlQueryString << " LIMIT " << limit << " OFFSET " << offset;
138 return sqlQueryString.str();
139 }
140
141 [[nodiscard]] std::string Update(std::string_view table,
142 std::string_view tableAlias,
143 std::string_view setFields,
144 std::string_view whereCondition) const override
145 {
146 if (tableAlias.empty())
147 return std::format(R"(UPDATE "{}" SET {}{})", table, setFields, whereCondition);
148 else
149 return std::format(R"(UPDATE "{}" AS "{}" SET {}{})", table, tableAlias, setFields, whereCondition);
150 }
151
152 [[nodiscard]] std::string Delete(std::string_view fromTable,
153 std::string_view fromTableAlias,
154 std::string_view tableJoins,
155 std::string_view whereCondition) const override
156 {
157 if (fromTableAlias.empty())
158 return std::format(R"(DELETE FROM "{}"{}{})", fromTable, tableJoins, whereCondition);
159 else
160 return std::format(R"(DELETE FROM "{}" AS "{}"{}{})", fromTable, fromTableAlias, tableJoins, whereCondition);
161 }
162
163 [[nodiscard]] virtual std::string BuildColumnDefinition(SqlColumnDeclaration const& column) const
164 {
165 std::stringstream sqlQueryString;
166
167 sqlQueryString << '"' << column.name << "\" ";
168
169 if (column.primaryKey != SqlPrimaryKeyType::AUTO_INCREMENT)
170 sqlQueryString << ColumnType(column.type);
171 else
172 sqlQueryString << ColumnType(SqlColumnTypeDefinitions::Integer {});
173
174 if (column.required)
175 sqlQueryString << " NOT NULL";
176
177 if (column.primaryKey == SqlPrimaryKeyType::AUTO_INCREMENT)
178 sqlQueryString << " PRIMARY KEY AUTOINCREMENT";
179 else if (column.primaryKey == SqlPrimaryKeyType::NONE && !column.index && column.unique)
180 sqlQueryString << " UNIQUE";
181
182 return sqlQueryString.str();
183 }
184
185 [[nodiscard]] static std::string BuildForeignKeyConstraint(std::string_view columnName,
186 SqlForeignKeyReferenceDefinition const& referencedColumn)
187 {
188 return std::format(R"(CONSTRAINT {} FOREIGN KEY ("{}") REFERENCES "{}"("{}"))",
189 std::format("FK_{}", columnName),
190 columnName,
191 referencedColumn.tableName,
192 referencedColumn.columnName);
193 }
194
195 // NOLINTNEXTLINE(readability-function-cognitive-complexity)
196 [[nodiscard]] StringList CreateTable(std::string_view tableName,
197 std::vector<SqlColumnDeclaration> const& columns) const override
198 {
199 auto sqlQueries = StringList {};
200
201 sqlQueries.emplace_back([&]() {
202 std::stringstream sqlQueryString;
203 sqlQueryString << "CREATE TABLE \"" << tableName << "\" (";
204 size_t currentColumn = 0;
205 std::string primaryKeyColumns;
206 std::string foreignKeyConstraints;
207 for (SqlColumnDeclaration const& column: columns)
208 {
209 if (currentColumn > 0)
210 sqlQueryString << ",";
211 ++currentColumn;
212 sqlQueryString << "\n ";
213 sqlQueryString << BuildColumnDefinition(column);
214 if (column.primaryKey == SqlPrimaryKeyType::MANUAL || column.primaryKey == SqlPrimaryKeyType::GUID)
215 {
216 if (!primaryKeyColumns.empty())
217 primaryKeyColumns += ", ";
218 primaryKeyColumns += '"';
219 primaryKeyColumns += column.name;
220 primaryKeyColumns += '"';
221 }
222 if (column.foreignKey)
223 {
224 foreignKeyConstraints += ",\n ";
225 foreignKeyConstraints += BuildForeignKeyConstraint(column.name, *column.foreignKey);
226 }
227 }
228 if (!primaryKeyColumns.empty())
229 sqlQueryString << ",\n PRIMARY KEY (" << primaryKeyColumns << ")";
230
231 sqlQueryString << foreignKeyConstraints;
232
233 sqlQueryString << "\n);";
234 return sqlQueryString.str();
235 }());
236
237 for (SqlColumnDeclaration const& column: columns)
238 {
239 if (column.index && column.primaryKey == SqlPrimaryKeyType::NONE)
240 {
241 // primary keys are always indexed
242 if (column.unique)
243 sqlQueries.emplace_back(std::format(R"(CREATE UNIQUE INDEX "{}_{}_index" ON "{}"("{}");)",
244 tableName,
245 column.name,
246 tableName,
247 column.name));
248 else
249 sqlQueries.emplace_back(std::format(
250 R"(CREATE INDEX "{}_{}_index" ON "{}"("{}");)", tableName, column.name, tableName, column.name));
251 }
252 }
253
254 return sqlQueries;
255 }
256
257 [[nodiscard]] StringList AlterTable(std::string_view tableName,
258 std::vector<SqlAlterTableCommand> const& commands) const override
259 {
260 std::stringstream sqlQueryString;
261
262 int currentCommand = 0;
263 for (SqlAlterTableCommand const& command: commands)
264 {
265 if (currentCommand > 0)
266 sqlQueryString << '\n';
267 ++currentCommand;
268
269 using namespace SqlAlterTableCommands;
270 sqlQueryString << std::visit(
271 detail::overloaded {
272 [tableName](RenameTable const& actualCommand) -> std::string {
273 return std::format(R"(ALTER TABLE "{}" RENAME TO "{}";)", tableName, actualCommand.newTableName);
274 },
275 [tableName, this](AddColumn const& actualCommand) -> std::string {
276 return std::format(R"(ALTER TABLE "{}" ADD COLUMN "{}" {} {};)",
277 tableName,
278 actualCommand.columnName,
279 ColumnType(actualCommand.columnType),
280 actualCommand.nullable == SqlNullable::NotNull ? "NOT NULL" : "NULL");
281 },
282 [tableName, this](AlterColumn const& actualCommand) -> std::string {
283 return std::format(R"(ALTER TABLE "{}" ALTER COLUMN "{}" {} {};)",
284 tableName,
285 actualCommand.columnName,
286 ColumnType(actualCommand.columnType),
287 actualCommand.nullable == SqlNullable::NotNull ? "NOT NULL" : "NULL");
288 },
289 [tableName](RenameColumn const& actualCommand) -> std::string {
290 return std::format(R"(ALTER TABLE "{}" RENAME COLUMN "{}" TO "{}";)",
291 tableName,
292 actualCommand.oldColumnName,
293 actualCommand.newColumnName);
294 },
295 [tableName](DropColumn const& actualCommand) -> std::string {
296 return std::format(R"(ALTER TABLE "{}" DROP COLUMN "{}";)", tableName, actualCommand.columnName);
297 },
298 [tableName](AddIndex const& actualCommand) -> std::string {
299 using namespace std::string_view_literals;
300 auto const uniqueStr = actualCommand.unique ? "UNIQUE "sv : ""sv;
301 return std::format(R"(CREATE {2}INDEX "{0}_{1}_index" ON "{0}"("{1}");)",
302 tableName,
303 actualCommand.columnName,
304 uniqueStr);
305 },
306 [tableName](DropIndex const& actualCommand) -> std::string {
307 return std::format(R"(DROP INDEX "{0}_{1}_index";)", tableName, actualCommand.columnName);
308 },
309 [tableName](AddForeignKey const& actualCommand) -> std::string {
310 return std::format(
311 R"(ALTER TABLE "{}" ADD {};)",
312 tableName,
313 BuildForeignKeyConstraint(actualCommand.columnName, actualCommand.referencedColumn));
314 },
315 [tableName](DropForeignKey const& actualCommand) -> std::string {
316 return std::format(R"(ALTER TABLE "{}" DROP CONSTRAINT "{}";)",
317 tableName,
318 std::format("FK_{}", actualCommand.columnName));
319 },
320 },
321 command);
322 }
323
324 return { sqlQueryString.str() };
325 }
326
327 [[nodiscard]] std::string ColumnType(SqlColumnTypeDefinition const& type) const override
328 {
329 using namespace SqlColumnTypeDefinitions;
330 return std::visit(detail::overloaded {
331 [](Bigint const&) -> std::string { return "BIGINT"; },
332 [](Binary const&) -> std::string { return "BLOB"; },
333 [](Bool const&) -> std::string { return "BOOLEAN"; },
334 [](Char const& type) -> std::string { return std::format("CHAR({})", type.size); },
335 [](Date const&) -> std::string { return "DATE"; },
336 [](DateTime const&) -> std::string { return "DATETIME"; },
337 [](Decimal const& type) -> std::string {
338 return std::format("DECIMAL({}, {})", type.precision, type.scale);
339 },
340 [](Guid const&) -> std::string { return "GUID"; },
341 [](Integer const&) -> std::string { return "INTEGER"; },
342 [](NChar const& type) -> std::string { return std::format("NCHAR({})", type.size); },
343 [](NVarchar const& type) -> std::string { return std::format("NVARCHAR({})", type.size); },
344 [](Real const&) -> std::string { return "REAL"; },
345 [](Smallint const&) -> std::string { return "SMALLINT"; },
346 [](Text const&) -> std::string { return "TEXT"; },
347 [](Time const&) -> std::string { return "TIME"; },
348 [](Timestamp const&) -> std::string { return "TIMESTAMP"; },
349 [](Tinyint const&) -> std::string { return "TINYINT"; },
350 [](VarBinary const& type) -> std::string { return std::format("VARBINARY({})", type.size); },
351 [](Varchar const& type) -> std::string { return std::format("VARCHAR({})", type.size); },
352 },
353 type);
354 }
355
356 [[nodiscard]] StringList DropTable(std::string_view const& tableName) const override
357 {
358 return { std::format(R"(DROP TABLE "{}";)", tableName) };
359 }
360};
361
362} // namespace Lightweight