4#include "../SqlQueryFormatter.hpp"
5#include "SQLiteFormatter.hpp"
7#include <reflection-cpp/reflection.hpp>
17class PostgreSqlFormatter final:
public SQLiteQueryFormatter
20 using SQLiteQueryFormatter::CreateTable;
22 [[nodiscard]]
bool RequiresTableRebuildForSchemaChange() const noexcept
override
27 [[nodiscard]]
bool IdentifiersAreCaseInsensitive() const noexcept
override
32 [[nodiscard]]
bool HasNativeGuidType() const noexcept
override
37 [[nodiscard]]
bool ComparesFixedCharWithPadSemantics() const noexcept
override
42 [[nodiscard]]
bool BoundsBulkLoadTransactionSize() const noexcept
override
47 [[nodiscard]] std::string FoldUnquotedIdentifier(std::string_view identifier)
const override
49 auto folded = std::string(identifier);
50 std::ranges::transform(
51 folded, folded.begin(), [](
char c) { return (c >=
'A' && c <=
'Z') ? static_cast<char>(c -
'A' +
'a') : c; });
55 [[nodiscard]]
bool SupportsSchemas() const noexcept
override
60 [[nodiscard]] std::string CreateSchemaIfNotExists(std::string_view schema)
const override
64 auto tag = std::string {
"$lw$" };
66 while (schema.contains(tag))
67 tag = std::format(
"$lw{}$", ++tagNumber);
68 return std::format(R
"(DO {0} BEGIN IF NOT EXISTS (SELECT 1 FROM pg_namespace WHERE nspname = '{1}') THEN )"
69 R"(CREATE SCHEMA "{2}"; END IF; END {0})",
75 [[nodiscard]]
StringList DropTable(std::string_view schemaName,
76 std::string_view
const& tableName,
77 bool ifExists =
false,
78 bool cascade =
false)
const override
80 std::string sql = ifExists ? std::format(
"DROP TABLE IF EXISTS {}",
FormatTableName(schemaName, tableName))
88 [[nodiscard]] std::string BinaryLiteral(std::span<uint8_t const> data)
const override
91 result.reserve((data.size() * 2) + 4);
93 for (uint8_t
byte: data)
94 result += std::format(
"{:02X}", byte);
99 [[nodiscard]] std::string QueryLastInsertId(std::string_view )
const override
104 return std::format(
"SELECT lastval();");
107 [[nodiscard]] std::string_view DateFunction() const noexcept
override
109 return "CURRENT_DATE";
117 [[nodiscard]] std::string SetDefaultSchemaStatement(std::string_view schema)
const override
121 return std::format(R
"(SET search_path TO "{}", public)", schema);
124 [[nodiscard]] std::string BuildColumnDefinition(SqlColumnDeclaration const& column)
const override
126 std::stringstream sqlQueryString;
128 sqlQueryString <<
'"' << column.name <<
"\" ";
133 bool const isAutoIncrementViaDefault = column.defaultValue.contains(
"nextval(");
134 bool const isAutoIncrement = column.primaryKey == SqlPrimaryKeyType::AUTO_INCREMENT || isAutoIncrementViaDefault;
137 sqlQueryString << SerialColumnType(column.type);
139 sqlQueryString << ColumnType(column.type);
142 sqlQueryString <<
" NOT NULL";
147 if (column.primaryKey == SqlPrimaryKeyType::AUTO_INCREMENT)
148 sqlQueryString <<
" PRIMARY KEY";
149 else if (column.primaryKey == SqlPrimaryKeyType::NONE && !column.index && column.unique)
150 sqlQueryString <<
" UNIQUE";
153 if (!column.defaultValue.empty() && !isAutoIncrement)
154 sqlQueryString <<
" DEFAULT " << column.defaultValue;
156 return sqlQueryString.str();
177 [[nodiscard]]
static std::string_view SerialColumnType(SqlColumnTypeDefinition
const& type)
179 using namespace SqlColumnTypeDefinitions;
181 return std::visit(detail::overloaded {
182 [](Bigint
const&) -> std::string_view {
return "BIGSERIAL"; },
183 [](Smallint
const&) -> std::string_view {
return "SMALLSERIAL"; },
186 [](Tinyint
const&) -> std::string_view {
return "SMALLSERIAL"; },
187 [](
auto const&) -> std::string_view {
return "SERIAL"; },
194 static constexpr std::size_t MaxVarcharLength = 10'485'760;
196 [[nodiscard]] std::string ColumnType(SqlColumnTypeDefinition
const& type)
const override
198 using namespace SqlColumnTypeDefinitions;
201 return std::visit(detail::overloaded {
202 [](Bigint
const&) -> std::string {
return "BIGINT"; },
203 [](Binary
const& type) -> std::string {
return std::format(
"BYTEA", type.size); },
204 [](Bool
const&) -> std::string {
return "BOOLEAN"; },
205 [](Char
const& type) -> std::string {
return std::format(
"CHAR({})", type.size); },
206 [](Date
const&) -> std::string {
return "DATE"; },
207 [](DateTime
const&) -> std::string {
return "TIMESTAMP"; },
208 [](Decimal
const& type) -> std::string {
209 return std::format(
"DECIMAL({}, {})", type.precision, type.scale);
211 [](Guid
const&) -> std::string {
return "UUID"; },
212 [](Integer
const&) -> std::string {
return "INTEGER"; },
213 [](NChar
const& type) -> std::string {
return std::format(
"CHAR({})", type.size); },
214 [](NVarchar
const& type) -> std::string {
215 if (type.size == 0 || type.size > MaxVarcharLength)
217 return std::format(
"VARCHAR({})", type.size);
219 [](Real
const& type) -> std::string {
223 return type.precision > 24 ?
"DOUBLE PRECISION" :
"REAL";
225 [](Smallint
const&) -> std::string {
return "SMALLINT"; },
226 [](Text
const&) -> std::string {
return "TEXT"; },
227 [](Time
const&) -> std::string {
return "TIME"; },
228 [](Timestamp
const&) -> std::string {
return "TIMESTAMP"; },
230 [](Tinyint
const&) -> std::string {
return "SMALLINT"; },
231 [](VarBinary
const& ) -> std::string {
return std::format(
"BYTEA"); },
232 [](Varchar
const& type) -> std::string {
233 if (type.size == 0 || type.size > MaxVarcharLength)
235 return std::format(
"VARCHAR({})", type.size);
242 [[nodiscard]]
StringList AlterTable(std::string_view schemaName,
243 std::string_view tableName,
244 std::vector<SqlAlterTableCommand>
const& commands)
const override
246 std::stringstream sqlQueryString;
248 int currentCommand = 0;
251 if (currentCommand > 0)
252 sqlQueryString <<
'\n';
255 using namespace SqlAlterTableCommands;
256 sqlQueryString << std::visit(
258 [schemaName, tableName](RenameTable
const& actualCommand) -> std::string {
259 return std::format(R
"(ALTER TABLE {} RENAME TO "{}";)",
261 actualCommand.newTableName);
263 [schemaName, tableName, this](AddColumn
const& actualCommand) -> std::string {
264 return std::format(R
"(ALTER TABLE {} ADD COLUMN "{}" {} {};)",
266 actualCommand.columnName,
267 ColumnType(actualCommand.columnType),
268 actualCommand.nullable == SqlNullable::NotNull ? "NOT NULL" :
"NULL");
270 [schemaName, tableName,
this](AlterColumn
const& actualCommand) -> std::string {
272 R
"(ALTER TABLE {0} ALTER COLUMN "{1}" TYPE {2}, ALTER COLUMN "{1}" {3} NOT NULL;)",
274 actualCommand.columnName,
275 ColumnType(actualCommand.columnType),
276 actualCommand.nullable == SqlNullable::NotNull ? "SET" :
"DROP");
278 [schemaName, tableName](RenameColumn
const& actualCommand) -> std::string {
279 return std::format(R
"(ALTER TABLE {} RENAME COLUMN "{}" TO "{}";)",
281 actualCommand.oldColumnName,
282 actualCommand.newColumnName);
284 [schemaName, tableName](DropColumn const& actualCommand) -> std::string {
285 return std::format(R
"(ALTER TABLE {} DROP COLUMN "{}";)",
287 actualCommand.columnName);
289 [schemaName, tableName](AddIndex const& actualCommand) -> std::string {
290 using namespace std::string_view_literals;
291 auto const uniqueStr = actualCommand.unique ?
"UNIQUE "sv :
""sv;
292 if (schemaName.empty())
293 return std::format(R
"(CREATE {2}INDEX "{0}_{1}_index" ON "{0}" ("{1}");)",
295 actualCommand.columnName,
298 return std::format(R
"(CREATE {3}INDEX "{0}_{1}_{2}_index" ON "{0}"."{1}" ("{2}");)",
301 actualCommand.columnName,
304 [schemaName, tableName](DropIndex const& actualCommand) -> std::string {
305 if (schemaName.empty())
306 return std::format(R
"(DROP INDEX "{0}_{1}_index";)", tableName, actualCommand.columnName);
309 R
"(DROP INDEX "{0}_{1}_{2}_index";)", schemaName, tableName, actualCommand.columnName);
311 [schemaName, tableName](AddForeignKey const& actualCommand) -> std::string {
316 "DO $$ BEGIN ALTER TABLE {} ADD {}; EXCEPTION WHEN duplicate_object THEN NULL; END $$;",
318 BuildForeignKeyConstraint(tableName, actualCommand.columnName, actualCommand.referencedColumn));
320 [schemaName, tableName](DropForeignKey
const& actualCommand) -> std::string {
321 return std::format(R
"(ALTER TABLE {} DROP CONSTRAINT "{}";)",
324 tableName, std::array { std::string_view { actualCommand.columnName } }));
326 [schemaName, tableName](AddCompositeForeignKey const& actualCommand) -> std::string {
327 std::stringstream ss;
328 ss <<
"ALTER TABLE " <<
FormatTableName(schemaName, tableName) <<
" ADD CONSTRAINT \""
332 for (
auto const& col: actualCommand.columns)
336 ss <<
'"' << col <<
'"';
338 auto const referencedSchemaName = actualCommand.referencedSchemaName.empty()
340 : std::string_view { actualCommand.referencedSchemaName };
341 ss <<
") REFERENCES " <<
FormatTableName(referencedSchemaName, actualCommand.referencedTableName)
345 for (
auto const& col: actualCommand.referencedColumns)
349 ss <<
'"' << col <<
'"';
354 [schemaName, tableName,
this](AddColumnIfNotExists
const& actualCommand) -> std::string {
356 return std::format(R
"(ALTER TABLE {} ADD COLUMN IF NOT EXISTS "{}" {} {};)",
358 actualCommand.columnName,
359 ColumnType(actualCommand.columnType),
360 actualCommand.nullable == SqlNullable::NotNull ? "NOT NULL" :
"NULL");
362 [schemaName, tableName](DropColumnIfExists
const& actualCommand) -> std::string {
364 return std::format(R
"(ALTER TABLE {} DROP COLUMN IF EXISTS "{}";)",
366 actualCommand.columnName);
368 [schemaName, tableName](DropIndexIfExists const& actualCommand) -> std::string {
370 if (schemaName.empty())
372 R
"(DROP INDEX IF EXISTS "{0}_{1}_index";)", tableName, actualCommand.columnName);
374 return std::format(R
"(DROP INDEX IF EXISTS "{0}_{1}_{2}_index";)",
377 actualCommand.columnName);
383 return { sqlQueryString.str() };
386 [[nodiscard]] std::string QueryServerVersion()
const override
388 return "SELECT version()";
392 [[nodiscard]]
StringList SqliteSettingsStatements(SqliteConnectionSettings
const& )
const override
400 [[nodiscard]] SqlAdvisoryLockHandler
const& AdvisoryLockOps()
const override
402 return PostgreSqlAdvisoryLockOps();
407 [[nodiscard]] SqlRetryClassifier
const& RetryOps() const noexcept
override
413 [[nodiscard]] std::string_view TableRowCountEstimateQuery() const noexcept
override
415 return "SELECT CAST(GREATEST(c.reltuples, 0) AS BIGINT) FROM pg_catalog.pg_class AS c "
416 "JOIN pg_catalog.pg_namespace AS n ON n.oid = c.relnamespace "
417 "WHERE n.nspname = ? AND c.relname = ?";
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.
LIGHTWEIGHT_API SqlRetryClassifier const & PostgreSqlRetryOps() noexcept
Returns the PostgreSQL-specific singleton classifier. See SqliteRetryOps().