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]]
bool DistinguishesNarrowAndWideText() const noexcept
override
44 [[nodiscard]]
bool IdentifiersAreCaseInsensitive() const noexcept
override
49 [[nodiscard]]
bool HasNativeGuidType() const noexcept
override
54 [[nodiscard]]
bool ParameterStatusIdentifiesFailedRows() const noexcept
override
59 [[nodiscard]]
bool ComparesFixedCharWithPadSemantics() const noexcept
override
64 [[nodiscard]]
bool BoundsBulkLoadTransactionSize() const noexcept
override
69 [[nodiscard]]
bool SupportsSchemas() const noexcept
override
74 [[nodiscard]] std::string CreateSchemaIfNotExists(std::string_view schema)
const override
78 return std::format(R
"(IF SCHEMA_ID(N'{}') IS NULL EXEC(N'CREATE SCHEMA "{}"'))",
83 [[nodiscard]]
StringList DropTable(std::string_view schemaName,
84 std::string_view
const& tableName,
85 bool ifExists =
false,
86 bool cascade =
false)
const override
97 std::string
const schemaFilter = schemaName.empty() ?
"SCHEMA_NAME()" : std::format(
"'{}'", schemaName);
99 result.emplace_back(std::format(
100 R
"(DECLARE @sql NVARCHAR(MAX) = N'';
101SELECT @sql = @sql + 'ALTER TABLE ' + QUOTENAME(OBJECT_SCHEMA_NAME(fk.parent_object_id)) + '.' + QUOTENAME(OBJECT_NAME(fk.parent_object_id)) + ' DROP CONSTRAINT ' + QUOTENAME(fk.name) + '; '
102FROM sys.foreign_keys fk
103WHERE OBJECT_NAME(fk.referenced_object_id) = '{}' AND OBJECT_SCHEMA_NAME(fk.referenced_object_id) = {};
104EXEC sp_executesql @sql;)",
111 result.emplace_back(std::format(
"DROP TABLE IF EXISTS {};",
FormatTableName(schemaName, tableName)));
113 result.emplace_back(std::format(
"DROP TABLE {};",
FormatTableName(schemaName, tableName)));
118 [[nodiscard]] std::string BinaryLiteral(std::span<uint8_t const> data)
const override
121 result.reserve((data.size() * 2) + 2);
123 for (uint8_t
byte: data)
124 result += std::format(
"{:02X}", byte);
158 [[nodiscard]] std::string StringLiteral(std::string_view value)
const noexcept override
160 return EncodeUnicodeLiteral(value);
163 [[nodiscard]] std::string StringLiteral(
char value)
const noexcept override
167 char const buf[1] = { value };
168 return EncodeUnicodeLiteral(std::string_view { buf, 1 });
175 static constexpr std::size_t Utf8SequenceLength(
unsigned char c)
noexcept
191 static constexpr std::pair<char32_t, std::size_t> DecodeUtf8(std::string_view s, std::size_t i)
noexcept
193 auto const len = Utf8SequenceLength(
static_cast<unsigned char>(s[i]));
194 if (len == 1 || i + len > s.size())
195 return {
static_cast<char32_t>(
static_cast<unsigned char>(s[i])), 1 };
197 auto const lead =
static_cast<unsigned char>(s[i]);
213 for (std::size_t k = 1; k < len; ++k)
214 cp = (cp << 6) | (
static_cast<unsigned char>(s[i + k]) & 0x3F);
221 static void AppendCodepoint(std::string& out,
bool& inQuotedRun,
char32_t cp)
223 auto const closeRun = [&] {
230 auto const openRun = [&] {
246 out +=
static_cast<char>(cp);
254 out += std::format(
"NCHAR({})",
static_cast<unsigned>(cp));
258 char32_t const adjusted = cp - 0x10000;
259 char32_t const hi = 0xD800 + (adjusted >> 10);
260 char32_t const lo = 0xDC00 + (adjusted & 0x3FF);
261 out += std::format(
"NCHAR({}) + NCHAR({})",
static_cast<unsigned>(hi),
static_cast<unsigned>(lo));
267 static std::string EncodeUnicodeLiteral(std::string_view value)
272 out.reserve(value.size() + 4);
273 bool inQuotedRun =
false;
275 while (i < value.size())
277 auto const [cp, len] = DecodeUtf8(value, i);
278 AppendCodepoint(out, inQuotedRun, cp);
287 [[nodiscard]] std::string QualifiedTableName(std::string_view schema, std::string_view table)
const override
290 return std::format(
"[{}]", table);
291 return std::format(
"[{}].[{}]", schema, table);
294 [[nodiscard]] std::string QueryLastInsertId(std::string_view )
const override
297 return std::format(
"SELECT @@IDENTITY");
300 [[nodiscard]] std::string_view BooleanLiteral(
bool literalValue)
const noexcept override
302 return literalValue ?
"1" :
"0";
305 [[nodiscard]] std::string_view DateFunction() const noexcept
override
310 [[nodiscard]] std::string_view ReadUncommittedTableHint() const noexcept
override
312 return " WITH (READUNCOMMITTED)";
315 [[nodiscard]] std::string_view ReportRowCountsStatement() const noexcept
override
317 return "SET NOCOUNT OFF";
323 [[nodiscard]] std::string_view TableRowCountEstimateQuery() const noexcept
override
325 return "SELECT COALESCE(SUM(p.rows), 0) FROM sys.partitions AS p "
326 "JOIN sys.tables AS t ON t.object_id = p.object_id "
327 "JOIN sys.schemas AS s ON s.schema_id = t.schema_id "
328 "WHERE s.name = ? AND t.name = ? AND p.index_id IN (0, 1)";
331 [[nodiscard]] std::string SelectFirst(
bool distinct,
333 std::string_view fields,
334 std::string_view fromTable,
335 std::string_view fromTableAlias,
336 std::string_view tableJoins,
337 std::string_view whereCondition,
338 std::string_view orderBy,
339 std::string_view groupBy,
340 size_t count)
const override
342 std::stringstream sqlQueryString;
343 sqlQueryString <<
"SELECT";
345 sqlQueryString <<
" DISTINCT";
346 sqlQueryString <<
" TOP " << count;
347 sqlQueryString <<
' ' << fields;
348 sqlQueryString <<
" FROM " << FormatFromTable(fromTable);
349 if (!fromTableAlias.empty())
350 sqlQueryString <<
" AS [" << fromTableAlias <<
']';
351 sqlQueryString << tableJoins;
352 sqlQueryString << whereCondition;
353 sqlQueryString << groupBy;
354 sqlQueryString << orderBy;
355 return sqlQueryString.str();
358 [[nodiscard]] std::string SelectRange(
bool distinct,
360 std::string_view fields,
361 std::string_view fromTable,
362 std::string_view fromTableAlias,
363 std::string_view tableJoins,
364 std::string_view whereCondition,
365 std::string_view orderBy,
366 std::string_view groupBy,
368 std::size_t limit)
const override
370 assert(!orderBy.empty());
371 std::stringstream sqlQueryString;
372 sqlQueryString <<
"SELECT ";
374 sqlQueryString <<
"DISTINCT ";
375 sqlQueryString << fields;
376 sqlQueryString <<
" FROM " << FormatFromTable(fromTable);
377 if (!fromTableAlias.empty())
378 sqlQueryString <<
" AS [" << fromTableAlias <<
']';
379 sqlQueryString << tableJoins;
380 sqlQueryString << whereCondition;
381 sqlQueryString << groupBy;
382 sqlQueryString << orderBy;
383 sqlQueryString <<
" OFFSET " << offset <<
" ROWS FETCH NEXT " << limit <<
" ROWS ONLY";
384 return sqlQueryString.str();
387 [[nodiscard]] std::string ColumnType(SqlColumnTypeDefinition
const& type)
const override
389 using namespace SqlColumnTypeDefinitions;
390 return std::visit(detail::overloaded {
391 [](Bigint
const&) -> std::string {
return "BIGINT"; },
392 [](Binary
const& type) -> std::string {
393 if (type.size == 0 || type.size > 8000)
394 return "VARBINARY(MAX)";
396 return std::format(
"VARBINARY({})", type.size);
398 [](Bool
const&) -> std::string {
return "BIT"; },
399 [](Char
const& type) -> std::string {
return std::format(
"CHAR({})", type.size); },
400 [](Date
const&) -> std::string {
return "DATE"; },
401 [](DateTime
const&) -> std::string {
return "DATETIME"; },
402 [](Decimal
const& type) -> std::string {
403 return std::format(
"DECIMAL({}, {})", type.precision, type.scale);
405 [](Guid
const&) -> std::string {
return "UNIQUEIDENTIFIER"; },
406 [](Integer
const&) -> std::string {
return "INTEGER"; },
407 [](NChar
const& type) -> std::string {
return std::format(
"NCHAR({})", type.size); },
408 [](NVarchar
const& type) -> std::string {
411 if (type.size == 0 || type.size > SqlOptimalMaxColumnSize)
412 return "NVARCHAR(MAX)";
414 return std::format(
"NVARCHAR({})", type.size);
416 [](Real
const& type) -> std::string {
420 return type.precision > 24 ?
"FLOAT(53)" :
"REAL";
422 [](Smallint
const&) -> std::string {
return "SMALLINT"; },
423 [](Text
const&) -> std::string {
return "VARCHAR(MAX)"; },
424 [](Time
const&) -> std::string {
return "TIME"; },
429 [](Timestamp
const&) -> std::string {
return "DATETIME2"; },
430 [](Tinyint
const&) -> std::string {
return "TINYINT"; },
431 [](VarBinary
const& type) -> std::string {
434 if (type.size == 0 || type.size > 8000)
435 return "VARBINARY(MAX)";
437 return std::format(
"VARBINARY({})", type.size);
439 [](Varchar
const& type) -> std::string {
442 if (type.size == 0 || type.size > SqlOptimalMaxColumnSize)
443 return "VARCHAR(MAX)";
445 return std::format(
"VARCHAR({})", type.size);
451 [[nodiscard]] std::string BuildColumnDefinition(SqlColumnDeclaration
const& column)
const override
453 std::stringstream sqlQueryString;
454 sqlQueryString <<
'"' << column.name <<
"\" " << ColumnType(column.type);
457 sqlQueryString <<
" NOT NULL";
459 if (column.primaryKey == SqlPrimaryKeyType::AUTO_INCREMENT)
460 sqlQueryString <<
" IDENTITY(1,1) PRIMARY KEY";
461 else if (column.primaryKey == SqlPrimaryKeyType::NONE && !column.index && column.unique)
462 sqlQueryString <<
" UNIQUE";
464 if (!column.defaultValue.empty())
465 sqlQueryString <<
" DEFAULT " << column.defaultValue;
467 return sqlQueryString.str();
471 [[nodiscard]]
StringList CreateTable(std::string_view schema,
472 std::string_view tableName,
473 std::vector<SqlColumnDeclaration>
const& columns,
474 std::vector<SqlCompositeForeignKeyConstraint>
const& foreignKeys,
475 bool ifNotExists =
false)
const override
477 std::stringstream ss;
482 std::string schemaFilter = schema.empty() ?
"dbo" : std::string(schema);
483 ss << std::format(
"IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = '{}' AND schema_id = SCHEMA_ID('{}'))\n",
488 ss << std::format(
"CREATE TABLE {} (",
FormatTableName(schema, tableName));
491 for (
auto const& column: columns)
496 ss <<
"\n " << BuildColumnDefinition(column);
499 auto const primaryKeys = [&]() -> std::vector<std::string> {
500 std::vector<std::pair<uint16_t, std::string>> indexedPrimaryKeys;
501 for (
auto const& col: columns)
503 indexedPrimaryKeys.emplace_back(col.primaryKeyIndex, col.name);
504 std::ranges::sort(indexedPrimaryKeys, [](
auto const& a,
auto const& b) {
return a.first < b.first; });
506 std::vector<std::string> primaryKeys;
507 primaryKeys.reserve(indexedPrimaryKeys.size());
508 for (
auto const& [index, name]: indexedPrimaryKeys)
509 primaryKeys.push_back(name);
513 if (!primaryKeys.empty())
523 bool hasIdentity =
false;
524 for (
auto const& col: columns)
530 ss <<
",\n PRIMARY KEY (";
532 for (
auto const& pk: primaryKeys)
537 ss <<
'"' << pk <<
'"';
543 if (!foreignKeys.empty())
545 for (
auto const& fk: foreignKeys)
551 for (
auto const& col: fk.columns)
555 ss <<
'"' << col <<
'"';
558 ss <<
") REFERENCES " <<
FormatTableName(schema, fk.referencedTableName) <<
" (";
561 for (
auto const& col: fk.referencedColumns)
565 ss <<
'"' << col <<
'"';
573 for (
auto const& column: columns)
575 if (column.foreignKey)
577 ss <<
",\n " << BuildForeignKeyConstraint(tableName, column.name, *column.foreignKey);
584 result.emplace_back(ss.str());
587 for (SqlColumnDeclaration
const& column: columns)
589 if (column.index && column.primaryKey == SqlPrimaryKeyType::NONE)
595 result.emplace_back(std::format(R
"(CREATE UNIQUE INDEX "{}_{}_index" ON "{}" ("{}");)",
601 result.emplace_back(std::format(R
"(CREATE UNIQUE INDEX "{}_{}_index" ON "{}"."{}" ("{}");)",
611 result.emplace_back(std::format(R
"(CREATE INDEX "{}_{}_index" ON "{}" ("{}");)",
617 result.emplace_back(std::format(R
"(CREATE INDEX "{}_{}_index" ON "{}"."{}" ("{}");)",
631 [[nodiscard]]
StringList AlterTable(std::string_view schemaName,
632 std::string_view tableName,
633 std::vector<SqlAlterTableCommand>
const& commands)
const override
635 std::stringstream sqlQueryString;
637 int currentCommand = 0;
640 if (currentCommand > 0)
641 sqlQueryString <<
'\n';
644 using namespace SqlAlterTableCommands;
645 sqlQueryString << std::visit(
647 [schemaName, tableName](RenameTable
const& actualCommand) -> std::string {
648 return std::format(R
"(ALTER TABLE {} RENAME TO "{}";)",
650 actualCommand.newTableName);
652 [schemaName, tableName, this](AddColumn
const& actualCommand) -> std::string {
653 return std::format(R
"(ALTER TABLE {} ADD "{}" {} {};)",
655 actualCommand.columnName,
656 ColumnType(actualCommand.columnType),
657 actualCommand.nullable == SqlNullable::NotNull ? "NOT NULL" :
"NULL");
659 [schemaName, tableName,
this](AlterColumn
const& actualCommand) -> std::string {
660 return std::format(R
"(ALTER TABLE {} ALTER COLUMN "{}" {} {};)",
662 actualCommand.columnName,
663 ColumnType(actualCommand.columnType),
664 actualCommand.nullable == SqlNullable::NotNull ? "NOT NULL" :
"NULL");
666 [schemaName, tableName](RenameColumn
const& actualCommand) -> std::string {
669 auto const bracket = [](std::string_view name) {
670 auto quoted = std::string {
"[" };
671 for (
char const ch: name)
680 auto objectName = std::string {};
681 if (!schemaName.empty())
682 objectName += bracket(schemaName) +
'.';
683 objectName += bracket(tableName) +
'.' + bracket(actualCommand.oldColumnName);
684 auto newName = std::string {};
685 for (
char const ch: actualCommand.newColumnName)
691 auto escapedObject = std::string {};
692 for (
char const ch: objectName)
696 escapedObject +=
'\'';
698 return std::format(
"EXEC sp_rename N'{}', N'{}', N'COLUMN';", escapedObject, newName);
700 [schemaName, tableName](DropColumn
const& actualCommand) -> std::string {
701 return std::format(R
"(ALTER TABLE {} DROP COLUMN "{}";)",
703 actualCommand.columnName);
705 [schemaName, tableName](AddIndex const& actualCommand) -> std::string {
706 using namespace std::string_view_literals;
707 auto const uniqueStr = actualCommand.unique ?
"UNIQUE "sv :
""sv;
708 if (schemaName.empty())
709 return std::format(R
"(CREATE {2}INDEX "{0}_{1}_index" ON "{0}" ("{1}");)",
711 actualCommand.columnName,
714 return std::format(R
"(CREATE {3}INDEX "{0}_{1}_{2}_index" ON "{0}"."{1}" ("{2}");)",
717 actualCommand.columnName,
720 [schemaName, tableName](DropIndex const& actualCommand) -> std::string {
721 if (schemaName.empty())
722 return std::format(R
"(DROP INDEX "{0}_{1}_index";)", tableName, actualCommand.columnName);
725 R
"(DROP INDEX "{0}_{1}_{2}_index";)", schemaName, tableName, actualCommand.columnName);
727 [schemaName, tableName](AddForeignKey const& actualCommand) -> std::string {
732 tableName, std::array { std::string_view { actualCommand.columnName } });
734 R
"(IF NOT EXISTS (SELECT 1 FROM sys.foreign_keys WHERE name = '{0}') ALTER TABLE {1} ADD {2};)",
737 BuildForeignKeyConstraint(tableName, actualCommand.columnName, actualCommand.referencedColumn));
739 [schemaName, tableName](DropForeignKey const& actualCommand) -> std::string {
740 return std::format(R
"(ALTER TABLE {} DROP CONSTRAINT "{}";)",
743 tableName, std::array { std::string_view { actualCommand.columnName } }));
745 [schemaName, tableName](AddCompositeForeignKey const& actualCommand) -> std::string {
746 std::stringstream ss;
747 ss <<
"ALTER TABLE " <<
FormatTableName(schemaName, tableName) <<
" ADD CONSTRAINT \""
751 for (
auto const& col: actualCommand.columns)
755 ss <<
'"' << col <<
'"';
757 auto const referencedSchemaName = actualCommand.referencedSchemaName.empty()
759 : std::string_view { actualCommand.referencedSchemaName };
760 ss <<
") REFERENCES " <<
FormatTableName(referencedSchemaName, actualCommand.referencedTableName)
764 for (
auto const& col: actualCommand.referencedColumns)
768 ss <<
'"' << col <<
'"';
773 [schemaName, tableName,
this](AddColumnIfNotExists
const& actualCommand) -> std::string {
776 R
"(IF NOT EXISTS (SELECT * FROM sys.columns WHERE object_id = OBJECT_ID('{}') AND name = '{}')
777ALTER TABLE {} ADD "{}" {} {};)",
779 actualCommand.columnName,
781 actualCommand.columnName,
782 ColumnType(actualCommand.columnType),
783 actualCommand.nullable == SqlNullable::NotNull ? "NOT NULL" :
"NULL");
785 [schemaName, tableName](DropColumnIfExists
const& actualCommand) -> std::string {
788 R
"(IF EXISTS (SELECT * FROM sys.columns WHERE object_id = OBJECT_ID('{}') AND name = '{}')
789ALTER TABLE {} DROP COLUMN "{}";)",
791 actualCommand.columnName,
793 actualCommand.columnName);
795 [schemaName, tableName](DropIndexIfExists const& actualCommand) -> std::string {
796 if (schemaName.empty())
798 R
"(IF EXISTS (SELECT * FROM sys.indexes WHERE name = '{0}_{1}_index' AND object_id = OBJECT_ID('{0}'))
799DROP INDEX "{0}_{1}_index" ON "{0}";)",
801 actualCommand.columnName);
804 R
"(IF EXISTS (SELECT * FROM sys.indexes WHERE name = '{0}_{1}_{2}_index')
805DROP INDEX "{0}_{1}_{2}_index" ON "{0}"."{1}";)",
808 actualCommand.columnName);
814 return { sqlQueryString.str() };
817 [[nodiscard]] std::string QueryServerVersion()
const override
819 return "SELECT @@VERSION";
823 [[nodiscard]]
StringList SqliteSettingsStatements(SqliteConnectionSettings
const& )
const override
831 [[nodiscard]] SqlAdvisoryLockHandler
const& AdvisoryLockOps()
const override
833 return SqlServerAdvisoryLockOps();
838 [[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().