relx 0.1.0
A Modern C++23 Type-Safe SQL Query Builder
Loading...
Searching...
No Matches
core.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "../schema/column.hpp"
4#include "../schema/core.hpp"
5#include "../schema/fixed_string.hpp"
6#include "../schema/table.hpp"
7
8#include <expected>
9#include <memory>
10#include <string>
11#include <type_traits>
12#include <vector>
13
14#include <boost/pfr.hpp>
15
16namespace relx::migrations {
17
29
33 std::string message;
34 std::string context;
35
38 const std::string& context = "") {
40 }
41
43 std::string format() const {
44 if (context.empty()) {
45 return message;
46 }
47 return context + ": " + message;
48 }
49};
50
52template <typename T>
53using MigrationResult = std::expected<T, MigrationError>;
54
70
73public:
74 virtual ~MigrationOperation() = default;
77 virtual OperationType type() const = 0;
79 return std::vector<std::string>{};
80 }
82 return std::vector<std::string>{};
83 }
84};
85
87template <schema::TableConcept Table>
89private:
90 const Table& table_;
91
92public:
93 explicit CreateTableOperation(const Table& table) : table_(table) {}
94
96 try {
97 return schema::create_table(table_).to_sql();
98 } catch (const std::exception& e) {
99 return std::unexpected(
101 "Failed to generate CREATE TABLE SQL: " + std::string(e.what()),
102 std::string(Table::table_name)));
103 }
104 }
105
107 try {
108 return schema::drop_table(table_).if_exists().to_sql();
109 } catch (const std::exception& e) {
110 return std::unexpected(
112 "Failed to generate DROP TABLE SQL: " + std::string(e.what()),
113 std::string(Table::table_name)));
114 }
115 }
116
117 OperationType type() const override { return OperationType::CREATE_TABLE; }
118};
119
121template <schema::TableConcept Table>
123private:
124 const Table& table_;
125
126public:
127 explicit DropTableOperation(const Table& table) : table_(table) {}
128
130 try {
131 return schema::drop_table(table_).if_exists().to_sql();
132 } catch (const std::exception& e) {
133 return std::unexpected(
135 "Failed to generate DROP TABLE SQL: " + std::string(e.what()),
136 std::string(Table::table_name)));
137 }
138 }
139
141 try {
142 return schema::create_table(table_).to_sql();
143 } catch (const std::exception& e) {
144 return std::unexpected(
146 "Failed to generate CREATE TABLE SQL: " + std::string(e.what()),
147 std::string(Table::table_name)));
148 }
149 }
150
151 OperationType type() const override { return OperationType::DROP_TABLE; }
152};
153
155template <typename Column>
157private:
158 std::string table_name_;
159 Column column_;
160
161public:
162 AddColumnOperation(std::string table_name, const Column& column)
163 : table_name_(std::move(table_name)), column_(column) {}
164
166 try {
167 return "ALTER TABLE " + table_name_ + " ADD COLUMN " + column_.sql_definition() + ";";
168 } catch (const std::exception& e) {
169 return std::unexpected(
171 "Failed to generate ADD COLUMN SQL: " + std::string(e.what()),
172 table_name_ + "." + std::string(Column::name)));
173 }
174 }
175
177 try {
178 return "ALTER TABLE " + table_name_ + " DROP COLUMN " + std::string(Column::name) + ";";
179 } catch (const std::exception& e) {
180 return std::unexpected(
182 "Failed to generate DROP COLUMN SQL: " + std::string(e.what()),
183 table_name_ + "." + std::string(Column::name)));
184 }
185 }
186
187 OperationType type() const override { return OperationType::ADD_COLUMN; }
188};
189
191template <typename Column>
193private:
194 std::string table_name_;
195 Column column_;
196
197public:
198 DropColumnOperation(std::string table_name, const Column& column)
199 : table_name_(std::move(table_name)), column_(column) {}
200
202 try {
203 return "ALTER TABLE " + table_name_ + " DROP COLUMN " + std::string(Column::name) + ";";
204 } catch (const std::exception& e) {
205 return std::unexpected(
207 "Failed to generate DROP COLUMN SQL: " + std::string(e.what()),
208 table_name_ + "." + std::string(Column::name)));
209 }
210 }
211
213 try {
214 return "ALTER TABLE " + table_name_ + " ADD COLUMN " + column_.sql_definition() + ";";
215 } catch (const std::exception& e) {
216 return std::unexpected(
218 "Failed to generate ADD COLUMN SQL: " + std::string(e.what()),
219 table_name_ + "." + std::string(Column::name)));
220 }
221 }
222
223 OperationType type() const override { return OperationType::DROP_COLUMN; }
224};
225
228private:
229 std::string table_name_;
230 std::string old_name_;
231 std::string new_name_;
232
233public:
234 RenameColumnOperation(std::string table_name, std::string old_name, std::string new_name)
235 : table_name_(std::move(table_name)), old_name_(std::move(old_name)),
236 new_name_(std::move(new_name)) {}
237
239 if (old_name_.empty() || new_name_.empty()) {
240 return std::unexpected(MigrationError::make(
241 MigrationErrorType::VALIDATION_FAILED, "Column names cannot be empty",
242 table_name_ + "." + old_name_ + " -> " + new_name_));
243 }
244 return "ALTER TABLE " + table_name_ + " RENAME COLUMN " + old_name_ + " TO " + new_name_ + ";";
245 }
246
248 if (old_name_.empty() || new_name_.empty()) {
249 return std::unexpected(MigrationError::make(
250 MigrationErrorType::VALIDATION_FAILED, "Column names cannot be empty",
251 table_name_ + "." + new_name_ + " -> " + old_name_));
252 }
253 return "ALTER TABLE " + table_name_ + " RENAME COLUMN " + new_name_ + " TO " + old_name_ + ";";
254 }
255
257};
258
261private:
262 std::string table_name_;
263 std::string old_name_;
264 std::string new_name_;
265
266public:
267 RenameConstraintOperation(std::string table_name, std::string old_name, std::string new_name)
268 : table_name_(std::move(table_name)), old_name_(std::move(old_name)),
269 new_name_(std::move(new_name)) {}
270
272 if (old_name_.empty() || new_name_.empty()) {
273 return std::unexpected(MigrationError::make(
274 MigrationErrorType::VALIDATION_FAILED, "Constraint names cannot be empty",
275 table_name_ + " constraint: " + old_name_ + " -> " + new_name_));
276 }
277 return "ALTER TABLE " + table_name_ + " RENAME CONSTRAINT " + old_name_ + " TO " + new_name_ +
278 ";";
279 }
280
282 if (old_name_.empty() || new_name_.empty()) {
283 return std::unexpected(MigrationError::make(
284 MigrationErrorType::VALIDATION_FAILED, "Constraint names cannot be empty",
285 table_name_ + " constraint: " + new_name_ + " -> " + old_name_));
286 }
287 return "ALTER TABLE " + table_name_ + " RENAME CONSTRAINT " + new_name_ + " TO " + old_name_ +
288 ";";
289 }
290
292};
293
296private:
297 std::string table_name_;
298 std::string target_column_;
299 std::string source_column_;
300 std::string forward_transform_;
301 std::string backward_transform_;
302
303public:
304 UpdateDataOperation(std::string table_name, std::string target_column, std::string source_column,
305 std::string forward_transform, std::string backward_transform)
306 : table_name_(std::move(table_name)), target_column_(std::move(target_column)),
307 source_column_(std::move(source_column)), forward_transform_(std::move(forward_transform)),
308 backward_transform_(std::move(backward_transform)) {}
309
311 if (forward_transform_.empty()) {
313 "Forward transformation cannot be empty",
314 table_name_ + "." + target_column_));
315 }
316 return "UPDATE " + table_name_ + " SET " + target_column_ + " = " + forward_transform_ + ";";
317 }
318
320 if (backward_transform_.empty()) {
322 "Backward transformation cannot be empty",
323 table_name_ + "." + source_column_));
324 }
325 return "UPDATE " + table_name_ + " SET " + source_column_ + " = " + backward_transform_ + ";";
326 }
327
328 OperationType type() const override { return OperationType::UPDATE_DATA; }
329};
330
333private:
334 std::vector<std::unique_ptr<MigrationOperation>> operations_;
335 std::string name_;
336
337public:
338 explicit Migration(std::string name) : name_(std::move(name)) {}
339
341 template <typename Op, typename... Args>
342 void add_operation(Args&&... args) {
343 operations_.push_back(std::make_unique<Op>(std::forward<Args>(args)...));
344 }
345
348 std::vector<std::string> sqls;
349 sqls.reserve(operations_.size());
350
351 for (const auto& op : operations_) {
352 auto sql_result = op->to_sql();
353 if (!sql_result) {
354 return std::unexpected(sql_result.error());
355 }
356 sqls.push_back(*sql_result);
357 }
358 return sqls;
359 }
360
363 std::vector<std::string> sqls;
364 sqls.reserve(operations_.size());
365
366 // Rollback operations should be in reverse order
367 for (auto it = operations_.rbegin(); it != operations_.rend(); ++it) {
368 auto sql_result = (*it)->rollback_sql();
369 if (!sql_result) {
370 return std::unexpected(sql_result.error());
371 }
372 sqls.push_back(*sql_result);
373 }
374 return sqls;
375 }
376
378 const std::string& name() const { return name_; }
379
381 bool empty() const { return operations_.empty(); }
382
384 size_t size() const { return operations_.size(); }
385};
386
387} // namespace relx::migrations
ADD COLUMN migration operation.
Definition core.hpp:156
MigrationResult< std::string > rollback_sql() const override
Definition core.hpp:176
AddColumnOperation(std::string table_name, const Column &column)
Definition core.hpp:162
MigrationResult< std::string > to_sql() const override
Definition core.hpp:165
OperationType type() const override
Definition core.hpp:187
CREATE TABLE migration operation.
Definition core.hpp:88
OperationType type() const override
Definition core.hpp:117
MigrationResult< std::string > to_sql() const override
Definition core.hpp:95
MigrationResult< std::string > rollback_sql() const override
Definition core.hpp:106
CreateTableOperation(const Table &table)
Definition core.hpp:93
DROP COLUMN migration operation.
Definition core.hpp:192
MigrationResult< std::string > to_sql() const override
Definition core.hpp:201
DropColumnOperation(std::string table_name, const Column &column)
Definition core.hpp:198
OperationType type() const override
Definition core.hpp:223
MigrationResult< std::string > rollback_sql() const override
Definition core.hpp:212
DROP TABLE migration operation.
Definition core.hpp:122
DropTableOperation(const Table &table)
Definition core.hpp:127
MigrationResult< std::string > to_sql() const override
Definition core.hpp:129
OperationType type() const override
Definition core.hpp:151
MigrationResult< std::string > rollback_sql() const override
Definition core.hpp:140
Base class for migration operations.
Definition core.hpp:72
virtual MigrationResult< std::vector< std::string > > bind_params() const
Definition core.hpp:78
virtual MigrationResult< std::string > to_sql() const =0
virtual MigrationResult< std::vector< std::string > > rollback_bind_params() const
Definition core.hpp:81
virtual MigrationResult< std::string > rollback_sql() const =0
virtual OperationType type() const =0
Container for migration operations.
Definition core.hpp:332
Migration(std::string name)
Definition core.hpp:338
const std::string & name() const
Get migration name.
Definition core.hpp:378
bool empty() const
Check if migration is empty.
Definition core.hpp:381
MigrationResult< std::vector< std::string > > rollback_sql() const
Generate rollback migration SQL.
Definition core.hpp:362
void add_operation(Args &&... args)
Add an operation to the migration.
Definition core.hpp:342
size_t size() const
Get number of operations.
Definition core.hpp:384
MigrationResult< std::vector< std::string > > forward_sql() const
Generate forward migration SQL.
Definition core.hpp:347
RENAME COLUMN migration operation.
Definition core.hpp:227
RenameColumnOperation(std::string table_name, std::string old_name, std::string new_name)
Definition core.hpp:234
MigrationResult< std::string > to_sql() const override
Definition core.hpp:238
MigrationResult< std::string > rollback_sql() const override
Definition core.hpp:247
OperationType type() const override
Definition core.hpp:256
RENAME CONSTRAINT migration operation.
Definition core.hpp:260
RenameConstraintOperation(std::string table_name, std::string old_name, std::string new_name)
Definition core.hpp:267
OperationType type() const override
Definition core.hpp:291
MigrationResult< std::string > rollback_sql() const override
Definition core.hpp:281
MigrationResult< std::string > to_sql() const override
Definition core.hpp:271
UPDATE DATA migration operation for column transformations.
Definition core.hpp:295
MigrationResult< std::string > rollback_sql() const override
Definition core.hpp:319
UpdateDataOperation(std::string table_name, std::string target_column, std::string source_column, std::string forward_transform, std::string backward_transform)
Definition core.hpp:304
OperationType type() const override
Definition core.hpp:328
MigrationResult< std::string > to_sql() const override
Definition core.hpp:310
Represents a column in a database table.
Definition column.hpp:217
Generate CREATE TABLE SQL statement for a table struct.
Definition table.hpp:113
std::string to_sql() const
Definition table.hpp:156
Generate DROP TABLE SQL statement for a table struct.
Definition table.hpp:190
std::string to_sql() const
Definition table.hpp:209
drop_table & if_exists(bool if_exists=true)
Definition table.hpp:194
std::expected< T, MigrationError > MigrationResult
Result type for migration operations.
Definition core.hpp:53
OperationType
Enum for migration operation types.
Definition core.hpp:56
MigrationErrorType
Error types for migration operations.
Definition core.hpp:19
STL namespace.
Error information for migration operations.
Definition core.hpp:31
MigrationErrorType type
Definition core.hpp:32
std::string format() const
Get a formatted error message.
Definition core.hpp:43
std::string context
Additional context (table name, column name, etc.)
Definition core.hpp:34
static MigrationError make(MigrationErrorType type, const std::string &message, const std::string &context="")
Create a MigrationError with automatic formatting.
Definition core.hpp:37