Qx v0.7
Qt Extensions Library
Loading...
Searching...
No Matches
qx-sqlquery.h
Go to the documentation of this file.
1#ifndef QX_SQLQUERY_H
2#define QX_SQLQUERY_H
3
4// Shared Lib Support
5#include "qx/sql/qx_sql_export.h"
6
7// Qt Includes
8#include <QSqlDatabase>
9#include <QUuid>
10#include <QSqlQuery>
11
12// Intra-component Includes
14#include "qx/sql/qx-sqlerror.h"
15#include "qx/sql/qx-sqlresult.h"
16#include "qx/sql/qx-sqlstring.h"
17#include "qx/sql/__private/qx-sqlquery_p.h"
18#include "qx/sql/__private/qx-sqlstring_helpers.h"
19
20// Extra-component Includes
22
23using namespace Qt::StringLiterals;
24
25//-Macros------------------------------------------------------------------
27#define __QX_SQL_META_STRUCT_INSIDE(id, meta_tuple) \
28 template <typename StructT> \
29 struct QxSqlMetaStructInside \
30 { \
31 static inline constexpr Qx::CStringLiteral ID = id; \
32 static inline constexpr Qx::CStringLiteral ID_QUOTED = "\"" id "\""; \
33 static inline constexpr auto memberMetadata() \
34 { \
35 return meta_tuple; \
36 } \
37 };
38
39#define __QX_SQL_META_STRUCT_OUTSIDE(self_type, id, meta_tuple) \
40 namespace QxSql \
41 { \
42 template <typename StructT> \
43 struct QxSqlMetaStructOutside<self_type, StructT> \
44 { \
45 static inline constexpr Qx::CStringLiteral ID = id; \
46 static inline constexpr Qx::CStringLiteral ID_QUOTED = "\"" id "\""; \
47 static inline constexpr auto memberMetadata() \
48 { \
49 return meta_tuple; \
50 } \
51 }; \
52 }
53
54#define __QX_SQL_QUERY_STRUCT_MEMBER(member) static inline const Qx::SqlString member = Qx::SqlString::makeIdentifier(u ## #member);
56
57#define QX_SQL_QUERY_STRUCT(struct_name, id, ...) \
58 struct struct_name \
59 { \
60 static inline const Qx::SqlString _ = Qx::SqlString::makeIdentifier(u ## id); \
61 QX_FOR_EACH(__QX_SQL_QUERY_STRUCT_MEMBER, __VA_ARGS__) \
62 };
63
64#define QX_SQL_MEMBER(member) QxSqlPrivate::makeMemberMetadata<#member>(&StructT::member)
65#define QX_SQL_MEMBER_ALIASED(member, field) QxSqlPrivate::makeMemberMetadata<field>(&StructT::member)
66
67/* Currently only practical to support outside or inside struct but not both, so going for outside to
68 * allow for less header bloat. Inside would have to make a nested Fields struct instead of a separate one
69 * so they cannot result in the same thing. I'm unsure if it makes sense to allow both since they'd have
70 * different ways to access the fields. Maybe it's not a big deal but this is fine for now
71 */
72
73#define QX_SQL_STRUCT(id, ...) __QX_SQL_META_STRUCT_INSIDE(id, std::make_tuple(QX_FOR_EACH_DELIM(QX_SQL_MEMBER, __VA_ARGS__)))
74#define QX_SQL_STRUCT_X(id, ...) __QX_SQL_META_STRUCT_INSIDE(id, std::make_tuple(__VA_ARGS__))
75
76#define QX_SQL_STRUCT_FULL(id, query_struct, ...) \
77 QX_SQL_STRUCT(id, __VA_ARGS__) \
78 QX_SQL_QUERY_STRUCT(query_struct, id, __VA_ARGS__)
79
80#define QX_SQL_STRUCT_OUTSIDE(Struct, id, ...) __QX_SQL_META_STRUCT_OUTSIDE(Struct, id, std::make_tuple(QX_FOR_EACH_DELIM(QX_SQL_MEMBER, __VA_ARGS__)))
81#define QX_SQL_STRUCT_OUTSIDE_X(Struct, id, ...) __QX_SQL_META_STRUCT_OUTSIDE(Struct, id, std::make_tuple(__VA_ARGS__))
82
83#define QX_SQL_STRUCT_OUTSIDE_FULL(Struct, id, query_struct, ...) \
84 QX_SQL_STRUCT_OUTSIDE(Struct, id, __VA_ARGS__) \
85 QX_SQL_QUERY_STRUCT(query_struct, id, __VA_ARGS__)
86
87
88#define QX_SQL_STRUCT_OUTSIDE_FRIEND(struct_type) friend QxSql::QxSqlMetaStructOutside<struct_type, struct_type>;
89
90#define QX_SQL_MEMBER_OVERRIDE(Struct, member, ...) \
91namespace QxSql \
92{ \
93 template<> \
94 struct MemberOverrideConverter<Struct, #member> \
95 { \
96 __VA_ARGS__\
97 }; \
98}
99
100// Helper macros
101#define __QX_SQL_QUERY_ADD_KEYWORD_ZERO_ARG_X(keyword, method) \
102 auto& method() \
103 { \
104 appendKeyword(u ## #keyword ## _s); \
105 return *this_d; \
106 }
107
108#define __QX_SQL_QUERY_ADD_KEYWORD_ZERO_ARG(keyword) __QX_SQL_QUERY_ADD_KEYWORD_ZERO_ARG_X(keyword, keyword)
109
110#define __QX_SQL_QUERY_ADD_KEYWORD_SINGLE_ARG_X(keyword, method) \
111 template<sql_stringable First> \
112 auto& method(First&& fs) \
113 { \
114 appendKeyword(u ## #keyword ## _s, std::forward<First>(fs)); \
115 return *this_d; \
116 }
117
118#define __QX_SQL_QUERY_ADD_KEYWORD_SINGLE_ARG_PAREN_X(keyword, method) \
119 template<sql_stringable First> \
120 auto& method(First&& fs) \
121 { \
122 using namespace QxSql; \
123 appendKeywordParen(u ## #keyword ## _s, std::forward<First>(fs)); \
124 return *this_d; \
125 }
126
127#define __QX_SQL_QUERY_ADD_KEYWORD_SINGLE_ARG(keyword) __QX_SQL_QUERY_ADD_KEYWORD_SINGLE_ARG_X(keyword, keyword)
128#define __QX_SQL_QUERY_ADD_KEYWORD_SINGLE_ARG_PAREN(keyword) __QX_SQL_QUERY_ADD_KEYWORD_SINGLE_ARG_PAREN_X(keyword, keyword)
129
130#define __QX_SQL_QUERY_ADD_KEYWORD_MULTI_ARG_X(keyword, method) \
131 template<sql_stringable First, sql_stringable ...Rest> \
132 auto& method(First&& fs, Rest&&... s) \
133 { \
134 appendKeyword(u ## #keyword ## _s, std::forward<First>(fs), std::forward<Rest>(s)...); \
135 return *this_d; \
136 }
137
138#define __QX_SQL_QUERY_ADD_KEYWORD_MULTI_ARG_PAREN_X(keyword, method) \
139 template<sql_stringable First, sql_stringable ...Rest> \
140 auto& method(First&& fs, Rest&&... s) \
141 { \
142 using namespace QxSql; \
143 appendKeywordParen(u ## #keyword ## _s, std::forward<First>(fs), std::forward<Rest>(s)...); \
144 return *this_d; \
145 }
146
147#define __QX_SQL_QUERY_ADD_KEYWORD_MULTI_ARG(keyword) __QX_SQL_QUERY_ADD_KEYWORD_MULTI_ARG_X(keyword, keyword)
148#define __QX_SQL_QUERY_ADD_KEYWORD_MULTI_ARG_PAREN(keyword) __QX_SQL_QUERY_ADD_KEYWORD_MULTI_ARG_PAREN_X(keyword, keyword)
149
150// TODO: Perfect forwarding instead?
151#define __QX_SQL_QUERY_ADD_KEYWORD_SUB_QUERY_X(keyword, method) \
152 auto& method(const SqlQuery& q) \
153 { \
154 appendKeywordParen(u ## #keyword ## _s, q.string()); \
155 return *this_d; \
156 }
157
158#define __QX_SQL_QUERY_ADD_KEYWORD_SUB_QUERY(keyword) __QX_SQL_QUERY_ADD_KEYWORD_SUB_QUERY_X(keyword, keyword)
159
160namespace Qx
161{
162
163class SqlDatabase;
164
165/* Base
166 *
167 * C++23 TODO: Use explicit object parameter here for returning self, then this doesn't need an additional abstract
168 * template base.
169 */
170class QX_SQL_EXPORT SqlQuery
171{
172//-Class Struct-----------------------------------------------------------------------------------------------
173private:
174 /* This is slightly contentious, as we keep around an extra copy of bind data here, which may have a minimal,
175 * but significant impact on memory footprint. The only to 100% avoid this is to not cache user binds and
176 * somehow handle the automatic binding methods a different way, though this is likely impossible without
177 * changing how they're used (i.e. would likely need user to manually perform a bind step for them later).
178 */
179 struct Binding
180 {
181 QString ph;
182 QVariant data;
183 inline Binding(const QString& ph, const QVariant& d) : ph(ph), data(d) {} // TODO: Not needed for Clang >= 16
184 };
185
186//-Instance Variables-----------------------------------------------------------------------------------------------
187private:
188 SqlDatabase* mDb; // Must remain valid through lifetime
189 QString mQueryStr;
190 QList<Binding> mBindings;
191
192//-Constructor-------------------------------------------------------------------------------------------------
194protected:
195 SqlQuery();
198
199//-Class Functions------------------------------------------------------------------------------------------------------
200protected:
202 /* TODO: C++23: Use "deducing this" to return self type from this so that callers can cut
203 * out the second line separate return
204 */
205 template<typename FirstArg, typename ...RestArgs>
206 void appendKeyword(const QString& word, FirstArg&& firstArg, RestArgs&&... restArgs)
207 {
208 _QxPrivate::appendKeyword(mQueryStr, word, std::forward<FirstArg>(firstArg), std::forward<RestArgs>(restArgs)...);
209 }
210
211 template<typename FirstArg, typename ...RestArgs>
212 void appendKeywordParen(const QString& word, FirstArg&& firstArg, RestArgs&&... restArgs)
213 {
214 _QxPrivate::appendKeywordParen(mQueryStr, word, std::forward<FirstArg>(firstArg), std::forward<RestArgs>(restArgs)...);
215 }
216
217 template <std::ranges::input_range R>
218 void appendKeywordParen(const QString& word, const R& range)
219 {
220 _QxPrivate::appendKeywordParen(mQueryStr, word, range);
221 }
222
223 void appendKeyword(const QString& word);
224 void append(QStringView sql, bool space = true);
226
227//-Instance Functions------------------------------------------------------------------------------------------------------
228protected:
230 QString autoBindValue(QVariant&& d);
231 SqlError executeQuery(QSqlQuery& result, bool forwardOnly);
233
234public:
235 QString string() const;
236 bool hasDatabase() const;
238 const SqlDatabase* database() const;
239
240 void bindValue(const QString& placeholder, const QVariant& val); // Don't support ParamType Out or In/Out for now
241};
242
243// Abstract (all common keywords)
244template<typename Derived>
246{
247//-Instance Variables------------------------------------------------------------------------------------------
248protected:
250 Derived* this_d = static_cast<Derived*>(this);
252
253//-Constructor-------------------------------------------------------------------------------------------------
254protected:
259
260//-Instance Functions------------------------------------------------------------------------------------------------------
261private:
262 template<sql_stringable First, sql_stringable ...Rest>
263 Derived& select_impl(bool distinct, First&& fsel, Rest&&... sel)
264 {
265 appendKeyword(distinct ? u"SELECT DISTINCT"_s : u"SELECT"_s, std::forward<First>(fsel), std::forward<Rest>(sel)...);
266 return *this_d;
267 }
268
269 template<QxSql::sql_struct... Structs>
270 Derived& select_impl(bool distinct)
271 {
272 // This lambda generates a tuple of column names for a single struct,
273 // handling both the ID prefix and the member name.
274 auto getColumnNames = []<typename Struct>() {
275 constexpr auto members = QxSqlPrivate::getMemberMeta<Struct>();
276
277 // We use std::apply to get a tuple of strings.
278 return std::apply([](auto const&... m) {
279 if constexpr (sizeof...(Structs) == 1)
280 return std::make_tuple(QLatin1String(m.M_NAME_QUOTED)...);
281 else
282 {
283 constexpr auto idq = QxSqlPrivate::getStructIdQuoted<Struct>().view();
284 return std::make_tuple(idq + "."_L1 + QLatin1String(m.M_NAME_QUOTED)...);
285 }
286 }, members);
287 };
288
289 // Use std::tuple_cat to combine the tuples from all structs into one.
290 auto allColumnNames = std::tuple_cat(getColumnNames.template operator()<Structs>()...);
291
292 // Call appendKeyword, spreading out the tuple into multiple arguments
293 std::apply([&](auto const&... cols) {
294 appendKeyword(distinct ? u"SELECT DISTINCT"_s : u"SELECT"_s, cols...);
295 }, allColumnNames);
296
297 return *this_d;
298 }
299
300//-Instance Functions------------------------------------------------------------------------------------------------------
301public:
302 /* TODO: Difficult as hell, but if possible it would be amazing to have a base query type
303 * (like now) that then many sub types derive from, with a final user type (like this one)
304 * that is actually used. Then, we returning *this, we can make the return type such that
305 * there is an up or downcast involved, such that the returned type in the chain has a limited
306 * number of methods so that the only keywords one is allowed to follow up with are the ones
307 * that make sense given the previous. We'd need to start by making a relational graph of all
308 * the keywords to see if it's even possible to ground them into different "types" (in the context
309 * of which ones follow another) because needing to have a subtype for every possible combination
310 * of valid follow-up words would be impractical, if the whole thing is pretty much case-by-case;
311 * however, a smaller set of exceptions would be fine, if most can be made into distinct groups.
312 */
313
314 /* TODO: Even in the current system, a few words in this base class can only be used for a few
315 * query types and not all of them, so introducing intermediate bases and deriving from them
316 * where appropriate instead would at least limit keyword usage to the relevant query types
317 */
318
319 // [BETWEEN]
320 __QX_SQL_QUERY_ADD_KEYWORD_SINGLE_ARG(AS);
321
322 // [BETWEEN]
323 __QX_SQL_QUERY_ADD_KEYWORD_SINGLE_ARG(BETWEEN);
324
325 // [CASE]
326 // Possibly should make the words related to this one "inline words" instead, but meh
327 __QX_SQL_QUERY_ADD_KEYWORD_ZERO_ARG(CASE);
328
329 // [DISTINCT]
330 __QX_SQL_QUERY_ADD_KEYWORD_ZERO_ARG(DISTINCT);
331
332 // [ELSE]
333 __QX_SQL_QUERY_ADD_KEYWORD_SINGLE_ARG(ELSE);
334
335 // [END]
336 __QX_SQL_QUERY_ADD_KEYWORD_ZERO_ARG(END);
337
338 // [FROM]
339 __QX_SQL_QUERY_ADD_KEYWORD_MULTI_ARG(FROM);
340
341 template<QxSql::sql_struct First, QxSql::sql_struct... Rest>
342 Derived& FROM()
343 {
344 constexpr auto idqs = std::make_tuple(
345 (QxSqlPrivate::getStructIdQuoted<First>()).view(),
346 (QxSqlPrivate::getStructIdQuoted<Rest>()).view()...
347 );
348 std::apply([&](const auto&... idqs) {
349 appendKeyword(u"FROM"_s, idqs...);
350 }, idqs);
351
352 return *this_d;
353 }
354
355 // [IN]
356 __QX_SQL_QUERY_ADD_KEYWORD_MULTI_ARG_PAREN(IN);
357 __QX_SQL_QUERY_ADD_KEYWORD_SUB_QUERY(IN);
358
359 template <std::ranges::input_range R>
361 Derived& IN(const R& range)
362 {
363 appendKeywordParen(u"IN"_s, range);
364 return *this_d;
365 }
366
367 // [IS]
368 __QX_SQL_QUERY_ADD_KEYWORD_ZERO_ARG(IS);
369 __QX_SQL_QUERY_ADD_KEYWORD_SINGLE_ARG(IS);
370
371 // [ON]
372 __QX_SQL_QUERY_ADD_KEYWORD_SINGLE_ARG(ON);
373
374 // [SELECT]
375 template<sql_stringable First, sql_stringable ...Rest>
376 Derived& SELECT(First&& fsel, Rest&&... sel)
377 {
378 return select_impl(false, std::forward<First>(fsel), std::forward<Rest>(sel)...);
379 }
380
381 template<sql_stringable First, sql_stringable ...Rest>
382 Derived& SELECT_DISTINCT(First&& fsel, Rest&&... sel)
383 {
384 return select_impl(true, std::forward<First>(fsel), std::forward<Rest>(sel)...);
385 }
386
387 template<QxSql::sql_struct First, QxSql::sql_struct... Rest>
388 Derived& SELECT() { return select_impl<First, Rest...>(false); }
389
390 template<QxSql::sql_struct First, QxSql::sql_struct... Rest>
391 Derived& SELECT_DISTINCT() { return select_impl<First, Rest...>(true); }
392
393 // [THEN]
394 __QX_SQL_QUERY_ADD_KEYWORD_SINGLE_ARG(THEN);
395
396 // [WHEN]
397 __QX_SQL_QUERY_ADD_KEYWORD_ZERO_ARG(WHEN);
398 __QX_SQL_QUERY_ADD_KEYWORD_SINGLE_ARG(WHEN);
399
400 // [WHERE]
401 __QX_SQL_QUERY_ADD_KEYWORD_ZERO_ARG(WHERE);
402 __QX_SQL_QUERY_ADD_KEYWORD_SINGLE_ARG(WHERE);
403
404 Derived& verbatim(const QString& sql, bool space = true) { append(sql, space); return *this_d; }
405
406};
407
408// DQL
409class QX_SQL_EXPORT SqlDqlQuery : public AbstractSqlQuery<SqlDqlQuery>
410{
411//-Constructor-------------------------------------------------------------------------------------------------
412public:
413 SqlDqlQuery();
415
416//-Instance Functions------------------------------------------------------------------------------------------------------
417private:
418 SqlError selectSizeWorkaround(int& size);
419 SqlError executeQueryWithSize(QSqlQuery& result, int& size, bool forwardOnly);
420
421public:
422 // [ESCAPE]
423 __QX_SQL_QUERY_ADD_KEYWORD_SINGLE_ARG(ESCAPE);
424
425 // [EXISTS]
426 __QX_SQL_QUERY_ADD_KEYWORD_SUB_QUERY(EXISTS);
427
428 // [GROUP BY]
429 __QX_SQL_QUERY_ADD_KEYWORD_SINGLE_ARG_X(GROUP BY, GROUP_BY);
430
431 // [HAVING]
432 __QX_SQL_QUERY_ADD_KEYWORD_SINGLE_ARG(HAVING);
433
434 // [ILIKE]
435 __QX_SQL_QUERY_ADD_KEYWORD_SINGLE_ARG(ILIKE);
436
437 // [JOIN]
438 __QX_SQL_QUERY_ADD_KEYWORD_SINGLE_ARG(JOIN);
439
440 // [LIKE]
441 __QX_SQL_QUERY_ADD_KEYWORD_SINGLE_ARG(LIKE);
442
443 // [LIMIT]
444 __QX_SQL_QUERY_ADD_KEYWORD_SINGLE_ARG(LIMIT);
445
446 // [NOT]
447 __QX_SQL_QUERY_ADD_KEYWORD_ZERO_ARG(NOT);
448 __QX_SQL_QUERY_ADD_KEYWORD_SINGLE_ARG(NOT);
449
450 // [OFFSET]
451 __QX_SQL_QUERY_ADD_KEYWORD_SINGLE_ARG(OFFSET);
452
453 // [ORDER BY]
454 __QX_SQL_QUERY_ADD_KEYWORD_MULTI_ARG_X(ORDER BY, ORDER_BY);
455
456 // [SIMILIAR]
457 __QX_SQL_QUERY_ADD_KEYWORD_SINGLE_ARG_X(SIMILAR TO, SIMILAR_TO);
458
459 // All rows - Does not clear buffer
460 template<QxSql::sql_containing Container>
461 SqlError appendExecute(Container& result)
462 {
463 // Execute underlying
464 QSqlQuery queryResult;
465 if(auto err = executeQuery(queryResult, true); err.isValid())
466 return err.withQuery(*this);
467
468 // Advance to first record since we're going to consume the result right now
469 queryResult.next();
470
471 // Check for empty result
472 if(!queryResult.isValid())
473 return SqlError();
474
475 // Process
476 if(auto err = QxSqlPrivate::RowConverter<Container>::fromSql(result, queryResult); err.isValid())
477 return err.withQuery(*this);
478
479 return SqlError();
480 }
481
482 // All rows - Clears buffer
483 template<QxSql::sql_containing Container>
484 SqlError execute(Container& result)
485 {
486 result.clear();
487 return appendExecute(result);
488 }
489
490 // Iterable result of all rows
491 template<typename T>
493 {
494 // Ensure result is reset
495 result = SqlResult<T>();
496
497 // Execute underlying
498 QSqlQuery queryResult;
499 int queryResultSize;
500 if(auto err = executeQueryWithSize(queryResult, queryResultSize, true); err.isValid())
501 return err.withQuery(*this);
502
503 // Check for empty result
504 if(queryResultSize < 1)
505 return SqlError();
506
507 // Check types
508 if(auto err = QxSqlPrivate::RowChecker<T>::check(queryResult.record()); err.isValid())
509 return err;
510
511 // Prepare result object
512 result = SqlResult<T>(std::move(queryResult), queryResultSize);
513
514 return SqlError();
515 }
516
517 // Single row TODO: constrain this to only SQL types if that concept is ever created
518 template<typename T>
519 SqlError execute(T& result)
520 {
521 static_assert(std::default_initializable<T>, "T must be default constructable to use this overload!");
522 // TODO: Implement this independently of the list version
523 QList<T> res;
524 auto err = execute(res);
525 result = (!err && !res.isEmpty()) ? res.first() : T{};
526 return err;
527 }
528};
529
530class QX_SQL_EXPORT SqlDmlQuery : public AbstractSqlQuery<SqlDmlQuery>
531{
532//-Constructor-------------------------------------------------------------------------------------------------
533public:
534 SqlDmlQuery();
536
537//-Instance Functions------------------------------------------------------------------------------------------------------
538public:
539 // [DELETE]
540 __QX_SQL_QUERY_ADD_KEYWORD_ZERO_ARG(DELETE);
541
542 // [INSERT INTO]
543 template<sql_stringable First, sql_stringable ...Rest> \
544 auto& INSERT_INTO(const SqlString& table, First&& first, Rest&&... rest) \
545 {
546 appendKeyword(u"INSERT INTO"_s, table, u"("_s, std::forward<First>(first), std::forward<Rest>(rest)..., u")"_s);
547 return *this;
548 }
549
550 template<QxSql::sql_struct Struct>
552 {
553 constexpr auto table = QxSqlPrivate::getStructIdQuoted<Struct>().view();
554 constexpr auto members = QxSqlPrivate::getMemberMeta<Struct>();
555 std::apply([&](const auto&... m) {
556 appendKeyword(u"INSERT INTO"_s, table, u"("_s, QLatin1String(m.M_NAME_QUOTED)..., u")"_s);
557 }, members);
558
559 return *this;
560 }
561
562 // [MATCHED] Possibly should be an inline word instead
563 __QX_SQL_QUERY_ADD_KEYWORD_ZERO_ARG(MATCHED);
564
565 // [MERGE INTO]
566 __QX_SQL_QUERY_ADD_KEYWORD_SINGLE_ARG_X(MERGE INTO, MERGE_INTO);
567
568 // [SET]
569 template<QxSql::sql_struct Struct>
570 auto& SET(const Struct& s)
571 {
572 // Done manually instead of via Converter<T> to easily manage auto-bindings
573 constexpr auto members = QxSqlPrivate::getMemberMeta<Struct>();
574 std::apply([&](auto&&... m) {
575 appendKeyword(
576 u"SET"_s,
577 ([&] {
578 using Meta = std::remove_reference_t<decltype(m)>;
579 using mType = typename Meta::M_TYPE;
580 constexpr QLatin1StringView mFieldId(Meta::M_NAME_QUOTED);
581 auto& mRef = s.*(m.mPtr);
582 return mFieldId + u" = "_s + autoBindValue(QxSql::Converter<mType>::toSql(mRef));
583 }())...
584 );
585 }, members);
586
587 return *this;
588 }
589
590 __QX_SQL_QUERY_ADD_KEYWORD_MULTI_ARG(SET);
591
592 // [UPDATE]
593 /* TODO: This and some other keywords should be locked to just taking SqlString, since nothing else makes sense,
594 * which means more macros are needed... or the functions are just written manually.
595 */
596 __QX_SQL_QUERY_ADD_KEYWORD_SINGLE_ARG(UPDATE);
597
598 template<QxSql::sql_struct Struct>
599 auto& UPDATE()
600 {
601 appendKeyword(u"UPDATE"_s, QxSqlPrivate::getStructIdQuoted<Struct>().view());
602 return *this_d;
603 }
604
605 // [VALUES]
606 template<QxSql::sql_struct Struct>
607 auto& VALUES(const Struct& s)
608 {
609 // Done manually instead of via Converter<T> to easily manage auto-bindings
610 constexpr auto members = QxSqlPrivate::getMemberMeta<Struct>();
611 std::apply([&](auto&&... m) {
612 appendKeyword(
613 u"VALUES"_s, u"("_s,
614 ([&] {
615 using Meta = std::remove_reference_t<decltype(m)>;
616 using mType = typename Meta::M_TYPE;
617 auto& mRef = s.*(m.mPtr);
618
619 return autoBindValue(QxSql::Converter<mType>::toSql(mRef));
620 }())...,
621 u")"_s
622 );
623 }, members);
624
625 return *this;
626 }
627
628 __QX_SQL_QUERY_ADD_KEYWORD_MULTI_ARG_PAREN(VALUES);
629
630 SqlError execute(int& affected);
631};
632
633}
634
635#undef __QX_SQL_QUERY_ADD_KEYWORD_ZERO_ARG_X
636#undef __QX_SQL_QUERY_ADD_KEYWORD_ZERO_ARG
637#undef __QX_SQL_QUERY_ADD_KEYWORD_SINGLE_ARG_X
638#undef __QX_SQL_QUERY_ADD_KEYWORD_SINGLE_ARG_PAREN_X
639#undef __QX_SQL_QUERY_ADD_KEYWORD_SINGLE_ARG
640#undef __QX_SQL_QUERY_ADD_KEYWORD_SINGLE_ARG_PAREN
641#undef __QX_SQL_QUERY_ADD_KEYWORD_MULTI_ARG_X
642#undef __QX_SQL_QUERY_ADD_KEYWORD_MULTI_ARG_PAREN_X
643#undef __QX_SQL_QUERY_ADD_KEYWORD_MULTI_ARG
644#undef __QX_SQL_QUERY_ADD_KEYWORD_MULTI_ARG_PAREN
645#undef __QX_SQL_QUERY_ADD_KEYWORD_SUB_QUERY_X
646#undef __QX_SQL_QUERY_ADD_KEYWORD_SUB_QUERY
647
648#endif // QX_SQLQUERY_H
AbstractSqlQuery is a common base class from which all query types are derived, and provides common S...
Definition qx-sqlquery.h:246
auto & ON(First &&fs)
auto & THEN(First &&fs)
Derived & verbatim(const QString &sql, bool space=true)
Definition qx-sqlquery.h:404
Derived & FROM()
Definition qx-sqlquery.h:342
Derived & SELECT_DISTINCT()
Definition qx-sqlquery.h:391
auto & ELSE(First &&fs)
Derived & SELECT_DISTINCT(First &&fsel, Rest &&... sel)
Definition qx-sqlquery.h:382
auto & AS(First &&fs)
Derived & SELECT()
Definition qx-sqlquery.h:388
Derived & SELECT(First &&fsel, Rest &&... sel)
Definition qx-sqlquery.h:376
auto & BETWEEN(First &&fs)
auto & FROM(First &&fs, Rest &&... s)
auto & IN(First &&fs, Rest &&... s)
Derived & IN(const R &range)
Definition qx-sqlquery.h:361
The SqlDatabase class provides straightforward access to an SQL database.
Definition qx-sqldatabase.h:36
auto & MERGE_INTO(First &&fs)
auto & INSERT_INTO(const SqlString &table, First &&first, Rest &&... rest)
Definition qx-sqlquery.h:544
SqlError execute(int &affected)
Definition qx-sqlquery.cpp:536
auto & INSERT_INTO()
Definition qx-sqlquery.h:551
auto & UPDATE(First &&fs)
auto & MATCHED()
auto & UPDATE()
Definition qx-sqlquery.h:599
auto & VALUES(const Struct &s)
Definition qx-sqlquery.h:607
auto & SET(const Struct &s)
Definition qx-sqlquery.h:570
SqlDmlQuery()
Definition qx-sqlquery.cpp:483
SqlError execute(T &result)
Definition qx-sqlquery.h:519
SqlError execute(Container &result)
Definition qx-sqlquery.h:484
auto & JOIN(First &&fs)
SqlDqlQuery()
Definition qx-sqlquery.cpp:354
auto & GROUP_BY(First &&fs)
auto & LIKE(First &&fs)
auto & EXISTS(const SqlQuery &q)
auto & ORDER_BY(First &&fs, Rest &&... s)
auto & HAVING(First &&fs)
auto & OFFSET(First &&fs)
auto & ILIKE(First &&fs)
SqlError appendExecute(Container &result)
Definition qx-sqlquery.h:461
auto & ESCAPE(First &&fs)
auto & SIMILAR_TO(First &&fs)
auto & LIMIT(First &&fs)
SqlError execute(SqlResult< T > &result)
Definition qx-sqlquery.h:492
The SqlError class is used to report errors related to database configuration and SQL queries.
Definition qx-sqlerror.h:21
SqlError & withQuery(const SqlQuery &q)
Definition qx-sqlerror.cpp:112
SqlQuery is a base class from which all query types derive.
Definition qx-sqlquery.h:171
SqlDatabase * database()
Definition qx-sqlquery.cpp:251
bool hasDatabase() const
Definition qx-sqlquery.cpp:246
QString string() const
Definition qx-sqlquery.cpp:240
void bindValue(const QString &placeholder, const QVariant &val)
Definition qx-sqlquery.cpp:265
The SqlResult class provides sequential, efficient access to a SELECT/DQL query result.
Definition qx-sqlresult.h:29
The SqlString class is a convenience class for more easily building SQL statements in a natural manne...
Definition qx-sqlstring.h:36
Specifies that a type is a SQL-tied struct.
Definition qx-sqlconcepts.h:42
Specifies that a type can be converted to SqlString, or used to construct a SqlString.
Definition qx-sqlstring.h:33
The Qx namespace is the main namespace through which all non-global functionality of the Qx library i...
Definition qx-abstracterror.cpp:13
@ First
Definition qx-global.h:19
The qx-concepts header file provides a set of various object-like and function-like macros that are d...
The qx-sqlconcepts header file provides a set of concepts that are specific to the Qx SQL module.