Database System 0.1.0
Advanced C++20 Database System with Multi-Backend Support
Loading...
Searching...
No Matches
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
5#include "query_builder.h"
6#include <sstream>
7#include <algorithm>
8#include <stdexcept>
9
10namespace database
11{
12 // query_condition implementation
13 query_condition::query_condition(const std::string& field, const std::string& op, const core::database_value& value)
14 : field_(field), operator_(op), value_(value)
15 {
16 }
17
21
22 std::string query_condition::to_sql() const
23 {
24 if (!raw_condition_.empty()) {
25 return raw_condition_;
26 }
27
28 if (!sub_conditions_.empty()) {
29 std::ostringstream oss;
30 oss << "(";
31 for (size_t i = 0; i < sub_conditions_.size(); ++i) {
32 if (i > 0) {
33 oss << " " << logical_operator_ << " ";
34 }
35 oss << sub_conditions_[i].to_sql();
36 }
37 oss << ")";
38 return oss.str();
39 }
40
41 std::ostringstream oss;
42 oss << field_ << " " << operator_ << " ";
43
44 std::visit([&oss](const auto& val) {
45 using T = std::decay_t<decltype(val)>;
46 if constexpr (std::is_same_v<T, std::string>) {
47 // Escape single quotes by doubling them (SQL standard)
48 std::string escaped;
49 escaped.reserve(val.size() + 10);
50 for (char c : val) {
51 if (c == '\'') {
52 escaped += "''";
53 } else {
54 escaped += c;
55 }
56 }
57 oss << "'" << escaped << "'";
58 } else if constexpr (std::is_same_v<T, int64_t>) {
59 oss << val;
60 } else if constexpr (std::is_same_v<T, double>) {
61 oss << val;
62 } else if constexpr (std::is_same_v<T, bool>) {
63 oss << (val ? "TRUE" : "FALSE");
64 } else if constexpr (std::is_same_v<T, std::monostate>) {
65 oss << "NULL";
66 }
67 }, value_);
68
69 return oss.str();
70 }
71
72 std::string query_condition::to_mongodb() const
73 {
74 if (!raw_condition_.empty()) {
75 return raw_condition_;
76 }
77
78 if (!sub_conditions_.empty()) {
79 std::ostringstream oss;
80 std::string mongo_op = (logical_operator_ == "AND") ? "$and" : "$or";
81 oss << "{ \"" << mongo_op << "\": [";
82 for (size_t i = 0; i < sub_conditions_.size(); ++i) {
83 if (i > 0) oss << ", ";
84 oss << sub_conditions_[i].to_mongodb();
85 }
86 oss << "] }";
87 return oss.str();
88 }
89
90 std::ostringstream oss;
91 oss << "{ \"" << field_ << "\": ";
92
93 if (operator_ == "=") {
94 std::visit([&oss](const auto& val) {
95 using T = std::decay_t<decltype(val)>;
96 if constexpr (std::is_same_v<T, std::string>) {
97 oss << "\"" << val << "\"";
98 } else if constexpr (std::is_same_v<T, int64_t>) {
99 oss << val;
100 } else if constexpr (std::is_same_v<T, double>) {
101 oss << val;
102 } else if constexpr (std::is_same_v<T, bool>) {
103 oss << (val ? "true" : "false");
104 } else if constexpr (std::is_same_v<T, std::monostate>) {
105 oss << "null";
106 }
107 }, value_);
108 } else {
109 std::string mongo_op;
110 if (operator_ == ">") mongo_op = "$gt";
111 else if (operator_ == ">=") mongo_op = "$gte";
112 else if (operator_ == "<") mongo_op = "$lt";
113 else if (operator_ == "<=") mongo_op = "$lte";
114 else if (operator_ == "!=") mongo_op = "$ne";
115 else mongo_op = "$eq";
116
117 oss << "{ \"" << mongo_op << "\": ";
118 std::visit([&oss](const auto& val) {
119 using T = std::decay_t<decltype(val)>;
120 if constexpr (std::is_same_v<T, std::string>) {
121 oss << "\"" << val << "\"";
122 } else if constexpr (std::is_same_v<T, int64_t>) {
123 oss << val;
124 } else if constexpr (std::is_same_v<T, double>) {
125 oss << val;
126 } else if constexpr (std::is_same_v<T, bool>) {
127 oss << (val ? "true" : "false");
128 } else if constexpr (std::is_same_v<T, std::monostate>) {
129 oss << "null";
130 }
131 }, value_);
132 oss << " }";
133 }
134
135 oss << " }";
136 return oss.str();
137 }
138
139 std::string query_condition::to_redis() const
140 {
141 return raw_condition_;
142 }
143
145 {
146 query_condition result;
147 result.sub_conditions_.push_back(*this);
148 result.sub_conditions_.push_back(other);
149 result.logical_operator_ = "AND";
150 return result;
151 }
152
154 {
155 query_condition result;
156 result.sub_conditions_.push_back(*this);
157 result.sub_conditions_.push_back(other);
158 result.logical_operator_ = "OR";
159 return result;
160 }
161
162 // query_builder implementation using Strategy pattern
164 : db_type_(db_type)
165 {
167 }
168
170 {
171 if (db_type_ != db_type) {
172 db_type_ = db_type;
173 dialect_.reset();
175 }
176 return *this;
177 }
178
179 query_builder& query_builder::select(const std::vector<std::string>& columns)
180 {
182 if (dialect_) {
183 dialect_->set_select_columns(columns);
184 }
185 return *this;
186 }
187
188 query_builder& query_builder::from(const std::string& table)
189 {
191 if (dialect_) {
192 dialect_->set_from_table(table);
193 }
194 return *this;
195 }
196
197 query_builder& query_builder::where(const std::string& field, const std::string& op, const core::database_value& value)
198 {
200 if (dialect_) {
201 dialect_->add_where_condition(field, op, value);
202 }
203 return *this;
204 }
205
207 {
209 if (dialect_) {
210 dialect_->add_where_condition(condition);
211 }
212 return *this;
213 }
214
215 query_builder& query_builder::join(const std::string& table, const std::string& condition, join_type type)
216 {
218 if (dialect_) {
219 dialect_->add_join(table, condition, type);
220 }
221 return *this;
222 }
223
224 query_builder& query_builder::order_by(const std::string& column, sort_order order)
225 {
227 if (dialect_) {
228 dialect_->add_order_by(column, order);
229 }
230 return *this;
231 }
232
233 query_builder& query_builder::group_by(const std::vector<std::string>& columns)
234 {
236 if (dialect_) {
237 dialect_->set_group_by(columns);
238 }
239 return *this;
240 }
241
242 query_builder& query_builder::group_by(const std::string& column)
243 {
244 return group_by(std::vector<std::string>{column});
245 }
246
247 query_builder& query_builder::having(const std::string& condition)
248 {
250 if (dialect_) {
251 dialect_->set_having(condition);
252 }
253 return *this;
254 }
255
257 {
259 if (dialect_) {
260 dialect_->set_limit(count);
261 }
262 return *this;
263 }
264
266 {
268 if (dialect_) {
269 dialect_->set_offset(count);
270 }
271 return *this;
272 }
273
274 query_builder& query_builder::insert_into(const std::string& table)
275 {
277 if (dialect_) {
278 dialect_->set_insert_table(table);
279 }
280 return *this;
281 }
282
283 query_builder& query_builder::values(const std::map<std::string, core::database_value>& data)
284 {
286 if (dialect_) {
287 dialect_->set_insert_data(data);
288 }
289 return *this;
290 }
291
292 query_builder& query_builder::values(const std::vector<std::map<std::string, core::database_value>>& rows)
293 {
295 if (dialect_) {
296 dialect_->set_insert_rows(rows);
297 }
298 return *this;
299 }
300
301 query_builder& query_builder::update(const std::string& table)
302 {
304 if (dialect_) {
305 dialect_->set_update_table(table);
306 }
307 return *this;
308 }
309
310 query_builder& query_builder::set(const std::string& field, const core::database_value& value)
311 {
313 if (dialect_) {
314 std::map<std::string, core::database_value> data;
315 data[field] = value;
316 dialect_->set_update_data(data);
317 }
318 return *this;
319 }
320
321 query_builder& query_builder::set(const std::map<std::string, core::database_value>& data)
322 {
324 if (dialect_) {
325 dialect_->set_update_data(data);
326 }
327 return *this;
328 }
329
330 query_builder& query_builder::delete_from(const std::string& table)
331 {
333 if (dialect_) {
334 dialect_->set_delete_table(table);
335 }
336 return *this;
337 }
338
339 query_builder& query_builder::collection(const std::string& name)
340 {
342 if (dialect_) {
343 dialect_->set_collection(name);
344 }
345 return *this;
346 }
347
348 query_builder& query_builder::key(const std::string& key)
349 {
351 if (dialect_) {
352 dialect_->set_key(key);
353 }
354 return *this;
355 }
356
357 std::string query_builder::build() const
358 {
359 if (dialect_) {
360 return dialect_->build();
361 }
362 return "";
363 }
364
366 {
367 if (!db) {
368 return {};
369 }
370
371 std::string query = build();
372 if (query.empty()) {
373 return {};
374 }
375
376 auto result = db->select_query(query);
377 if (result.is_err()) {
378 return {};
379 }
380 return result.value();
381 }
382
384 {
385 if (dialect_) {
386 dialect_->reset();
387 }
388 }
389
394
401
402} // namespace database
Abstract base class for database backends.
virtual kcenon::common::Result< database_result > select_query(const std::string &query_string)=0
Execute a SELECT query.
Universal query builder that adapts to different database types.
query_builder & select(const std::vector< std::string > &columns)
query_builder & for_database(database_types db_type)
std::string build() const
query_builder & group_by(const std::vector< std::string > &columns)
query_builder & where(const std::string &field, const std::string &op, const core::database_value &value)
query_builder & limit(size_t count)
core::database_result execute(core::database_backend *db) const
query_builder & update(const std::string &table)
query_builder(database_types db_type=database_types::none)
database_types get_database_type() const
query_builder & having(const std::string &condition)
query_builder & collection(const std::string &name)
query_builder & from(const std::string &table)
std::unique_ptr< query_dialect > dialect_
query_builder & order_by(const std::string &column, sort_order order=sort_order::asc)
query_builder & offset(size_t count)
query_builder & key(const std::string &key)
query_builder & set(const std::string &field, const core::database_value &value)
query_builder & delete_from(const std::string &table)
query_builder & join(const std::string &table, const std::string &condition, join_type type=join_type::inner)
query_builder & insert_into(const std::string &table)
query_builder & values(const std::map< std::string, core::database_value > &data)
Represents a WHERE condition in a query.
std::vector< query_condition > sub_conditions_
std::string to_redis() const
core::database_value value_
std::string to_mongodb() const
query_condition operator||(const query_condition &other) const
query_condition operator&&(const query_condition &other) const
std::string to_sql() const
std::vector< database_row > database_result
std::variant< std::string, int64_t, double, bool, std::nullptr_t > database_value
std::unique_ptr< query_dialect > create_dialect(database_types type)
Factory function to create appropriate dialect for database type.
database_types
Represents various database backends or modes.
@ none
No specific database type is set.
sort_order
Sort order for ORDER BY clauses.
join_type
Types of SQL joins.