-
Notifications
You must be signed in to change notification settings - Fork 0
Implement nullable reflected fields #37
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
5806a12
6296389
aefc4f9
232bdf0
d1bb7f6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -42,6 +42,7 @@ struct REFLECTION_EXPORT SqlValue { | |
| bool bool_value; | ||
| double real_value; | ||
| std::string text_value; | ||
| bool is_null; | ||
| }; | ||
|
|
||
| /// The base class of all WHERE predicates used in SQLite queries | ||
|
|
@@ -103,13 +104,26 @@ class REFLECTION_EXPORT QueryPredicate : public QueryPredicateBase { | |
| : QueryPredicate(fn, value, symbol, | ||
| [&](void* v, SqliteStorageClass storage_class) { return GetSqlValue(v, storage_class); }) {} | ||
|
|
||
| template <typename T, typename R> | ||
| QueryPredicate(fcpp::optional_t<R> T::* fn, R value, const std::string& symbol) | ||
| : QueryPredicate(fn, fcpp::optional_t<R>(value), symbol, [&](void* v, SqliteStorageClass storage_class) { | ||
| return GetOptionalSqlValue(v, storage_class); | ||
| }) {} | ||
|
|
||
| template <typename T, typename R> | ||
| QueryPredicate(fcpp::optional_t<R> T::* fn, fcpp::optional_t<R> value, const std::string& symbol) | ||
| : QueryPredicate(fn, value, symbol, [&](void* v, SqliteStorageClass storage_class) { | ||
| return GetOptionalSqlValue(v, storage_class); | ||
| }) {} | ||
|
|
||
| QueryPredicate(const std::string& symbol, const std::string& member_name, const SqlValue& value) | ||
| : symbol_(symbol), member_name_(member_name), value_(value) {} | ||
|
|
||
| /// Returns the value used for the current query, against which the struct member | ||
| /// (defined from the pointer-to-member function) will be compared. The value needs | ||
| /// to be type-erased, so that the header file is not bloated with unnecessary implementation details | ||
| virtual SqlValue GetSqlValue(void* v, SqliteStorageClass storage_class) const; | ||
| virtual SqlValue GetOptionalSqlValue(void* v, SqliteStorageClass storage_class) const; | ||
|
|
||
| /// The symbol used for the comparison, for example "=" for equality | ||
| std::string symbol_; | ||
|
|
@@ -122,6 +136,47 @@ class REFLECTION_EXPORT QueryPredicate : public QueryPredicateBase { | |
| SqlValue value_; | ||
| }; | ||
|
|
||
| /// A wrapper for an empty predicate, used to fetch all elements of an SQLite table | ||
| class REFLECTION_EXPORT NullPredicate : public QueryPredicateBase { | ||
| public: | ||
| template <typename T, typename R> | ||
| NullPredicate(R T::* fn, const std::string& symbol) : symbol_(symbol) { | ||
| auto record = GetRecordFromTypeId(typeid(T).name()); | ||
| auto offset = OffsetFromStart(fn); | ||
| for (auto i = 0; i < record.member_metadata.size(); ++i) { | ||
| if (record.member_metadata[i].offset == offset) { | ||
| member_name_ = record.member_metadata[i].name; | ||
| return; | ||
| } | ||
| } | ||
| throw std::runtime_error("No registered member of '" + record.name + | ||
| "' matches the given pointer-to-member (type id: " + typeid(T).name() + ")"); | ||
| } | ||
|
|
||
| std::string Evaluate() const override; | ||
| std::vector<SqlValue> Bindings() const override; | ||
| QueryPredicateBase* Clone() const override; | ||
|
|
||
| protected: | ||
| NullPredicate(const std::string& symbol, const std::string& member_name) | ||
| : symbol_(symbol), member_name_(member_name) {} | ||
|
|
||
| std::string symbol_; | ||
| std::string member_name_; | ||
| }; | ||
|
|
||
| class REFLECTION_EXPORT IsNull final : public NullPredicate { | ||
| public: | ||
| template <typename T, typename R> | ||
| explicit IsNull(R T::* fn) : NullPredicate(fn, "IS NULL") {} | ||
| }; | ||
|
|
||
| class REFLECTION_EXPORT IsNotNull final : public NullPredicate { | ||
| public: | ||
| template <typename T, typename R> | ||
| explicit IsNotNull(R T::* fn) : NullPredicate(fn, "IS NOT NULL") {} | ||
| }; | ||
|
|
||
| /// A wrapper for an empty predicate, used to fetch all elements of an SQLite table | ||
| class REFLECTION_EXPORT EmptyPredicate final : public QueryPredicateBase { | ||
| public: | ||
|
|
@@ -137,6 +192,12 @@ class REFLECTION_EXPORT Equal final : public QueryPredicate { | |
| template <typename T, typename R> | ||
| explicit Equal(R T::* fn, R value) : QueryPredicate(fn, value, "=") {} | ||
|
|
||
| template <typename T, typename R> | ||
| explicit Equal(fcpp::optional_t<R> T::* fn, R value) : QueryPredicate(fn, value, "=") {} | ||
|
|
||
| template <typename T, typename R> | ||
| explicit Equal(fcpp::optional_t<R> T::* fn, fcpp::optional_t<R> value) : QueryPredicate(fn, value, "=") {} | ||
|
|
||
| template <typename T> | ||
| explicit Equal(int64_t T::* fn, int value) : Equal(fn, (int64_t)value) {} | ||
|
|
||
|
|
@@ -151,6 +212,12 @@ class REFLECTION_EXPORT Unequal final : public QueryPredicate { | |
| template <typename T, typename R> | ||
| explicit Unequal(R T::* fn, R value) : QueryPredicate(fn, value, "!=") {} | ||
|
|
||
| template <typename T, typename R> | ||
| explicit Unequal(fcpp::optional_t<R> T::* fn, R value) : QueryPredicate(fn, value, "!=") {} | ||
|
|
||
| template <typename T, typename R> | ||
| explicit Unequal(fcpp::optional_t<R> T::* fn, fcpp::optional_t<R> value) : QueryPredicate(fn, value, "!=") {} | ||
|
|
||
| template <typename T> | ||
| explicit Unequal(int64_t T::* fn, int value) : Unequal(fn, (int64_t)value) {} | ||
|
|
||
|
|
@@ -167,6 +234,18 @@ class REFLECTION_EXPORT Like final : public QueryPredicate { | |
| : QueryPredicate(fn, value, "LIKE", | ||
| [&](void* v, SqliteStorageClass storage_class) { return GetSqlValue(v, storage_class); }) {} | ||
|
|
||
| template <typename T, typename R> | ||
| explicit Like(fcpp::optional_t<R> T::* fn, R value) | ||
| : QueryPredicate(fn, fcpp::optional_t<R>(value), "LIKE", [&](void* v, SqliteStorageClass storage_class) { | ||
| return GetOptionalSqlValue(v, storage_class); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When a Useful? React with 👍 / 👎. |
||
| }) {} | ||
|
|
||
| template <typename T, typename R> | ||
| explicit Like(fcpp::optional_t<R> T::* fn, fcpp::optional_t<R> value) | ||
| : QueryPredicate(fn, value, "LIKE", [&](void* v, SqliteStorageClass storage_class) { | ||
| return GetOptionalSqlValue(v, storage_class); | ||
| }) {} | ||
|
|
||
| template <typename T> | ||
| explicit Like(int64_t T::* fn, int value) : Like(fn, (int64_t)value) {} | ||
|
|
||
|
|
@@ -189,6 +268,12 @@ class REFLECTION_EXPORT GreaterThan final : public QueryPredicate { | |
|
|
||
| template <typename T> | ||
| explicit GreaterThan(double T::* fn, double value) : QueryPredicate(fn, value, ">") {} | ||
|
|
||
| template <typename T> | ||
| explicit GreaterThan(fcpp::optional_t<int64_t> T::* fn, int64_t value) : QueryPredicate(fn, value, ">") {} | ||
|
|
||
| template <typename T> | ||
| explicit GreaterThan(fcpp::optional_t<double> T::* fn, double value) : QueryPredicate(fn, value, ">") {} | ||
| }; | ||
|
|
||
| /// A wrapper for a comparison predicate, for which the value of the | ||
|
|
@@ -203,6 +288,12 @@ class REFLECTION_EXPORT GreaterThanOrEqual final : public QueryPredicate { | |
|
|
||
| template <typename T> | ||
| explicit GreaterThanOrEqual(double T::* fn, double value) : QueryPredicate(fn, value, ">=") {} | ||
|
|
||
| template <typename T> | ||
| explicit GreaterThanOrEqual(fcpp::optional_t<int64_t> T::* fn, int64_t value) : QueryPredicate(fn, value, ">=") {} | ||
|
|
||
| template <typename T> | ||
| explicit GreaterThanOrEqual(fcpp::optional_t<double> T::* fn, double value) : QueryPredicate(fn, value, ">=") {} | ||
| }; | ||
|
|
||
| /// A wrapper for a comparison predicate, for which the value of the | ||
|
|
@@ -217,6 +308,12 @@ class REFLECTION_EXPORT SmallerThan final : public QueryPredicate { | |
|
|
||
| template <typename T> | ||
| explicit SmallerThan(double T::* fn, double value) : QueryPredicate(fn, value, "<") {} | ||
|
|
||
| template <typename T> | ||
| explicit SmallerThan(fcpp::optional_t<int64_t> T::* fn, int64_t value) : QueryPredicate(fn, value, "<") {} | ||
|
|
||
| template <typename T> | ||
| explicit SmallerThan(fcpp::optional_t<double> T::* fn, double value) : QueryPredicate(fn, value, "<") {} | ||
| }; | ||
|
|
||
| /// A wrapper for a comparison predicate, for which the value of the | ||
|
|
@@ -231,6 +328,12 @@ class REFLECTION_EXPORT SmallerThanOrEqual final : public QueryPredicate { | |
|
|
||
| template <typename T> | ||
| explicit SmallerThanOrEqual(double T::* fn, double value) : QueryPredicate(fn, value, "<=") {} | ||
|
|
||
| template <typename T> | ||
| explicit SmallerThanOrEqual(fcpp::optional_t<int64_t> T::* fn, int64_t value) : QueryPredicate(fn, value, "<=") {} | ||
|
|
||
| template <typename T> | ||
| explicit SmallerThanOrEqual(fcpp::optional_t<double> T::* fn, double value) : QueryPredicate(fn, value, "<=") {} | ||
| }; | ||
|
|
||
| /// A wrapper of a compound predicate, which combines two other predicates, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When callers pass an
fcpp::optional_t<T>control value to a nullable field, e.g.Equal(&NullableRecord::optional_text, fcpp::optional_t<std::wstring>{}), overload resolution bypasses this contained-value overload and selects the genericEqual(R T::*, R)overload instead. That path treats the optional object's storage as the containedT, so empty optionals can bind garbage or throw rather than performing a null-aware comparison; either delete optional-valued controls or route them toIS NULL/contained-value handling.Useful? React with 👍 / 👎.