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