Lightweight 0.20250904.0
Loading...
Searching...
No Matches
SqlServerFormatter.hpp
1// SPDX-License-Identifier: Apache-2.0
2#pragma once
3
4#include "../SqlQueryFormatter.hpp"
5#include "SQLiteFormatter.hpp"
6
7#include <reflection-cpp/reflection.hpp>
8
9#include <cassert>
10#include <format>
11
12namespace Lightweight
13{
14
15class SqlServerQueryFormatter final: public SQLiteQueryFormatter
16{
17 public:
18 [[nodiscard]] std::string QueryLastInsertId(std::string_view /*tableName*/) const override
19 {
20 // TODO: Figure out how to get the last insert id in SQL Server for a given table.
21 return std::format("SELECT @@IDENTITY");
22 }
23
24 [[nodiscard]] std::string_view BooleanLiteral(bool literalValue) const noexcept override
25 {
26 return literalValue ? "1" : "0";
27 }
28
29 [[nodiscard]] std::string SelectFirst(bool distinct,
30 // NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
31 std::string_view fields,
32 std::string_view fromTable,
33 std::string_view fromTableAlias,
34 std::string_view tableJoins,
35 std::string_view whereCondition,
36 std::string_view orderBy,
37 size_t count) const override
38 {
39 std::stringstream sqlQueryString;
40 sqlQueryString << "SELECT";
41 if (distinct)
42 sqlQueryString << " DISTINCT";
43 sqlQueryString << " TOP " << count;
44 sqlQueryString << ' ' << fields;
45 sqlQueryString << " FROM \"" << fromTable << '"';
46 if (!fromTableAlias.empty())
47 sqlQueryString << " AS \"" << fromTableAlias << '"';
48 sqlQueryString << tableJoins;
49 sqlQueryString << whereCondition;
50 sqlQueryString << orderBy;
51 ;
52 return sqlQueryString.str();
53 }
54
55 [[nodiscard]] std::string SelectRange(bool distinct,
56 // NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
57 std::string_view fields,
58 std::string_view fromTable,
59 std::string_view fromTableAlias,
60 std::string_view tableJoins,
61 std::string_view whereCondition,
62 std::string_view orderBy,
63 std::string_view groupBy,
64 std::size_t offset,
65 std::size_t limit) const override
66 {
67 assert(!orderBy.empty());
68 std::stringstream sqlQueryString;
69 sqlQueryString << "SELECT " << fields;
70 if (distinct)
71 sqlQueryString << " DISTINCT";
72 sqlQueryString << " FROM \"" << fromTable << "\"";
73 if (!fromTableAlias.empty())
74 sqlQueryString << " AS \"" << fromTableAlias << "\"";
75 sqlQueryString << tableJoins;
76 sqlQueryString << whereCondition;
77 sqlQueryString << groupBy;
78 sqlQueryString << orderBy;
79 sqlQueryString << " OFFSET " << offset << " ROWS FETCH NEXT " << limit << " ROWS ONLY";
80 return sqlQueryString.str();
81 }
82
83 [[nodiscard]] std::string ColumnType(SqlColumnTypeDefinition const& type) const override
84 {
85 using namespace SqlColumnTypeDefinitions;
86 return std::visit(detail::overloaded {
87 [](Bigint const&) -> std::string { return "BIGINT"; },
88 [](Binary const& type) -> std::string { return std::format("VARBINARY({})", type.size); },
89 [](Bool const&) -> std::string { return "BIT"; },
90 [](Char const& type) -> std::string { return std::format("CHAR({})", type.size); },
91 [](Date const&) -> std::string { return "DATE"; },
92 [](DateTime const&) -> std::string { return "DATETIME"; },
93 [](Decimal const& type) -> std::string {
94 return std::format("DECIMAL({}, {})", type.precision, type.scale);
95 },
96 [](Guid const&) -> std::string { return "UNIQUEIDENTIFIER"; },
97 [](Integer const&) -> std::string { return "INTEGER"; },
98 [](NChar const& type) -> std::string { return std::format("NCHAR({})", type.size); },
99 [](NVarchar const& type) -> std::string {
100 if (type.size == 0 || type.size > SqlOptimalMaxColumnSize)
101 return "NVARCHAR(MAX)";
102 else
103 return std::format("NVARCHAR({})", type.size);
104 },
105 [](Real const&) -> std::string { return "REAL"; },
106 [](Smallint const&) -> std::string { return "SMALLINT"; },
107 [](Text const&) -> std::string { return "VARCHAR(MAX)"; },
108 [](Time const&) -> std::string { return "TIME"; },
109 [](Timestamp const&) -> std::string { return "TIMESTAMP"; },
110 [](Tinyint const&) -> std::string { return "TINYINT"; },
111 [](VarBinary const& type) -> std::string { return std::format("VARBINARY({})", type.size); },
112 [](Varchar const& type) -> std::string {
113 if (type.size == 0 || type.size > SqlOptimalMaxColumnSize)
114 return "VARCHAR(MAX)";
115 else
116 return std::format("VARCHAR({})", type.size);
117 },
118 },
119 type);
120 }
121
122 [[nodiscard]] std::string BuildColumnDefinition(SqlColumnDeclaration const& column) const override
123 {
124 std::stringstream sqlQueryString;
125 sqlQueryString << '"' << column.name << "\" " << ColumnType(column.type);
126
127 if (column.required)
128 sqlQueryString << " NOT NULL";
129
130 if (column.primaryKey == SqlPrimaryKeyType::AUTO_INCREMENT)
131 sqlQueryString << " IDENTITY(1,1) PRIMARY KEY";
132 else if (column.primaryKey == SqlPrimaryKeyType::NONE && !column.index && column.unique)
133 sqlQueryString << " UNIQUE";
134
135 return sqlQueryString.str();
136 }
137
138 [[nodiscard]] StringList AlterTable(std::string_view tableName,
139 std::vector<SqlAlterTableCommand> const& commands) const override
140 {
141 std::stringstream sqlQueryString;
142
143 int currentCommand = 0;
144 for (SqlAlterTableCommand const& command: commands)
145 {
146 if (currentCommand > 0)
147 sqlQueryString << '\n';
148 ++currentCommand;
149
150 using namespace SqlAlterTableCommands;
151 sqlQueryString << std::visit(
152 detail::overloaded {
153 [tableName](RenameTable const& actualCommand) -> std::string {
154 return std::format(R"(ALTER TABLE "{}" RENAME TO "{}";)", tableName, actualCommand.newTableName);
155 },
156 [tableName, this](AddColumn const& actualCommand) -> std::string {
157 return std::format(R"(ALTER TABLE "{}" ADD "{}" {} {};)",
158 tableName,
159 actualCommand.columnName,
160 ColumnType(actualCommand.columnType),
161 actualCommand.nullable == SqlNullable::NotNull ? "NOT NULL" : "NULL");
162 },
163 [tableName, this](AlterColumn const& actualCommand) -> std::string {
164 return std::format(R"(ALTER TABLE "{}" ALTER COLUMN "{}" {} {};)",
165 tableName,
166 actualCommand.columnName,
167 ColumnType(actualCommand.columnType),
168 actualCommand.nullable == SqlNullable::NotNull ? "NOT NULL" : "NULL");
169 },
170 [tableName](RenameColumn const& actualCommand) -> std::string {
171 return std::format(R"(ALTER TABLE "{}" RENAME COLUMN "{}" TO "{}";)",
172 tableName,
173 actualCommand.oldColumnName,
174 actualCommand.newColumnName);
175 },
176 [tableName](DropColumn const& actualCommand) -> std::string {
177 return std::format(R"(ALTER TABLE "{}" DROP COLUMN "{}";)", tableName, actualCommand.columnName);
178 },
179 [tableName](AddIndex const& actualCommand) -> std::string {
180 using namespace std::string_view_literals;
181 auto const uniqueStr = actualCommand.unique ? "UNIQUE "sv : ""sv;
182 return std::format(R"(CREATE {2}INDEX "{0}_{1}_index" ON "{0}"("{1}");)",
183 tableName,
184 actualCommand.columnName,
185 uniqueStr);
186 },
187 [tableName](DropIndex const& actualCommand) -> std::string {
188 return std::format(R"(DROP INDEX "{0}_{1}_index";)", tableName, actualCommand.columnName);
189 },
190 [tableName](AddForeignKey const& actualCommand) -> std::string {
191 return std::format(
192 R"(ALTER TABLE "{}" ADD {};)",
193 tableName,
194 BuildForeignKeyConstraint(actualCommand.columnName, actualCommand.referencedColumn));
195 },
196 [tableName](DropForeignKey const& actualCommand) -> std::string {
197 return std::format(R"(ALTER TABLE "{}" DROP CONSTRAINT "{}";)",
198 tableName,
199 std::format("FK_{}", actualCommand.columnName));
200 },
201 },
202 command);
203 }
204
205 return { sqlQueryString.str() };
206 }
207};
208
209} // namespace Lightweight