relx 0.1.0
A Modern C++23 Type-Safe SQL Query Builder
Loading...
Searching...
No Matches
table.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "column.hpp"
4#include "fixed_string.hpp"
5
6#include <optional>
7#include <string>
8#include <string_view>
9#include <tuple>
10#include <unordered_set>
11#include <utility>
12#include <vector>
13
14#include <boost/pfr.hpp>
15
16namespace relx::schema {
17
19template <typename T>
20concept is_column = requires {
21 { T::name } -> std::convertible_to<std::string_view>;
22 T::sql_type;
23 std::declval<T>().sql_definition();
24};
25
30template <typename T>
31concept TableConcept = requires {
32 { T::table_name } -> std::convertible_to<std::string_view>;
33 requires std::is_const_v<std::remove_reference_t<decltype(T::table_name)>>; // Ensure it's
34 // const/constexpr
35};
36
38template <typename T>
39concept is_constraint = requires(T t) {
40 { t.sql_definition() } -> std::convertible_to<std::string>;
41} && !is_column<T>; // Ensure a constraint is not also a column
42
47template <TableConcept Table>
48std::string collect_column_definitions(const Table& table_instance) {
49 std::vector<std::string> column_defs;
50 std::unordered_set<std::string> added_columns; // Track added columns by name
51
52 boost::pfr::for_each_field(table_instance, [&](const auto& field) {
53 if constexpr (is_column<std::remove_cvref_t<decltype(field)>>) {
54 // Use column name as key to avoid duplicates
55 std::string col_name = std::string(std::remove_cvref_t<decltype(field)>::name);
56 if (added_columns.find(col_name) == added_columns.end()) {
57 column_defs.push_back(field.sql_definition());
58 added_columns.insert(col_name);
59 }
60 }
61 });
62
63 // Join the column definitions with commas
64 if (column_defs.empty()) {
65 return "";
66 }
67
68 std::string result = column_defs[0];
69 for (size_t i = 1; i < column_defs.size(); ++i) {
70 result += ",\n" + column_defs[i];
71 }
72
73 return result;
74}
75
80template <TableConcept Table>
81std::string collect_constraint_definitions(const Table& table_instance) {
82 std::vector<std::string> constraint_defs;
83 std::unordered_set<std::string> added_constraints; // Track constraints to avoid duplicates
84
85 boost::pfr::for_each_field(table_instance, [&](const auto& field) {
86 if constexpr (is_constraint<std::remove_cvref_t<decltype(field)>>) {
87 std::string constraint = field.sql_definition();
88 if (added_constraints.find(constraint) == added_constraints.end()) {
89 constraint_defs.push_back(constraint);
90 added_constraints.insert(constraint);
91 }
92 }
93 });
94
95 // Join the constraint definitions with commas
96 if (constraint_defs.empty()) {
97 return "";
98 }
99
100 std::string result = constraint_defs[0];
101 for (size_t i = 1; i < constraint_defs.size(); ++i) {
102 result += ",\n" + constraint_defs[i];
103 }
104
105 return result;
106}
107
112template <TableConcept Table>
114private:
115 const Table& table_instance_;
116 bool if_not_exists_ = false;
117 bool if_exists_ = false;
118
119 bool cascade_ = false;
120 bool restrict_ = false;
121
122 std::vector<std::string> bind_params_;
123
124public:
125 create_table(const Table& table_instance) : table_instance_(table_instance) {}
126
128 if_not_exists_ = if_not_exists;
129 return *this;
130 }
131
133 if_exists_ = if_exists;
134 return *this;
135 }
136
138 cascade_ = cascade;
139 return *this;
140 }
141
143 restrict_ = restrict;
144 return *this;
145 }
146
149 std::optional<std::string> validate() const {
150 if (if_exists_ && if_not_exists_) {
151 return "if_exists and if_not_exists cannot both be true";
152 }
153 return std::nullopt;
154 }
155
156 std::string to_sql() const {
157 std::string sql = "CREATE TABLE ";
158 if (if_not_exists_) {
159 sql += "IF NOT EXISTS ";
160 }
161 if (if_exists_) {
162 sql += "IF EXISTS ";
163 }
164
165 sql += std::string(Table::table_name) + " (\n";
166
167 // Add column definitions
168 sql += collect_column_definitions(table_instance_);
169
170 // Add constraint definitions
171 std::string constraints = collect_constraint_definitions(table_instance_);
172 if (!constraints.empty()) {
173 sql += ",\n" + constraints;
174 }
175
176 sql += "\n);";
177 return sql;
178 }
179
180 const std::vector<std::string>& bind_params() const { return bind_params_; }
181};
182
188
189template <TableConcept Table>
191public:
192 drop_table(const Table& table_instance) : table_instance_(table_instance) {}
193
195 if_exists_ = if_exists;
196 return *this;
197 }
198
199 drop_table& cascade(bool cascade = true) {
200 cascade_ = cascade;
201 return *this;
202 }
203
205 restrict_ = restrict;
206 return *this;
207 }
208
209 std::string to_sql() const {
210 std::string sql = "DROP TABLE ";
211
212 if (if_exists_) {
213 sql += "IF EXISTS ";
214 }
215
216 sql += std::string(Table::table_name);
217
218 if (cascade_) {
219 sql += " CASCADE";
220 }
221
222 if (restrict_) {
223 sql += " RESTRICT";
224 }
225
226 sql += ";";
227
228 return sql;
229 }
230
231 const std::vector<std::string>& bind_params() const { return bind_params_; }
232
233private:
234 std::vector<std::string> bind_params_;
235 const Table& table_instance_;
236 bool if_exists_ = true;
237 bool cascade_ = false;
238 bool restrict_ = false;
239};
240} // namespace relx::schema
Generate CREATE TABLE SQL statement for a table struct.
Definition table.hpp:113
create_table & restrict(bool restrict=true)
Definition table.hpp:142
create_table & if_exists(bool if_exists=true)
Definition table.hpp:132
const std::vector< std::string > & bind_params() const
Definition table.hpp:180
create_table(const Table &table_instance)
Definition table.hpp:125
std::string to_sql() const
Definition table.hpp:156
std::optional< std::string > validate() const
Validate the create table configuration.
Definition table.hpp:149
create_table & cascade(bool cascade=true)
Definition table.hpp:137
create_table & if_not_exists(bool if_not_exists=true)
Definition table.hpp:127
Generate DROP TABLE SQL statement for a table struct.
Definition table.hpp:190
std::string to_sql() const
Definition table.hpp:209
const std::vector< std::string > & bind_params() const
Definition table.hpp:231
drop_table & cascade(bool cascade=true)
Definition table.hpp:199
drop_table(const Table &table_instance)
Definition table.hpp:192
drop_table & restrict(bool restrict=true)
Definition table.hpp:204
drop_table & if_exists(bool if_exists=true)
Definition table.hpp:194
Concept for a database table type.
Definition table.hpp:31
Helper to detect column members in a table.
Definition table.hpp:20
Helper to detect constraint members in a table.
Definition table.hpp:39
std::string collect_column_definitions(const Table &table_instance)
Generate SQL column definitions from a table struct using Boost PFR.
Definition table.hpp:48
std::string collect_constraint_definitions(const Table &table_instance)
Generate SQL constraint definitions from a table struct.
Definition table.hpp:81