relx 0.1.0
A Modern C++23 Type-Safe SQL Query Builder
Loading...
Searching...
No Matches
update.hpp
Go to the documentation of this file.
1#pragma once
2
4#include "condition.hpp"
5#include "core.hpp"
6#include "function.hpp"
7#include "meta.hpp"
8#include "operators.hpp"
9#include "value.hpp"
10
11#include <iostream>
12#include <memory>
13#include <optional>
14#include <sstream>
15#include <string>
16#include <tuple>
17#include <utility>
18#include <vector>
19
20namespace relx::query {
21
23template <ColumnType Column, SqlExpr Value>
24struct SetItem {
27
28 // Constructor to ensure the SetItem can be properly initialized
29 SetItem(ColumnRef<Column> col, Value val) : column(std::move(col)), value(std::move(val)) {}
30
31 std::string to_sql() const { return column.column_name() + " = " + value.to_sql(); }
32
33 std::vector<std::string> bind_params() const { return value.bind_params(); }
34};
35
41template <TableType Table, typename Sets = std::tuple<>, typename Where = std::nullopt_t,
42 typename ReturningColumns = std::tuple<>>
44private:
45 Table table_;
46 Sets sets_;
47 Where where_;
48 ReturningColumns returning_columns_;
49
50 // Helper to convert the RETURNING clause to SQL
51 std::string returning_to_sql() const {
52 if constexpr (is_empty_tuple<ReturningColumns>()) {
53 return "";
54 } else {
55 std::stringstream ss;
56 ss << " RETURNING ";
57 int i = 0;
58 std::apply(
59 [&](const auto&... cols) { ((ss << (i++ > 0 ? ", " : "") << cols.to_sql()), ...); },
60 returning_columns_);
61 return ss.str();
62 }
63 }
64
65 // Helper to collect bind parameters from RETURNING clause
66 std::vector<std::string> returning_bind_params() const {
67 std::vector<std::string> params;
68
69 if constexpr (!is_empty_tuple<ReturningColumns>()) {
70 std::apply(
71 [&](const auto&... cols) {
72 auto process_col = [&params](const auto& col) {
73 auto col_params = col.bind_params();
74 params.insert(params.end(), col_params.begin(), col_params.end());
75 };
76
77 (process_col(cols), ...);
78 },
79 returning_columns_);
80 }
81
82 return params;
83 }
84
85public:
86 using table_type = Table;
87 using sets_type = Sets;
88 using where_type = Where;
89 using returning_columns_type = ReturningColumns;
90
96 explicit UpdateQuery(Table table, Sets sets = {}, Where where = std::nullopt,
97 ReturningColumns returning_columns = {})
98 : table_(std::move(table)), sets_(std::move(sets)), where_(std::move(where)),
99 returning_columns_(std::move(returning_columns)) {}
100
103 std::string to_sql() const {
104 std::stringstream ss;
105 ss << "UPDATE " << table_.table_name;
106
107 // Add SET clause
108 if constexpr (!is_empty_tuple<Sets>()) {
109 ss << " SET ";
110 ss << tuple_to_sql(sets_, ", ");
111 }
112
113 // Add WHERE clause
114 if constexpr (!std::is_same_v<Where, std::nullopt_t>) {
115 if (where_.has_value()) {
116 ss << " WHERE " << where_.value().to_sql();
117 }
118 }
119
120 // Add RETURNING clause if specified
121 ss << returning_to_sql();
122
123 return ss.str();
124 }
125
128 std::vector<std::string> bind_params() const {
129 std::vector<std::string> params;
130
131 // Collect parameters from SET items
132 if constexpr (!is_empty_tuple<Sets>()) {
133 auto set_params = tuple_bind_params(sets_);
134 params.insert(params.end(), set_params.begin(), set_params.end());
135 }
136
137 // Collect where parameter
138 if constexpr (!std::is_same_v<Where, std::nullopt_t>) {
139 if (where_.has_value()) {
140 auto where_params = where_.value().bind_params();
141 params.insert(params.end(), where_params.begin(), where_params.end());
142 }
143 }
144
145 // Add RETURNING bind parameters
146 auto returning_params = returning_bind_params();
147 params.insert(params.end(), returning_params.begin(), returning_params.end());
148
149 return params;
150 }
151
158 template <ColumnType Col, SqlExpr Val>
159 auto set(const Col& column, Val&& val) const {
160 using SetItemType = SetItem<Col, std::remove_cvref_t<Val>>;
161
162 // Create a new SetItem
163 auto col_ref = ColumnRef<Col>(column);
164 auto set_item = SetItemType(col_ref, std::forward<Val>(val));
165
166 // Create a tuple with the new SetItem
167 auto new_item_tuple = std::make_tuple(std::move(set_item));
168
169 // Concatenate with existing sets
170 auto new_sets = std::tuple_cat(sets_, std::move(new_item_tuple));
171
172 // Return a new query with the updated sets
174 table_, std::move(new_sets), where_, returning_columns_);
175 }
176
183 template <ColumnType Col, typename T>
184 requires(!SqlExpr<T>)
185 auto set(const Col& column, T&& val) const {
186 // Note: For now, we'll use a basic type check until we can access the type_checking utilities
187 // This provides a good foundation for type safety in UPDATE operations
188
189 // Wrap the raw value in a Value expression
190 auto value_expr = value(std::forward<T>(val));
191 return set(column, std::move(value_expr));
192 }
193
198 template <ConditionExpr Condition>
199 auto where(const Condition& cond) const {
201 table_, sets_, std::optional<Condition>(cond), returning_columns_);
202 }
203
210 template <ColumnType Col, std::ranges::range Range>
211 requires std::convertible_to<std::ranges::range_value_t<Range>, std::string>
212 auto where_in(const Col& column, const Range& values) const {
213 auto col_expr = column_ref(column);
214 auto in_condition = in(col_expr, values);
215 return where(in_condition);
216 }
217
222 template <typename... Args>
223 auto returning(const Args&... args) const {
224 // Helper to convert columns to ColumnRef expressions if they're not already SqlExpr
225 auto to_expr = [](const auto& arg) {
226 if constexpr (SqlExpr<std::remove_cvref_t<decltype(arg)>>) {
227 return arg;
228 } else if constexpr (ColumnType<std::remove_cvref_t<decltype(arg)>>) {
229 return column_ref(arg);
230 } else {
231 static_assert(SqlExpr<std::remove_cvref_t<decltype(arg)>> ||
232 ColumnType<std::remove_cvref_t<decltype(arg)>>,
233 "Arguments to returning() must be either columns or SQL expressions");
234 // This line is never reached, it's just to make the compiler happy
235 return arg;
236 }
237 };
238
239 using ReturningTuple = std::tuple<decltype(to_expr(std::declval<Args>()))...>;
240 auto returning_tuple = std::make_tuple(to_expr(args)...);
241
242 return UpdateQuery<Table, Sets, Where, ReturningTuple>(table_, sets_, where_,
243 std::move(returning_tuple));
244 }
245};
246
251template <TableType Table>
252auto update(const Table& table) {
253 return UpdateQuery<Table>(table);
254}
255
256} // namespace relx::query
Column reference expression.
Base UPDATE query builder.
Definition update.hpp:43
std::string to_sql() const
Generate the SQL for this UPDATE query.
Definition update.hpp:103
ReturningColumns returning_columns_type
Definition update.hpp:89
auto set(const Col &column, Val &&val) const
Add or replace a SET clause assignment.
Definition update.hpp:159
auto where(const Condition &cond) const
Add a WHERE clause to the query.
Definition update.hpp:199
auto where_in(const Col &column, const Range &values) const
Set a condition for filtering the rows to update using IN with values.
Definition update.hpp:212
auto returning(const Args &... args) const
Specify columns to return after update.
Definition update.hpp:223
UpdateQuery(Table table, Sets sets={}, Where where=std::nullopt, ReturningColumns returning_columns={})
Constructor for the UPDATE query builder.
Definition update.hpp:96
std::vector< std::string > bind_params() const
Get the bind parameters for this UPDATE query.
Definition update.hpp:128
auto set(const Col &column, T &&val) const
Add or replace a SET clause assignment with a raw value (with type checking)
Definition update.hpp:185
Represents a literal value in a SQL query.
Definition value.hpp:15
std::vector< std::string > bind_params() const override
Definition value.hpp:23
std::string to_sql() const override
Definition value.hpp:21
Represents a column in a database table.
Definition column.hpp:217
Concept for column types.
Definition core.hpp:40
Concept for SQL expression components.
Definition core.hpp:29
std::string tuple_to_sql(const Tuple &tuple, const char *separator)
Helper to convert a tuple of expressions to SQL.
Definition meta.hpp:20
auto in(const schema::column< TableT, Name, T, Modifiers... > &col, Range values)
Create an IN condition with type checking for columns.
auto column_ref(const Column &col)
Create a column reference expression.
auto update(const Table &table)
Create an UPDATE query for the specified table.
Definition update.hpp:252
auto to_expr(const C &col, std::string_view table_name="")
Helper to wrap a schema column in a SQL expression.
auto value(T val)
Create a value expression.
Definition value.hpp:120
std::vector< std::string > tuple_bind_params(const Tuple &tuple)
Helper to collect bind parameters from a tuple of expressions.
Definition meta.hpp:31
auto val(const char *str)
Helper to create a value expression from a string literal.
Definition value.hpp:127
STL namespace.
Represents a SET clause assignment in an UPDATE statement.
Definition update.hpp:24
std::string to_sql() const
Definition update.hpp:31
ColumnRef< Column > column
Definition update.hpp:25
std::vector< std::string > bind_params() const
Definition update.hpp:33
SetItem(ColumnRef< Column > col, Value val)
Definition update.hpp:29