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