4#include "../SqlQueryFormatter.hpp"
5#include "SQLiteFormatter.hpp"
7#include <reflection-cpp/reflection.hpp>
15class SqlServerQueryFormatter final:
public SQLiteQueryFormatter
18 [[nodiscard]]
static std::string FormatFromTable(std::string_view table)
21 if (!table.empty() && (table.front() ==
'[' || table.front() ==
'"'))
22 return std::string(table);
25 return std::format(R
"("{}")", table);
29 [[nodiscard]]
bool RequiresTableRebuildForSchemaChange() const noexcept
override
34 [[nodiscard]]
bool SupportsBatchedSchemaIntrospection() const noexcept
override
39 [[nodiscard]]
StringList DropTable(std::string_view schemaName,
40 std::string_view
const& tableName,
41 bool ifExists =
false,
42 bool cascade =
false)
const override
53 std::string
const schemaFilter = schemaName.empty() ?
"SCHEMA_NAME()" : std::format(
"'{}'", schemaName);
55 result.emplace_back(std::format(
56 R
"(DECLARE @sql NVARCHAR(MAX) = N'';
57SELECT @sql = @sql + 'ALTER TABLE ' + QUOTENAME(OBJECT_SCHEMA_NAME(fk.parent_object_id)) + '.' + QUOTENAME(OBJECT_NAME(fk.parent_object_id)) + ' DROP CONSTRAINT ' + QUOTENAME(fk.name) + '; '
58FROM sys.foreign_keys fk
59WHERE OBJECT_NAME(fk.referenced_object_id) = '{}' AND OBJECT_SCHEMA_NAME(fk.referenced_object_id) = {};
60EXEC sp_executesql @sql;)",
67 result.emplace_back(std::format(
"DROP TABLE IF EXISTS {};",
FormatTableName(schemaName, tableName)));
69 result.emplace_back(std::format(
"DROP TABLE {};",
FormatTableName(schemaName, tableName)));
74 [[nodiscard]] std::string BinaryLiteral(std::span<uint8_t const> data)
const override
77 result.reserve((data.size() * 2) + 2);
79 for (uint8_t
byte: data)
80 result += std::format(
"{:02X}", byte);
114 [[nodiscard]] std::string StringLiteral(std::string_view value)
const noexcept override
116 return EncodeUnicodeLiteral(value);
119 [[nodiscard]] std::string StringLiteral(
char value)
const noexcept override
123 char const buf[1] = { value };
124 return EncodeUnicodeLiteral(std::string_view { buf, 1 });
131 static constexpr std::size_t Utf8SequenceLength(
unsigned char c)
noexcept
147 static constexpr std::pair<char32_t, std::size_t> DecodeUtf8(std::string_view s, std::size_t i)
noexcept
149 auto const len = Utf8SequenceLength(
static_cast<unsigned char>(s[i]));
150 if (len == 1 || i + len > s.size())
151 return {
static_cast<char32_t>(
static_cast<unsigned char>(s[i])), 1 };
153 auto const lead =
static_cast<unsigned char>(s[i]);
169 for (std::size_t k = 1; k < len; ++k)
170 cp = (cp << 6) | (
static_cast<unsigned char>(s[i + k]) & 0x3F);
177 static void AppendCodepoint(std::string& out,
bool& inQuotedRun,
char32_t cp)
179 auto const closeRun = [&] {
186 auto const openRun = [&] {
202 out +=
static_cast<char>(cp);
210 out += std::format(
"NCHAR({})",
static_cast<unsigned>(cp));
214 char32_t const adjusted = cp - 0x10000;
215 char32_t const hi = 0xD800 + (adjusted >> 10);
216 char32_t const lo = 0xDC00 + (adjusted & 0x3FF);
217 out += std::format(
"NCHAR({}) + NCHAR({})",
static_cast<unsigned>(hi),
static_cast<unsigned>(lo));
223 static std::string EncodeUnicodeLiteral(std::string_view value)
228 out.reserve(value.size() + 4);
229 bool inQuotedRun =
false;
231 while (i < value.size())
233 auto const [cp, len] = DecodeUtf8(value, i);
234 AppendCodepoint(out, inQuotedRun, cp);
243 [[nodiscard]] std::string QualifiedTableName(std::string_view schema, std::string_view table)
const override
246 return std::format(
"[{}]", table);
247 return std::format(
"[{}].[{}]", schema, table);
250 [[nodiscard]] std::string QueryLastInsertId(std::string_view )
const override
253 return std::format(
"SELECT @@IDENTITY");
256 [[nodiscard]] std::string_view BooleanLiteral(
bool literalValue)
const noexcept override
258 return literalValue ?
"1" :
"0";
261 [[nodiscard]] std::string_view DateFunction() const noexcept
override
266 [[nodiscard]] std::string SelectFirst(
bool distinct,
268 std::string_view fields,
269 std::string_view fromTable,
270 std::string_view fromTableAlias,
271 std::string_view tableJoins,
272 std::string_view whereCondition,
273 std::string_view orderBy,
274 std::string_view groupBy,
275 size_t count)
const override
277 std::stringstream sqlQueryString;
278 sqlQueryString <<
"SELECT";
280 sqlQueryString <<
" DISTINCT";
281 sqlQueryString <<
" TOP " << count;
282 sqlQueryString <<
' ' << fields;
283 sqlQueryString <<
" FROM " << FormatFromTable(fromTable);
284 if (!fromTableAlias.empty())
285 sqlQueryString <<
" AS [" << fromTableAlias <<
']';
286 sqlQueryString << tableJoins;
287 sqlQueryString << whereCondition;
288 sqlQueryString << groupBy;
289 sqlQueryString << orderBy;
290 return sqlQueryString.str();
293 [[nodiscard]] std::string SelectRange(
bool distinct,
295 std::string_view fields,
296 std::string_view fromTable,
297 std::string_view fromTableAlias,
298 std::string_view tableJoins,
299 std::string_view whereCondition,
300 std::string_view orderBy,
301 std::string_view groupBy,
303 std::size_t limit)
const override
305 assert(!orderBy.empty());
306 std::stringstream sqlQueryString;
307 sqlQueryString <<
"SELECT ";
309 sqlQueryString <<
"DISTINCT ";
310 sqlQueryString << fields;
311 sqlQueryString <<
" FROM " << FormatFromTable(fromTable);
312 if (!fromTableAlias.empty())
313 sqlQueryString <<
" AS [" << fromTableAlias <<
']';
314 sqlQueryString << tableJoins;
315 sqlQueryString << whereCondition;
316 sqlQueryString << groupBy;
317 sqlQueryString << orderBy;
318 sqlQueryString <<
" OFFSET " << offset <<
" ROWS FETCH NEXT " << limit <<
" ROWS ONLY";
319 return sqlQueryString.str();
322 [[nodiscard]] std::string ColumnType(SqlColumnTypeDefinition
const& type)
const override
324 using namespace SqlColumnTypeDefinitions;
325 return std::visit(detail::overloaded {
326 [](Bigint
const&) -> std::string {
return "BIGINT"; },
327 [](Binary
const& type) -> std::string {
328 if (type.size == 0 || type.size > 8000)
329 return "VARBINARY(MAX)";
331 return std::format(
"VARBINARY({})", type.size);
333 [](Bool
const&) -> std::string {
return "BIT"; },
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);
340 [](Guid
const&) -> std::string {
return "UNIQUEIDENTIFIER"; },
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 {
344 if (type.size == 0 || type.size > SqlOptimalMaxColumnSize)
345 return "NVARCHAR(MAX)";
347 return std::format(
"NVARCHAR({})", type.size);
349 [](Real
const& type) -> std::string {
353 return type.precision > 24 ?
"FLOAT(53)" :
"REAL";
355 [](Smallint
const&) -> std::string {
return "SMALLINT"; },
356 [](Text
const&) -> std::string {
return "VARCHAR(MAX)"; },
357 [](Time
const&) -> std::string {
return "TIME"; },
362 [](Timestamp
const&) -> std::string {
return "DATETIME2"; },
363 [](Tinyint
const&) -> std::string {
return "TINYINT"; },
364 [](VarBinary
const& type) -> std::string {
365 if (type.size == 0 || type.size > 8000)
366 return "VARBINARY(MAX)";
368 return std::format(
"VARBINARY({})", type.size);
370 [](Varchar
const& type) -> std::string {
371 if (type.size == 0 || type.size > SqlOptimalMaxColumnSize)
372 return "VARCHAR(MAX)";
374 return std::format(
"VARCHAR({})", type.size);
380 [[nodiscard]] std::string BuildColumnDefinition(SqlColumnDeclaration
const& column)
const override
382 std::stringstream sqlQueryString;
383 sqlQueryString <<
'"' << column.name <<
"\" " << ColumnType(column.type);
386 sqlQueryString <<
" NOT NULL";
388 if (column.primaryKey == SqlPrimaryKeyType::AUTO_INCREMENT)
389 sqlQueryString <<
" IDENTITY(1,1) PRIMARY KEY";
390 else if (column.primaryKey == SqlPrimaryKeyType::NONE && !column.index && column.unique)
391 sqlQueryString <<
" UNIQUE";
393 if (!column.defaultValue.empty())
394 sqlQueryString <<
" DEFAULT " << column.defaultValue;
396 return sqlQueryString.str();
400 [[nodiscard]]
StringList CreateTable(std::string_view schema,
401 std::string_view tableName,
402 std::vector<SqlColumnDeclaration>
const& columns,
403 std::vector<SqlCompositeForeignKeyConstraint>
const& foreignKeys,
404 bool ifNotExists =
false)
const override
406 std::stringstream ss;
411 std::string schemaFilter = schema.empty() ?
"dbo" : std::string(schema);
412 ss << std::format(
"IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = '{}' AND schema_id = SCHEMA_ID('{}'))\n",
417 ss << std::format(
"CREATE TABLE {} (",
FormatTableName(schema, tableName));
420 for (
auto const& column: columns)
425 ss <<
"\n " << BuildColumnDefinition(column);
428 auto const primaryKeys = [&]() -> std::vector<std::string> {
429 std::vector<std::pair<uint16_t, std::string>> indexedPrimaryKeys;
430 for (
auto const& col: columns)
432 indexedPrimaryKeys.emplace_back(col.primaryKeyIndex, col.name);
433 std::ranges::sort(indexedPrimaryKeys, [](
auto const& a,
auto const& b) {
return a.first < b.first; });
435 std::vector<std::string> primaryKeys;
436 primaryKeys.reserve(indexedPrimaryKeys.size());
437 for (
auto const& [index, name]: indexedPrimaryKeys)
438 primaryKeys.push_back(name);
442 if (!primaryKeys.empty())
452 bool hasIdentity =
false;
453 for (
auto const& col: columns)
459 ss <<
",\n PRIMARY KEY (";
461 for (
auto const& pk: primaryKeys)
466 ss <<
'"' << pk <<
'"';
472 if (!foreignKeys.empty())
474 for (
auto const& fk: foreignKeys)
480 for (
auto const& col: fk.columns)
484 ss <<
'"' << col <<
'"';
487 ss <<
") REFERENCES " <<
FormatTableName(schema, fk.referencedTableName) <<
" (";
490 for (
auto const& col: fk.referencedColumns)
494 ss <<
'"' << col <<
'"';
502 for (
auto const& column: columns)
504 if (column.foreignKey)
506 ss <<
",\n " << BuildForeignKeyConstraint(tableName, column.name, *column.foreignKey);
513 result.emplace_back(ss.str());
516 for (SqlColumnDeclaration
const& column: columns)
518 if (column.index && column.primaryKey == SqlPrimaryKeyType::NONE)
524 result.emplace_back(std::format(R
"(CREATE UNIQUE INDEX "{}_{}_index" ON "{}" ("{}");)",
530 result.emplace_back(std::format(R
"(CREATE UNIQUE INDEX "{}_{}_index" ON "{}"."{}" ("{}");)",
540 result.emplace_back(std::format(R
"(CREATE INDEX "{}_{}_index" ON "{}" ("{}");)",
546 result.emplace_back(std::format(R
"(CREATE INDEX "{}_{}_index" ON "{}"."{}" ("{}");)",
560 [[nodiscard]]
StringList AlterTable(std::string_view schemaName,
561 std::string_view tableName,
562 std::vector<SqlAlterTableCommand>
const& commands)
const override
564 std::stringstream sqlQueryString;
566 int currentCommand = 0;
569 if (currentCommand > 0)
570 sqlQueryString <<
'\n';
573 using namespace SqlAlterTableCommands;
574 sqlQueryString << std::visit(
576 [schemaName, tableName](RenameTable
const& actualCommand) -> std::string {
577 return std::format(R
"(ALTER TABLE {} RENAME TO "{}";)",
579 actualCommand.newTableName);
581 [schemaName, tableName, this](AddColumn
const& actualCommand) -> std::string {
582 return std::format(R
"(ALTER TABLE {} ADD "{}" {} {};)",
584 actualCommand.columnName,
585 ColumnType(actualCommand.columnType),
586 actualCommand.nullable == SqlNullable::NotNull ? "NOT NULL" :
"NULL");
588 [schemaName, tableName,
this](AlterColumn
const& actualCommand) -> std::string {
589 return std::format(R
"(ALTER TABLE {} ALTER COLUMN "{}" {} {};)",
591 actualCommand.columnName,
592 ColumnType(actualCommand.columnType),
593 actualCommand.nullable == SqlNullable::NotNull ? "NOT NULL" :
"NULL");
595 [schemaName, tableName](RenameColumn
const& actualCommand) -> std::string {
596 return std::format(R
"(ALTER TABLE {} RENAME COLUMN "{}" TO "{}";)",
598 actualCommand.oldColumnName,
599 actualCommand.newColumnName);
601 [schemaName, tableName](DropColumn const& actualCommand) -> std::string {
602 return std::format(R
"(ALTER TABLE {} DROP COLUMN "{}";)",
604 actualCommand.columnName);
606 [schemaName, tableName](AddIndex const& actualCommand) -> std::string {
607 using namespace std::string_view_literals;
608 auto const uniqueStr = actualCommand.unique ?
"UNIQUE "sv :
""sv;
609 if (schemaName.empty())
610 return std::format(R
"(CREATE {2}INDEX "{0}_{1}_index" ON "{0}" ("{1}");)",
612 actualCommand.columnName,
615 return std::format(R
"(CREATE {3}INDEX "{0}_{1}_{2}_index" ON "{0}"."{1}" ("{2}");)",
618 actualCommand.columnName,
621 [schemaName, tableName](DropIndex const& actualCommand) -> std::string {
622 if (schemaName.empty())
623 return std::format(R
"(DROP INDEX "{0}_{1}_index";)", tableName, actualCommand.columnName);
626 R
"(DROP INDEX "{0}_{1}_{2}_index";)", schemaName, tableName, actualCommand.columnName);
628 [schemaName, tableName](AddForeignKey const& actualCommand) -> std::string {
633 tableName, std::array { std::string_view { actualCommand.columnName } });
635 R
"(IF NOT EXISTS (SELECT 1 FROM sys.foreign_keys WHERE name = '{0}') ALTER TABLE {1} ADD {2};)",
638 BuildForeignKeyConstraint(tableName, actualCommand.columnName, actualCommand.referencedColumn));
640 [schemaName, tableName](DropForeignKey const& actualCommand) -> std::string {
641 return std::format(R
"(ALTER TABLE {} DROP CONSTRAINT "{}";)",
644 tableName, std::array { std::string_view { actualCommand.columnName } }));
646 [schemaName, tableName](AddCompositeForeignKey const& actualCommand) -> std::string {
647 std::stringstream ss;
648 ss <<
"ALTER TABLE " <<
FormatTableName(schemaName, tableName) <<
" ADD CONSTRAINT \""
652 for (
auto const& col: actualCommand.columns)
656 ss <<
'"' << col <<
'"';
658 ss <<
") REFERENCES " <<
FormatTableName(schemaName, actualCommand.referencedTableName) <<
" (";
661 for (
auto const& col: actualCommand.referencedColumns)
665 ss <<
'"' << col <<
'"';
670 [schemaName, tableName,
this](AddColumnIfNotExists
const& actualCommand) -> std::string {
673 R
"(IF NOT EXISTS (SELECT * FROM sys.columns WHERE object_id = OBJECT_ID('{}') AND name = '{}')
674ALTER TABLE {} ADD "{}" {} {};)",
676 actualCommand.columnName,
678 actualCommand.columnName,
679 ColumnType(actualCommand.columnType),
680 actualCommand.nullable == SqlNullable::NotNull ? "NOT NULL" :
"NULL");
682 [schemaName, tableName](DropColumnIfExists
const& actualCommand) -> std::string {
685 R
"(IF EXISTS (SELECT * FROM sys.columns WHERE object_id = OBJECT_ID('{}') AND name = '{}')
686ALTER TABLE {} DROP COLUMN "{}";)",
688 actualCommand.columnName,
690 actualCommand.columnName);
692 [schemaName, tableName](DropIndexIfExists const& actualCommand) -> std::string {
693 if (schemaName.empty())
695 R
"(IF EXISTS (SELECT * FROM sys.indexes WHERE name = '{0}_{1}_index' AND object_id = OBJECT_ID('{0}'))
696DROP INDEX "{0}_{1}_index" ON "{0}";)",
698 actualCommand.columnName);
701 R
"(IF EXISTS (SELECT * FROM sys.indexes WHERE name = '{0}_{1}_{2}_index')
702DROP INDEX "{0}_{1}_{2}_index" ON "{0}"."{1}";)",
705 actualCommand.columnName);
711 return { sqlQueryString.str() };
714 [[nodiscard]] std::string QueryServerVersion()
const override
716 return "SELECT @@VERSION";
722 [[nodiscard]] SqlAdvisoryLockHandler
const& AdvisoryLockOps()
const override
724 return SqlServerAdvisoryLockOps();
729 [[nodiscard]] SqlRetryClassifier
const& RetryOps() const noexcept
override
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.
SqlPrimaryKeyType
Represents a primary key type.
LIGHTWEIGHT_API SqlRetryClassifier const & SqlServerRetryOps() noexcept
Returns the SQL Server-specific singleton classifier. See SqliteRetryOps().