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"); },
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(R"(ALTER TABLE "{}" RENAME TO "{}";)", tableName, actualCommand.newTableName);
93 },
94 [tableName, this](AddColumn const& actualCommand) -> std::string {
95 return std::format(R"(ALTER TABLE "{}" ADD COLUMN "{}" {} {};)",
96 tableName,
97 actualCommand.columnName,
98 ColumnType(actualCommand.columnType),
99 actualCommand.nullable == SqlNullable::NotNull ? "NOT NULL" : "NULL");
100 },
101 [tableName, this](AlterColumn const& actualCommand) -> std::string {
102 return std::format(
103 R"(ALTER TABLE "{0}" ALTER COLUMN "{1}" TYPE {2}, ALTER COLUMN "{1}" {3} NOT NULL;)",
104 tableName,
105 actualCommand.columnName,
106 ColumnType(actualCommand.columnType),
107 actualCommand.nullable == SqlNullable::NotNull ? "SET" : "DROP");
108 },
109 [tableName](RenameColumn const& actualCommand) -> std::string {
110 return std::format(R"(ALTER TABLE "{}" RENAME COLUMN "{}" TO "{}";)",
111 tableName,
112 actualCommand.oldColumnName,
113 actualCommand.newColumnName);
114 },
115 [tableName](DropColumn const& actualCommand) -> std::string {
116 return std::format(R"(ALTER TABLE "{}" DROP COLUMN "{}";)", tableName, actualCommand.columnName);
117 },
118 [tableName](AddIndex const& actualCommand) -> std::string {
119 using namespace std::string_view_literals;
120 auto const uniqueStr = actualCommand.unique ? "UNIQUE "sv : ""sv;
121 return std::format(R"(CREATE {2}INDEX "{0}_{1}_index" ON "{0}"("{1}");)",
122 tableName,
123 actualCommand.columnName,
124 uniqueStr);
125 },
126 [tableName](DropIndex const& actualCommand) -> std::string {
127 return std::format(R"(DROP INDEX "{0}_{1}_index";)", tableName, actualCommand.columnName);
128 },
129 [tableName](AddForeignKey const& actualCommand) -> std::string {
130 return std::format(
131 R"(ALTER TABLE "{}" ADD {};)",
132 tableName,
133 BuildForeignKeyConstraint(actualCommand.columnName, actualCommand.referencedColumn));
134 },
135 [tableName](DropForeignKey const& actualCommand) -> std::string {
136 return std::format(R"(ALTER TABLE "{}" DROP CONSTRAINT "{}";)",
137 tableName,
138 std::format("FK_{}", actualCommand.columnName));
139 },
140 },
141 command);
142 }
143
144 return { sqlQueryString.str() };
145 }
146};
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.