Lightweight 0.1.0
Loading...
Searching...
No Matches
PostgreSqlFormatter.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 <format>
10
11class PostgreSqlFormatter final: public SQLiteQueryFormatter
12{
13 public:
14 [[nodiscard]] std::string QueryLastInsertId(std::string_view /*tableName*/) const override
15 {
16 // NB: Find a better way to do this on the given table.
17 // In our case it works, because we're expected to call this right after an insert.
18 // But a race condition may still happen if another client inserts a row at the same time too.
19 return std::format("SELECT lastval();");
20 }
21
22 [[nodiscard]] std::string BuildColumnDefinition(SqlColumnDeclaration const& column) const override
23 {
24 std::stringstream sqlQueryString;
25
26 sqlQueryString << '"' << column.name << "\" ";
27
28 if (column.primaryKey == SqlPrimaryKeyType::AUTO_INCREMENT)
29 sqlQueryString << "SERIAL";
30 else
31 sqlQueryString << ColumnType(column.type);
32
33 if (column.required)
34 sqlQueryString << " NOT NULL";
35
36 if (column.primaryKey == SqlPrimaryKeyType::AUTO_INCREMENT)
37 sqlQueryString << " PRIMARY KEY";
38 else if (column.primaryKey == SqlPrimaryKeyType::NONE && !column.index && column.unique)
39 sqlQueryString << " UNIQUE";
40
41 return sqlQueryString.str();
42 }
43
44 [[nodiscard]] std::string ColumnType(SqlColumnTypeDefinition const& type) const override
45 {
46 using namespace SqlColumnTypeDefinitions;
47
48 // PostgreSQL stores all strings as UTF-8
49 return std::visit(detail::overloaded {
50 [](Bigint const&) -> std::string { return "BIGINT"; },
51 [](Binary const& type) -> std::string { return std::format("BYTEA", type.size); },
52 [](Bool const&) -> std::string { return "BOOLEAN"; },
53 [](Char const& type) -> std::string { return std::format("CHAR({})", type.size); },
54 [](Date const&) -> std::string { return "DATE"; },
55 [](DateTime const&) -> std::string { return "TIMESTAMP"; },
56 [](Decimal const& type) -> std::string {
57 return std::format("DECIMAL({}, {})", type.precision, type.scale);
58 },
59 [](Guid const&) -> std::string { return "UUID"; },
60 [](Integer const&) -> std::string { return "INTEGER"; },
61 [](NChar const& type) -> std::string { return std::format("CHAR({})", type.size); },
62 [](NVarchar const& type) -> std::string { return std::format("VARCHAR({})", type.size); },
63 [](Real const&) -> std::string { return "REAL"; },
64 [](Smallint const&) -> std::string { return "SMALLINT"; },
65 [](Text const&) -> std::string { return "TEXT"; },
66 [](Time const&) -> std::string { return "TIME"; },
67 [](Timestamp const&) -> std::string { return "TIMESTAMP"; },
68 // NB: PostgreSQL doesn't have a TINYINT type, but it does have a SMALLINT type.
69 [](Tinyint const&) -> std::string { return "SMALLINT"; },
70 [](VarBinary const& type) -> std::string { return std::format("BYTEA({})", type.size); },
71 [](Varchar const& type) -> std::string { return std::format("VARCHAR({})", type.size); },
72 },
73 type);
74 }
75
76 [[nodiscard]] StringList AlterTable(std::string_view tableName,
77 std::vector<SqlAlterTableCommand> const& commands) const override
78 {
79 std::stringstream sqlQueryString;
80
81 int currentCommand = 0;
82 for (SqlAlterTableCommand const& command: commands)
83 {
84 if (currentCommand > 0)
85 sqlQueryString << '\n';
86 ++currentCommand;
87
88 using namespace SqlAlterTableCommands;
89 sqlQueryString << std::visit(
90 detail::overloaded {
91 [tableName](RenameTable const& actualCommand) -> std::string {
92 return std::format(
93 R"(ALTER TABLE "{}" RENAME TO "{}";)", tableName, actualCommand.newTableName);
94 },
95 [tableName, this](AddColumn const& actualCommand) -> std::string {
96 return std::format(R"(ALTER TABLE "{}" ADD COLUMN "{}" {} {};)",
97 tableName,
98 actualCommand.columnName,
99 ColumnType(actualCommand.columnType),
100 actualCommand.nullable == SqlNullable::NotNull ? "NOT NULL" : "NULL");
101 },
102 [tableName, this](AlterColumn const& actualCommand) -> std::string {
103 return std::format(
104 R"(ALTER TABLE "{0}" ALTER COLUMN "{1}" TYPE {2}, ALTER COLUMN "{1}" {3} NOT NULL;)",
105 tableName,
106 actualCommand.columnName,
107 ColumnType(actualCommand.columnType),
108 actualCommand.nullable == SqlNullable::NotNull ? "SET" : "DROP");
109 },
110 [tableName](RenameColumn const& actualCommand) -> std::string {
111 return std::format(R"(ALTER TABLE "{}" RENAME COLUMN "{}" TO "{}";)",
112 tableName,
113 actualCommand.oldColumnName,
114 actualCommand.newColumnName);
115 },
116 [tableName](DropColumn const& actualCommand) -> std::string {
117 return std::format(
118 R"(ALTER TABLE "{}" DROP COLUMN "{}";)", tableName, actualCommand.columnName);
119 },
120 [tableName](AddIndex const& actualCommand) -> std::string {
121 using namespace std::string_view_literals;
122 auto const uniqueStr = actualCommand.unique ? "UNIQUE "sv : ""sv;
123 return std::format(R"(CREATE {2}INDEX "{0}_{1}_index" ON "{0}"("{1}");)",
124 tableName,
125 actualCommand.columnName,
126 uniqueStr);
127 },
128 [tableName](DropIndex const& actualCommand) -> std::string {
129 return std::format(R"(DROP INDEX "{0}_{1}_index";)", tableName, actualCommand.columnName);
130 },
131 [tableName](AddForeignKey const& actualCommand) -> std::string {
132 return std::format(
133 R"(ALTER TABLE "{}" ADD {};)",
134 tableName,
135 BuildForeignKeyConstraint(actualCommand.columnName, actualCommand.referencedColumn));
136 },
137 [tableName](DropForeignKey const& actualCommand) -> std::string {
138 return std::format(R"(ALTER TABLE "{}" DROP CONSTRAINT "{}";)",
139 tableName,
140 std::format("FK_{}", actualCommand.columnName));
141 },
142 },
143 command);
144 }
145
146 return { sqlQueryString.str() };
147 }
148};
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).
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.