Lightweight 0.20260921.0
Loading...
Searching...
No Matches
Update.hpp
1// SPDX-License-Identifier: Apache-2.0
2
3#pragma once
4
5#include "Core.hpp"
6
7#include <string>
8#include <string_view>
9#include <vector>
10
11namespace Lightweight
12{
13
14/// @brief Query builder for building UPDATE ... queries.
15///
16/// @ingroup QueryBuilder
17class [[nodiscard]] SqlUpdateQueryBuilder final: public SqlWhereClauseBuilder<SqlUpdateQueryBuilder>
18{
19 public:
20 /// Constructs a new SqlUpdateQueryBuilder object.
21 ///
22 /// @param formatter The SQL query formatter to use. One of SqlServerQueryFormatter, PostgreSqlFormatter
23 /// @param table The name of the table to update.
24 /// @param tableAlias The alias of the table to update.
25 /// @param inputBindings The input bindings to use for the query.
26 SqlUpdateQueryBuilder(SqlQueryFormatter const& formatter,
27 std::string table,
28 std::string tableAlias,
29 std::vector<SqlVariant>* inputBindings) noexcept:
30 SqlWhereClauseBuilder<SqlUpdateQueryBuilder> {},
31 m_formatter { formatter }
32 {
33 m_searchCondition.tableName = std::move(table);
34 m_searchCondition.tableAlias = std::move(tableAlias);
35 m_searchCondition.inputBindings = inputBindings;
36 }
37
38 /// Returns the search condition for the query.
39 SqlSearchCondition& SearchCondition() noexcept // NOLINT(bugprone-derived-method-shadowing-base-method)
40 {
41 return m_searchCondition;
42 }
43
44 /// @brief Returns the SQL query formatter.
45 ///
46 /// Named Formatter() because SqlWhereClauseBuilder resolves it through the CRTP derived type;
47 /// the apparent shadowing is the mechanism, not an accident.
48 [[nodiscard]] SqlQueryFormatter const& Formatter()
49 const noexcept // NOLINT(bugprone-derived-method-shadowing-base-method)
50 {
51 return m_formatter;
52 }
53
54 /// Adds a single column to the SET clause.
55 template <typename ColumnValue>
56 SqlUpdateQueryBuilder& Set(std::string_view columnName, ColumnValue const& value);
57
58 /// Adds a single column to the SET clause with the value being a string literal.
59 template <std::size_t N>
60 SqlUpdateQueryBuilder& Set(std::string_view columnName, char const (&value)[N]);
61
62 /// Finalizes building the query as UPDATE ... query.
63 [[nodiscard]] std::string ToSql() const;
64
65 private:
66 SqlQueryFormatter const& m_formatter;
67 std::string m_values;
68 SqlSearchCondition m_searchCondition;
69};
70
71template <typename ColumnValue>
72SqlUpdateQueryBuilder& SqlUpdateQueryBuilder::Set(std::string_view columnName, ColumnValue const& value)
73{
74 using namespace std::string_view_literals;
75
76 if (!m_values.empty())
77 m_values += ", "sv;
78
79 m_values += '"';
80 m_values += columnName;
81 m_values += R"(" = )"sv;
82
83 if constexpr (std::is_same_v<ColumnValue, SqlNullType>)
84 m_values += "NULL"sv;
85 else if constexpr (std::is_same_v<ColumnValue, SqlWildcardType>)
86 m_values += '?';
87 else if constexpr (std::is_same_v<ColumnValue, SqlVariant>)
88 {
89 // A NULL variant becomes the SQL literal NULL rather than a bound parameter -- see the same
90 // branch in SqlInsertQueryBuilder::Set (Insert.hpp) for why a bound NULL cannot be typed
91 // reliably on MS SQL Server (SQLDescribeParam is rejected with 07009 once a parameter has been
92 // bound). A literal needs no type resolution.
93 if (value.IsNull())
94 m_values += "NULL"sv;
95 else if (m_searchCondition.inputBindings)
96 {
97 m_values += '?';
98 m_searchCondition.inputBindings->emplace_back(value);
99 }
100 else
101 m_values += m_formatter.StringLiteral(std::format("{}", value));
102 }
103 else if (m_searchCondition.inputBindings)
104 {
105 m_values += '?';
106 m_searchCondition.inputBindings->emplace_back(value);
107 }
108 else if constexpr (std::is_same_v<ColumnValue, char>)
109 m_values += m_formatter.StringLiteral(value);
110 else if constexpr (std::is_arithmetic_v<ColumnValue>)
111 m_values += std::format("{}", value);
112 else if constexpr (!WhereConditionLiteralType<ColumnValue>::needsQuotes)
113 m_values += std::format("{}", value);
114 else
115 {
116 // Route through the formatter rather than quoting by hand: it escapes embedded quotes and
117 // applies the dialect's literal rules (e.g. SQL Server's N'…' + NCHAR() encoding).
118 // SqlInsertQueryBuilder::Set does the same — see Insert.hpp.
119 m_values += m_formatter.StringLiteral(std::format("{}", value));
120 }
121
122 return *this;
123}
124
125template <std::size_t N>
126SqlUpdateQueryBuilder& SqlUpdateQueryBuilder::Set(std::string_view columnName, char const (&value)[N])
127{
128 return Set(columnName, std::string_view { value, N - 1 });
129}
130
131inline LIGHTWEIGHT_FORCE_INLINE std::string SqlUpdateQueryBuilder::ToSql() const
132{
133 return m_formatter.Update(
134 m_searchCondition.tableName, m_searchCondition.tableAlias, m_values, m_searchCondition.condition);
135}
136
137} // namespace Lightweight
LIGHTWEIGHT_API std::vector< std::string > ToSql(SqlQueryFormatter const &formatter, SqlMigrationPlanElement const &element)