Database System 0.1.0
Advanced C++20 Database System with Multi-Backend Support
Loading...
Searching...
No Matches
immutable_query_builder.cpp
Go to the documentation of this file.
1// BSD 3-Clause License
2// Copyright (c) 2025, 🍀☀🌕🌥 🌊
3// See the LICENSE file in the project root for full license information.
4
6#include <sstream>
7#include <algorithm>
8
9namespace database
10{
11 // Public constructor
13 : table_(table)
14 , select_fields_()
15 , conditions_()
16 , order_by_()
17 , limit_(std::nullopt)
18 , offset_(std::nullopt)
19 , joins_()
20 , group_by_fields_()
21 , having_clause_()
22 {
23 }
24
25 // Private constructor for copy-with-modification
27 std::string table,
28 std::vector<std::string> select_fields,
29 std::vector<query_condition> conditions,
30 std::vector<std::pair<std::string, sort_order>> order_by,
31 std::optional<uint32_t> limit,
32 std::optional<uint32_t> offset,
33 std::vector<std::tuple<std::string, std::string, join_type>> joins,
34 std::vector<std::string> group_by_fields,
35 std::string having_clause
36 )
37 : table_(std::move(table))
38 , select_fields_(std::move(select_fields))
39 , conditions_(std::move(conditions))
40 , order_by_(std::move(order_by))
41 , limit_(limit)
42 , offset_(offset)
43 , joins_(std::move(joins))
44 , group_by_fields_(std::move(group_by_fields))
45 , having_clause_(std::move(having_clause))
46 {
47 }
48
49 immutable_query_builder immutable_query_builder::select(std::vector<std::string> fields) const
50 {
52 table_,
53 std::move(fields),
56 limit_,
57 offset_,
58 joins_,
61 );
62 }
63
64 immutable_query_builder immutable_query_builder::where(const std::string& field, const std::string& op, const core::database_value& value) const
65 {
66 auto new_conditions = conditions_;
67 new_conditions.emplace_back(field, op, value);
68
70 table_,
72 std::move(new_conditions),
74 limit_,
75 offset_,
76 joins_,
79 );
80 }
81
83 {
84 auto new_conditions = conditions_;
85 new_conditions.push_back(condition);
86
88 table_,
90 std::move(new_conditions),
92 limit_,
93 offset_,
94 joins_,
97 );
98 }
99
101 {
102 auto new_order_by = order_by_;
103 new_order_by.emplace_back(field, order);
104
106 table_,
109 std::move(new_order_by),
110 limit_,
111 offset_,
112 joins_,
115 );
116 }
117
119 {
121 table_,
124 order_by_,
125 count,
126 offset_,
127 joins_,
130 );
131 }
132
134 {
136 table_,
139 order_by_,
140 limit_,
141 count,
142 joins_,
145 );
146 }
147
148 immutable_query_builder immutable_query_builder::join(const std::string& table, const std::string& condition, join_type type) const
149 {
150 auto new_joins = joins_;
151 new_joins.emplace_back(table, condition, type);
152
154 table_,
157 order_by_,
158 limit_,
159 offset_,
160 std::move(new_joins),
163 );
164 }
165
166 immutable_query_builder immutable_query_builder::group_by(std::vector<std::string> fields) const
167 {
169 table_,
172 order_by_,
173 limit_,
174 offset_,
175 joins_,
176 std::move(fields),
178 );
179 }
180
182 {
184 table_,
187 order_by_,
188 limit_,
189 offset_,
190 joins_,
192 condition
193 );
194 }
195
200
202 {
203 std::ostringstream query;
204
205 // SELECT clause
206 query << "SELECT ";
207 if (select_fields_.empty())
208 {
209 query << "*";
210 }
211 else
212 {
213 for (size_t i = 0; i < select_fields_.size(); ++i)
214 {
215 if (i > 0)
216 query << ", ";
217 query << escape_identifier(select_fields_[i], db_type);
218 }
219 }
220
221 // FROM clause
222 query << " FROM " << escape_identifier(table_, db_type);
223
224 // JOIN clauses
225 for (const auto& [join_table, join_condition, join_type_val] : joins_)
226 {
227 query << " " << join_type_to_string(join_type_val) << " JOIN "
228 << escape_identifier(join_table, db_type)
229 << " ON " << join_condition;
230 }
231
232 // WHERE clause
233 if (!conditions_.empty())
234 {
235 query << " WHERE ";
236 for (size_t i = 0; i < conditions_.size(); ++i)
237 {
238 if (i > 0)
239 query << " AND ";
240 query << conditions_[i].to_sql();
241 }
242 }
243
244 // GROUP BY clause
245 if (!group_by_fields_.empty())
246 {
247 query << " GROUP BY ";
248 for (size_t i = 0; i < group_by_fields_.size(); ++i)
249 {
250 if (i > 0)
251 query << ", ";
252 query << escape_identifier(group_by_fields_[i], db_type);
253 }
254 }
255
256 // HAVING clause
257 if (!having_clause_.empty())
258 {
259 query << " HAVING " << having_clause_;
260 }
261
262 // ORDER BY clause
263 if (!order_by_.empty())
264 {
265 query << " ORDER BY ";
266 for (size_t i = 0; i < order_by_.size(); ++i)
267 {
268 if (i > 0)
269 query << ", ";
270 query << escape_identifier(order_by_[i].first, db_type) << " "
271 << (order_by_[i].second == sort_order::asc ? "ASC" : "DESC");
272 }
273 }
274
275 // LIMIT clause
276 if (limit_.has_value())
277 {
278 query << " LIMIT " << limit_.value();
279 }
280
281 // OFFSET clause
282 if (offset_.has_value())
283 {
284 query << " OFFSET " << offset_.value();
285 }
286
287 return query.str();
288 }
289
290 std::string immutable_query_builder::escape_identifier(const std::string& identifier, database_types db_type) const
291 {
292 switch (db_type)
293 {
295 return "\"" + identifier + "\"";
297 return "\"" + identifier + "\"";
298 default:
299 return identifier;
300 }
301 }
302
304 {
305 if (std::holds_alternative<std::string>(value))
306 {
307 const auto& str = std::get<std::string>(value);
308 std::string escaped;
309 escaped.reserve(str.size() + 2);
310 escaped += "'";
311 for (char c : str)
312 {
313 if (c == '\'')
314 escaped += "''";
315 else
316 escaped += c;
317 }
318 escaped += "'";
319 return escaped;
320 }
321 else if (std::holds_alternative<int64_t>(value))
322 {
323 return std::to_string(std::get<int64_t>(value));
324 }
325 else if (std::holds_alternative<double>(value))
326 {
327 return std::to_string(std::get<double>(value));
328 }
329 else if (std::holds_alternative<bool>(value))
330 {
331 return std::get<bool>(value) ? "TRUE" : "FALSE";
332 }
333 else if (std::holds_alternative<std::nullptr_t>(value))
334 {
335 return "NULL";
336 }
337 return "NULL";
338 }
339
341 {
342 switch (type)
343 {
344 case join_type::inner:
345 return "INNER";
346 case join_type::left:
347 return "LEFT";
348 case join_type::right:
349 return "RIGHT";
351 return "FULL OUTER";
352 case join_type::cross:
353 return "CROSS";
354 default:
355 return "INNER";
356 }
357 }
358
359} // namespace database
Thread-safe immutable query builder using functional programming style.
immutable_query_builder join(const std::string &table, const std::string &condition, join_type type=join_type::inner) const
JOIN clause - adds a join.
std::string format_value(const core::database_value &value, database_types db_type) const
immutable_query_builder group_by(std::vector< std::string > fields) const
GROUP BY clause - groups results.
const std::optional< uint32_t > limit_
const std::optional< uint32_t > offset_
immutable_query_builder order_by(const std::string &field, sort_order order=sort_order::asc) const
ORDER BY clause - specifies sorting.
const std::vector< std::tuple< std::string, std::string, join_type > > joins_
const std::vector< query_condition > conditions_
std::string join_type_to_string(join_type type) const
const std::vector< std::string > select_fields_
immutable_query_builder select(std::vector< std::string > fields) const
SELECT clause - specifies which columns to select.
std::string escape_identifier(const std::string &identifier, database_types db_type) const
std::string build() const
Builds the final SQL query string.
const std::vector< std::string > group_by_fields_
immutable_query_builder where(const std::string &field, const std::string &op, const core::database_value &value) const
WHERE clause - adds a condition.
immutable_query_builder(const std::string &table)
Constructs a new immutable query builder.
immutable_query_builder offset(uint32_t count) const
OFFSET clause - skips rows.
const std::vector< std::pair< std::string, sort_order > > order_by_
immutable_query_builder having(const std::string &condition) const
HAVING clause - filters grouped results.
immutable_query_builder limit(uint32_t count) const
LIMIT clause - limits number of results.
std::string build_for_database(database_types db_type) const
Builds the SQL query for a specific database type.
Represents a WHERE condition in a query.
std::variant< std::string, int64_t, double, bool, std::nullptr_t > database_value
database_types
Represents various database backends or modes.
@ sqlite
Indicates a SQLite database.
@ postgres
Indicates a PostgreSQL database.
sort_order
Sort order for ORDER BY clauses.
join_type
Types of SQL joins.