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