Skip to content

Commit 005fdbd

Browse files
committed
WIP
1 parent 57d6933 commit 005fdbd

18 files changed

+2201
-53
lines changed

lib/core/include/qx/core/qx-abstracterror.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,17 @@ class QX_CORE_EXPORT IError
3333
static inline const QString T_NAME_DUPE = u"Error type name %1 is already claimed!"_s;
3434
static inline const QString T_CODE_RESERVED = u"Error type code %1 is reserved!"_s;
3535
static inline constinit QHash<quint16, const QString*> codeRegistry;
36-
static constexpr std::array<QStringView, 8> RESERVED_NAMES{
36+
static constexpr std::array<QStringView, 10> RESERVED_NAMES{
3737
u"Qx::InternalError",
3838
u"Qx::GenericError",
3939
u"Qx::IoOpReport",
4040
u"Qx::SystemError",
4141
u"Qx::DownloadManagerReport",
4242
u"Qx::DownloadOpReport",
4343
u"Qx::JsonError",
44-
u"QJsonParseError"
44+
u"QJsonParseError",
45+
u"Qx::SqlError",
46+
u"Qx::SqlSchemaReport",
4547
};
4648
// TODO: If this becomes sufficiently large, move to a constexpr set (hopefully available in std by that time)
4749

lib/sql/CMakeLists.txt

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,21 @@
11
#================= Add Component ==========================
22
qx_add_component("Sql"
33
HEADERS_API
4-
qx-common-sql.h
4+
qx-sqlconcepts.h
5+
qx-sqldatabase.h
6+
qx-sqlerror.h
7+
qx-sqlinlines.h
8+
qx-sqlquery.h
9+
qx-sqlresult.h
10+
qx-sqlschemareport.h
11+
qx-sqlstring.h
12+
__private/qx-sqlstring_helpers.h
513
IMPLEMENTATION
6-
qx-common-sql.cpp
14+
qx-sqldatabase.cpp
15+
qx-sqlerror.cpp
16+
qx-sqlquery.cpp
17+
qx-sqlstring.cpp
18+
qx-sqlschemareport.cpp
719
#DOC_ONLY
820
LINKS
921
PUBLIC
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#ifndef QX_SQLSTRING_HELPERS_H
2+
#define QX_SQLSTRING_HELPERS_H
3+
4+
// Shared Lib Support
5+
6+
// Qt Includes
7+
#include <QStringView>
8+
#include <QString>
9+
10+
// Intra-component Includes
11+
#include <qx/sql/qx-sqlstring.h>
12+
13+
/*! @cond */
14+
namespace QxPrivate
15+
{
16+
17+
namespace __private
18+
{
19+
template<typename S>
20+
requires std::constructible_from<QString, S>
21+
decltype(auto) qstring_compat(S&& s) { return std::forward<S>(s); }
22+
inline QString qstring_compat(const Qx::SqlString& s) { return s.toString(); }
23+
24+
template<typename First, typename ...Rest>
25+
QString join(First&& first, Rest&&... rest)
26+
{
27+
QString result = qstring_compat(first);
28+
((result += "," + qstring_compat(rest)), ...);
29+
return result;
30+
}
31+
}
32+
33+
void inline append(QString& str, QStringView sql, bool space = true)
34+
{
35+
using namespace Qt::StringLiterals;
36+
if(!str.isEmpty() && space)
37+
str += u" "_s;
38+
39+
str += sql;
40+
}
41+
template<typename FirstArg, typename ...RestArgs>
42+
void appendKeyword(QString& str, const QString& word, FirstArg&& firstArg, RestArgs&&... restArgs)
43+
{
44+
append(str, word);
45+
append(str, __private::join(std::forward<FirstArg>(firstArg), std::forward<RestArgs>(restArgs)...));
46+
}
47+
48+
void inline appendKeyword(QString& str, const QString& word) { append(str, word); }
49+
50+
}
51+
/*! @endcond */
52+
53+
#endif // QX_SQLSTRING_HELPERS_H

lib/sql/include/qx/sql/qx-common-sql.h

Lines changed: 0 additions & 27 deletions
This file was deleted.
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
#ifndef QX_SQLCONCEPTS_H
2+
#define QX_SQLCONCEPTS_H
3+
4+
// Standard Library Includes
5+
#include <concepts>
6+
7+
// Qt Includes
8+
#include <QVariant>
9+
#include <QSqlQuery>
10+
11+
// Intra-component Includes
12+
#include "qx/sql/qx-sqlerror.h"
13+
14+
// Extra-component Includes
15+
#include "qx/utility/qx-stringliteral.h"
16+
17+
namespace QxSql
18+
{
19+
20+
//-Forwards---------------------------------------------------------------
21+
template<typename T>
22+
struct Converter;
23+
24+
template<class Struct, Qx::CStringLiteral member>
25+
struct MemberOverrideConverter;
26+
27+
template<typename SelfType, typename DelayedSelfType>
28+
struct QxSqlMetaStructOutside;
29+
30+
//-Concepts--------------------------------------------------------------
31+
template<typename T>
32+
concept sql_struct_inside = requires {
33+
T::template QxSqlMetaStructInside<T>::memberMetadata();
34+
};
35+
36+
template<typename T>
37+
concept sql_struct_outside = requires {
38+
QxSqlMetaStructOutside<T, T>::memberMetadata();
39+
};
40+
41+
template<typename T>
42+
concept sql_struct = sql_struct_inside<T> || sql_struct_outside<T>;
43+
44+
template<typename T>
45+
concept sql_convertible = requires(T& tValue) {
46+
{ Converter<T>::fromSql(tValue, QVariant()) } -> std::same_as<Qx::SqlError>;
47+
} || requires(T& tValue) {
48+
{ Converter<T>::fromSql(tValue, QSqlQuery()) } -> std::same_as<Qx::SqlError>;
49+
};
50+
51+
template<class K, typename T, Qx::CStringLiteral N>
52+
concept sql_override_convertible = requires(T& tValue) {
53+
{ MemberOverrideConverter<K, N>::fromSql(tValue, QVariant()) } -> std::same_as<Qx::SqlError>;
54+
};
55+
56+
template<typename Key, class Value>
57+
concept sql_keyable = requires(const Value& v) {
58+
{ keygen<Key, Value>(v) } -> std::same_as<Key>;
59+
};
60+
61+
template<typename T>
62+
concept sql_collective = Qx::qcollective<T> &&
63+
sql_convertible<typename T::value_type>;
64+
65+
template<typename T>
66+
concept sql_associative = Qx::qassociative<T> &&
67+
sql_convertible<typename T::mapped_type> &&
68+
sql_keyable<typename T::key_type, typename T::mapped_type>;
69+
70+
template<typename T>
71+
concept sql_containing = sql_collective<T> ||
72+
sql_associative<T>;
73+
74+
template<typename T>
75+
concept sql_optional = Qx::specializes<T, std::optional> &&
76+
sql_convertible<typename T::value_type>;
77+
}
78+
79+
#endif // QX_SQLCONCEPTS_H
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
#ifndef QX_SQLDATABASE_H
2+
#define QX_SQLDATABASE_H
3+
4+
// Shared Lib Support
5+
#include "qx/sql/qx_sql_export.h"
6+
7+
// Qt Includes
8+
#include <QSqlDatabase>
9+
#include <QReadWriteLock>
10+
#include <QUuid>
11+
12+
// Intra-component Includes
13+
#include "qx/sql/qx-sqlquery.h"
14+
#include "qx/sql/qx-sqlschemareport.h"
15+
16+
// Extra-component Includes
17+
18+
using namespace Qt::StringLiterals;
19+
20+
class QThread;
21+
22+
namespace Qx
23+
{
24+
25+
/* Careful use of the thread safe static functions of QSqlDatabase here allow us to avoid
26+
* needing our own lock and connection tracker, AND ensure that a lambda can be used to
27+
* handle connection closures due to thread-termination without having a potential dangling
28+
* reference to the this pointer in the capture, thereby avoiding the overhead of needing
29+
* to make this a QObject in order to disconnect when this is destroyed.
30+
*
31+
* This introduces a small amount of overhead, particularly when it comes to closing all
32+
* connections at destruction, but the added simplicity is worth it..
33+
*/
34+
35+
class QX_SQL_EXPORT SqlDatabase
36+
{
37+
//-Class Variables-----------------------------------------------------------------------------------------------
38+
private:
39+
static inline const QString ID_NAMESPACE = u"Qx::SqlDatabase"_s;
40+
41+
//-Instance Variables-----------------------------------------------------------------------------------------------
42+
private:
43+
// NOTE: These are not const to allow move semantics, but DO NOT write to them after construction, or else this
44+
// isn't thread safe.
45+
QString mDatabaseName; /* TODO: Have opt in ctor var for shared connections in the same thread; that is,
46+
* shared instances don't salt their pointer and reuse the same connection as long
47+
* as they're in the same thread, then non-shared instances (default) will only use
48+
* the same connection through the same instance, as is now. Perhaps specify this by
49+
* an optional connection name input that defaults to empty, which will cause this
50+
* class to use the db name as part of the connection name by default, then if the
51+
* connection name is different, that is used instead of the
52+
*/
53+
QString mDriver;
54+
QString mId;
55+
56+
//-Constructor-------------------------------------------------------------------------------------------------
57+
public:
58+
// TODO: Instead of passing only the driver and db name, allow passing a struct that holds those
59+
// and all of the stuff there are setters for in QSqlDatabase so that we can allow full customization
60+
// before/at construction time, but dont need to allow setting the values after and therefore don't need
61+
// our own mutex.
62+
explicit SqlDatabase(const QString& databaseName, const QString& driver);
63+
SqlDatabase(const SqlDatabase& other);
64+
SqlDatabase(SqlDatabase&& other);
65+
66+
//-Destructor-------------------------------------------------------------------------------------------------
67+
public:
68+
~SqlDatabase();
69+
70+
//-Class Functions------------------------------------------------------------------------------------------------------
71+
private:
72+
static QString connectionName(QStringView id, const QThread* thread);
73+
static bool closeConnection(const QString& connectionName);
74+
static bool closeConnection(QStringView id, const QThread* thread);
75+
76+
//-Instance Functions------------------------------------------------------------------------------------------------------
77+
private:
78+
void closeAllConnections();
79+
QString connectionName(const QThread* thread) const;
80+
81+
public:
82+
SqlError database(QSqlDatabase& db, bool connect = true);
83+
SqlError connect();
84+
QString driver() const;
85+
QString databaseName() const;
86+
bool isConnected() const;
87+
bool closeConnection();
88+
89+
template<QxSql::sql_struct First, QxSql::sql_struct... Rest>
90+
SqlError checkSchema(SqlSchemaReport& report, SqlSchemaReport::StrictnessFlags strictness = SqlSchemaReport::Lenient)
91+
{
92+
QSqlDatabase db;
93+
if(auto err = database(db))
94+
return err;
95+
96+
report = SqlSchemaReport::generate<First, Rest...>(db, strictness);
97+
return SqlError();
98+
}
99+
100+
// SQL
101+
template<sql_string First, sql_string ...Rest>
102+
SqlDqlQuery SELECT(First&& fs, Rest&&... s)
103+
{
104+
SqlDqlQuery q(*this);
105+
q.SELECT(std::forward<First>(fs), std::forward(s)...);
106+
return q;
107+
}
108+
109+
template<QxSql::sql_struct First, QxSql::sql_struct... Rest>
110+
SqlDqlQuery SELECT()
111+
{
112+
SqlDqlQuery q(*this);
113+
q.SELECT<First, Rest...>();
114+
return q;
115+
}
116+
117+
template<sql_string First, sql_string ...Rest>
118+
SqlDqlQuery SELECT_DISTINCT(First&& fs, Rest&&... s)
119+
{
120+
SqlDqlQuery q(*this);
121+
q.SELECT_DISTINCT(std::forward<First>(fs), std::forward(s)...);
122+
return q;
123+
}
124+
125+
template<QxSql::sql_struct First, QxSql::sql_struct... Rest>
126+
SqlDqlQuery SELECT_DISTINCT()
127+
{
128+
SqlDqlQuery q(*this);
129+
q.SELECT<First, Rest...>();
130+
return q;
131+
}
132+
133+
//-Operators------------------------------------------------------------------------------------------------------
134+
public:
135+
SqlDatabase& operator=(const SqlDatabase& other);
136+
SqlDatabase& operator=(SqlDatabase&& other);
137+
};
138+
139+
}
140+
141+
#endif // QX_SQLDATABASE_H

0 commit comments

Comments
 (0)