mirror of
https://github.com/getml/sqlgen.git
synced 2026-02-14 04:38:45 -06:00
Added numerous operations like concat(...), coalesce(...), abs(...), etc (#20)
This commit is contained in:
committed by
GitHub
parent
a6bad187fe
commit
30ba548f1e
11
README.md
11
README.md
@@ -227,6 +227,17 @@ const auto query = read<std::vector<Person>> |
|
||||
const auto query = read<std::vector<Person>> |
|
||||
where("age"_c == "Homer");
|
||||
|
||||
// Compile-time error: "age" must be aggregated or included in GROUP BY
|
||||
const auto query = select_from<Person>(
|
||||
"last_name"_c,
|
||||
"age"_c
|
||||
) | group_by("last_name"_c);
|
||||
|
||||
// Compile-time error: Cannot add string and int
|
||||
const auto query = select_from<Person>(
|
||||
"last_name"_c + "age"_c
|
||||
);
|
||||
|
||||
// Runtime protection against SQL injection
|
||||
std::vector<Person> get_people(const auto& conn,
|
||||
const sqlgen::AlphaNumeric& first_name) {
|
||||
|
||||
@@ -26,6 +26,7 @@ Welcome to the sqlgen documentation. This guide provides detailed information ab
|
||||
- [sqlgen::group_by and Aggregations](group_by_and_aggregations.md) - How generate GROUP BY queries and aggregate data
|
||||
- [sqlgen::insert](insert.md) - How to insert data within transactions
|
||||
- [sqlgen::update](update.md) - How to update data in a table
|
||||
- [Other Operations and Functions](other_operations.md) - How to use SQL functions like `coalesce`, `concat`, `abs`, `cast`, and more
|
||||
|
||||
## Data Types and Validation
|
||||
|
||||
|
||||
249
docs/other_operations.md
Normal file
249
docs/other_operations.md
Normal file
@@ -0,0 +1,249 @@
|
||||
# Other Operations and Functions
|
||||
|
||||
The `sqlgen` library provides a rich set of SQL operations and functions that can be used in a type-safe and composable way within C++ queries. These operations cover mathematical, string, type conversion, and null-handling functions, and are designed to closely mirror SQL's expressive power.
|
||||
|
||||
## Usage
|
||||
|
||||
You can use these functions in your `select_from` queries, often in combination with column expressions, literals, and other operations. All functions are available in the `sqlgen` namespace.
|
||||
|
||||
---
|
||||
|
||||
## Mathematical Functions
|
||||
|
||||
### `abs`
|
||||
Returns the absolute value of a numeric expression.
|
||||
|
||||
```cpp
|
||||
abs("age"_c * (-1)) | as<"abs_age">
|
||||
```
|
||||
|
||||
### `ceil` / `floor`
|
||||
Rounds a numeric value up (`ceil`) or down (`floor`) to the nearest integer.
|
||||
|
||||
```cpp
|
||||
ceil("salary"_c) | as<"salary_ceiled">
|
||||
floor("salary"_c) | as<"salary_floored">
|
||||
```
|
||||
|
||||
### `exp`, `ln`, `log2`, `sqrt`
|
||||
- `exp(x)`: Exponential function (e^x)
|
||||
- `ln(x)`: Natural logarithm
|
||||
- `log2(x)`: Base-2 logarithm
|
||||
- `sqrt(x)`: Square root
|
||||
|
||||
```cpp
|
||||
round(exp(cast<double>("age"_c)), 2) | as<"exp_age">
|
||||
round(sqrt(cast<double>("age"_c)), 2) | as<"sqrt_age">
|
||||
```
|
||||
|
||||
### `sin`, `cos`, `tan`
|
||||
Trigonometric functions.
|
||||
|
||||
```cpp
|
||||
sin("angle"_c) | as<"sin_angle">
|
||||
cos("angle"_c) | as<"cos_angle">
|
||||
tan("angle"_c) | as<"tan_angle">
|
||||
```
|
||||
|
||||
### `round`
|
||||
Rounds a numeric value to a specified number of decimal places.
|
||||
|
||||
```cpp
|
||||
round("price"_c, 2) | as<"rounded_price">
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## String Functions
|
||||
|
||||
### `length`
|
||||
Returns the length of a string.
|
||||
|
||||
```cpp
|
||||
length(trim("first_name"_c)) | as<"length_first_name">
|
||||
```
|
||||
|
||||
### `lower` / `upper`
|
||||
Converts a string to lowercase or uppercase.
|
||||
|
||||
```cpp
|
||||
lower("first_name"_c) | as<"first_name_lower">
|
||||
upper("first_name"_c) | as<"first_name_upper">
|
||||
```
|
||||
|
||||
### `ltrim`, `rtrim`, `trim`
|
||||
Removes whitespace (or a specified character) from the left, right, or both sides of a string.
|
||||
|
||||
```cpp
|
||||
ltrim("first_name"_c) | as<"ltrimmed_name">
|
||||
rtrim("last_name"_c) | as<"rtrimmed_name">
|
||||
trim("nickname"_c) | as<"trimmed_nickname">
|
||||
// With custom characters:
|
||||
ltrim("field"_c, "_ ") | as<"ltrimmed_field">
|
||||
```
|
||||
|
||||
### `replace`
|
||||
Replaces all occurrences of a substring with another substring.
|
||||
|
||||
```cpp
|
||||
replace("first_name"_c, "Bart", "Hugo") | as<"first_name_replaced">
|
||||
```
|
||||
|
||||
### `concat`
|
||||
Concatenates multiple strings or expressions.
|
||||
|
||||
```cpp
|
||||
concat("first_name"_c, " ", "last_name"_c) | as<"full_name">
|
||||
concat(upper("last_name"_c), ", ", "first_name"_c) | as<"full_name">
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Type Conversion
|
||||
|
||||
### `cast`
|
||||
Casts a value to a different type (e.g., int to double).
|
||||
|
||||
```cpp
|
||||
cast<double>("age"_c) | as<"age_as_double">
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Null Handling
|
||||
|
||||
### `coalesce`
|
||||
Returns the first non-null value in the argument list.
|
||||
|
||||
```cpp
|
||||
coalesce("last_name"_c, "none") | as<"last_name_or_none">
|
||||
coalesce(upper("last_name"_c), "none") | as<"last_name_or_none">
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Nullable Values
|
||||
|
||||
When using these operations on nullable columns (e.g., `std::optional<T>`), the result will also be nullable if any operand is nullable. For example, adding two `std::optional<int>` columns will yield a `std::optional<int>`. The `coalesce` function is especially useful for providing default values for nullable columns.
|
||||
|
||||
---
|
||||
|
||||
## Nullability Propagation and `coalesce` Semantics
|
||||
|
||||
### General Nullability Rules
|
||||
|
||||
- **Unary operations** (e.g., `abs`, `upper`, `sqrt`):
|
||||
- If the operand is nullable (`std::optional<T>`), the result is also nullable.
|
||||
- If the operand is not nullable, the result is not nullable.
|
||||
- **Binary or ternary operations** (e.g., `+`, `concat`, `replace`, etc.):
|
||||
- If *any* operand is nullable, the result is nullable (`std::optional<ResultType>`).
|
||||
- If *all* operands are non-nullable, the result is non-nullable.
|
||||
- **Type conversion (`cast`)**:
|
||||
- If the source is nullable, the result is nullable of the target type.
|
||||
- If the source is not nullable, the result is not nullable.
|
||||
- **String operations** (e.g., `concat`, `replace`, `ltrim`, `rtrim`, `trim`):
|
||||
- If any input is nullable, the result is nullable.
|
||||
- All string operands must have the same underlying type (checked at compile time).
|
||||
|
||||
### `coalesce` Nullability Semantics
|
||||
|
||||
The `coalesce` function returns the first non-null value from its arguments. Its nullability is determined as follows:
|
||||
|
||||
- If **all** arguments are nullable, the result is nullable (`std::optional<T>`).
|
||||
- If **any** argument is non-nullable, the result is non-nullable (`T`).
|
||||
- All arguments must have the same underlying type (ignoring nullability), enforced at compile time.
|
||||
|
||||
#### Examples
|
||||
|
||||
```cpp
|
||||
// All arguments nullable: result is nullable
|
||||
coalesce(std::optional<int>{}, std::optional<int>{}) // -> std::optional<int>
|
||||
|
||||
// At least one argument non-nullable: result is non-nullable
|
||||
coalesce(std::optional<int>{}, 42) // -> int
|
||||
coalesce(42, std::optional<int>{}) // -> int
|
||||
|
||||
// All arguments non-nullable: result is non-nullable
|
||||
coalesce(1, 2) // -> int
|
||||
|
||||
// Mixed string example
|
||||
coalesce(std::optional<std::string>{}, "default") // -> std::string
|
||||
|
||||
// Compile-time error: mismatched types
|
||||
// coalesce(std::optional<int>{}, std::optional<double>{}) // Error
|
||||
```
|
||||
|
||||
#### Practical Usage
|
||||
|
||||
```cpp
|
||||
// Provide a default for a nullable column
|
||||
coalesce("last_name"_c, "none") | as<"last_name_or_none"> // Result is std::string
|
||||
coalesce("middle_name"_c, "nickname"_c) | as<"any_name">
|
||||
```
|
||||
|
||||
### Advanced: How sqlgen Enforces Nullability
|
||||
|
||||
The nullability rules are enforced at compile time using template metaprogramming (see `underlying_t.hpp`). This ensures that:
|
||||
- You cannot accidentally assign a nullable result to a non-nullable field.
|
||||
- All arguments to `coalesce` must have the same base type (e.g., all `int` or all `std::string`).
|
||||
- The result type of any operation is always correct and safe to use in your result structs.
|
||||
|
||||
---
|
||||
|
||||
## Example: Combining Operations
|
||||
|
||||
```cpp
|
||||
struct Children {
|
||||
int id_plus_age;
|
||||
int age_times_2;
|
||||
int id_plus_2_minus_age;
|
||||
int abs_age;
|
||||
double exp_age;
|
||||
double sqrt_age;
|
||||
size_t length_first_name;
|
||||
std::string full_name;
|
||||
std::string first_name_lower;
|
||||
std::string first_name_upper;
|
||||
std::string first_name_replaced;
|
||||
};
|
||||
|
||||
const auto get_children = select_from<Person>(
|
||||
("id"_c + "age"_c) | as<"id_plus_age">,
|
||||
("age"_c * 2) | as<"age_times_2">,
|
||||
abs("age"_c * (-1)) | as<"abs_age">,
|
||||
round(exp(cast<double>("age"_c)), 2) | as<"exp_age">,
|
||||
round(sqrt(cast<double>("age"_c)), 2) | as<"sqrt_age">,
|
||||
length(trim("first_name"_c)) | as<"length_first_name">,
|
||||
concat("first_name"_c, " ", "last_name"_c) | as<"full_name">,
|
||||
lower("first_name"_c) | as<"first_name_lower">,
|
||||
upper("first_name"_c, " ") | as<"first_name_upper">,
|
||||
replace("first_name"_c, "Bart", "Hugo") | as<"first_name_replaced">
|
||||
) | where("age"_c < 18) | to<std::vector<Children>>;
|
||||
```
|
||||
|
||||
This generates the following SQL:
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
("id" + "age") AS "id_plus_age",
|
||||
("age" * 2) AS "age_times_2",
|
||||
ABS(("age" * -1)) AS "abs_age",
|
||||
ROUND(EXP(CAST("age" AS NUMERIC)), 2) AS "exp_age",
|
||||
ROUND(SQRT(CAST("age" AS NUMERIC)), 2) AS "sqrt_age",
|
||||
LENGTH(TRIM("first_name")) AS "length_first_name",
|
||||
("first_name" || ' ' || "last_name") AS "full_name",
|
||||
LOWER("first_name") AS "first_name_lower",
|
||||
UPPER("first_name") AS "first_name_upper",
|
||||
REPLACE("first_name", 'Bart', 'Hugo') AS "first_name_replaced"
|
||||
FROM "Person"
|
||||
WHERE "age" < 18;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
- All functions are type-safe and map to the appropriate SQL operations for the target database.
|
||||
- You can chain and nest operations as needed.
|
||||
- Use the `as<"alias">(...)` or `| as<"alias">` syntax to alias expressions for mapping to struct fields.
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "sqlgen/insert.hpp"
|
||||
#include "sqlgen/is_connection.hpp"
|
||||
#include "sqlgen/limit.hpp"
|
||||
#include "sqlgen/operations.hpp"
|
||||
#include "sqlgen/order_by.hpp"
|
||||
#include "sqlgen/patterns.hpp"
|
||||
#include "sqlgen/read.hpp"
|
||||
|
||||
@@ -5,48 +5,61 @@
|
||||
#include <type_traits>
|
||||
|
||||
#include "col.hpp"
|
||||
#include "transpilation/aggregations.hpp"
|
||||
#include "transpilation/Aggregation.hpp"
|
||||
#include "transpilation/AggregationOp.hpp"
|
||||
#include "transpilation/to_transpilation_type.hpp"
|
||||
|
||||
namespace sqlgen {
|
||||
|
||||
template <rfl::internal::StringLiteral _name>
|
||||
auto avg(const Col<_name>&) {
|
||||
return transpilation::aggregations::Avg<transpilation::Col<_name>>{
|
||||
.val = transpilation::Col<_name>{}};
|
||||
template <class T>
|
||||
auto avg(const T& _t) {
|
||||
using Type =
|
||||
typename transpilation::ToTranspilationType<std::remove_cvref_t<T>>::Type;
|
||||
return transpilation::Aggregation<transpilation::AggregationOp::avg, Type>{
|
||||
.val = transpilation::to_transpilation_type(_t)};
|
||||
}
|
||||
|
||||
inline auto count() {
|
||||
return transpilation::aggregations::Count<transpilation::aggregations::All>{};
|
||||
return transpilation::Aggregation<transpilation::AggregationOp::count,
|
||||
transpilation::All>{};
|
||||
}
|
||||
|
||||
template <rfl::internal::StringLiteral _name>
|
||||
auto count(const Col<_name>&) {
|
||||
return transpilation::aggregations::Count<transpilation::Col<_name>>{
|
||||
return transpilation::Aggregation<transpilation::AggregationOp::count,
|
||||
transpilation::Col<_name>>{
|
||||
.val = transpilation::Col<_name>{}};
|
||||
}
|
||||
|
||||
template <rfl::internal::StringLiteral _name>
|
||||
auto count_distinct(const Col<_name>&) {
|
||||
return transpilation::aggregations::Count<transpilation::Col<_name>>{
|
||||
return transpilation::Aggregation<transpilation::AggregationOp::count,
|
||||
transpilation::Col<_name>>{
|
||||
.val = transpilation::Col<_name>{}, .distinct = true};
|
||||
}
|
||||
|
||||
template <rfl::internal::StringLiteral _name>
|
||||
auto max(const Col<_name>&) {
|
||||
return transpilation::aggregations::Max<transpilation::Col<_name>>{
|
||||
.val = transpilation::Col<_name>{}};
|
||||
template <class T>
|
||||
auto max(const T& _t) {
|
||||
using Type =
|
||||
typename transpilation::ToTranspilationType<std::remove_cvref_t<T>>::Type;
|
||||
return transpilation::Aggregation<transpilation::AggregationOp::max, Type>{
|
||||
.val = transpilation::to_transpilation_type(_t)};
|
||||
}
|
||||
|
||||
template <rfl::internal::StringLiteral _name>
|
||||
auto min(const Col<_name>&) {
|
||||
return transpilation::aggregations::Min<transpilation::Col<_name>>{
|
||||
.val = transpilation::Col<_name>{}};
|
||||
template <class T>
|
||||
auto min(const T& _t) {
|
||||
using Type =
|
||||
typename transpilation::ToTranspilationType<std::remove_cvref_t<T>>::Type;
|
||||
return transpilation::Aggregation<transpilation::AggregationOp::min, Type>{
|
||||
.val = transpilation::to_transpilation_type(_t)};
|
||||
}
|
||||
|
||||
template <rfl::internal::StringLiteral _name>
|
||||
auto sum(const Col<_name>&) {
|
||||
return transpilation::aggregations::Sum<transpilation::Col<_name>>{
|
||||
.val = transpilation::Col<_name>{}};
|
||||
template <class T>
|
||||
auto sum(const T& _t) {
|
||||
using Type =
|
||||
typename transpilation::ToTranspilationType<std::remove_cvref_t<T>>::Type;
|
||||
return transpilation::Aggregation<transpilation::AggregationOp::sum, Type>{
|
||||
.val = transpilation::to_transpilation_type(_t)};
|
||||
}
|
||||
|
||||
} // namespace sqlgen
|
||||
|
||||
@@ -8,9 +8,12 @@
|
||||
#include "transpilation/Col.hpp"
|
||||
#include "transpilation/Condition.hpp"
|
||||
#include "transpilation/Desc.hpp"
|
||||
#include "transpilation/Operation.hpp"
|
||||
#include "transpilation/Operator.hpp"
|
||||
#include "transpilation/Set.hpp"
|
||||
#include "transpilation/Value.hpp"
|
||||
#include "transpilation/conditions.hpp"
|
||||
#include "transpilation/to_transpilation_type.hpp"
|
||||
|
||||
namespace sqlgen {
|
||||
|
||||
@@ -77,6 +80,103 @@ struct Col {
|
||||
return transpilation::Set<transpilation::Col<_name>, std::string>{.to =
|
||||
_to};
|
||||
}
|
||||
|
||||
template <class T>
|
||||
friend auto operator==(const Col&, const T& _t) {
|
||||
return transpilation::make_condition(transpilation::conditions::equal(
|
||||
transpilation::Col<_name>{}, transpilation::to_transpilation_type(_t)));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
friend auto operator!=(const Col&, const T& _t) {
|
||||
return transpilation::make_condition(transpilation::conditions::not_equal(
|
||||
transpilation::Col<_name>{}, transpilation::to_transpilation_type(_t)));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
friend auto operator<(const Col&, const T& _t) {
|
||||
return transpilation::make_condition(transpilation::conditions::lesser_than(
|
||||
transpilation::Col<_name>{}, transpilation::to_transpilation_type(_t)));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
friend auto operator<=(const Col&, const T& _t) {
|
||||
return transpilation::make_condition(
|
||||
transpilation::conditions::lesser_equal(
|
||||
transpilation::Col<_name>{},
|
||||
transpilation::to_transpilation_type(_t)));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
friend auto operator>(const Col&, const T& _t) {
|
||||
return transpilation::make_condition(
|
||||
transpilation::conditions::greater_than(
|
||||
transpilation::Col<_name>{},
|
||||
transpilation::to_transpilation_type(_t)));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
friend auto operator>=(const Col&, const T& _t) {
|
||||
return transpilation::make_condition(
|
||||
transpilation::conditions::greater_equal(
|
||||
transpilation::Col<_name>{},
|
||||
transpilation::to_transpilation_type(_t)));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
friend auto operator/(const Col&, const T& _op2) noexcept {
|
||||
using OtherType = typename transpilation::ToTranspilationType<
|
||||
std::remove_cvref_t<T>>::Type;
|
||||
|
||||
return transpilation::Operation<transpilation::Operator::divides,
|
||||
transpilation::Col<_name>, OtherType>{
|
||||
.operand1 = transpilation::Col<_name>{},
|
||||
.operand2 = transpilation::to_transpilation_type(_op2)};
|
||||
}
|
||||
|
||||
template <class T>
|
||||
friend auto operator-(const Col&, const T& _op2) noexcept {
|
||||
using OtherType = typename transpilation::ToTranspilationType<
|
||||
std::remove_cvref_t<T>>::Type;
|
||||
|
||||
return transpilation::Operation<transpilation::Operator::minus,
|
||||
transpilation::Col<_name>, OtherType>{
|
||||
.operand1 = transpilation::Col<_name>{},
|
||||
.operand2 = transpilation::to_transpilation_type(_op2)};
|
||||
}
|
||||
|
||||
template <class T>
|
||||
friend auto operator%(const Col&, const T& _op2) noexcept {
|
||||
using OtherType = typename transpilation::ToTranspilationType<
|
||||
std::remove_cvref_t<T>>::Type;
|
||||
|
||||
return transpilation::Operation<transpilation::Operator::mod,
|
||||
transpilation::Col<_name>, OtherType>{
|
||||
.operand1 = transpilation::Col<_name>{},
|
||||
.operand2 = transpilation::to_transpilation_type(_op2)};
|
||||
}
|
||||
|
||||
template <class T>
|
||||
friend auto operator*(const Col&, const T& _op2) noexcept {
|
||||
using OtherType = typename transpilation::ToTranspilationType<
|
||||
std::remove_cvref_t<T>>::Type;
|
||||
|
||||
return transpilation::Operation<transpilation::Operator::multiplies,
|
||||
transpilation::Col<_name>, OtherType>{
|
||||
.operand1 = transpilation::Col<_name>{},
|
||||
.operand2 = transpilation::to_transpilation_type(_op2)};
|
||||
}
|
||||
|
||||
template <class T>
|
||||
friend auto operator+(const Col&, const T& _op2) noexcept {
|
||||
using OtherType = typename transpilation::ToTranspilationType<
|
||||
std::remove_cvref_t<T>>::Type;
|
||||
|
||||
return transpilation::Operation<transpilation::Operator::plus,
|
||||
transpilation::Col<_name>, OtherType>{
|
||||
.operand1 = transpilation::Col<_name>{},
|
||||
.operand2 = transpilation::to_transpilation_type(_op2)};
|
||||
}
|
||||
};
|
||||
|
||||
template <rfl::internal::StringLiteral _name>
|
||||
@@ -87,83 +187,18 @@ auto operator"" _c() {
|
||||
return Col<_name>{};
|
||||
}
|
||||
|
||||
template <rfl::internal::StringLiteral _name1,
|
||||
rfl::internal::StringLiteral _name2>
|
||||
auto operator==(const Col<_name1>&, const Col<_name2>&) {
|
||||
return transpilation::make_condition(transpilation::conditions::equal(
|
||||
transpilation::Col<_name1>{}, transpilation::Col<_name2>{}));
|
||||
}
|
||||
namespace transpilation {
|
||||
|
||||
template <rfl::internal::StringLiteral _name1, class T>
|
||||
auto operator==(const Col<_name1>&, const T& _t) {
|
||||
return transpilation::make_condition(transpilation::conditions::equal(
|
||||
transpilation::Col<_name1>{}, transpilation::make_value(_t)));
|
||||
}
|
||||
template <rfl::internal::StringLiteral _name>
|
||||
struct ToTranspilationType<sqlgen::Col<_name>> {
|
||||
using Type = transpilation::Col<_name>;
|
||||
|
||||
template <rfl::internal::StringLiteral _name1,
|
||||
rfl::internal::StringLiteral _name2>
|
||||
auto operator!=(const Col<_name1>&, const Col<_name2>&) {
|
||||
return transpilation::make_condition(transpilation::conditions::not_equal(
|
||||
transpilation::Col<_name1>{}, transpilation::Col<_name2>{}));
|
||||
}
|
||||
Type operator()(const auto&) const noexcept {
|
||||
return transpilation::Col<_name>{};
|
||||
}
|
||||
};
|
||||
|
||||
template <rfl::internal::StringLiteral _name1, class T>
|
||||
auto operator!=(const Col<_name1>&, const T& _t) {
|
||||
return transpilation::make_condition(transpilation::conditions::not_equal(
|
||||
transpilation::Col<_name1>{}, transpilation::make_value(_t)));
|
||||
}
|
||||
|
||||
template <rfl::internal::StringLiteral _name1,
|
||||
rfl::internal::StringLiteral _name2>
|
||||
auto operator<(const Col<_name1>&, const Col<_name2>&) {
|
||||
return transpilation::make_condition(transpilation::conditions::lesser_than(
|
||||
transpilation::Col<_name1>{}, transpilation::Col<_name2>{}));
|
||||
}
|
||||
|
||||
template <rfl::internal::StringLiteral _name1, class T>
|
||||
auto operator<(const Col<_name1>&, const T& _t) {
|
||||
return transpilation::make_condition(transpilation::conditions::lesser_than(
|
||||
transpilation::Col<_name1>{}, transpilation::make_value(_t)));
|
||||
}
|
||||
|
||||
template <rfl::internal::StringLiteral _name1,
|
||||
rfl::internal::StringLiteral _name2>
|
||||
auto operator<=(const Col<_name1>&, const Col<_name2>&) {
|
||||
return transpilation::make_condition(transpilation::conditions::lesser_equal(
|
||||
transpilation::Col<_name1>{}, transpilation::Col<_name2>{}));
|
||||
}
|
||||
|
||||
template <rfl::internal::StringLiteral _name1, class T>
|
||||
auto operator<=(const Col<_name1>&, const T& _t) {
|
||||
return transpilation::make_condition(transpilation::conditions::lesser_equal(
|
||||
transpilation::Col<_name1>{}, transpilation::make_value(_t)));
|
||||
}
|
||||
|
||||
template <rfl::internal::StringLiteral _name1,
|
||||
rfl::internal::StringLiteral _name2>
|
||||
auto operator>(const Col<_name1>&, const Col<_name2>&) {
|
||||
return transpilation::make_condition(transpilation::conditions::greater_than(
|
||||
transpilation::Col<_name1>{}, transpilation::Col<_name2>{}));
|
||||
}
|
||||
|
||||
template <rfl::internal::StringLiteral _name1, class T>
|
||||
auto operator>(const Col<_name1>&, const T& _t) {
|
||||
return transpilation::make_condition(transpilation::conditions::greater_than(
|
||||
transpilation::Col<_name1>{}, transpilation::make_value(_t)));
|
||||
}
|
||||
|
||||
template <rfl::internal::StringLiteral _name1,
|
||||
rfl::internal::StringLiteral _name2>
|
||||
auto operator>=(const Col<_name1>&, const Col<_name2>&) {
|
||||
return transpilation::make_condition(transpilation::conditions::greater_equal(
|
||||
transpilation::Col<_name1>{}, transpilation::Col<_name2>{}));
|
||||
}
|
||||
|
||||
template <rfl::internal::StringLiteral _name1, class T>
|
||||
auto operator>=(const Col<_name1>&, const T& _t) {
|
||||
return transpilation::make_condition(transpilation::conditions::greater_equal(
|
||||
transpilation::Col<_name1>{}, transpilation::make_value(_t)));
|
||||
}
|
||||
} // namespace transpilation
|
||||
|
||||
} // namespace sqlgen
|
||||
|
||||
|
||||
@@ -1,44 +1,11 @@
|
||||
#ifndef SQLGEN_DYNAMIC_AGGREGATION_HPP_
|
||||
#define SQLGEN_DYNAMIC_AGGREGATION_HPP_
|
||||
|
||||
#include <optional>
|
||||
#include <rfl.hpp>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "Column.hpp"
|
||||
#include "ColumnOrValue.hpp"
|
||||
#include "Operation.hpp"
|
||||
|
||||
namespace sqlgen::dynamic {
|
||||
|
||||
struct Aggregation {
|
||||
struct Avg {
|
||||
ColumnOrValue val;
|
||||
};
|
||||
|
||||
struct Count {
|
||||
std::optional<Column> val;
|
||||
bool distinct = false;
|
||||
};
|
||||
|
||||
struct Max {
|
||||
ColumnOrValue val;
|
||||
};
|
||||
|
||||
struct Min {
|
||||
ColumnOrValue val;
|
||||
};
|
||||
|
||||
struct Sum {
|
||||
ColumnOrValue val;
|
||||
};
|
||||
|
||||
using ReflectionType = rfl::TaggedUnion<"what", Avg, Count, Max, Min, Sum>;
|
||||
|
||||
const ReflectionType& reflection() const { return val; }
|
||||
|
||||
ReflectionType val;
|
||||
};
|
||||
using Aggregation = Operation::Aggregation;
|
||||
|
||||
} // namespace sqlgen::dynamic
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "../Ref.hpp"
|
||||
#include "Column.hpp"
|
||||
#include "ColumnOrValue.hpp"
|
||||
#include "Operation.hpp"
|
||||
|
||||
namespace sqlgen::dynamic {
|
||||
|
||||
@@ -16,50 +17,54 @@ struct Condition {
|
||||
};
|
||||
|
||||
struct Equal {
|
||||
Column op1;
|
||||
ColumnOrValue op2;
|
||||
Operation op1;
|
||||
Operation op2;
|
||||
};
|
||||
|
||||
struct GreaterEqual {
|
||||
Column op1;
|
||||
ColumnOrValue op2;
|
||||
Operation op1;
|
||||
Operation op2;
|
||||
};
|
||||
|
||||
struct GreaterThan {
|
||||
Column op1;
|
||||
ColumnOrValue op2;
|
||||
Operation op1;
|
||||
Operation op2;
|
||||
};
|
||||
|
||||
struct IsNotNull {
|
||||
Column op;
|
||||
Operation op;
|
||||
};
|
||||
|
||||
struct IsNull {
|
||||
Column op;
|
||||
Operation op;
|
||||
};
|
||||
|
||||
struct LesserEqual {
|
||||
Column op1;
|
||||
ColumnOrValue op2;
|
||||
Operation op1;
|
||||
Operation op2;
|
||||
};
|
||||
|
||||
struct LesserThan {
|
||||
Column op1;
|
||||
ColumnOrValue op2;
|
||||
Operation op1;
|
||||
Operation op2;
|
||||
};
|
||||
|
||||
struct Like {
|
||||
Column op;
|
||||
Operation op;
|
||||
dynamic::Value pattern;
|
||||
};
|
||||
|
||||
struct Not {
|
||||
Ref<Condition> cond;
|
||||
};
|
||||
|
||||
struct NotEqual {
|
||||
Column op1;
|
||||
ColumnOrValue op2;
|
||||
Operation op1;
|
||||
Operation op2;
|
||||
};
|
||||
|
||||
struct NotLike {
|
||||
Column op;
|
||||
Operation op;
|
||||
dynamic::Value pattern;
|
||||
};
|
||||
|
||||
@@ -70,7 +75,7 @@ struct Condition {
|
||||
|
||||
using ReflectionType =
|
||||
rfl::TaggedUnion<"what", And, Equal, GreaterEqual, GreaterThan, IsNull,
|
||||
IsNotNull, LesserEqual, LesserThan, Like, NotEqual,
|
||||
IsNotNull, LesserEqual, LesserThan, Like, Not, NotEqual,
|
||||
NotLike, Or>;
|
||||
|
||||
const ReflectionType& reflection() const { return val; }
|
||||
|
||||
175
include/sqlgen/dynamic/Operation.hpp
Normal file
175
include/sqlgen/dynamic/Operation.hpp
Normal file
@@ -0,0 +1,175 @@
|
||||
#ifndef SQLGEN_DYNAMIC_OPERATION_HPP_
|
||||
#define SQLGEN_DYNAMIC_OPERATION_HPP_
|
||||
|
||||
#include <optional>
|
||||
#include <rfl.hpp>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "../Ref.hpp"
|
||||
#include "Column.hpp"
|
||||
#include "Type.hpp"
|
||||
#include "Value.hpp"
|
||||
|
||||
namespace sqlgen::dynamic {
|
||||
|
||||
struct Operation {
|
||||
struct Abs {
|
||||
Ref<Operation> op1;
|
||||
};
|
||||
|
||||
struct Aggregation {
|
||||
struct Avg {
|
||||
Ref<Operation> val;
|
||||
};
|
||||
|
||||
struct Count {
|
||||
std::optional<Column> val;
|
||||
bool distinct = false;
|
||||
};
|
||||
|
||||
struct Max {
|
||||
Ref<Operation> val;
|
||||
};
|
||||
|
||||
struct Min {
|
||||
Ref<Operation> val;
|
||||
};
|
||||
|
||||
struct Sum {
|
||||
Ref<Operation> val;
|
||||
};
|
||||
|
||||
using ReflectionType = rfl::TaggedUnion<"what", Avg, Count, Max, Min, Sum>;
|
||||
|
||||
const ReflectionType& reflection() const { return val; }
|
||||
|
||||
ReflectionType val;
|
||||
};
|
||||
|
||||
struct Cast {
|
||||
Ref<Operation> op1;
|
||||
Type target_type;
|
||||
};
|
||||
|
||||
struct Ceil {
|
||||
Ref<Operation> op1;
|
||||
};
|
||||
|
||||
struct Coalesce {
|
||||
std::vector<Ref<Operation>> ops;
|
||||
};
|
||||
|
||||
struct Concat {
|
||||
std::vector<Ref<Operation>> ops;
|
||||
};
|
||||
|
||||
struct Cos {
|
||||
Ref<Operation> op1;
|
||||
};
|
||||
|
||||
struct Divides {
|
||||
Ref<Operation> op1;
|
||||
Ref<Operation> op2;
|
||||
};
|
||||
|
||||
struct Exp {
|
||||
Ref<Operation> op1;
|
||||
};
|
||||
|
||||
struct Floor {
|
||||
Ref<Operation> op1;
|
||||
};
|
||||
|
||||
struct Length {
|
||||
Ref<Operation> op1;
|
||||
};
|
||||
|
||||
struct Ln {
|
||||
Ref<Operation> op1;
|
||||
};
|
||||
|
||||
struct Lower {
|
||||
Ref<Operation> op1;
|
||||
};
|
||||
|
||||
struct LTrim {
|
||||
Ref<Operation> op1;
|
||||
Ref<Operation> op2;
|
||||
};
|
||||
|
||||
struct Log2 {
|
||||
Ref<Operation> op1;
|
||||
};
|
||||
|
||||
struct Minus {
|
||||
Ref<Operation> op1;
|
||||
Ref<Operation> op2;
|
||||
};
|
||||
|
||||
struct Mod {
|
||||
Ref<Operation> op1;
|
||||
Ref<Operation> op2;
|
||||
};
|
||||
|
||||
struct Multiplies {
|
||||
Ref<Operation> op1;
|
||||
Ref<Operation> op2;
|
||||
};
|
||||
|
||||
struct Plus {
|
||||
Ref<Operation> op1;
|
||||
Ref<Operation> op2;
|
||||
};
|
||||
|
||||
struct Replace {
|
||||
Ref<Operation> op1;
|
||||
Ref<Operation> op2;
|
||||
Ref<Operation> op3;
|
||||
};
|
||||
|
||||
struct Round {
|
||||
Ref<Operation> op1;
|
||||
Ref<Operation> op2;
|
||||
};
|
||||
|
||||
struct RTrim {
|
||||
Ref<Operation> op1;
|
||||
Ref<Operation> op2;
|
||||
};
|
||||
|
||||
struct Sin {
|
||||
Ref<Operation> op1;
|
||||
};
|
||||
|
||||
struct Sqrt {
|
||||
Ref<Operation> op1;
|
||||
};
|
||||
|
||||
struct Tan {
|
||||
Ref<Operation> op1;
|
||||
};
|
||||
|
||||
struct Trim {
|
||||
Ref<Operation> op1;
|
||||
Ref<Operation> op2;
|
||||
};
|
||||
|
||||
struct Upper {
|
||||
Ref<Operation> op1;
|
||||
};
|
||||
|
||||
using ReflectionType =
|
||||
rfl::TaggedUnion<"what", Abs, Aggregation, Cast, Ceil, Column, Coalesce,
|
||||
Concat, Cos, Divides, Exp, Floor, Length, Ln, Log2,
|
||||
Lower, LTrim, Minus, Mod, Multiplies, Plus, Replace,
|
||||
Round, RTrim, Sin, Sqrt, Tan, Trim, Upper, Value>;
|
||||
|
||||
const ReflectionType& reflection() const { return val; }
|
||||
|
||||
ReflectionType val;
|
||||
};
|
||||
|
||||
} // namespace sqlgen::dynamic
|
||||
|
||||
#endif
|
||||
@@ -6,20 +6,18 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "Aggregation.hpp"
|
||||
#include "Column.hpp"
|
||||
#include "Condition.hpp"
|
||||
#include "GroupBy.hpp"
|
||||
#include "Limit.hpp"
|
||||
#include "Operation.hpp"
|
||||
#include "OrderBy.hpp"
|
||||
#include "Table.hpp"
|
||||
#include "Value.hpp"
|
||||
|
||||
namespace sqlgen::dynamic {
|
||||
|
||||
struct SelectFrom {
|
||||
struct Field {
|
||||
rfl::TaggedUnion<"type", Aggregation, Column, Value> val;
|
||||
Operation val;
|
||||
std::optional<std::string> as;
|
||||
};
|
||||
|
||||
|
||||
@@ -18,7 +18,11 @@ struct String {
|
||||
std::string val;
|
||||
};
|
||||
|
||||
using Value = rfl::TaggedUnion<"type", Float, Integer, String>;
|
||||
struct Value {
|
||||
using ReflectionType = rfl::TaggedUnion<"type", Float, Integer, String>;
|
||||
const auto& reflection() const { return val; }
|
||||
ReflectionType val;
|
||||
};
|
||||
|
||||
} // namespace sqlgen::dynamic
|
||||
|
||||
|
||||
229
include/sqlgen/operations.hpp
Normal file
229
include/sqlgen/operations.hpp
Normal file
@@ -0,0 +1,229 @@
|
||||
#ifndef SQLGEN_OPERATIONS_HPP_
|
||||
#define SQLGEN_OPERATIONS_HPP_
|
||||
|
||||
#include <rfl.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
#include "col.hpp"
|
||||
#include "transpilation/Operation.hpp"
|
||||
#include "transpilation/Operator.hpp"
|
||||
#include "transpilation/to_transpilation_type.hpp"
|
||||
|
||||
namespace sqlgen {
|
||||
|
||||
template <class T>
|
||||
auto abs(const T& _t) {
|
||||
using Type =
|
||||
typename transpilation::ToTranspilationType<std::remove_cvref_t<T>>::Type;
|
||||
return transpilation::Operation<transpilation::Operator::abs, Type>{
|
||||
.operand1 = transpilation::to_transpilation_type(_t)};
|
||||
}
|
||||
|
||||
template <class TargetType, class T>
|
||||
auto cast(const T& _t) {
|
||||
using Type =
|
||||
typename transpilation::ToTranspilationType<std::remove_cvref_t<T>>::Type;
|
||||
return transpilation::Operation<
|
||||
transpilation::Operator::cast, Type,
|
||||
transpilation::TypeHolder<std::remove_cvref_t<TargetType>>>{
|
||||
.operand1 = transpilation::to_transpilation_type(_t)};
|
||||
}
|
||||
|
||||
template <class T>
|
||||
auto ceil(const T& _t) {
|
||||
using Type =
|
||||
typename transpilation::ToTranspilationType<std::remove_cvref_t<T>>::Type;
|
||||
return transpilation::Operation<transpilation::Operator::ceil, Type>{
|
||||
.operand1 = transpilation::to_transpilation_type(_t)};
|
||||
}
|
||||
|
||||
template <class... Ts>
|
||||
auto coalesce(const Ts&... _ts) {
|
||||
static_assert(sizeof...(_ts) > 1,
|
||||
"coalesce(...) must have at least two inputs.");
|
||||
using Type = rfl::Tuple<typename transpilation::ToTranspilationType<
|
||||
std::remove_cvref_t<Ts>>::Type...>;
|
||||
return transpilation::Operation<transpilation::Operator::coalesce, Type>{
|
||||
.operand1 = Type(transpilation::to_transpilation_type(_ts)...)};
|
||||
}
|
||||
|
||||
template <class... Ts>
|
||||
auto concat(const Ts&... _ts) {
|
||||
static_assert(sizeof...(_ts) > 0,
|
||||
"concat(...) must have at least one input.");
|
||||
using Type = rfl::Tuple<typename transpilation::ToTranspilationType<
|
||||
std::remove_cvref_t<Ts>>::Type...>;
|
||||
return transpilation::Operation<transpilation::Operator::concat, Type>{
|
||||
.operand1 = Type(transpilation::to_transpilation_type(_ts)...)};
|
||||
}
|
||||
|
||||
template <class T>
|
||||
auto cos(const T& _t) {
|
||||
using Type =
|
||||
typename transpilation::ToTranspilationType<std::remove_cvref_t<T>>::Type;
|
||||
return transpilation::Operation<transpilation::Operator::cos, Type>{
|
||||
.operand1 = transpilation::to_transpilation_type(_t)};
|
||||
}
|
||||
|
||||
template <class T>
|
||||
auto exp(const T& _t) {
|
||||
using Type =
|
||||
typename transpilation::ToTranspilationType<std::remove_cvref_t<T>>::Type;
|
||||
return transpilation::Operation<transpilation::Operator::exp, Type>{
|
||||
.operand1 = transpilation::to_transpilation_type(_t)};
|
||||
}
|
||||
|
||||
template <class T>
|
||||
auto floor(const T& _t) {
|
||||
using Type =
|
||||
typename transpilation::ToTranspilationType<std::remove_cvref_t<T>>::Type;
|
||||
return transpilation::Operation<transpilation::Operator::floor, Type>{
|
||||
.operand1 = transpilation::to_transpilation_type(_t)};
|
||||
}
|
||||
|
||||
template <class T>
|
||||
auto length(const T& _t) {
|
||||
using Type =
|
||||
typename transpilation::ToTranspilationType<std::remove_cvref_t<T>>::Type;
|
||||
return transpilation::Operation<transpilation::Operator::length, Type>{
|
||||
.operand1 = transpilation::to_transpilation_type(_t)};
|
||||
}
|
||||
|
||||
template <class T>
|
||||
auto ln(const T& _t) {
|
||||
using Type =
|
||||
typename transpilation::ToTranspilationType<std::remove_cvref_t<T>>::Type;
|
||||
return transpilation::Operation<transpilation::Operator::ln, Type>{
|
||||
.operand1 = transpilation::to_transpilation_type(_t)};
|
||||
}
|
||||
|
||||
template <class T>
|
||||
auto log2(const T& _t) {
|
||||
using Type =
|
||||
typename transpilation::ToTranspilationType<std::remove_cvref_t<T>>::Type;
|
||||
return transpilation::Operation<transpilation::Operator::log2, Type>{
|
||||
.operand1 = transpilation::to_transpilation_type(_t)};
|
||||
}
|
||||
|
||||
template <class T>
|
||||
auto lower(const T& _t) {
|
||||
using Type =
|
||||
typename transpilation::ToTranspilationType<std::remove_cvref_t<T>>::Type;
|
||||
return transpilation::Operation<transpilation::Operator::lower, Type>{
|
||||
.operand1 = transpilation::to_transpilation_type(_t)};
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
auto ltrim(const T& _t, const U& _u) {
|
||||
using Type1 =
|
||||
typename transpilation::ToTranspilationType<std::remove_cvref_t<T>>::Type;
|
||||
using Type2 =
|
||||
typename transpilation::ToTranspilationType<std::remove_cvref_t<U>>::Type;
|
||||
return transpilation::Operation<transpilation::Operator::ltrim, Type1, Type2>{
|
||||
.operand1 = transpilation::to_transpilation_type(_t),
|
||||
.operand2 = transpilation::to_transpilation_type(_u)};
|
||||
}
|
||||
|
||||
template <class T>
|
||||
auto ltrim(const T& _t) {
|
||||
return ltrim(_t, std::string(" "));
|
||||
}
|
||||
|
||||
template <class StringType, class FromType, class ToType>
|
||||
auto replace(const StringType& _str, const FromType& _from, const ToType& _to) {
|
||||
using Type1 = typename transpilation::ToTranspilationType<
|
||||
std::remove_cvref_t<StringType>>::Type;
|
||||
using Type2 = typename transpilation::ToTranspilationType<
|
||||
std::remove_cvref_t<FromType>>::Type;
|
||||
using Type3 = typename transpilation::ToTranspilationType<
|
||||
std::remove_cvref_t<ToType>>::Type;
|
||||
return transpilation::Operation<transpilation::Operator::replace, Type1,
|
||||
Type2, Type3>{
|
||||
.operand1 = transpilation::to_transpilation_type(_str),
|
||||
.operand2 = transpilation::to_transpilation_type(_from),
|
||||
.operand3 = transpilation::to_transpilation_type(_to)};
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
auto round(const T& _t, const U& _u) {
|
||||
using Type1 =
|
||||
typename transpilation::ToTranspilationType<std::remove_cvref_t<T>>::Type;
|
||||
using Type2 =
|
||||
typename transpilation::ToTranspilationType<std::remove_cvref_t<U>>::Type;
|
||||
return transpilation::Operation<transpilation::Operator::round, Type1, Type2>{
|
||||
.operand1 = transpilation::to_transpilation_type(_t),
|
||||
.operand2 = transpilation::to_transpilation_type(_u)};
|
||||
}
|
||||
|
||||
template <class T>
|
||||
auto round(const T& _t) {
|
||||
return round(_t, 0);
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
auto rtrim(const T& _t, const U& _u) {
|
||||
using Type1 =
|
||||
typename transpilation::ToTranspilationType<std::remove_cvref_t<T>>::Type;
|
||||
using Type2 =
|
||||
typename transpilation::ToTranspilationType<std::remove_cvref_t<U>>::Type;
|
||||
return transpilation::Operation<transpilation::Operator::rtrim, Type1, Type2>{
|
||||
.operand1 = transpilation::to_transpilation_type(_t),
|
||||
.operand2 = transpilation::to_transpilation_type(_u)};
|
||||
}
|
||||
|
||||
template <class T>
|
||||
auto rtrim(const T& _t) {
|
||||
return rtrim(_t, std::string(" "));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
auto sin(const T& _t) {
|
||||
using Type =
|
||||
typename transpilation::ToTranspilationType<std::remove_cvref_t<T>>::Type;
|
||||
return transpilation::Operation<transpilation::Operator::sin, Type>{
|
||||
.operand1 = transpilation::to_transpilation_type(_t)};
|
||||
}
|
||||
|
||||
template <class T>
|
||||
auto sqrt(const T& _t) {
|
||||
using Type =
|
||||
typename transpilation::ToTranspilationType<std::remove_cvref_t<T>>::Type;
|
||||
return transpilation::Operation<transpilation::Operator::sqrt, Type>{
|
||||
.operand1 = transpilation::to_transpilation_type(_t)};
|
||||
}
|
||||
|
||||
template <class T>
|
||||
auto tan(const T& _t) {
|
||||
using Type =
|
||||
typename transpilation::ToTranspilationType<std::remove_cvref_t<T>>::Type;
|
||||
return transpilation::Operation<transpilation::Operator::tan, Type>{
|
||||
.operand1 = transpilation::to_transpilation_type(_t)};
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
auto trim(const T& _t, const U& _u) {
|
||||
using Type1 =
|
||||
typename transpilation::ToTranspilationType<std::remove_cvref_t<T>>::Type;
|
||||
using Type2 =
|
||||
typename transpilation::ToTranspilationType<std::remove_cvref_t<U>>::Type;
|
||||
return transpilation::Operation<transpilation::Operator::trim, Type1, Type2>{
|
||||
.operand1 = transpilation::to_transpilation_type(_t),
|
||||
.operand2 = transpilation::to_transpilation_type(_u)};
|
||||
}
|
||||
|
||||
template <class T>
|
||||
auto trim(const T& _t) {
|
||||
return trim(_t, std::string(" "));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
auto upper(const T& _t) {
|
||||
using Type =
|
||||
typename transpilation::ToTranspilationType<std::remove_cvref_t<T>>::Type;
|
||||
return transpilation::Operation<transpilation::Operator::upper, Type>{
|
||||
.operand1 = transpilation::to_transpilation_type(_t)};
|
||||
}
|
||||
|
||||
} // namespace sqlgen
|
||||
|
||||
#endif
|
||||
86
include/sqlgen/transpilation/Aggregation.hpp
Normal file
86
include/sqlgen/transpilation/Aggregation.hpp
Normal file
@@ -0,0 +1,86 @@
|
||||
#ifndef SQLGEN_TRANSPILATION_AGGREGATION_HPP_
|
||||
#define SQLGEN_TRANSPILATION_AGGREGATION_HPP_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "AggregationOp.hpp"
|
||||
#include "As.hpp"
|
||||
#include "Operation.hpp"
|
||||
#include "Operator.hpp"
|
||||
#include "to_transpilation_type.hpp"
|
||||
|
||||
namespace sqlgen::transpilation {
|
||||
|
||||
/// To be used when we want to count everything.
|
||||
struct All {};
|
||||
|
||||
template <AggregationOp _agg, class _ValueType>
|
||||
struct Aggregation {
|
||||
static constexpr auto agg = _agg;
|
||||
using ValueType = _ValueType;
|
||||
|
||||
template <rfl::internal::StringLiteral _new_name>
|
||||
auto as() const noexcept {
|
||||
using T = std::remove_cvref_t<decltype(*this)>;
|
||||
return transpilation::As<T, _new_name>{.val = *this};
|
||||
}
|
||||
|
||||
ValueType val;
|
||||
bool distinct = false;
|
||||
|
||||
template <class T>
|
||||
friend auto operator/(const Aggregation& _op1, const T& _op2) noexcept {
|
||||
using OtherType = typename transpilation::ToTranspilationType<
|
||||
std::remove_cvref_t<T>>::Type;
|
||||
|
||||
return Operation<Operator::divides, Aggregation, OtherType>{
|
||||
.operand1 = _op1, .operand2 = to_transpilation_type(_op2)};
|
||||
}
|
||||
|
||||
template <class T>
|
||||
friend auto operator-(const Aggregation& _op1, const T& _op2) noexcept {
|
||||
using OtherType = typename transpilation::ToTranspilationType<
|
||||
std::remove_cvref_t<T>>::Type;
|
||||
|
||||
return Operation<Operator::minus, Aggregation, OtherType>{
|
||||
.operand1 = _op1, .operand2 = to_transpilation_type(_op2)};
|
||||
}
|
||||
|
||||
template <class T>
|
||||
friend auto operator%(const Aggregation& _op1, const T& _op2) noexcept {
|
||||
using OtherType = typename transpilation::ToTranspilationType<
|
||||
std::remove_cvref_t<T>>::Type;
|
||||
|
||||
return Operation<Operator::mod, Aggregation, OtherType>{
|
||||
.operand1 = _op1, .operand2 = to_transpilation_type(_op2)};
|
||||
}
|
||||
|
||||
template <class T>
|
||||
friend auto operator*(const Aggregation& _op1, const T& _op2) noexcept {
|
||||
using OtherType = typename transpilation::ToTranspilationType<
|
||||
std::remove_cvref_t<T>>::Type;
|
||||
|
||||
return Operation<Operator::multiplies, Aggregation, OtherType>{
|
||||
.operand1 = _op1, .operand2 = to_transpilation_type(_op2)};
|
||||
}
|
||||
|
||||
template <class T>
|
||||
friend auto operator+(const Aggregation& _op1, const T& _op2) noexcept {
|
||||
using OtherType = typename transpilation::ToTranspilationType<
|
||||
std::remove_cvref_t<T>>::Type;
|
||||
|
||||
return Operation<Operator::plus, Aggregation, OtherType>{
|
||||
.operand1 = _op1, .operand2 = to_transpilation_type(_op2)};
|
||||
}
|
||||
};
|
||||
|
||||
template <AggregationOp _agg, class _ValueType>
|
||||
struct ToTranspilationType<Aggregation<_agg, _ValueType>> {
|
||||
using Type = Aggregation<_agg, _ValueType>;
|
||||
|
||||
Type operator()(const Type& _val) const noexcept { return _val; }
|
||||
};
|
||||
|
||||
} // namespace sqlgen::transpilation
|
||||
|
||||
#endif
|
||||
10
include/sqlgen/transpilation/AggregationOp.hpp
Normal file
10
include/sqlgen/transpilation/AggregationOp.hpp
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifndef SQLGEN_TRANSPILATION_AGGREGATIONOP_HPP_
|
||||
#define SQLGEN_TRANSPILATION_AGGREGATIONOP_HPP_
|
||||
|
||||
namespace sqlgen::transpilation {
|
||||
|
||||
enum class AggregationOp { avg, count, max, min, sum };
|
||||
|
||||
} // namespace sqlgen::transpilation
|
||||
|
||||
#endif
|
||||
@@ -11,6 +11,22 @@ template <class _ConditionType>
|
||||
struct Condition {
|
||||
using ConditionType = _ConditionType;
|
||||
ConditionType condition;
|
||||
|
||||
auto operator!() {
|
||||
return make_condition(conditions::Not<_ConditionType>{.cond = condition});
|
||||
}
|
||||
|
||||
template <class C2>
|
||||
friend auto operator&&(const Condition& _cond1, const Condition<C2>& _cond2) {
|
||||
return make_condition(conditions::And<_ConditionType, C2>{
|
||||
.cond1 = _cond1.condition, .cond2 = _cond2.condition});
|
||||
}
|
||||
|
||||
template <class C2>
|
||||
friend auto operator||(const Condition& _cond1, const Condition<C2>& _cond2) {
|
||||
return make_condition(conditions::Or<_ConditionType, C2>{
|
||||
.cond1 = _cond1.condition, .cond2 = _cond2.condition});
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
@@ -18,18 +34,6 @@ auto make_condition(T&& _t) {
|
||||
return Condition<std::remove_cvref_t<T>>{.condition = _t};
|
||||
}
|
||||
|
||||
template <class C1, class C2>
|
||||
auto operator&&(const Condition<C1>& _cond1, const Condition<C2>& _cond2) {
|
||||
return make_condition(conditions::And<C1, C2>{.cond1 = _cond1.condition,
|
||||
.cond2 = _cond2.condition});
|
||||
}
|
||||
|
||||
template <class C1, class C2>
|
||||
auto operator||(const Condition<C1>& _cond1, const Condition<C2>& _cond2) {
|
||||
return make_condition(conditions::Or<C1, C2>{.cond1 = _cond1.condition,
|
||||
.cond2 = _cond2.condition});
|
||||
}
|
||||
|
||||
} // namespace sqlgen::transpilation
|
||||
|
||||
#endif
|
||||
|
||||
153
include/sqlgen/transpilation/Operation.hpp
Normal file
153
include/sqlgen/transpilation/Operation.hpp
Normal file
@@ -0,0 +1,153 @@
|
||||
#ifndef SQLGEN_TRANSPILATION_OPERATION_HPP_
|
||||
#define SQLGEN_TRANSPILATION_OPERATION_HPP_
|
||||
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
#include "../Result.hpp"
|
||||
#include "As.hpp"
|
||||
#include "Condition.hpp"
|
||||
#include "Operator.hpp"
|
||||
#include "conditions.hpp"
|
||||
#include "to_transpilation_type.hpp"
|
||||
|
||||
namespace sqlgen::transpilation {
|
||||
|
||||
/// Simple abstraction to be used for the cast operation.
|
||||
template <class T>
|
||||
struct TypeHolder {};
|
||||
|
||||
template <Operator _op, class _Operand1Type, class _Operand2Type = Nothing,
|
||||
class _Operand3Type = Nothing>
|
||||
struct Operation {
|
||||
static constexpr Operator op = _op;
|
||||
|
||||
using Operand1Type = _Operand1Type;
|
||||
using Operand2Type = _Operand2Type;
|
||||
using Operand3Type = _Operand3Type;
|
||||
|
||||
Operand1Type operand1;
|
||||
Operand2Type operand2;
|
||||
Operand3Type operand3;
|
||||
|
||||
template <rfl::internal::StringLiteral _new_name>
|
||||
auto as() const noexcept {
|
||||
using T = std::remove_cvref_t<decltype(*this)>;
|
||||
return transpilation::As<T, _new_name>{.val = *this};
|
||||
}
|
||||
|
||||
/// Returns an IS NULL condition.
|
||||
auto is_null() const noexcept {
|
||||
return make_condition(conditions::is_null(*this));
|
||||
}
|
||||
|
||||
/// Returns a IS NOT NULL condition.
|
||||
auto is_not_null() const noexcept {
|
||||
return make_condition(conditions::is_not_null(*this));
|
||||
}
|
||||
|
||||
/// Returns a LIKE condition.
|
||||
auto like(const std::string& _pattern) const noexcept {
|
||||
return make_condition(conditions::like(*this, _pattern));
|
||||
}
|
||||
|
||||
/// Returns a NOT LIKE condition.
|
||||
auto not_like(const std::string& _pattern) const noexcept {
|
||||
return make_condition(conditions::not_like(*this, _pattern));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
friend auto operator==(const Operation& _o, const T& _t) {
|
||||
return make_condition(conditions::equal(_o, to_transpilation_type(_t)));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
friend auto operator!=(const Operation& _o, const T& _t) {
|
||||
return make_condition(conditions::not_equal(_o, to_transpilation_type(_t)));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
friend auto operator<(const Operation& _o, const T& _t) {
|
||||
return make_condition(
|
||||
conditions::lesser_than(_o, to_transpilation_type(_t)));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
friend auto operator<=(const Operation& _o, const T& _t) {
|
||||
return make_condition(
|
||||
conditions::lesser_equal(_o, to_transpilation_type(_t)));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
friend auto operator>(const Operation& _o, const T& _t) {
|
||||
return make_condition(
|
||||
conditions::greater_than(_o, to_transpilation_type(_t)));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
friend auto operator>=(const Operation& _o, const T& _t) {
|
||||
return make_condition(
|
||||
conditions::greater_equal(_o, to_transpilation_type(_t)));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
friend auto operator/(const Operation& _op1, const T& _op2) noexcept {
|
||||
using OtherType = typename transpilation::ToTranspilationType<
|
||||
std::remove_cvref_t<T>>::Type;
|
||||
|
||||
return Operation<Operator::divides,
|
||||
Operation<_op, _Operand1Type, _Operand2Type>, OtherType>{
|
||||
.operand1 = _op1, .operand2 = to_transpilation_type(_op2)};
|
||||
}
|
||||
|
||||
template <class T>
|
||||
friend auto operator-(const Operation& _op1, const T& _op2) noexcept {
|
||||
using OtherType = typename transpilation::ToTranspilationType<
|
||||
std::remove_cvref_t<T>>::Type;
|
||||
|
||||
return Operation<Operator::minus,
|
||||
Operation<_op, _Operand1Type, _Operand2Type>, OtherType>{
|
||||
.operand1 = _op1, .operand2 = to_transpilation_type(_op2)};
|
||||
}
|
||||
|
||||
template <class T>
|
||||
friend auto operator%(const Operation& _op1, const T& _op2) noexcept {
|
||||
using OtherType = typename transpilation::ToTranspilationType<
|
||||
std::remove_cvref_t<T>>::Type;
|
||||
|
||||
return Operation<Operator::mod,
|
||||
Operation<_op, _Operand1Type, _Operand2Type>, OtherType>{
|
||||
.operand1 = _op1, .operand2 = to_transpilation_type(_op2)};
|
||||
}
|
||||
|
||||
template <class T>
|
||||
friend auto operator*(const Operation& _op1, const T& _op2) noexcept {
|
||||
using OtherType = typename transpilation::ToTranspilationType<
|
||||
std::remove_cvref_t<T>>::Type;
|
||||
|
||||
return Operation<Operator::multiplies,
|
||||
Operation<_op, _Operand1Type, _Operand2Type>, OtherType>{
|
||||
.operand1 = _op1, .operand2 = to_transpilation_type(_op2)};
|
||||
}
|
||||
|
||||
template <class T>
|
||||
friend auto operator+(const Operation& _op1, const T& _op2) noexcept {
|
||||
using OtherType = typename transpilation::ToTranspilationType<
|
||||
std::remove_cvref_t<T>>::Type;
|
||||
|
||||
return Operation<Operator::plus,
|
||||
Operation<_op, _Operand1Type, _Operand2Type>, OtherType>{
|
||||
.operand1 = _op1, .operand2 = to_transpilation_type(_op2)};
|
||||
}
|
||||
};
|
||||
|
||||
template <Operator _op, class _Operand1Type, class _Operand2Type>
|
||||
struct ToTranspilationType<Operation<_op, _Operand1Type, _Operand2Type>> {
|
||||
using Type = Operation<_op, _Operand1Type, _Operand2Type>;
|
||||
|
||||
Type operator()(const Type& _val) const noexcept { return _val; }
|
||||
};
|
||||
|
||||
} // namespace sqlgen::transpilation
|
||||
|
||||
#endif
|
||||
37
include/sqlgen/transpilation/Operator.hpp
Normal file
37
include/sqlgen/transpilation/Operator.hpp
Normal file
@@ -0,0 +1,37 @@
|
||||
#ifndef SQLGEN_TRANSPILATION_OPERATOR_HPP_
|
||||
#define SQLGEN_TRANSPILATION_OPERATOR_HPP_
|
||||
|
||||
namespace sqlgen::transpilation {
|
||||
|
||||
enum class Operator {
|
||||
abs,
|
||||
cast,
|
||||
ceil,
|
||||
coalesce,
|
||||
concat,
|
||||
cos,
|
||||
divides,
|
||||
exp,
|
||||
floor,
|
||||
length,
|
||||
ln,
|
||||
log2,
|
||||
lower,
|
||||
ltrim,
|
||||
minus,
|
||||
mod,
|
||||
multiplies,
|
||||
plus,
|
||||
replace,
|
||||
round,
|
||||
rtrim,
|
||||
sin,
|
||||
sqrt,
|
||||
tan,
|
||||
trim,
|
||||
upper
|
||||
};
|
||||
|
||||
} // namespace sqlgen::transpilation
|
||||
|
||||
#endif
|
||||
10
include/sqlgen/transpilation/OperatorCategory.hpp
Normal file
10
include/sqlgen/transpilation/OperatorCategory.hpp
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifndef SQLGEN_TRANSPILATION_OPERATORCATEGORY_HPP_
|
||||
#define SQLGEN_TRANSPILATION_OPERATORCATEGORY_HPP_
|
||||
|
||||
namespace sqlgen::transpilation {
|
||||
|
||||
enum class OperatorCategory { numerical, string, other };
|
||||
|
||||
} // namespace sqlgen::transpilation
|
||||
|
||||
#endif
|
||||
@@ -1,81 +0,0 @@
|
||||
#ifndef SQLGEN_TRANSPILATION_AGGREGATIONS_HPP_
|
||||
#define SQLGEN_TRANSPILATION_AGGREGATIONS_HPP_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "As.hpp"
|
||||
|
||||
namespace sqlgen::transpilation::aggregations {
|
||||
|
||||
/// To be used when we want to count everything.
|
||||
struct All {};
|
||||
|
||||
template <class _ValueType>
|
||||
struct Avg {
|
||||
using ValueType = _ValueType;
|
||||
|
||||
template <rfl::internal::StringLiteral _new_name>
|
||||
auto as() const noexcept {
|
||||
using T = std::remove_cvref_t<decltype(*this)>;
|
||||
return transpilation::As<T, _new_name>{.val = *this};
|
||||
}
|
||||
|
||||
ValueType val;
|
||||
};
|
||||
|
||||
template <class _ValueType>
|
||||
struct Count {
|
||||
using ValueType = _ValueType;
|
||||
|
||||
template <rfl::internal::StringLiteral _new_name>
|
||||
auto as() const noexcept {
|
||||
using T = std::remove_cvref_t<decltype(*this)>;
|
||||
return transpilation::As<T, _new_name>{.val = *this};
|
||||
}
|
||||
|
||||
ValueType val;
|
||||
bool distinct = false;
|
||||
};
|
||||
|
||||
template <class _ValueType>
|
||||
struct Max {
|
||||
using ValueType = _ValueType;
|
||||
|
||||
template <rfl::internal::StringLiteral _new_name>
|
||||
auto as() const noexcept {
|
||||
using T = std::remove_cvref_t<decltype(*this)>;
|
||||
return transpilation::As<T, _new_name>{.val = *this};
|
||||
}
|
||||
|
||||
ValueType val;
|
||||
};
|
||||
|
||||
template <class _ValueType>
|
||||
struct Min {
|
||||
using ValueType = _ValueType;
|
||||
|
||||
template <rfl::internal::StringLiteral _new_name>
|
||||
auto as() const noexcept {
|
||||
using T = std::remove_cvref_t<decltype(*this)>;
|
||||
return transpilation::As<T, _new_name>{.val = *this};
|
||||
}
|
||||
|
||||
ValueType val;
|
||||
};
|
||||
|
||||
template <class _ValueType>
|
||||
struct Sum {
|
||||
using ValueType = _ValueType;
|
||||
|
||||
template <rfl::internal::StringLiteral _new_name>
|
||||
auto as() const noexcept {
|
||||
using T = std::remove_cvref_t<decltype(*this)>;
|
||||
return transpilation::As<T, _new_name>{.val = *this};
|
||||
}
|
||||
|
||||
ValueType val;
|
||||
};
|
||||
|
||||
} // namespace sqlgen::transpilation::aggregations
|
||||
|
||||
#endif
|
||||
@@ -49,7 +49,8 @@ struct CheckAggregation<StructType, rfl::Tuple<FieldTypes...>,
|
||||
static constexpr bool value =
|
||||
(true && ... &&
|
||||
(MakeField<StructType, FieldTypes>::is_aggregation ||
|
||||
!MakeField<StructType, FieldTypes>::is_column ||
|
||||
(!MakeField<StructType, FieldTypes>::is_column &&
|
||||
!MakeField<StructType, FieldTypes>::is_operation) ||
|
||||
included_in_group_by<FieldTypes>));
|
||||
|
||||
static_assert(value,
|
||||
|
||||
@@ -126,6 +126,13 @@ struct NotEqual {
|
||||
OpType2 op2;
|
||||
};
|
||||
|
||||
template <class CondType>
|
||||
struct Not {
|
||||
using ResultType = bool;
|
||||
|
||||
CondType cond;
|
||||
};
|
||||
|
||||
template <class OpType1, class OpType2>
|
||||
auto not_equal(const OpType1& _op1, const OpType2& _op2) {
|
||||
return NotEqual<std::remove_cvref_t<OpType1>, std::remove_cvref_t<OpType2>>{
|
||||
|
||||
42
include/sqlgen/transpilation/dynamic_aggregation_t.hpp
Normal file
42
include/sqlgen/transpilation/dynamic_aggregation_t.hpp
Normal file
@@ -0,0 +1,42 @@
|
||||
#ifndef SQLGEN_TRANSPILATION_DYNAMICAGGREGATIONT_HPP_
|
||||
#define SQLGEN_TRANSPILATION_DYNAMICAGGREGATIONT_HPP_
|
||||
|
||||
#include "../dynamic/Aggregation.hpp"
|
||||
#include "AggregationOp.hpp"
|
||||
|
||||
namespace sqlgen::transpilation {
|
||||
|
||||
template <AggregationOp agg>
|
||||
struct DynamicAggregation;
|
||||
|
||||
template <>
|
||||
struct DynamicAggregation<AggregationOp::avg> {
|
||||
using Type = dynamic::Aggregation::Avg;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct DynamicAggregation<AggregationOp::count> {
|
||||
using Type = dynamic::Aggregation::Count;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct DynamicAggregation<AggregationOp::max> {
|
||||
using Type = dynamic::Aggregation::Max;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct DynamicAggregation<AggregationOp::min> {
|
||||
using Type = dynamic::Aggregation::Min;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct DynamicAggregation<AggregationOp::sum> {
|
||||
using Type = dynamic::Aggregation::Sum;
|
||||
};
|
||||
|
||||
template <AggregationOp agg>
|
||||
using dynamic_aggregation_t = typename DynamicAggregation<agg>::Type;
|
||||
|
||||
} // namespace sqlgen::transpilation
|
||||
|
||||
#endif
|
||||
208
include/sqlgen/transpilation/dynamic_operator_t.hpp
Normal file
208
include/sqlgen/transpilation/dynamic_operator_t.hpp
Normal file
@@ -0,0 +1,208 @@
|
||||
#ifndef SQLGEN_TRANSPILATION_DYNAMICOPERATORT_HPP_
|
||||
#define SQLGEN_TRANSPILATION_DYNAMICOPERATORT_HPP_
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include "../dynamic/Operation.hpp"
|
||||
#include "Operator.hpp"
|
||||
#include "OperatorCategory.hpp"
|
||||
|
||||
namespace sqlgen::transpilation {
|
||||
|
||||
template <Operator op>
|
||||
struct DynamicOperator;
|
||||
|
||||
template <>
|
||||
struct DynamicOperator<Operator::abs> {
|
||||
static constexpr size_t num_operands = 1;
|
||||
static constexpr auto category = OperatorCategory::numerical;
|
||||
using Type = dynamic::Operation::Abs;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct DynamicOperator<Operator::cast> {
|
||||
static constexpr size_t num_operands = 1;
|
||||
static constexpr auto category = OperatorCategory::other;
|
||||
using Type = dynamic::Operation::Cast;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct DynamicOperator<Operator::ceil> {
|
||||
static constexpr size_t num_operands = 1;
|
||||
static constexpr auto category = OperatorCategory::numerical;
|
||||
using Type = dynamic::Operation::Ceil;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct DynamicOperator<Operator::coalesce> {
|
||||
static constexpr size_t num_operands = std::numeric_limits<size_t>::max();
|
||||
static constexpr auto category = OperatorCategory::other;
|
||||
using Type = dynamic::Operation::Coalesce;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct DynamicOperator<Operator::concat> {
|
||||
static constexpr size_t num_operands = std::numeric_limits<size_t>::max();
|
||||
static constexpr auto category = OperatorCategory::string;
|
||||
using Type = dynamic::Operation::Concat;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct DynamicOperator<Operator::cos> {
|
||||
static constexpr size_t num_operands = 1;
|
||||
static constexpr auto category = OperatorCategory::numerical;
|
||||
using Type = dynamic::Operation::Cos;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct DynamicOperator<Operator::divides> {
|
||||
static constexpr size_t num_operands = 2;
|
||||
static constexpr auto category = OperatorCategory::numerical;
|
||||
using Type = dynamic::Operation::Divides;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct DynamicOperator<Operator::exp> {
|
||||
static constexpr size_t num_operands = 1;
|
||||
static constexpr auto category = OperatorCategory::numerical;
|
||||
using Type = dynamic::Operation::Exp;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct DynamicOperator<Operator::floor> {
|
||||
static constexpr size_t num_operands = 1;
|
||||
static constexpr auto category = OperatorCategory::numerical;
|
||||
using Type = dynamic::Operation::Floor;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct DynamicOperator<Operator::length> {
|
||||
static constexpr size_t num_operands = 1;
|
||||
static constexpr auto category = OperatorCategory::string;
|
||||
using Type = dynamic::Operation::Length;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct DynamicOperator<Operator::ln> {
|
||||
static constexpr size_t num_operands = 1;
|
||||
static constexpr auto category = OperatorCategory::numerical;
|
||||
using Type = dynamic::Operation::Ln;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct DynamicOperator<Operator::log2> {
|
||||
static constexpr size_t num_operands = 1;
|
||||
static constexpr auto category = OperatorCategory::numerical;
|
||||
using Type = dynamic::Operation::Log2;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct DynamicOperator<Operator::lower> {
|
||||
static constexpr size_t num_operands = 1;
|
||||
static constexpr auto category = OperatorCategory::string;
|
||||
using Type = dynamic::Operation::Lower;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct DynamicOperator<Operator::ltrim> {
|
||||
static constexpr size_t num_operands = 2;
|
||||
static constexpr auto category = OperatorCategory::string;
|
||||
using Type = dynamic::Operation::LTrim;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct DynamicOperator<Operator::minus> {
|
||||
static constexpr size_t num_operands = 2;
|
||||
static constexpr auto category = OperatorCategory::numerical;
|
||||
using Type = dynamic::Operation::Minus;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct DynamicOperator<Operator::mod> {
|
||||
static constexpr size_t num_operands = 2;
|
||||
static constexpr auto category = OperatorCategory::numerical;
|
||||
using Type = dynamic::Operation::Mod;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct DynamicOperator<Operator::multiplies> {
|
||||
static constexpr size_t num_operands = 2;
|
||||
static constexpr auto category = OperatorCategory::numerical;
|
||||
using Type = dynamic::Operation::Multiplies;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct DynamicOperator<Operator::plus> {
|
||||
static constexpr size_t num_operands = 2;
|
||||
static constexpr auto category = OperatorCategory::numerical;
|
||||
using Type = dynamic::Operation::Plus;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct DynamicOperator<Operator::replace> {
|
||||
static constexpr size_t num_operands = 3;
|
||||
static constexpr auto category = OperatorCategory::other;
|
||||
using Type = dynamic::Operation::Replace;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct DynamicOperator<Operator::round> {
|
||||
static constexpr size_t num_operands = 2;
|
||||
static constexpr auto category = OperatorCategory::other;
|
||||
using Type = dynamic::Operation::Round;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct DynamicOperator<Operator::rtrim> {
|
||||
static constexpr size_t num_operands = 2;
|
||||
static constexpr auto category = OperatorCategory::string;
|
||||
using Type = dynamic::Operation::RTrim;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct DynamicOperator<Operator::sin> {
|
||||
static constexpr size_t num_operands = 1;
|
||||
static constexpr auto category = OperatorCategory::numerical;
|
||||
using Type = dynamic::Operation::Sin;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct DynamicOperator<Operator::sqrt> {
|
||||
static constexpr size_t num_operands = 1;
|
||||
static constexpr auto category = OperatorCategory::numerical;
|
||||
using Type = dynamic::Operation::Sqrt;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct DynamicOperator<Operator::tan> {
|
||||
static constexpr size_t num_operands = 1;
|
||||
static constexpr auto category = OperatorCategory::numerical;
|
||||
using Type = dynamic::Operation::Tan;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct DynamicOperator<Operator::trim> {
|
||||
static constexpr size_t num_operands = 2;
|
||||
static constexpr auto category = OperatorCategory::string;
|
||||
using Type = dynamic::Operation::Trim;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct DynamicOperator<Operator::upper> {
|
||||
static constexpr size_t num_operands = 1;
|
||||
static constexpr auto category = OperatorCategory::string;
|
||||
using Type = dynamic::Operation::Upper;
|
||||
};
|
||||
|
||||
template <Operator op>
|
||||
using dynamic_operator_t = typename DynamicOperator<op>::Type;
|
||||
|
||||
template <Operator op>
|
||||
inline constexpr size_t num_operands_v = DynamicOperator<op>::num_operands;
|
||||
|
||||
template <Operator op>
|
||||
inline constexpr auto operator_category_v = DynamicOperator<op>::category;
|
||||
|
||||
} // namespace sqlgen::transpilation
|
||||
|
||||
#endif
|
||||
64
include/sqlgen/transpilation/flatten_fields_t.hpp
Normal file
64
include/sqlgen/transpilation/flatten_fields_t.hpp
Normal file
@@ -0,0 +1,64 @@
|
||||
#ifndef SQLGEN_TRANSPILATION_FLATTEN_FIELDS_T_HPP_
|
||||
#define SQLGEN_TRANSPILATION_FLATTEN_FIELDS_T_HPP_
|
||||
|
||||
#include <rfl.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
#include "As.hpp"
|
||||
#include "make_field.hpp"
|
||||
#include "remove_as_t.hpp"
|
||||
|
||||
namespace sqlgen::transpilation {
|
||||
|
||||
template <class TupleType>
|
||||
struct TupleWrapper;
|
||||
|
||||
template <class... FieldTypes>
|
||||
struct TupleWrapper<rfl::Tuple<FieldTypes...>> {
|
||||
using Type = rfl::Tuple<FieldTypes...>;
|
||||
|
||||
template <class... OtherFieldTypes>
|
||||
friend consteval auto operator+(
|
||||
const TupleWrapper&,
|
||||
const TupleWrapper<rfl::Tuple<OtherFieldTypes...>>&) {
|
||||
return TupleWrapper<rfl::Tuple<FieldTypes..., OtherFieldTypes...>>{};
|
||||
}
|
||||
};
|
||||
|
||||
template <class StructType, class FieldsType>
|
||||
struct FlattenFields;
|
||||
|
||||
template <class StructType, class FieldType>
|
||||
struct ExtractFields;
|
||||
|
||||
template <class StructType, class FieldType>
|
||||
requires(!MakeField<StructType, remove_as_t<FieldType>>::is_operation)
|
||||
struct ExtractFields<StructType, FieldType> {
|
||||
using Type = rfl::Tuple<remove_as_t<FieldType>>;
|
||||
};
|
||||
|
||||
template <class StructType, class FieldType>
|
||||
requires(MakeField<StructType, remove_as_t<FieldType>>::is_operation)
|
||||
struct ExtractFields<StructType, FieldType> {
|
||||
using Type = typename FlattenFields<
|
||||
StructType,
|
||||
typename MakeField<StructType, remove_as_t<FieldType>>::Operands>::Type;
|
||||
};
|
||||
|
||||
template <class StructType, class... FieldTypes>
|
||||
struct FlattenFields<StructType, rfl::Tuple<FieldTypes...>> {
|
||||
static constexpr auto wrapper =
|
||||
(TupleWrapper<rfl::Tuple<>>{} + ... +
|
||||
TupleWrapper<typename ExtractFields<StructType, FieldTypes>::Type>{});
|
||||
|
||||
using Type = typename decltype(wrapper)::Type;
|
||||
};
|
||||
|
||||
template <class StructType, class FieldsType>
|
||||
using flatten_fields_t =
|
||||
typename FlattenFields<std::remove_cvref_t<StructType>,
|
||||
std::remove_cvref_t<FieldsType>>::Type;
|
||||
|
||||
} // namespace sqlgen::transpilation
|
||||
|
||||
#endif
|
||||
@@ -7,11 +7,18 @@
|
||||
#include "../Literal.hpp"
|
||||
#include "../Result.hpp"
|
||||
#include "../dynamic/SelectFrom.hpp"
|
||||
#include "../parsing/Parser.hpp"
|
||||
#include "Aggregation.hpp"
|
||||
#include "AggregationOp.hpp"
|
||||
#include "As.hpp"
|
||||
#include "Col.hpp"
|
||||
#include "Operation.hpp"
|
||||
#include "Operator.hpp"
|
||||
#include "OperatorCategory.hpp"
|
||||
#include "Value.hpp"
|
||||
#include "aggregations.hpp"
|
||||
#include "all_columns_exist.hpp"
|
||||
#include "dynamic_aggregation_t.hpp"
|
||||
#include "dynamic_operator_t.hpp"
|
||||
#include "remove_nullable_t.hpp"
|
||||
#include "to_value.hpp"
|
||||
#include "underlying_t.hpp"
|
||||
@@ -21,16 +28,18 @@ namespace sqlgen::transpilation {
|
||||
template <class StructType, class FieldType>
|
||||
struct MakeField;
|
||||
|
||||
template <class StructType, class ValueType>
|
||||
template <class StructType, class T>
|
||||
struct MakeField {
|
||||
static constexpr bool is_aggregation = false;
|
||||
static constexpr bool is_column = false;
|
||||
static constexpr bool is_operation = false;
|
||||
|
||||
using Name = Nothing;
|
||||
using Type = ValueType;
|
||||
using Type = std::remove_cvref_t<T>;
|
||||
|
||||
dynamic::SelectFrom::Field operator()(const auto& _val) const {
|
||||
return dynamic::SelectFrom::Field{.val = to_value(_val)};
|
||||
return dynamic::SelectFrom::Field{
|
||||
dynamic::Operation{.val = to_value(_val)}};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -41,13 +50,29 @@ struct MakeField<StructType, Col<_name>> {
|
||||
|
||||
static constexpr bool is_aggregation = false;
|
||||
static constexpr bool is_column = true;
|
||||
static constexpr bool is_operation = false;
|
||||
|
||||
using Name = Literal<_name>;
|
||||
using Type = rfl::field_type_t<_name, StructType>;
|
||||
|
||||
dynamic::SelectFrom::Field operator()(const auto&) const {
|
||||
return dynamic::SelectFrom::Field{.val =
|
||||
dynamic::Column{.name = _name.str()}};
|
||||
return dynamic::SelectFrom::Field{
|
||||
dynamic::Operation{.val = dynamic::Column{.name = _name.str()}}};
|
||||
}
|
||||
};
|
||||
|
||||
template <class StructType, class T>
|
||||
struct MakeField<StructType, Value<T>> {
|
||||
static constexpr bool is_aggregation = false;
|
||||
static constexpr bool is_column = false;
|
||||
static constexpr bool is_operation = false;
|
||||
|
||||
using Name = Nothing;
|
||||
using Type = std::remove_cvref_t<T>;
|
||||
|
||||
dynamic::SelectFrom::Field operator()(const auto& _val) const {
|
||||
return dynamic::SelectFrom::Field{
|
||||
dynamic::Operation{.val = to_value(_val.val)}};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -57,6 +82,8 @@ struct MakeField<StructType, As<ValueType, _new_name>> {
|
||||
static constexpr bool is_aggregation =
|
||||
MakeField<StructType, ValueType>::is_aggregation;
|
||||
static constexpr bool is_column = MakeField<StructType, ValueType>::is_column;
|
||||
static constexpr bool is_operation =
|
||||
MakeField<StructType, ValueType>::is_operation;
|
||||
|
||||
using Name = Literal<_new_name>;
|
||||
using Type =
|
||||
@@ -64,132 +91,286 @@ struct MakeField<StructType, As<ValueType, _new_name>> {
|
||||
|
||||
dynamic::SelectFrom::Field operator()(const auto& _as) const {
|
||||
return dynamic::SelectFrom::Field{
|
||||
.val = MakeField<StructType, std::remove_cvref_t<ValueType>>{}(_as.val)
|
||||
.val,
|
||||
.val =
|
||||
dynamic::Operation{
|
||||
.val = MakeField<StructType, std::remove_cvref_t<ValueType>>{}(
|
||||
_as.val)
|
||||
.val.val},
|
||||
.as = _new_name.str()};
|
||||
}
|
||||
};
|
||||
|
||||
template <class StructType, rfl::internal::StringLiteral _name>
|
||||
struct MakeField<StructType, aggregations::Avg<Col<_name>>> {
|
||||
static_assert(all_columns_exist<StructType, Col<_name>>(),
|
||||
"A column required in the AVG aggregation does not exist.");
|
||||
|
||||
static_assert(
|
||||
std::is_integral_v<
|
||||
remove_nullable_t<underlying_t<StructType, Col<_name>>>> ||
|
||||
std::is_floating_point_v<
|
||||
remove_nullable_t<underlying_t<StructType, Col<_name>>>>,
|
||||
"Values inside the AVG aggregation must be numerical.");
|
||||
template <class StructType, AggregationOp _agg, class ValueType>
|
||||
struct MakeField<StructType, Aggregation<_agg, ValueType>> {
|
||||
static_assert(std::is_integral_v<
|
||||
remove_nullable_t<underlying_t<StructType, ValueType>>> ||
|
||||
std::is_floating_point_v<
|
||||
remove_nullable_t<underlying_t<StructType, ValueType>>>,
|
||||
"Values inside the aggregation must be numerical.");
|
||||
|
||||
static constexpr bool is_aggregation = true;
|
||||
static constexpr bool is_column = true;
|
||||
static constexpr bool is_operation = false;
|
||||
|
||||
using Name = Literal<_name>;
|
||||
using Type = rfl::field_type_t<_name, StructType>;
|
||||
using Name = Nothing;
|
||||
using Type =
|
||||
typename MakeField<StructType, std::remove_cvref_t<ValueType>>::Type;
|
||||
|
||||
dynamic::SelectFrom::Field operator()(const auto&) const {
|
||||
return dynamic::SelectFrom::Field{
|
||||
.val = dynamic::Aggregation{dynamic::Aggregation::Avg{
|
||||
.val = dynamic::Column{.name = _name.str()}}}};
|
||||
dynamic::SelectFrom::Field operator()(const auto& _val) const {
|
||||
using DynamicAggregationType = dynamic_aggregation_t<_agg>;
|
||||
return dynamic::SelectFrom::Field{dynamic::Operation{
|
||||
.val = dynamic::Aggregation{DynamicAggregationType{
|
||||
.val = Ref<dynamic::Operation>::make(
|
||||
MakeField<StructType, std::remove_cvref_t<ValueType>>{}(
|
||||
_val.val)
|
||||
.val)}}}};
|
||||
}
|
||||
};
|
||||
|
||||
template <class StructType, rfl::internal::StringLiteral _name>
|
||||
struct MakeField<StructType, aggregations::Count<Col<_name>>> {
|
||||
struct MakeField<StructType, Aggregation<AggregationOp::count, Col<_name>>> {
|
||||
static_assert(all_columns_exist<StructType, Col<_name>>(),
|
||||
"A column required in the COUNT or COUNT_DISTINCT aggregation "
|
||||
"does not exist.");
|
||||
|
||||
static constexpr bool is_aggregation = true;
|
||||
static constexpr bool is_column = true;
|
||||
static constexpr bool is_operation = false;
|
||||
|
||||
using Name = Literal<_name>;
|
||||
using Type = size_t;
|
||||
|
||||
dynamic::SelectFrom::Field operator()(const auto& _agg) const {
|
||||
return dynamic::SelectFrom::Field{
|
||||
return dynamic::SelectFrom::Field{dynamic::Operation{
|
||||
.val = dynamic::Aggregation{dynamic::Aggregation::Count{
|
||||
.val = dynamic::Column{.name = _name.str()},
|
||||
.distinct = _agg.distinct}},
|
||||
};
|
||||
.distinct = _agg.distinct}}}};
|
||||
}
|
||||
};
|
||||
|
||||
template <class StructType>
|
||||
struct MakeField<StructType, aggregations::Count<aggregations::All>> {
|
||||
struct MakeField<StructType, Aggregation<AggregationOp::count, All>> {
|
||||
static constexpr bool is_aggregation = true;
|
||||
static constexpr bool is_column = true;
|
||||
static constexpr bool is_operation = false;
|
||||
|
||||
using Name = Nothing;
|
||||
using Type = size_t;
|
||||
|
||||
dynamic::SelectFrom::Field operator()(const auto&) const {
|
||||
return dynamic::SelectFrom::Field{
|
||||
return dynamic::SelectFrom::Field{dynamic::Operation{
|
||||
.val = dynamic::Aggregation{
|
||||
dynamic::Aggregation::Count{.val = std::nullopt, .distinct = false},
|
||||
}};
|
||||
}}};
|
||||
}
|
||||
};
|
||||
|
||||
template <class StructType, rfl::internal::StringLiteral _name>
|
||||
struct MakeField<StructType, aggregations::Max<Col<_name>>> {
|
||||
static_assert(all_columns_exist<StructType, Col<_name>>(),
|
||||
"A column required in the MAX aggregation does not exist.");
|
||||
template <class StructType, class Operand1Type, class TargetType>
|
||||
struct MakeField<StructType, Operation<Operator::cast, Operand1Type,
|
||||
TypeHolder<TargetType>>> {
|
||||
static constexpr bool is_aggregation = false;
|
||||
static constexpr bool is_column = false;
|
||||
static constexpr bool is_operation = true;
|
||||
|
||||
static constexpr bool is_aggregation = true;
|
||||
static constexpr bool is_column = true;
|
||||
using Name = Nothing;
|
||||
using Type = std::remove_cvref_t<TargetType>;
|
||||
using Operands = rfl::Tuple<Operand1Type>;
|
||||
|
||||
using Name = Literal<_name>;
|
||||
using Type = rfl::field_type_t<_name, StructType>;
|
||||
|
||||
dynamic::SelectFrom::Field operator()(const auto&) const {
|
||||
dynamic::SelectFrom::Field operator()(const auto& _o) const {
|
||||
return dynamic::SelectFrom::Field{
|
||||
.val = dynamic::Aggregation{dynamic::Aggregation::Max{
|
||||
.val = dynamic::Column{.name = _name.str()}}}};
|
||||
dynamic::Operation{dynamic::Operation::Cast{
|
||||
.op1 = Ref<dynamic::Operation>::make(
|
||||
MakeField<StructType, std::remove_cvref_t<Operand1Type>>{}(
|
||||
_o.operand1)
|
||||
.val),
|
||||
.target_type =
|
||||
parsing::Parser<std::remove_cvref_t<TargetType>>::to_type()}}};
|
||||
}
|
||||
};
|
||||
|
||||
template <class StructType, rfl::internal::StringLiteral _name>
|
||||
struct MakeField<StructType, aggregations::Min<Col<_name>>> {
|
||||
static_assert(all_columns_exist<StructType, Col<_name>>(),
|
||||
"A column required in MIN aggregation does not exist.");
|
||||
template <class StructType, Operator _op, class... OperandTypes>
|
||||
requires((_op == Operator::coalesce) || (_op == Operator::concat))
|
||||
struct MakeField<StructType, Operation<_op, rfl::Tuple<OperandTypes...>>> {
|
||||
static constexpr bool is_aggregation = false;
|
||||
static constexpr bool is_column = false;
|
||||
static constexpr bool is_operation = true;
|
||||
|
||||
static constexpr bool is_aggregation = true;
|
||||
static constexpr bool is_column = true;
|
||||
using Name = Nothing;
|
||||
using Type =
|
||||
underlying_t<StructType, Operation<_op, rfl::Tuple<OperandTypes...>>>;
|
||||
using Operands = rfl::Tuple<OperandTypes...>;
|
||||
|
||||
using Name = Literal<_name>;
|
||||
using Type = rfl::field_type_t<_name, StructType>;
|
||||
|
||||
dynamic::SelectFrom::Field operator()(const auto&) const {
|
||||
return dynamic::SelectFrom::Field{
|
||||
.val = dynamic::Aggregation{dynamic::Aggregation::Min{
|
||||
.val = dynamic::Column{.name = _name.str()}}}};
|
||||
dynamic::SelectFrom::Field operator()(const auto& _o) const {
|
||||
using DynamicOperatorType = dynamic_operator_t<_op>;
|
||||
return dynamic::SelectFrom::Field{dynamic::Operation{DynamicOperatorType{
|
||||
.ops = rfl::apply(
|
||||
[](const auto&... _ops) {
|
||||
return std::vector<Ref<dynamic::Operation>>(
|
||||
{Ref<dynamic::Operation>::make(
|
||||
MakeField<StructType,
|
||||
std::remove_cvref_t<OperandTypes>>{}(_ops)
|
||||
.val)...});
|
||||
},
|
||||
_o.operand1)}}};
|
||||
}
|
||||
};
|
||||
|
||||
template <class StructType, rfl::internal::StringLiteral _name>
|
||||
struct MakeField<StructType, aggregations::Sum<Col<_name>>> {
|
||||
static_assert(all_columns_exist<StructType, Col<_name>>(),
|
||||
"A column required in SUM aggregation does not exist.");
|
||||
template <class StructType, class Operand1Type, class Operand2Type,
|
||||
class Operand3Type>
|
||||
struct MakeField<StructType, Operation<Operator::replace, Operand1Type,
|
||||
Operand2Type, Operand3Type>> {
|
||||
static constexpr bool is_aggregation = false;
|
||||
static constexpr bool is_column = false;
|
||||
static constexpr bool is_operation = true;
|
||||
|
||||
static_assert(
|
||||
std::is_integral_v<
|
||||
remove_nullable_t<underlying_t<StructType, Col<_name>>>> ||
|
||||
std::is_floating_point_v<
|
||||
remove_nullable_t<underlying_t<StructType, Col<_name>>>>,
|
||||
"Values inside the SUM aggregation must be numerical.");
|
||||
using Name = Nothing;
|
||||
using Type =
|
||||
underlying_t<StructType, Operation<Operator::replace, Operand1Type,
|
||||
Operand2Type, Operand3Type>>;
|
||||
using Operands = rfl::Tuple<Operand1Type, Operand2Type, Operand3Type>;
|
||||
|
||||
static constexpr bool is_aggregation = true;
|
||||
static constexpr bool is_column = true;
|
||||
|
||||
using Name = Literal<_name>;
|
||||
using Type = rfl::field_type_t<_name, StructType>;
|
||||
|
||||
dynamic::SelectFrom::Field operator()(const auto&) const {
|
||||
dynamic::SelectFrom::Field operator()(const auto& _o) const {
|
||||
return dynamic::SelectFrom::Field{
|
||||
.val = dynamic::Aggregation{dynamic::Aggregation::Sum{
|
||||
.val = dynamic::Column{.name = _name.str()}}}};
|
||||
dynamic::Operation{dynamic::Operation::Replace{
|
||||
.op1 = Ref<dynamic::Operation>::make(
|
||||
MakeField<StructType, std::remove_cvref_t<Operand1Type>>{}(
|
||||
_o.operand1)
|
||||
.val),
|
||||
.op2 = Ref<dynamic::Operation>::make(
|
||||
MakeField<StructType, std::remove_cvref_t<Operand2Type>>{}(
|
||||
_o.operand2)
|
||||
.val),
|
||||
.op3 = Ref<dynamic::Operation>::make(
|
||||
MakeField<StructType, std::remove_cvref_t<Operand3Type>>{}(
|
||||
_o.operand3)
|
||||
.val)}}};
|
||||
}
|
||||
};
|
||||
|
||||
template <class StructType, class Operand1Type, class Operand2Type>
|
||||
struct MakeField<StructType,
|
||||
Operation<Operator::round, Operand1Type, Operand2Type>> {
|
||||
static constexpr bool is_aggregation = false;
|
||||
static constexpr bool is_column = false;
|
||||
static constexpr bool is_operation = true;
|
||||
|
||||
using Name = Nothing;
|
||||
using Type =
|
||||
underlying_t<StructType,
|
||||
Operation<Operator::round, Operand1Type, Operand2Type>>;
|
||||
using Operands = rfl::Tuple<Operand1Type, Operand2Type>;
|
||||
|
||||
dynamic::SelectFrom::Field operator()(const auto& _o) const {
|
||||
return dynamic::SelectFrom::Field{
|
||||
dynamic::Operation{dynamic::Operation::Round{
|
||||
.op1 = Ref<dynamic::Operation>::make(
|
||||
MakeField<StructType, std::remove_cvref_t<Operand1Type>>{}(
|
||||
_o.operand1)
|
||||
.val),
|
||||
.op2 = Ref<dynamic::Operation>::make(
|
||||
MakeField<StructType, std::remove_cvref_t<Operand2Type>>{}(
|
||||
_o.operand2)
|
||||
.val)}}};
|
||||
}
|
||||
};
|
||||
|
||||
template <class StructType, Operator _op, class Operand1Type>
|
||||
requires((num_operands_v<_op>) == 1 &&
|
||||
(operator_category_v<_op>) == OperatorCategory::string)
|
||||
struct MakeField<StructType, Operation<_op, Operand1Type>> {
|
||||
static constexpr bool is_aggregation = false;
|
||||
static constexpr bool is_column = false;
|
||||
static constexpr bool is_operation = true;
|
||||
|
||||
using Name = Nothing;
|
||||
using Type = underlying_t<StructType, Operation<_op, Operand1Type>>;
|
||||
using Operands = rfl::Tuple<Operand1Type>;
|
||||
|
||||
dynamic::SelectFrom::Field operator()(const auto& _o) const {
|
||||
using DynamicOperatorType = dynamic_operator_t<_op>;
|
||||
return dynamic::SelectFrom::Field{dynamic::Operation{DynamicOperatorType{
|
||||
.op1 = Ref<dynamic::Operation>::make(
|
||||
MakeField<StructType, std::remove_cvref_t<Operand1Type>>{}(
|
||||
_o.operand1)
|
||||
.val)}}};
|
||||
}
|
||||
};
|
||||
|
||||
template <class StructType, Operator _op, class Operand1Type,
|
||||
class Operand2Type>
|
||||
requires((num_operands_v<_op>) == 2 &&
|
||||
(operator_category_v<_op>) == OperatorCategory::string)
|
||||
struct MakeField<StructType, Operation<_op, Operand1Type, Operand2Type>> {
|
||||
static constexpr bool is_aggregation = false;
|
||||
static constexpr bool is_column = false;
|
||||
static constexpr bool is_operation = true;
|
||||
|
||||
using Name = Nothing;
|
||||
using Type =
|
||||
underlying_t<StructType, Operation<_op, Operand1Type, Operand2Type>>;
|
||||
using Operands = rfl::Tuple<Operand1Type, Operand2Type>;
|
||||
|
||||
dynamic::SelectFrom::Field operator()(const auto& _o) const {
|
||||
using DynamicOperatorType = dynamic_operator_t<_op>;
|
||||
return dynamic::SelectFrom::Field{dynamic::Operation{DynamicOperatorType{
|
||||
.op1 = Ref<dynamic::Operation>::make(
|
||||
MakeField<StructType, std::remove_cvref_t<Operand1Type>>{}(
|
||||
_o.operand1)
|
||||
.val),
|
||||
.op2 = Ref<dynamic::Operation>::make(
|
||||
MakeField<StructType, std::remove_cvref_t<Operand2Type>>{}(
|
||||
_o.operand2)
|
||||
.val)}}};
|
||||
}
|
||||
};
|
||||
|
||||
template <class StructType, Operator _op, class Operand1Type>
|
||||
requires((num_operands_v<_op>) == 1 &&
|
||||
(operator_category_v<_op>) == OperatorCategory::numerical)
|
||||
struct MakeField<StructType, Operation<_op, Operand1Type>> {
|
||||
static constexpr bool is_aggregation = false;
|
||||
static constexpr bool is_column = false;
|
||||
static constexpr bool is_operation = true;
|
||||
|
||||
using Name = Nothing;
|
||||
using Type = underlying_t<StructType, Operation<_op, Operand1Type>>;
|
||||
using Operands = rfl::Tuple<Operand1Type>;
|
||||
|
||||
dynamic::SelectFrom::Field operator()(const auto& _o) const {
|
||||
using DynamicOperatorType = dynamic_operator_t<_op>;
|
||||
return dynamic::SelectFrom::Field{dynamic::Operation{DynamicOperatorType{
|
||||
.op1 = Ref<dynamic::Operation>::make(
|
||||
MakeField<StructType, std::remove_cvref_t<Operand1Type>>{}(
|
||||
_o.operand1)
|
||||
.val)}}};
|
||||
}
|
||||
};
|
||||
|
||||
template <class StructType, Operator _op, class Operand1Type,
|
||||
class Operand2Type>
|
||||
requires((num_operands_v<_op>) == 2 &&
|
||||
(operator_category_v<_op>) == OperatorCategory::numerical)
|
||||
struct MakeField<StructType, Operation<_op, Operand1Type, Operand2Type>> {
|
||||
static constexpr bool is_aggregation = false;
|
||||
static constexpr bool is_column = false;
|
||||
static constexpr bool is_operation = true;
|
||||
|
||||
using Name = Nothing;
|
||||
using Type =
|
||||
underlying_t<StructType, Operation<_op, Operand1Type, Operand2Type>>;
|
||||
using Operands = rfl::Tuple<Operand1Type, Operand2Type>;
|
||||
|
||||
dynamic::SelectFrom::Field operator()(const auto& _o) const {
|
||||
using DynamicOperatorType = dynamic_operator_t<_op>;
|
||||
return dynamic::SelectFrom::Field{dynamic::Operation{DynamicOperatorType{
|
||||
.op1 = Ref<dynamic::Operation>::make(
|
||||
MakeField<StructType, std::remove_cvref_t<Operand1Type>>{}(
|
||||
_o.operand1)
|
||||
.val),
|
||||
.op2 = Ref<dynamic::Operation>::make(
|
||||
MakeField<StructType, std::remove_cvref_t<Operand2Type>>{}(
|
||||
_o.operand2)
|
||||
.val)}}};
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
#include "Condition.hpp"
|
||||
#include "all_columns_exist.hpp"
|
||||
#include "conditions.hpp"
|
||||
#include "to_value.hpp"
|
||||
#include "make_field.hpp"
|
||||
#include "to_transpilation_type.hpp"
|
||||
#include "underlying_t.hpp"
|
||||
|
||||
namespace sqlgen::transpilation {
|
||||
@@ -40,245 +41,135 @@ struct ToCondition<T, conditions::And<CondType1, CondType2>> {
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, rfl::internal::StringLiteral _name1,
|
||||
rfl::internal::StringLiteral _name2>
|
||||
struct ToCondition<T, conditions::Equal<Col<_name1>, Col<_name2>>> {
|
||||
static_assert(all_columns_exist<T, Col<_name1>, Col<_name2>>(),
|
||||
"All columns must exist.");
|
||||
static_assert(std::equality_comparable_with<underlying_t<T, Col<_name1>>,
|
||||
underlying_t<T, Col<_name2>>>,
|
||||
template <class T, class Op1Type, class Op2Type>
|
||||
struct ToCondition<T, conditions::Equal<Op1Type, Op2Type>> {
|
||||
static_assert(std::equality_comparable_with<underlying_t<T, Op1Type>,
|
||||
underlying_t<T, Op2Type>>,
|
||||
"Must be equality comparable.");
|
||||
|
||||
dynamic::Condition operator()(const auto& _cond) const {
|
||||
return dynamic::Condition{.val = dynamic::Condition::Equal{
|
||||
.op1 = dynamic::Column{.name = _name1.str()},
|
||||
.op2 = dynamic::Column{.name = _name2.str()},
|
||||
}};
|
||||
return dynamic::Condition{
|
||||
.val = dynamic::Condition::Equal{.op1 = make_field<T>(_cond.op1).val,
|
||||
.op2 = make_field<T>(_cond.op2).val}};
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, rfl::internal::StringLiteral _name, class V>
|
||||
struct ToCondition<T, conditions::Equal<Col<_name>, Value<V>>> {
|
||||
static_assert(all_columns_exist<T, Col<_name>>(), "All columns must exist.");
|
||||
static_assert(std::equality_comparable_with<underlying_t<T, Col<_name>>,
|
||||
underlying_t<T, Value<V>>>,
|
||||
"Must be equality comparable.");
|
||||
|
||||
dynamic::Condition operator()(const auto& _cond) const {
|
||||
return dynamic::Condition{.val = dynamic::Condition::Equal{
|
||||
.op1 = dynamic::Column{.name = _name.str()},
|
||||
.op2 = to_value(_cond.op2.val),
|
||||
}};
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, rfl::internal::StringLiteral _name1,
|
||||
rfl::internal::StringLiteral _name2>
|
||||
struct ToCondition<T, conditions::GreaterEqual<Col<_name1>, Col<_name2>>> {
|
||||
static_assert(all_columns_exist<T, Col<_name1>, Col<_name2>>(),
|
||||
"All columns must exist.");
|
||||
static_assert(std::totally_ordered_with<underlying_t<T, Col<_name1>>,
|
||||
underlying_t<T, Col<_name2>>>,
|
||||
template <class T, class Op1Type, class Op2Type>
|
||||
struct ToCondition<T, conditions::GreaterEqual<Op1Type, Op2Type>> {
|
||||
static_assert(std::totally_ordered_with<underlying_t<T, Op1Type>,
|
||||
underlying_t<T, Op2Type>>,
|
||||
"Must be totally ordered.");
|
||||
|
||||
dynamic::Condition operator()(const auto& _cond) const {
|
||||
return dynamic::Condition{.val = dynamic::Condition::GreaterEqual{
|
||||
.op1 = dynamic::Column{.name = _name1.str()},
|
||||
.op2 = dynamic::Column{.name = _name2.str()},
|
||||
}};
|
||||
.op1 = make_field<T>(_cond.op1).val,
|
||||
.op2 = make_field<T>(_cond.op2).val}};
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, rfl::internal::StringLiteral _name, class V>
|
||||
struct ToCondition<T, conditions::GreaterEqual<Col<_name>, Value<V>>> {
|
||||
static_assert(all_columns_exist<T, Col<_name>>(), "All columns must exist.");
|
||||
static_assert(std::totally_ordered_with<underlying_t<T, Col<_name>>,
|
||||
underlying_t<T, Value<V>>>,
|
||||
"Must be totally ordered.");
|
||||
|
||||
dynamic::Condition operator()(const auto& _cond) const {
|
||||
return dynamic::Condition{.val = dynamic::Condition::GreaterEqual{
|
||||
.op1 = dynamic::Column{.name = _name.str()},
|
||||
.op2 = to_value(_cond.op2.val),
|
||||
}};
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, rfl::internal::StringLiteral _name1,
|
||||
rfl::internal::StringLiteral _name2>
|
||||
struct ToCondition<T, conditions::GreaterThan<Col<_name1>, Col<_name2>>> {
|
||||
static_assert(all_columns_exist<T, Col<_name1>, Col<_name2>>(),
|
||||
"All columns must exist.");
|
||||
static_assert(std::totally_ordered_with<underlying_t<T, Col<_name1>>,
|
||||
underlying_t<T, Col<_name2>>>,
|
||||
template <class T, class Op1Type, class Op2Type>
|
||||
struct ToCondition<T, conditions::GreaterThan<Op1Type, Op2Type>> {
|
||||
static_assert(std::totally_ordered_with<underlying_t<T, Op1Type>,
|
||||
underlying_t<T, Op2Type>>,
|
||||
"Must be totally ordered.");
|
||||
|
||||
dynamic::Condition operator()(const auto& _cond) const {
|
||||
return dynamic::Condition{.val = dynamic::Condition::GreaterThan{
|
||||
.op1 = dynamic::Column{.name = _name1.str()},
|
||||
.op2 = dynamic::Column{.name = _name2.str()},
|
||||
}};
|
||||
.op1 = make_field<T>(_cond.op1).val,
|
||||
.op2 = make_field<T>(_cond.op2).val}};
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, rfl::internal::StringLiteral _name, class V>
|
||||
struct ToCondition<T, conditions::GreaterThan<Col<_name>, Value<V>>> {
|
||||
static_assert(all_columns_exist<T, Col<_name>>(), "All columns must exist.");
|
||||
static_assert(std::totally_ordered_with<underlying_t<T, Col<_name>>,
|
||||
underlying_t<T, Value<V>>>,
|
||||
"Must be totally ordered.");
|
||||
|
||||
dynamic::Condition operator()(const auto& _cond) const {
|
||||
return dynamic::Condition{.val = dynamic::Condition::GreaterThan{
|
||||
.op1 = dynamic::Column{.name = _name.str()},
|
||||
.op2 = to_value(_cond.op2.val),
|
||||
}};
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, rfl::internal::StringLiteral _name1,
|
||||
rfl::internal::StringLiteral _name2>
|
||||
struct ToCondition<T, conditions::LesserEqual<Col<_name1>, Col<_name2>>> {
|
||||
static_assert(all_columns_exist<T, Col<_name1>, Col<_name2>>(),
|
||||
"All columns must exist.");
|
||||
static_assert(std::totally_ordered_with<underlying_t<T, Col<_name1>>,
|
||||
underlying_t<T, Col<_name2>>>,
|
||||
template <class T, class Op1Type, class Op2Type>
|
||||
struct ToCondition<T, conditions::LesserEqual<Op1Type, Op2Type>> {
|
||||
static_assert(std::totally_ordered_with<underlying_t<T, Op1Type>,
|
||||
underlying_t<T, Op2Type>>,
|
||||
"Must be totally ordered.");
|
||||
|
||||
dynamic::Condition operator()(const auto& _cond) const {
|
||||
return dynamic::Condition{.val = dynamic::Condition::LesserEqual{
|
||||
.op1 = dynamic::Column{.name = _name1.str()},
|
||||
.op2 = dynamic::Column{.name = _name2.str()},
|
||||
}};
|
||||
.op1 = make_field<T>(_cond.op1).val,
|
||||
.op2 = make_field<T>(_cond.op2).val}};
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, rfl::internal::StringLiteral _name, class V>
|
||||
struct ToCondition<T, conditions::LesserEqual<Col<_name>, Value<V>>> {
|
||||
static_assert(all_columns_exist<T, Col<_name>>(), "All columns must exist.");
|
||||
static_assert(std::totally_ordered_with<underlying_t<T, Col<_name>>,
|
||||
underlying_t<T, Value<V>>>,
|
||||
"Must be totally ordered.");
|
||||
|
||||
dynamic::Condition operator()(const auto& _cond) const {
|
||||
return dynamic::Condition{.val = dynamic::Condition::LesserEqual{
|
||||
.op1 = dynamic::Column{.name = _name.str()},
|
||||
.op2 = to_value(_cond.op2.val),
|
||||
}};
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, rfl::internal::StringLiteral _name1,
|
||||
rfl::internal::StringLiteral _name2>
|
||||
struct ToCondition<T, conditions::LesserThan<Col<_name1>, Col<_name2>>> {
|
||||
static_assert(all_columns_exist<T, Col<_name1>, Col<_name2>>(),
|
||||
"All columns must exist.");
|
||||
static_assert(std::totally_ordered_with<underlying_t<T, Col<_name1>>,
|
||||
underlying_t<T, Col<_name2>>>,
|
||||
template <class T, class Op1Type, class Op2Type>
|
||||
struct ToCondition<T, conditions::LesserThan<Op1Type, Op2Type>> {
|
||||
static_assert(std::totally_ordered_with<underlying_t<T, Op1Type>,
|
||||
underlying_t<T, Op2Type>>,
|
||||
"Must be totally ordered.");
|
||||
|
||||
dynamic::Condition operator()(const auto& _cond) const {
|
||||
return dynamic::Condition{.val = dynamic::Condition::LesserThan{
|
||||
.op1 = dynamic::Column{.name = _name1.str()},
|
||||
.op2 = dynamic::Column{.name = _name2.str()},
|
||||
}};
|
||||
.op1 = make_field<T>(_cond.op1).val,
|
||||
.op2 = make_field<T>(_cond.op2).val}};
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, rfl::internal::StringLiteral _name, class V>
|
||||
struct ToCondition<T, conditions::LesserThan<Col<_name>, Value<V>>> {
|
||||
static_assert(all_columns_exist<T, Col<_name>>(), "All columns must exist.");
|
||||
static_assert(std::totally_ordered_with<underlying_t<T, Col<_name>>,
|
||||
underlying_t<T, Value<V>>>,
|
||||
"Must be totally ordered.");
|
||||
|
||||
dynamic::Condition operator()(const auto& _cond) const {
|
||||
return dynamic::Condition{.val = dynamic::Condition::LesserThan{
|
||||
.op1 = dynamic::Column{.name = _name.str()},
|
||||
.op2 = to_value(_cond.op2.val),
|
||||
}};
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, rfl::internal::StringLiteral _name>
|
||||
struct ToCondition<T, conditions::Like<Col<_name>>> {
|
||||
static_assert(all_columns_exist<T, Col<_name>>(), "All columns must exist.");
|
||||
template <class T, class OpType>
|
||||
struct ToCondition<T, conditions::Like<OpType>> {
|
||||
static_assert(
|
||||
std::equality_comparable_with<underlying_t<T, Col<_name>>,
|
||||
std::equality_comparable_with<underlying_t<T, OpType>,
|
||||
underlying_t<T, Value<std::string>>>,
|
||||
"Must be equality comparable with a string.");
|
||||
|
||||
dynamic::Condition operator()(const auto& _cond) const {
|
||||
return dynamic::Condition{.val = dynamic::Condition::Like{
|
||||
.op = dynamic::Column{.name = _name.str()},
|
||||
.pattern = to_value(_cond.pattern)}};
|
||||
return dynamic::Condition{
|
||||
.val = dynamic::Condition::Like{.op = make_field<T>(_cond.op).val,
|
||||
.pattern = to_value(_cond.pattern)}};
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, rfl::internal::StringLiteral _name>
|
||||
struct ToCondition<T, conditions::IsNotNull<Col<_name>>> {
|
||||
static_assert(all_columns_exist<T, Col<_name>>(), "All columns must exist.");
|
||||
|
||||
dynamic::Condition operator()(const auto&) const {
|
||||
template <class T, class OpType>
|
||||
struct ToCondition<T, conditions::IsNotNull<OpType>> {
|
||||
dynamic::Condition operator()(const auto& _cond) const {
|
||||
return dynamic::Condition{.val = dynamic::Condition::IsNotNull{
|
||||
.op = dynamic::Column{.name = _name.str()}}};
|
||||
.op = make_field<T>(_cond.op).val}};
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, rfl::internal::StringLiteral _name>
|
||||
struct ToCondition<T, conditions::IsNull<Col<_name>>> {
|
||||
static_assert(all_columns_exist<T, Col<_name>>(), "All columns must exist.");
|
||||
|
||||
dynamic::Condition operator()(const auto&) const {
|
||||
return dynamic::Condition{.val = dynamic::Condition::IsNull{
|
||||
.op = dynamic::Column{.name = _name.str()}}};
|
||||
template <class T, class OpType>
|
||||
struct ToCondition<T, conditions::IsNull<OpType>> {
|
||||
dynamic::Condition operator()(const auto& _cond) const {
|
||||
return dynamic::Condition{
|
||||
.val = dynamic::Condition::IsNull{.op = make_field<T>(_cond.op).val}};
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, rfl::internal::StringLiteral _name1,
|
||||
rfl::internal::StringLiteral _name2>
|
||||
struct ToCondition<T, conditions::NotEqual<Col<_name1>, Col<_name2>>> {
|
||||
static_assert(all_columns_exist<T, Col<_name1>, Col<_name2>>(),
|
||||
"All columns must exist.");
|
||||
static_assert(std::equality_comparable_with<underlying_t<T, Col<_name1>>,
|
||||
underlying_t<T, Col<_name2>>>,
|
||||
template <class T, class CondType>
|
||||
struct ToCondition<T, conditions::Not<CondType>> {
|
||||
dynamic::Condition operator()(const auto& _cond) const {
|
||||
return dynamic::Condition{
|
||||
.val = dynamic::Condition::Not{
|
||||
.cond = Ref<dynamic::Condition>::make(
|
||||
ToCondition<T, std::remove_cvref_t<CondType>>{}(_cond.cond))}};
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, class Op1Type, class Op2Type>
|
||||
struct ToCondition<T, conditions::NotEqual<Op1Type, Op2Type>> {
|
||||
static_assert(std::equality_comparable_with<underlying_t<T, Op1Type>,
|
||||
underlying_t<T, Op2Type>>,
|
||||
"Must be equality comparable.");
|
||||
|
||||
dynamic::Condition operator()(const auto& _cond) const {
|
||||
return dynamic::Condition{.val = dynamic::Condition::NotEqual{
|
||||
.op1 = dynamic::Column{.name = _name1.str()},
|
||||
.op2 = dynamic::Column{.name = _name2.str()},
|
||||
}};
|
||||
.op1 = make_field<T>(_cond.op1).val,
|
||||
.op2 = make_field<T>(_cond.op2).val}};
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, rfl::internal::StringLiteral _name, class V>
|
||||
struct ToCondition<T, conditions::NotEqual<Col<_name>, Value<V>>> {
|
||||
static_assert(all_columns_exist<T, Col<_name>>(), "All columns must exist.");
|
||||
static_assert(std::equality_comparable_with<underlying_t<T, Col<_name>>,
|
||||
underlying_t<T, Value<V>>>,
|
||||
"Must be equality comparable.");
|
||||
|
||||
dynamic::Condition operator()(const auto& _cond) const {
|
||||
return dynamic::Condition{.val = dynamic::Condition::NotEqual{
|
||||
.op1 = dynamic::Column{.name = _name.str()},
|
||||
.op2 = to_value(_cond.op2.val),
|
||||
}};
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, rfl::internal::StringLiteral _name>
|
||||
struct ToCondition<T, conditions::NotLike<Col<_name>>> {
|
||||
static_assert(all_columns_exist<T, Col<_name>>(), "All columns must exist.");
|
||||
template <class T, class OpType>
|
||||
struct ToCondition<T, conditions::NotLike<OpType>> {
|
||||
static_assert(
|
||||
std::equality_comparable_with<underlying_t<T, Col<_name>>,
|
||||
std::equality_comparable_with<underlying_t<T, OpType>,
|
||||
underlying_t<T, Value<std::string>>>,
|
||||
"Must be equality comparable with a string.");
|
||||
|
||||
dynamic::Condition operator()(const auto& _cond) const {
|
||||
return dynamic::Condition{.val = dynamic::Condition::NotLike{
|
||||
.op = dynamic::Column{.name = _name.str()},
|
||||
.pattern = to_value(_cond.pattern)}};
|
||||
return dynamic::Condition{
|
||||
.val = dynamic::Condition::NotLike{.op = make_field<T>(_cond.op).val,
|
||||
.pattern = to_value(_cond.pattern)}};
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "../dynamic/Table.hpp"
|
||||
#include "../internal/collect/vector.hpp"
|
||||
#include "check_aggregations.hpp"
|
||||
#include "flatten_fields_t.hpp"
|
||||
#include "get_schema.hpp"
|
||||
#include "get_tablename.hpp"
|
||||
#include "make_fields.hpp"
|
||||
@@ -31,9 +32,11 @@ template <class StructType, class FieldsType, class WhereType,
|
||||
dynamic::SelectFrom to_select_from(const FieldsType& _fields,
|
||||
const WhereType& _where,
|
||||
const LimitType& _limit) {
|
||||
static_assert(check_aggregations<StructType, FieldsType, GroupByType>(),
|
||||
"The aggregations were not set up correctly. Please check the "
|
||||
"trace for a more detailed error message.");
|
||||
static_assert(
|
||||
check_aggregations<StructType, flatten_fields_t<StructType, FieldsType>,
|
||||
GroupByType>(),
|
||||
"The aggregations were not set up correctly. Please check the "
|
||||
"trace for a more detailed error message.");
|
||||
|
||||
const auto fields = make_fields<StructType, FieldsType>(
|
||||
_fields,
|
||||
|
||||
@@ -77,9 +77,9 @@ struct ToSQL<Read<ContainerType, WhereType, OrderByType, LimitType>> {
|
||||
};
|
||||
|
||||
template <class StructType, class FieldsType, class WhereType,
|
||||
class GroupByType, class OrderByType, class LimitType>
|
||||
class GroupByType, class OrderByType, class LimitType, class ToType>
|
||||
struct ToSQL<SelectFrom<StructType, FieldsType, WhereType, GroupByType,
|
||||
OrderByType, LimitType>> {
|
||||
OrderByType, LimitType, ToType>> {
|
||||
dynamic::Statement operator()(const auto& _select_from) const {
|
||||
return to_select_from<StructType, FieldsType, WhereType, GroupByType,
|
||||
OrderByType, LimitType>(
|
||||
|
||||
46
include/sqlgen/transpilation/to_transpilation_type.hpp
Normal file
46
include/sqlgen/transpilation/to_transpilation_type.hpp
Normal file
@@ -0,0 +1,46 @@
|
||||
#ifndef SQLGEN_TRANSPILATION_TO_TRANSPILATION_TYPE_HPP_
|
||||
#define SQLGEN_TRANSPILATION_TO_TRANSPILATION_TYPE_HPP_
|
||||
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
#include "Value.hpp"
|
||||
|
||||
namespace sqlgen::transpilation {
|
||||
|
||||
template <class T>
|
||||
struct ToTranspilationType;
|
||||
|
||||
template <class T>
|
||||
struct ToTranspilationType {
|
||||
using Type = Value<T>;
|
||||
|
||||
Type operator()(const T& _val) const noexcept { return make_value(_val); }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct ToTranspilationType<const char*> {
|
||||
using Type = Value<std::string>;
|
||||
|
||||
Type operator()(const char* _val) const noexcept { return make_value(_val); }
|
||||
};
|
||||
|
||||
template <int _length>
|
||||
struct ToTranspilationType<char[_length]> {
|
||||
using Type = Value<std::string>;
|
||||
|
||||
Type operator()(const char* _val) const noexcept { return make_value(_val); }
|
||||
};
|
||||
|
||||
template <class T>
|
||||
auto to_transpilation_type(const T& _t) {
|
||||
return ToTranspilationType<std::remove_cvref_t<T>>{}(_t);
|
||||
}
|
||||
|
||||
inline auto to_transpilation_type(const char* _t) {
|
||||
return ToTranspilationType<const char*>{}(_t);
|
||||
}
|
||||
|
||||
} // namespace sqlgen::transpilation
|
||||
|
||||
#endif
|
||||
@@ -14,13 +14,13 @@ template <class T>
|
||||
dynamic::Value to_value(const T& _t) {
|
||||
using Type = std::remove_cvref_t<T>;
|
||||
if constexpr (std::is_floating_point_v<Type>) {
|
||||
return dynamic::Float{.val = static_cast<double>(_t)};
|
||||
return dynamic::Value{dynamic::Float{.val = static_cast<double>(_t)}};
|
||||
|
||||
} else if constexpr (std::is_integral_v<Type>) {
|
||||
return dynamic::Integer{.val = static_cast<int64_t>(_t)};
|
||||
return dynamic::Value{dynamic::Integer{.val = static_cast<int64_t>(_t)}};
|
||||
|
||||
} else if constexpr (std::is_convertible_v<Type, std::string>) {
|
||||
return dynamic::String{.val = std::string(_t)};
|
||||
return dynamic::Value{dynamic::String{.val = std::string(_t)}};
|
||||
|
||||
} else if constexpr (has_reflection_method<Type>) {
|
||||
return to_value(_t.reflection());
|
||||
|
||||
@@ -2,11 +2,19 @@
|
||||
#define SQLGEN_TRANSPILATION_UNDERLYINGT_HPP_
|
||||
|
||||
#include <rfl.hpp>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
#include "Aggregation.hpp"
|
||||
#include "AggregationOp.hpp"
|
||||
#include "Col.hpp"
|
||||
#include "Desc.hpp"
|
||||
#include "Operation.hpp"
|
||||
#include "Value.hpp"
|
||||
#include "all_columns_exist.hpp"
|
||||
#include "dynamic_operator_t.hpp"
|
||||
#include "is_nullable.hpp"
|
||||
#include "remove_nullable_t.hpp"
|
||||
#include "remove_reflection_t.hpp"
|
||||
|
||||
namespace sqlgen::transpilation {
|
||||
@@ -14,8 +22,14 @@ namespace sqlgen::transpilation {
|
||||
template <class T, class _Type>
|
||||
struct Underlying;
|
||||
|
||||
template <class T, AggregationOp _op, class ValueType>
|
||||
struct Underlying<T, Aggregation<_op, ValueType>> {
|
||||
using Type = typename Underlying<T, std::remove_cvref_t<ValueType>>::Type;
|
||||
};
|
||||
|
||||
template <class T, rfl::internal::StringLiteral _name>
|
||||
struct Underlying<T, Col<_name>> {
|
||||
static_assert(all_columns_exist<T, Col<_name>>(), "All columns must exist.");
|
||||
using Type = remove_reflection_t<rfl::field_type_t<_name, T>>;
|
||||
};
|
||||
|
||||
@@ -24,6 +38,162 @@ struct Underlying<T, Desc<Col<_name>>> {
|
||||
using Type = remove_reflection_t<rfl::field_type_t<_name, T>>;
|
||||
};
|
||||
|
||||
template <class T, class Operand1Type, class TargetType>
|
||||
struct Underlying<
|
||||
T, Operation<Operator::cast, Operand1Type, TypeHolder<TargetType>>> {
|
||||
using Type =
|
||||
std::conditional_t<is_nullable_v<typename Underlying<
|
||||
T, std::remove_cvref_t<Operand1Type>>::Type>,
|
||||
std::optional<std::remove_cvref_t<TargetType>>,
|
||||
std::remove_cvref_t<TargetType>>;
|
||||
};
|
||||
|
||||
template <class T, class Head, class... Tail>
|
||||
struct Underlying<T, Operation<Operator::coalesce, rfl::Tuple<Head, Tail...>>> {
|
||||
using Operand1Type = typename Underlying<T, std::remove_cvref_t<Head>>::Type;
|
||||
|
||||
static_assert((true && ... &&
|
||||
std::is_same_v<remove_nullable_t<Operand1Type>,
|
||||
remove_nullable_t<typename Underlying<
|
||||
T, std::remove_cvref_t<Tail>>::Type>>),
|
||||
"All inputs into coalesce(...) must have the same type.");
|
||||
|
||||
using Type = std::conditional_t<
|
||||
(is_nullable_v<Operand1Type> && ... &&
|
||||
is_nullable_v<typename Underlying<T, std::remove_cvref_t<Tail>>::Type>),
|
||||
std::optional<remove_nullable_t<Operand1Type>>,
|
||||
remove_nullable_t<Operand1Type>>;
|
||||
};
|
||||
|
||||
template <class T, class... OperandTypes>
|
||||
struct Underlying<T, Operation<Operator::concat, rfl::Tuple<OperandTypes...>>> {
|
||||
static_assert(
|
||||
(true && ... &&
|
||||
std::is_same_v<remove_nullable_t<typename Underlying<
|
||||
T, std::remove_cvref_t<OperandTypes>>::Type>,
|
||||
std::string>),
|
||||
"Must be a string");
|
||||
|
||||
using Type =
|
||||
std::conditional_t<(false || ... ||
|
||||
is_nullable_v<typename Underlying<
|
||||
T, std::remove_cvref_t<OperandTypes>>::Type>),
|
||||
std::optional<std::string>, std::string>;
|
||||
};
|
||||
|
||||
template <class T, class Operand1Type, class Operand2Type, class Operand3Type>
|
||||
struct Underlying<
|
||||
T, Operation<Operator::replace, Operand1Type, Operand2Type, Operand3Type>> {
|
||||
using Underlying1 =
|
||||
typename Underlying<T, std::remove_cvref_t<Operand1Type>>::Type;
|
||||
using Underlying2 =
|
||||
typename Underlying<T, std::remove_cvref_t<Operand2Type>>::Type;
|
||||
using Underlying3 =
|
||||
typename Underlying<T, std::remove_cvref_t<Operand3Type>>::Type;
|
||||
|
||||
static_assert(std::is_same_v<remove_nullable_t<Underlying1>, std::string>,
|
||||
"Must be a string");
|
||||
static_assert(std::is_same_v<remove_nullable_t<Underlying2>, std::string>,
|
||||
"Must be a string");
|
||||
static_assert(std::is_same_v<remove_nullable_t<Underlying3>, std::string>,
|
||||
"Must be a string");
|
||||
|
||||
using Type = std::conditional_t<is_nullable_v<Underlying1> ||
|
||||
is_nullable_v<Underlying2> ||
|
||||
is_nullable_v<Underlying3>,
|
||||
std::optional<std::string>, std::string>;
|
||||
};
|
||||
|
||||
template <class T, class Operand1Type, class Operand2Type>
|
||||
struct Underlying<T, Operation<Operator::round, Operand1Type, Operand2Type>> {
|
||||
using Underlying1 =
|
||||
typename Underlying<T, std::remove_cvref_t<Operand1Type>>::Type;
|
||||
using Underlying2 =
|
||||
typename Underlying<T, std::remove_cvref_t<Operand2Type>>::Type;
|
||||
|
||||
static_assert(std::is_integral_v<remove_nullable_t<Underlying1>> ||
|
||||
std::is_floating_point_v<remove_nullable_t<Underlying1>>,
|
||||
"Must be a numerical type");
|
||||
static_assert(std::is_integral_v<Underlying2>, "Must be an integral type");
|
||||
|
||||
using Type = Underlying1;
|
||||
};
|
||||
|
||||
template <class T, Operator _op, class Operand1Type>
|
||||
requires((num_operands_v<_op>) == 1 &&
|
||||
(operator_category_v<_op>) == OperatorCategory::string)
|
||||
struct Underlying<T, Operation<_op, Operand1Type>> {
|
||||
using Underlying1 =
|
||||
typename Underlying<T, std::remove_cvref_t<Operand1Type>>::Type;
|
||||
|
||||
static_assert(std::is_same_v<remove_nullable_t<Underlying1>, std::string>,
|
||||
"Must be a string");
|
||||
|
||||
using StringType =
|
||||
std::conditional_t<is_nullable_v<Underlying1>, std::optional<std::string>,
|
||||
std::string>;
|
||||
using SizeType = std::conditional_t<is_nullable_v<Underlying1>,
|
||||
std::optional<size_t>, size_t>;
|
||||
using Type =
|
||||
std::conditional_t<_op == Operator::length, SizeType, StringType>;
|
||||
};
|
||||
|
||||
template <class T, Operator _op, class Operand1Type, class Operand2Type>
|
||||
requires((num_operands_v<_op>) == 2 &&
|
||||
(operator_category_v<_op>) == OperatorCategory::string)
|
||||
struct Underlying<T, Operation<_op, Operand1Type, Operand2Type>> {
|
||||
using Underlying1 =
|
||||
typename Underlying<T, std::remove_cvref_t<Operand1Type>>::Type;
|
||||
using Underlying2 =
|
||||
typename Underlying<T, std::remove_cvref_t<Operand2Type>>::Type;
|
||||
|
||||
static_assert(std::is_same_v<remove_nullable_t<Underlying1>, std::string>,
|
||||
"Must be a string");
|
||||
static_assert(std::is_same_v<remove_nullable_t<Underlying2>, std::string>,
|
||||
"Must be a string");
|
||||
|
||||
using Type = std::conditional_t<is_nullable_v<Underlying1> ||
|
||||
is_nullable_v<Underlying2>,
|
||||
std::optional<std::string>, std::string>;
|
||||
};
|
||||
|
||||
template <class T, Operator _op, class Operand1Type>
|
||||
requires((num_operands_v<_op>) == 1 &&
|
||||
(operator_category_v<_op>) == OperatorCategory::numerical)
|
||||
struct Underlying<T, Operation<_op, Operand1Type>> {
|
||||
using Underlying1 =
|
||||
typename Underlying<T, std::remove_cvref_t<Operand1Type>>::Type;
|
||||
|
||||
static_assert(std::is_integral_v<remove_nullable_t<Underlying1>> ||
|
||||
std::is_floating_point_v<remove_nullable_t<Underlying1>>,
|
||||
"Must be a numerical type");
|
||||
|
||||
using Type = Underlying1;
|
||||
};
|
||||
|
||||
template <class T, Operator _op, class Operand1Type, class Operand2Type>
|
||||
requires((num_operands_v<_op>) == 2 &&
|
||||
(operator_category_v<_op>) == OperatorCategory::numerical)
|
||||
struct Underlying<T, Operation<_op, Operand1Type, Operand2Type>> {
|
||||
using Underlying1 =
|
||||
typename Underlying<T, std::remove_cvref_t<Operand1Type>>::Type;
|
||||
using Underlying2 =
|
||||
typename Underlying<T, std::remove_cvref_t<Operand2Type>>::Type;
|
||||
|
||||
static_assert(
|
||||
requires(remove_nullable_t<Underlying1> op1,
|
||||
remove_nullable_t<Underlying2> op2) { op1 + op2; },
|
||||
"Binary operations are not possible on these types.");
|
||||
|
||||
using ResultType = std::invoke_result_t<
|
||||
decltype([](const auto& op1, const auto& op2) { return op1 + op2; }),
|
||||
remove_nullable_t<Underlying1>, remove_nullable_t<Underlying2>>;
|
||||
|
||||
using Type = std::conditional_t<is_nullable_v<Underlying1> ||
|
||||
is_nullable_v<Underlying2>,
|
||||
std::optional<ResultType>, ResultType>;
|
||||
};
|
||||
|
||||
template <class T, class _Type>
|
||||
struct Underlying<T, Value<_Type>> {
|
||||
using Type = _Type;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
|
||||
#include "sqlgen/dynamic/Operation.hpp"
|
||||
#include "sqlgen/internal/collect/vector.hpp"
|
||||
#include "sqlgen/internal/strings/strings.hpp"
|
||||
|
||||
@@ -43,6 +44,8 @@ std::vector<std::string> get_primary_keys(
|
||||
|
||||
std::string insert_to_sql(const dynamic::Insert& _stmt) noexcept;
|
||||
|
||||
std::string operation_to_sql(const dynamic::Operation& _stmt) noexcept;
|
||||
|
||||
std::string select_from_to_sql(const dynamic::SelectFrom& _stmt) noexcept;
|
||||
|
||||
std::string type_to_sql(const dynamic::Type& _type) noexcept;
|
||||
@@ -69,33 +72,29 @@ std::string add_not_null_if_necessary(
|
||||
std::string aggregation_to_sql(
|
||||
const dynamic::Aggregation& _aggregation) noexcept {
|
||||
return _aggregation.val.visit([](const auto& _agg) -> std::string {
|
||||
std::stringstream stream;
|
||||
|
||||
using Type = std::remove_cvref_t<decltype(_agg)>;
|
||||
|
||||
std::stringstream stream;
|
||||
if constexpr (std::is_same_v<Type, dynamic::Aggregation::Avg>) {
|
||||
stream << "AVG(" << column_or_value_to_sql(_agg.val) << ")";
|
||||
stream << "AVG(" << operation_to_sql(*_agg.val) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Aggregation::Count>) {
|
||||
stream << "COUNT("
|
||||
<< std::string(_agg.val && _agg.distinct ? " DISTINCT " : "")
|
||||
<< (_agg.val ? column_or_value_to_sql(*_agg.val)
|
||||
: std::string("*"))
|
||||
<< ")";
|
||||
const auto val =
|
||||
std::string(_agg.val && _agg.distinct ? "DISTINCT " : "") +
|
||||
(_agg.val ? column_or_value_to_sql(*_agg.val) : std::string("*"));
|
||||
stream << "COUNT(" << val << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Aggregation::Max>) {
|
||||
stream << "MAX(" << column_or_value_to_sql(_agg.val) << ")";
|
||||
stream << "MAX(" << operation_to_sql(*_agg.val) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Aggregation::Min>) {
|
||||
stream << "MIN(" << column_or_value_to_sql(_agg.val) << ")";
|
||||
stream << "MIN(" << operation_to_sql(*_agg.val) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Aggregation::Sum>) {
|
||||
stream << "SUM(" << column_or_value_to_sql(_agg.val) << ")";
|
||||
stream << "SUM(" << operation_to_sql(*_agg.val) << ")";
|
||||
|
||||
} else {
|
||||
static_assert(rfl::always_false_v<Type>, "Not all cases were covered.");
|
||||
}
|
||||
|
||||
return stream.str();
|
||||
});
|
||||
}
|
||||
@@ -116,7 +115,7 @@ std::string column_or_value_to_sql(
|
||||
if constexpr (std::is_same_v<Type, dynamic::Column>) {
|
||||
return wrap_in_quotes(_c.name);
|
||||
} else {
|
||||
return _c.visit(handle_value);
|
||||
return _c.val.visit(handle_value);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -129,6 +128,7 @@ std::string condition_to_sql(const dynamic::Condition& _cond) noexcept {
|
||||
template <class ConditionType>
|
||||
std::string condition_to_sql_impl(const ConditionType& _condition) noexcept {
|
||||
using C = std::remove_cvref_t<ConditionType>;
|
||||
|
||||
std::stringstream stream;
|
||||
|
||||
if constexpr (std::is_same_v<C, dynamic::Condition::And>) {
|
||||
@@ -136,41 +136,44 @@ std::string condition_to_sql_impl(const ConditionType& _condition) noexcept {
|
||||
<< condition_to_sql(*_condition.cond2) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<C, dynamic::Condition::Equal>) {
|
||||
stream << column_or_value_to_sql(_condition.op1) << " = "
|
||||
<< column_or_value_to_sql(_condition.op2);
|
||||
stream << operation_to_sql(_condition.op1) << " = "
|
||||
<< operation_to_sql(_condition.op2);
|
||||
|
||||
} else if constexpr (std::is_same_v<C, dynamic::Condition::GreaterEqual>) {
|
||||
stream << column_or_value_to_sql(_condition.op1)
|
||||
<< " >= " << column_or_value_to_sql(_condition.op2);
|
||||
stream << operation_to_sql(_condition.op1)
|
||||
<< " >= " << operation_to_sql(_condition.op2);
|
||||
|
||||
} else if constexpr (std::is_same_v<C, dynamic::Condition::GreaterThan>) {
|
||||
stream << column_or_value_to_sql(_condition.op1) << " > "
|
||||
<< column_or_value_to_sql(_condition.op2);
|
||||
stream << operation_to_sql(_condition.op1) << " > "
|
||||
<< operation_to_sql(_condition.op2);
|
||||
|
||||
} else if constexpr (std::is_same_v<C, dynamic::Condition::IsNull>) {
|
||||
stream << column_or_value_to_sql(_condition.op) << " IS NULL";
|
||||
stream << operation_to_sql(_condition.op) << " IS NULL";
|
||||
|
||||
} else if constexpr (std::is_same_v<C, dynamic::Condition::IsNotNull>) {
|
||||
stream << column_or_value_to_sql(_condition.op) << " IS NOT NULL";
|
||||
stream << operation_to_sql(_condition.op) << " IS NOT NULL";
|
||||
|
||||
} else if constexpr (std::is_same_v<C, dynamic::Condition::LesserEqual>) {
|
||||
stream << column_or_value_to_sql(_condition.op1)
|
||||
<< " <= " << column_or_value_to_sql(_condition.op2);
|
||||
stream << operation_to_sql(_condition.op1)
|
||||
<< " <= " << operation_to_sql(_condition.op2);
|
||||
|
||||
} else if constexpr (std::is_same_v<C, dynamic::Condition::LesserThan>) {
|
||||
stream << column_or_value_to_sql(_condition.op1) << " < "
|
||||
<< column_or_value_to_sql(_condition.op2);
|
||||
stream << operation_to_sql(_condition.op1) << " < "
|
||||
<< operation_to_sql(_condition.op2);
|
||||
|
||||
} else if constexpr (std::is_same_v<C, dynamic::Condition::Like>) {
|
||||
stream << column_or_value_to_sql(_condition.op) << " LIKE "
|
||||
stream << operation_to_sql(_condition.op) << " LIKE "
|
||||
<< column_or_value_to_sql(_condition.pattern);
|
||||
|
||||
} else if constexpr (std::is_same_v<C, dynamic::Condition::Not>) {
|
||||
stream << "NOT (" << condition_to_sql(*_condition.cond) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<C, dynamic::Condition::NotEqual>) {
|
||||
stream << column_or_value_to_sql(_condition.op1)
|
||||
<< " != " << column_or_value_to_sql(_condition.op2);
|
||||
stream << operation_to_sql(_condition.op1)
|
||||
<< " != " << operation_to_sql(_condition.op2);
|
||||
|
||||
} else if constexpr (std::is_same_v<C, dynamic::Condition::NotLike>) {
|
||||
stream << column_or_value_to_sql(_condition.op) << " NOT LIKE "
|
||||
stream << operation_to_sql(_condition.op) << " NOT LIKE "
|
||||
<< column_or_value_to_sql(_condition.pattern);
|
||||
|
||||
} else if constexpr (std::is_same_v<C, dynamic::Condition::Or>) {
|
||||
@@ -180,7 +183,6 @@ std::string condition_to_sql_impl(const ConditionType& _condition) noexcept {
|
||||
} else {
|
||||
static_assert(rfl::always_false_v<C>, "Not all cases were covered.");
|
||||
}
|
||||
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
@@ -309,14 +311,7 @@ std::string escape_single_quote(const std::string& _str) noexcept {
|
||||
std::string field_to_str(const dynamic::SelectFrom::Field& _field) noexcept {
|
||||
std::stringstream stream;
|
||||
|
||||
stream << _field.val.visit([](const auto& _val) -> std::string {
|
||||
using Type = std::remove_cvref_t<decltype(_val)>;
|
||||
if constexpr (std::is_same_v<Type, dynamic::Aggregation>) {
|
||||
return aggregation_to_sql(_val);
|
||||
} else {
|
||||
return column_or_value_to_sql(_val);
|
||||
}
|
||||
});
|
||||
stream << operation_to_sql(_field.val);
|
||||
|
||||
if (_field.as) {
|
||||
stream << " AS " << wrap_in_quotes(*_field.as);
|
||||
@@ -369,6 +364,131 @@ std::string insert_to_sql(const dynamic::Insert& _stmt) noexcept {
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
std::string operation_to_sql(const dynamic::Operation& _stmt) noexcept {
|
||||
using namespace std::ranges::views;
|
||||
return _stmt.val.visit([](const auto& _s) -> std::string {
|
||||
using Type = std::remove_cvref_t<decltype(_s)>;
|
||||
|
||||
std::stringstream stream;
|
||||
|
||||
if constexpr (std::is_same_v<Type, dynamic::Operation::Abs>) {
|
||||
stream << "abs(" << operation_to_sql(*_s.op1) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Aggregation>) {
|
||||
stream << aggregation_to_sql(_s);
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Operation::Cast>) {
|
||||
stream << "cast(" << operation_to_sql(*_s.op1) << " as "
|
||||
<< type_to_sql(_s.target_type) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Operation::Coalesce>) {
|
||||
stream << "coalesce("
|
||||
<< internal::strings::join(
|
||||
", ", internal::collect::vector(
|
||||
_s.ops | transform([](const auto& _op) {
|
||||
return operation_to_sql(*_op);
|
||||
})))
|
||||
<< ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Operation::Ceil>) {
|
||||
stream << "ceil(" << operation_to_sql(*_s.op1) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Column>) {
|
||||
stream << column_or_value_to_sql(_s);
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Operation::Concat>) {
|
||||
stream << "("
|
||||
<< internal::strings::join(
|
||||
" || ", internal::collect::vector(
|
||||
_s.ops | transform([](const auto& _op) {
|
||||
return operation_to_sql(*_op);
|
||||
})))
|
||||
<< ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Operation::Cos>) {
|
||||
stream << "cos(" << operation_to_sql(*_s.op1) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Operation::Divides>) {
|
||||
stream << "(" << operation_to_sql(*_s.op1) << ") / ("
|
||||
<< operation_to_sql(*_s.op2) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Operation::Exp>) {
|
||||
stream << "exp(" << operation_to_sql(*_s.op1) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Operation::Floor>) {
|
||||
stream << "floor(" << operation_to_sql(*_s.op1) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Operation::Length>) {
|
||||
stream << "length(" << operation_to_sql(*_s.op1) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Operation::Ln>) {
|
||||
stream << "ln(" << operation_to_sql(*_s.op1) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Operation::Log2>) {
|
||||
stream << "log(2.0, " << operation_to_sql(*_s.op1) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Operation::Lower>) {
|
||||
stream << "lower(" << operation_to_sql(*_s.op1) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Operation::LTrim>) {
|
||||
stream << "ltrim(" << operation_to_sql(*_s.op1) << ", "
|
||||
<< operation_to_sql(*_s.op2) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Operation::Minus>) {
|
||||
stream << "(" << operation_to_sql(*_s.op1) << ") - ("
|
||||
<< operation_to_sql(*_s.op2) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Operation::Mod>) {
|
||||
stream << "mod(" << operation_to_sql(*_s.op1) << ", "
|
||||
<< operation_to_sql(*_s.op2) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Operation::Multiplies>) {
|
||||
stream << "(" << operation_to_sql(*_s.op1) << ") * ("
|
||||
<< operation_to_sql(*_s.op2) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Operation::Plus>) {
|
||||
stream << "(" << operation_to_sql(*_s.op1) << ") + ("
|
||||
<< operation_to_sql(*_s.op2) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Operation::Replace>) {
|
||||
stream << "replace(" << operation_to_sql(*_s.op1) << ", "
|
||||
<< operation_to_sql(*_s.op2) << ", " << operation_to_sql(*_s.op3)
|
||||
<< ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Operation::Round>) {
|
||||
stream << "round(" << operation_to_sql(*_s.op1) << ", "
|
||||
<< operation_to_sql(*_s.op2) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Operation::RTrim>) {
|
||||
stream << "rtrim(" << operation_to_sql(*_s.op1) << ", "
|
||||
<< operation_to_sql(*_s.op2) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Operation::Sin>) {
|
||||
stream << "sin(" << operation_to_sql(*_s.op1) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Operation::Sqrt>) {
|
||||
stream << "sqrt(" << operation_to_sql(*_s.op1) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Operation::Tan>) {
|
||||
stream << "tan(" << operation_to_sql(*_s.op1) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Operation::Trim>) {
|
||||
stream << "trim(" << operation_to_sql(*_s.op1) << ", "
|
||||
<< operation_to_sql(*_s.op2) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Operation::Upper>) {
|
||||
stream << "upper(" << operation_to_sql(*_s.op1) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Value>) {
|
||||
stream << column_or_value_to_sql(_s);
|
||||
|
||||
} else {
|
||||
static_assert(rfl::always_false_v<Type>, "Unsupported type.");
|
||||
}
|
||||
return stream.str();
|
||||
});
|
||||
}
|
||||
|
||||
std::string select_from_to_sql(const dynamic::SelectFrom& _stmt) noexcept {
|
||||
using namespace std::ranges::views;
|
||||
|
||||
@@ -466,10 +586,9 @@ std::string type_to_sql(const dynamic::Type& _type) noexcept {
|
||||
} else if constexpr (std::is_same_v<T, dynamic::types::Int64> ||
|
||||
std::is_same_v<T, dynamic::types::UInt64>) {
|
||||
return "BIGINT";
|
||||
} else if constexpr (std::is_same_v<T, dynamic::types::Float32>) {
|
||||
return "REAL";
|
||||
} else if constexpr (std::is_same_v<T, dynamic::types::Float64>) {
|
||||
return "DOUBLE PRECISION";
|
||||
} else if constexpr (std::is_same_v<T, dynamic::types::Float32> ||
|
||||
std::is_same_v<T, dynamic::types::Float64>) {
|
||||
return "NUMERIC";
|
||||
} else if constexpr (std::is_same_v<T, dynamic::types::Text>) {
|
||||
return "TEXT";
|
||||
} else if constexpr (std::is_same_v<T, dynamic::types::VarChar>) {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <rfl.hpp>
|
||||
#include <sstream>
|
||||
|
||||
#include "sqlgen/dynamic/Operation.hpp"
|
||||
#include "sqlgen/internal/collect/vector.hpp"
|
||||
#include "sqlgen/internal/strings/strings.hpp"
|
||||
#include "sqlgen/sqlite/Connection.hpp"
|
||||
@@ -36,6 +37,8 @@ std::string field_to_str(const dynamic::SelectFrom::Field& _field) noexcept;
|
||||
template <class InsertOrWrite>
|
||||
std::string insert_or_write_to_sql(const InsertOrWrite& _stmt) noexcept;
|
||||
|
||||
std::string operation_to_sql(const dynamic::Operation& _stmt) noexcept;
|
||||
|
||||
std::string properties_to_sql(const dynamic::types::Properties& _p) noexcept;
|
||||
|
||||
std::string select_from_to_sql(const dynamic::SelectFrom& _stmt) noexcept;
|
||||
@@ -47,33 +50,29 @@ std::string update_to_sql(const dynamic::Update& _stmt) noexcept;
|
||||
std::string aggregation_to_sql(
|
||||
const dynamic::Aggregation& _aggregation) noexcept {
|
||||
return _aggregation.val.visit([](const auto& _agg) -> std::string {
|
||||
std::stringstream stream;
|
||||
|
||||
using Type = std::remove_cvref_t<decltype(_agg)>;
|
||||
|
||||
std::stringstream stream;
|
||||
if constexpr (std::is_same_v<Type, dynamic::Aggregation::Avg>) {
|
||||
stream << "AVG(" << column_or_value_to_sql(_agg.val) << ")";
|
||||
stream << "AVG(" << operation_to_sql(*_agg.val) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Aggregation::Count>) {
|
||||
stream << "COUNT("
|
||||
<< std::string(_agg.val && _agg.distinct ? " DISTINCT " : "")
|
||||
<< (_agg.val ? column_or_value_to_sql(*_agg.val)
|
||||
: std::string("*"))
|
||||
<< ")";
|
||||
const auto val =
|
||||
std::string(_agg.val && _agg.distinct ? "DISTINCT " : "") +
|
||||
(_agg.val ? column_or_value_to_sql(*_agg.val) : std::string("*"));
|
||||
stream << "COUNT(" << val << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Aggregation::Max>) {
|
||||
stream << "MAX(" << column_or_value_to_sql(_agg.val) << ")";
|
||||
stream << "MAX(" << operation_to_sql(*_agg.val) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Aggregation::Min>) {
|
||||
stream << "MIN(" << column_or_value_to_sql(_agg.val) << ")";
|
||||
stream << "MIN(" << operation_to_sql(*_agg.val) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Aggregation::Sum>) {
|
||||
stream << "SUM(" << column_or_value_to_sql(_agg.val) << ")";
|
||||
stream << "SUM(" << operation_to_sql(*_agg.val) << ")";
|
||||
|
||||
} else {
|
||||
static_assert(rfl::always_false_v<Type>, "Not all cases were covered.");
|
||||
}
|
||||
|
||||
return stream.str();
|
||||
});
|
||||
}
|
||||
@@ -94,7 +93,7 @@ std::string column_or_value_to_sql(
|
||||
if constexpr (std::is_same_v<Type, dynamic::Column>) {
|
||||
return "\"" + _c.name + "\"";
|
||||
} else {
|
||||
return _c.visit(handle_value);
|
||||
return _c.val.visit(handle_value);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -113,6 +112,7 @@ std::string condition_to_sql(const dynamic::Condition& _cond) noexcept {
|
||||
template <class ConditionType>
|
||||
std::string condition_to_sql_impl(const ConditionType& _condition) noexcept {
|
||||
using C = std::remove_cvref_t<ConditionType>;
|
||||
|
||||
std::stringstream stream;
|
||||
|
||||
if constexpr (std::is_same_v<C, dynamic::Condition::And>) {
|
||||
@@ -120,41 +120,44 @@ std::string condition_to_sql_impl(const ConditionType& _condition) noexcept {
|
||||
<< condition_to_sql(*_condition.cond2) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<C, dynamic::Condition::Equal>) {
|
||||
stream << column_or_value_to_sql(_condition.op1) << " = "
|
||||
<< column_or_value_to_sql(_condition.op2);
|
||||
stream << operation_to_sql(_condition.op1) << " = "
|
||||
<< operation_to_sql(_condition.op2);
|
||||
|
||||
} else if constexpr (std::is_same_v<C, dynamic::Condition::GreaterEqual>) {
|
||||
stream << column_or_value_to_sql(_condition.op1)
|
||||
<< " >= " << column_or_value_to_sql(_condition.op2);
|
||||
stream << operation_to_sql(_condition.op1)
|
||||
<< " >= " << operation_to_sql(_condition.op2);
|
||||
|
||||
} else if constexpr (std::is_same_v<C, dynamic::Condition::GreaterThan>) {
|
||||
stream << column_or_value_to_sql(_condition.op1) << " > "
|
||||
<< column_or_value_to_sql(_condition.op2);
|
||||
stream << operation_to_sql(_condition.op1) << " > "
|
||||
<< operation_to_sql(_condition.op2);
|
||||
|
||||
} else if constexpr (std::is_same_v<C, dynamic::Condition::IsNull>) {
|
||||
stream << column_or_value_to_sql(_condition.op) << " IS NULL";
|
||||
stream << operation_to_sql(_condition.op) << " IS NULL";
|
||||
|
||||
} else if constexpr (std::is_same_v<C, dynamic::Condition::IsNotNull>) {
|
||||
stream << column_or_value_to_sql(_condition.op) << " IS NOT NULL";
|
||||
stream << operation_to_sql(_condition.op) << " IS NOT NULL";
|
||||
|
||||
} else if constexpr (std::is_same_v<C, dynamic::Condition::LesserEqual>) {
|
||||
stream << column_or_value_to_sql(_condition.op1)
|
||||
<< " <= " << column_or_value_to_sql(_condition.op2);
|
||||
stream << operation_to_sql(_condition.op1)
|
||||
<< " <= " << operation_to_sql(_condition.op2);
|
||||
|
||||
} else if constexpr (std::is_same_v<C, dynamic::Condition::LesserThan>) {
|
||||
stream << column_or_value_to_sql(_condition.op1) << " < "
|
||||
<< column_or_value_to_sql(_condition.op2);
|
||||
stream << operation_to_sql(_condition.op1) << " < "
|
||||
<< operation_to_sql(_condition.op2);
|
||||
|
||||
} else if constexpr (std::is_same_v<C, dynamic::Condition::Like>) {
|
||||
stream << column_or_value_to_sql(_condition.op) << " LIKE "
|
||||
stream << operation_to_sql(_condition.op) << " LIKE "
|
||||
<< column_or_value_to_sql(_condition.pattern);
|
||||
|
||||
} else if constexpr (std::is_same_v<C, dynamic::Condition::Not>) {
|
||||
stream << "NOT (" << condition_to_sql(*_condition.cond) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<C, dynamic::Condition::NotEqual>) {
|
||||
stream << column_or_value_to_sql(_condition.op1)
|
||||
<< " != " << column_or_value_to_sql(_condition.op2);
|
||||
stream << operation_to_sql(_condition.op1)
|
||||
<< " != " << operation_to_sql(_condition.op2);
|
||||
|
||||
} else if constexpr (std::is_same_v<C, dynamic::Condition::NotLike>) {
|
||||
stream << column_or_value_to_sql(_condition.op) << " NOT LIKE "
|
||||
stream << operation_to_sql(_condition.op) << " NOT LIKE "
|
||||
<< column_or_value_to_sql(_condition.pattern);
|
||||
|
||||
} else if constexpr (std::is_same_v<C, dynamic::Condition::Or>) {
|
||||
@@ -164,7 +167,6 @@ std::string condition_to_sql_impl(const ConditionType& _condition) noexcept {
|
||||
} else {
|
||||
static_assert(rfl::always_false_v<C>, "Not all cases were covered.");
|
||||
}
|
||||
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
@@ -280,14 +282,7 @@ std::string escape_single_quote(const std::string& _str) noexcept {
|
||||
std::string field_to_str(const dynamic::SelectFrom::Field& _field) noexcept {
|
||||
std::stringstream stream;
|
||||
|
||||
stream << _field.val.visit([](const auto& _val) -> std::string {
|
||||
using Type = std::remove_cvref_t<decltype(_val)>;
|
||||
if constexpr (std::is_same_v<Type, dynamic::Aggregation>) {
|
||||
return aggregation_to_sql(_val);
|
||||
} else {
|
||||
return column_or_value_to_sql(_val);
|
||||
}
|
||||
});
|
||||
stream << operation_to_sql(_field.val);
|
||||
|
||||
if (_field.as) {
|
||||
stream << " AS " << "\"" << *_field.as << "\"";
|
||||
@@ -329,6 +324,131 @@ std::string insert_or_write_to_sql(const InsertOrWrite& _stmt) noexcept {
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
std::string operation_to_sql(const dynamic::Operation& _stmt) noexcept {
|
||||
using namespace std::ranges::views;
|
||||
return _stmt.val.visit([](const auto& _s) -> std::string {
|
||||
using Type = std::remove_cvref_t<decltype(_s)>;
|
||||
|
||||
std::stringstream stream;
|
||||
|
||||
if constexpr (std::is_same_v<Type, dynamic::Operation::Abs>) {
|
||||
stream << "abs(" << operation_to_sql(*_s.op1) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Aggregation>) {
|
||||
stream << aggregation_to_sql(_s);
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Operation::Cast>) {
|
||||
stream << "cast(" << operation_to_sql(*_s.op1) << " as "
|
||||
<< type_to_sql(_s.target_type) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Operation::Coalesce>) {
|
||||
stream << "coalesce("
|
||||
<< internal::strings::join(
|
||||
", ", internal::collect::vector(
|
||||
_s.ops | transform([](const auto& _op) {
|
||||
return operation_to_sql(*_op);
|
||||
})))
|
||||
<< ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Operation::Ceil>) {
|
||||
stream << "ceil(" << operation_to_sql(*_s.op1) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Column>) {
|
||||
stream << column_or_value_to_sql(_s);
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Operation::Concat>) {
|
||||
stream << "("
|
||||
<< internal::strings::join(
|
||||
" || ", internal::collect::vector(
|
||||
_s.ops | transform([](const auto& _op) {
|
||||
return operation_to_sql(*_op);
|
||||
})))
|
||||
<< ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Operation::Cos>) {
|
||||
stream << "cos(" << operation_to_sql(*_s.op1) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Operation::Divides>) {
|
||||
stream << "(" << operation_to_sql(*_s.op1) << ") / ("
|
||||
<< operation_to_sql(*_s.op2) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Operation::Exp>) {
|
||||
stream << "exp(" << operation_to_sql(*_s.op1) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Operation::Floor>) {
|
||||
stream << "floor(" << operation_to_sql(*_s.op1) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Operation::Length>) {
|
||||
stream << "length(" << operation_to_sql(*_s.op1) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Operation::Ln>) {
|
||||
stream << "ln(" << operation_to_sql(*_s.op1) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Operation::Log2>) {
|
||||
stream << "log2(" << operation_to_sql(*_s.op1) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Operation::Lower>) {
|
||||
stream << "lower(" << operation_to_sql(*_s.op1) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Operation::LTrim>) {
|
||||
stream << "ltrim(" << operation_to_sql(*_s.op1) << ", "
|
||||
<< operation_to_sql(*_s.op2) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Operation::Minus>) {
|
||||
stream << "(" << operation_to_sql(*_s.op1) << ") - ("
|
||||
<< operation_to_sql(*_s.op2) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Operation::Mod>) {
|
||||
stream << "mod(" << operation_to_sql(*_s.op1) << ", "
|
||||
<< operation_to_sql(*_s.op2) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Operation::Multiplies>) {
|
||||
stream << "(" << operation_to_sql(*_s.op1) << ") * ("
|
||||
<< operation_to_sql(*_s.op2) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Operation::Plus>) {
|
||||
stream << "(" << operation_to_sql(*_s.op1) << ") + ("
|
||||
<< operation_to_sql(*_s.op2) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Operation::Replace>) {
|
||||
stream << "replace(" << operation_to_sql(*_s.op1) << ", "
|
||||
<< operation_to_sql(*_s.op2) << ", " << operation_to_sql(*_s.op3)
|
||||
<< ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Operation::Round>) {
|
||||
stream << "round(" << operation_to_sql(*_s.op1) << ", "
|
||||
<< operation_to_sql(*_s.op2) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Operation::RTrim>) {
|
||||
stream << "rtrim(" << operation_to_sql(*_s.op1) << ", "
|
||||
<< operation_to_sql(*_s.op2) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Operation::Sin>) {
|
||||
stream << "sin(" << operation_to_sql(*_s.op1) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Operation::Sqrt>) {
|
||||
stream << "sqrt(" << operation_to_sql(*_s.op1) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Operation::Tan>) {
|
||||
stream << "tan(" << operation_to_sql(*_s.op1) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Operation::Trim>) {
|
||||
stream << "trim(" << operation_to_sql(*_s.op1) << ", "
|
||||
<< operation_to_sql(*_s.op2) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Operation::Upper>) {
|
||||
stream << "upper(" << operation_to_sql(*_s.op1) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<Type, dynamic::Value>) {
|
||||
stream << column_or_value_to_sql(_s);
|
||||
|
||||
} else {
|
||||
static_assert(rfl::always_false_v<Type>, "Unsupported type.");
|
||||
}
|
||||
return stream.str();
|
||||
});
|
||||
}
|
||||
|
||||
std::string properties_to_sql(const dynamic::types::Properties& _p) noexcept {
|
||||
return std::string(_p.primary ? " PRIMARY KEY" : "") +
|
||||
std::string(_p.nullable ? "" : " NOT NULL");
|
||||
|
||||
72
tests/postgres/test_group_by_with_operations.cpp
Normal file
72
tests/postgres/test_group_by_with_operations.cpp
Normal file
@@ -0,0 +1,72 @@
|
||||
#ifndef SQLGEN_BUILD_DRY_TESTS_ONLY
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <ranges>
|
||||
#include <rfl.hpp>
|
||||
#include <rfl/json.hpp>
|
||||
#include <sqlgen.hpp>
|
||||
#include <sqlgen/postgres.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace test_group_by_with_operations {
|
||||
|
||||
struct Person {
|
||||
sqlgen::PrimaryKey<uint32_t> id;
|
||||
std::string first_name;
|
||||
std::string last_name;
|
||||
int age;
|
||||
};
|
||||
|
||||
TEST(postgres, test_group_by) {
|
||||
static_assert(std::ranges::input_range<sqlgen::Range<Person>>,
|
||||
"Must be an input range.");
|
||||
|
||||
const auto people1 = std::vector<Person>(
|
||||
{Person{
|
||||
.id = 0, .first_name = "Homer", .last_name = "Simpson", .age = 45},
|
||||
Person{.id = 1, .first_name = "Bart", .last_name = "Simpson", .age = 10},
|
||||
Person{.id = 2, .first_name = "Lisa", .last_name = "Simpson", .age = 8},
|
||||
Person{
|
||||
.id = 3, .first_name = "Maggie", .last_name = "Simpson", .age = 0}});
|
||||
|
||||
const auto credentials = sqlgen::postgres::Credentials{.user = "postgres",
|
||||
.password = "password",
|
||||
.host = "localhost",
|
||||
.dbname = "postgres"};
|
||||
|
||||
using namespace sqlgen;
|
||||
|
||||
struct Children {
|
||||
std::string last_name;
|
||||
std::string last_name_trimmed;
|
||||
double avg_age;
|
||||
double max_age_plus_one;
|
||||
double min_age_plus_one;
|
||||
};
|
||||
|
||||
const auto get_children =
|
||||
select_from<Person>(
|
||||
"last_name"_c, trim("last_name"_c).as<"last_name_trimmed">(),
|
||||
max(cast<double>("age"_c) + 1.0).as<"max_age_plus_one">(),
|
||||
(min(cast<double>("age"_c)) + 1.0).as<"min_age_plus_one">(),
|
||||
round(avg(cast<double>("age"_c))).as<"avg_age">()) |
|
||||
where("age"_c < 18) | group_by("last_name"_c) | to<std::vector<Children>>;
|
||||
|
||||
const auto children = postgres::connect(credentials)
|
||||
.and_then(drop<Person> | if_exists)
|
||||
.and_then(write(std::ref(people1)))
|
||||
.and_then(get_children)
|
||||
.value();
|
||||
|
||||
EXPECT_EQ(children.size(), 1);
|
||||
EXPECT_EQ(children.at(0).last_name, "Simpson");
|
||||
EXPECT_EQ(children.at(0).last_name_trimmed, "Simpson");
|
||||
EXPECT_EQ(children.at(0).avg_age, 6.0);
|
||||
EXPECT_EQ(children.at(0).max_age_plus_one, 11.0);
|
||||
EXPECT_EQ(children.at(0).min_age_plus_one, 1.0);
|
||||
}
|
||||
|
||||
} // namespace test_group_by_with_operations
|
||||
|
||||
#endif
|
||||
83
tests/postgres/test_operations.cpp
Normal file
83
tests/postgres/test_operations.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
#ifndef SQLGEN_BUILD_DRY_TESTS_ONLY
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <ranges>
|
||||
#include <rfl.hpp>
|
||||
#include <rfl/json.hpp>
|
||||
#include <sqlgen.hpp>
|
||||
#include <sqlgen/postgres.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace test_operations {
|
||||
|
||||
struct Person {
|
||||
sqlgen::PrimaryKey<uint32_t> id;
|
||||
std::string first_name;
|
||||
std::string last_name;
|
||||
int age;
|
||||
};
|
||||
|
||||
TEST(postgres, test_operations) {
|
||||
const auto people1 = std::vector<Person>(
|
||||
{Person{
|
||||
.id = 0, .first_name = "Homer", .last_name = "Simpson", .age = 45},
|
||||
Person{.id = 1, .first_name = "Bart", .last_name = "Simpson", .age = 10},
|
||||
Person{.id = 2, .first_name = "Lisa", .last_name = "Simpson", .age = 8},
|
||||
Person{
|
||||
.id = 3, .first_name = "Maggie", .last_name = "Simpson", .age = 0}});
|
||||
|
||||
const auto credentials = sqlgen::postgres::Credentials{.user = "postgres",
|
||||
.password = "password",
|
||||
.host = "localhost",
|
||||
.dbname = "postgres"};
|
||||
|
||||
using namespace sqlgen;
|
||||
|
||||
struct Children {
|
||||
int id_plus_age;
|
||||
int age_times_2;
|
||||
int id_plus_2_minus_age;
|
||||
int age_mod_3;
|
||||
int abs_age;
|
||||
double exp_age;
|
||||
double sqrt_age;
|
||||
size_t length_first_name;
|
||||
std::string full_name;
|
||||
std::string first_name_lower;
|
||||
std::string first_name_upper;
|
||||
std::string first_name_replaced;
|
||||
};
|
||||
|
||||
const auto get_children =
|
||||
select_from<Person>(
|
||||
("id"_c + "age"_c) | as<"id_plus_age">,
|
||||
("age"_c * 2) | as<"age_times_2">, ("age"_c % 3) | as<"age_mod_3">,
|
||||
abs("age"_c * (-1)) | as<"abs_age">,
|
||||
round(exp(cast<double>("age"_c)), 2) | as<"exp_age">,
|
||||
round(sqrt(cast<double>("age"_c)), 2) | as<"sqrt_age">,
|
||||
("id"_c + 2 - "age"_c) | as<"id_plus_2_minus_age">,
|
||||
length(trim("first_name"_c)) | as<"length_first_name">,
|
||||
concat(ltrim("first_name"_c), " ", rtrim("last_name"_c)) |
|
||||
as<"full_name">,
|
||||
upper(rtrim(concat("first_name"_c, " "))) | as<"first_name_upper">,
|
||||
lower(ltrim(concat(" ", "first_name"_c))) | as<"first_name_lower">,
|
||||
replace("first_name"_c, "Bart", "Hugo") | as<"first_name_replaced">) |
|
||||
where("age"_c < 18) | order_by("age"_c.desc()) |
|
||||
to<std::vector<Children>>;
|
||||
|
||||
const auto children = postgres::connect(credentials)
|
||||
.and_then(drop<Person> | if_exists)
|
||||
.and_then(write(std::ref(people1)))
|
||||
.and_then(get_children)
|
||||
.value();
|
||||
|
||||
const std::string expected =
|
||||
R"([{"id_plus_age":11,"age_times_2":20,"id_plus_2_minus_age":-7,"age_mod_3":1,"abs_age":10,"exp_age":22026.47,"sqrt_age":3.16,"length_first_name":4,"full_name":"Bart Simpson","first_name_lower":"bart","first_name_upper":"BART","first_name_replaced":"Hugo"},{"id_plus_age":10,"age_times_2":16,"id_plus_2_minus_age":-4,"age_mod_3":2,"abs_age":8,"exp_age":2980.96,"sqrt_age":2.83,"length_first_name":4,"full_name":"Lisa Simpson","first_name_lower":"lisa","first_name_upper":"LISA","first_name_replaced":"Lisa"},{"id_plus_age":3,"age_times_2":0,"id_plus_2_minus_age":5,"age_mod_3":0,"abs_age":0,"exp_age":1.0,"sqrt_age":0.0,"length_first_name":6,"full_name":"Maggie Simpson","first_name_lower":"maggie","first_name_upper":"MAGGIE","first_name_replaced":"Maggie"}])";
|
||||
|
||||
EXPECT_EQ(rfl::json::write(children), expected);
|
||||
}
|
||||
|
||||
} // namespace test_operations
|
||||
|
||||
#endif
|
||||
69
tests/postgres/test_operations_with_nullable.cpp
Normal file
69
tests/postgres/test_operations_with_nullable.cpp
Normal file
@@ -0,0 +1,69 @@
|
||||
#ifndef SQLGEN_BUILD_DRY_TESTS_ONLY
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <ranges>
|
||||
#include <rfl.hpp>
|
||||
#include <rfl/json.hpp>
|
||||
#include <sqlgen.hpp>
|
||||
#include <sqlgen/postgres.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace test_operations_with_nullable {
|
||||
|
||||
struct Person {
|
||||
sqlgen::PrimaryKey<uint32_t> id;
|
||||
std::string first_name;
|
||||
std::optional<std::string> last_name;
|
||||
std::optional<int> age;
|
||||
};
|
||||
|
||||
TEST(postgres, test_operations_with_nullable) {
|
||||
const auto people1 = std::vector<Person>(
|
||||
{Person{
|
||||
.id = 0, .first_name = "Homer", .last_name = "Simpson", .age = 45},
|
||||
Person{.id = 1, .first_name = "Bart", .last_name = "Simpson", .age = 10},
|
||||
Person{.id = 2, .first_name = "Hugo", .age = 10},
|
||||
Person{.id = 3, .first_name = "Lisa", .last_name = "Simpson", .age = 8},
|
||||
Person{
|
||||
.id = 4, .first_name = "Maggie", .last_name = "Simpson", .age = 0}});
|
||||
|
||||
const auto credentials = sqlgen::postgres::Credentials{.user = "postgres",
|
||||
.password = "password",
|
||||
.host = "localhost",
|
||||
.dbname = "postgres"};
|
||||
|
||||
using namespace sqlgen;
|
||||
|
||||
struct Children {
|
||||
std::optional<int> id_plus_age;
|
||||
std::optional<int> age_times_2;
|
||||
std::optional<int> id_plus_2_minus_age;
|
||||
std::optional<std::string> full_name;
|
||||
std::string last_name_or_none;
|
||||
};
|
||||
|
||||
const auto get_children =
|
||||
select_from<Person>(
|
||||
("id"_c + "age"_c) | as<"id_plus_age">,
|
||||
("age"_c * 2) | as<"age_times_2">,
|
||||
("id"_c + 2 - "age"_c) | as<"id_plus_2_minus_age">,
|
||||
concat(upper("last_name"_c), ", ", "first_name"_c) | as<"full_name">,
|
||||
coalesce("last_name"_c, "none") | as<"last_name_or_none">) |
|
||||
where("age"_c < 18) | to<std::vector<Children>>;
|
||||
|
||||
const auto children = postgres::connect(credentials)
|
||||
.and_then(drop<Person> | if_exists)
|
||||
.and_then(write(std::ref(people1)))
|
||||
.and_then(get_children)
|
||||
.value();
|
||||
|
||||
const std::string expected =
|
||||
R"([{"id_plus_age":11,"age_times_2":20,"id_plus_2_minus_age":-7,"full_name":"SIMPSON, Bart","last_name_or_none":"Simpson"},{"id_plus_age":12,"age_times_2":20,"id_plus_2_minus_age":-6,"last_name_or_none":"none"},{"id_plus_age":11,"age_times_2":16,"id_plus_2_minus_age":-3,"full_name":"SIMPSON, Lisa","last_name_or_none":"Simpson"},{"id_plus_age":4,"age_times_2":0,"id_plus_2_minus_age":6,"full_name":"SIMPSON, Maggie","last_name_or_none":"Simpson"}])";
|
||||
|
||||
EXPECT_EQ(rfl::json::write(children), expected);
|
||||
}
|
||||
|
||||
} // namespace test_operations_with_nullable
|
||||
|
||||
#endif
|
||||
@@ -41,7 +41,7 @@ TEST(postgres, test_where) {
|
||||
sqlgen::write(conn, people1).value();
|
||||
|
||||
const auto query = sqlgen::read<std::vector<Person>> |
|
||||
where("age"_c < 18 and "first_name"_c != "Hugo") |
|
||||
where("age"_c < 18 and not("first_name"_c == "Hugo")) |
|
||||
order_by("age"_c);
|
||||
|
||||
const auto people2 = query(conn).value();
|
||||
|
||||
57
tests/postgres/test_where_with_operations.cpp
Normal file
57
tests/postgres/test_where_with_operations.cpp
Normal file
@@ -0,0 +1,57 @@
|
||||
#ifndef SQLGEN_BUILD_DRY_TESTS_ONLY
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <rfl.hpp>
|
||||
#include <rfl/json.hpp>
|
||||
#include <sqlgen.hpp>
|
||||
#include <sqlgen/postgres.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace test_where_with_operations {
|
||||
|
||||
struct Person {
|
||||
sqlgen::PrimaryKey<uint32_t> id;
|
||||
std::string first_name;
|
||||
std::string last_name;
|
||||
int age;
|
||||
};
|
||||
|
||||
TEST(postgres, test_where_with_operations) {
|
||||
const auto people1 = std::vector<Person>(
|
||||
{Person{
|
||||
.id = 0, .first_name = "Homer", .last_name = "Simpson", .age = 45},
|
||||
Person{.id = 1, .first_name = "Bart", .last_name = "Simpson", .age = 10},
|
||||
Person{.id = 2, .first_name = "Lisa", .last_name = "Simpson", .age = 8},
|
||||
Person{
|
||||
.id = 3, .first_name = "Maggie", .last_name = "Simpson", .age = 0},
|
||||
Person{
|
||||
.id = 4, .first_name = "Hugo", .last_name = "Simpson", .age = 10}});
|
||||
|
||||
const auto credentials = sqlgen::postgres::Credentials{.user = "postgres",
|
||||
.password = "password",
|
||||
.host = "localhost",
|
||||
.dbname = "postgres"};
|
||||
|
||||
using namespace sqlgen;
|
||||
|
||||
const auto conn =
|
||||
sqlgen::postgres::connect(credentials).and_then(drop<Person> | if_exists);
|
||||
|
||||
sqlgen::write(conn, people1).value();
|
||||
|
||||
const auto query = sqlgen::read<std::vector<Person>> |
|
||||
where("age"_c * 2 + 4 < 40 and "first_name"_c != "Hugo") |
|
||||
order_by("age"_c);
|
||||
|
||||
const auto people2 = query(conn).value();
|
||||
|
||||
const std::string expected =
|
||||
R"([{"id":3,"first_name":"Maggie","last_name":"Simpson","age":0},{"id":2,"first_name":"Lisa","last_name":"Simpson","age":8},{"id":1,"first_name":"Bart","last_name":"Simpson","age":10}])";
|
||||
|
||||
EXPECT_EQ(rfl::json::write(people2), expected);
|
||||
}
|
||||
|
||||
} // namespace test_where_with_operations
|
||||
|
||||
#endif
|
||||
62
tests/sqlite/test_group_by_with_operations.cpp
Normal file
62
tests/sqlite/test_group_by_with_operations.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <ranges>
|
||||
#include <rfl.hpp>
|
||||
#include <rfl/json.hpp>
|
||||
#include <sqlgen.hpp>
|
||||
#include <sqlgen/sqlite.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace test_group_by_with_operations {
|
||||
|
||||
struct Person {
|
||||
sqlgen::PrimaryKey<uint32_t> id;
|
||||
std::string first_name;
|
||||
std::string last_name;
|
||||
int age;
|
||||
};
|
||||
|
||||
TEST(sqlite, test_group_by_with_operations) {
|
||||
const auto people1 = std::vector<Person>(
|
||||
{Person{
|
||||
.id = 0, .first_name = "Homer", .last_name = "Simpson", .age = 45},
|
||||
Person{.id = 1, .first_name = "Bart", .last_name = "Simpson", .age = 10},
|
||||
Person{.id = 2, .first_name = "Lisa", .last_name = "Simpson", .age = 8},
|
||||
Person{
|
||||
.id = 3, .first_name = "Maggie", .last_name = "Simpson", .age = 0}});
|
||||
|
||||
using namespace sqlgen;
|
||||
|
||||
struct Children {
|
||||
std::string last_name;
|
||||
std::string last_name_trimmed;
|
||||
double avg_age;
|
||||
double max_age_plus_one;
|
||||
double min_age_plus_one;
|
||||
};
|
||||
|
||||
const auto get_children =
|
||||
select_from<Person>(
|
||||
"last_name"_c, trim("last_name"_c).as<"last_name_trimmed">(),
|
||||
max(cast<double>("age"_c) + 1.0).as<"max_age_plus_one">(),
|
||||
(min(cast<double>("age"_c)) + 1.0).as<"min_age_plus_one">(),
|
||||
round(avg(cast<double>("age"_c))).as<"avg_age">()) |
|
||||
where("age"_c < 18) | group_by("last_name"_c) | to<std::vector<Children>>;
|
||||
|
||||
const auto children = sqlite::connect()
|
||||
.and_then(drop<Person> | if_exists)
|
||||
.and_then(write(std::ref(people1)))
|
||||
.and_then(get_children)
|
||||
.value();
|
||||
|
||||
EXPECT_EQ(children.size(), 1);
|
||||
EXPECT_EQ(children.at(0).last_name, "Simpson");
|
||||
EXPECT_EQ(children.at(0).last_name_trimmed, "Simpson");
|
||||
EXPECT_EQ(children.at(0).avg_age, 6.0);
|
||||
EXPECT_EQ(children.at(0).max_age_plus_one, 11.0);
|
||||
EXPECT_EQ(children.at(0).min_age_plus_one, 1.0);
|
||||
}
|
||||
|
||||
} // namespace test_group_by_with_operations
|
||||
|
||||
75
tests/sqlite/test_operations.cpp
Normal file
75
tests/sqlite/test_operations.cpp
Normal file
@@ -0,0 +1,75 @@
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <ranges>
|
||||
#include <rfl.hpp>
|
||||
#include <rfl/json.hpp>
|
||||
#include <sqlgen.hpp>
|
||||
#include <sqlgen/sqlite.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace test_operations {
|
||||
|
||||
struct Person {
|
||||
sqlgen::PrimaryKey<uint32_t> id;
|
||||
std::string first_name;
|
||||
std::string last_name;
|
||||
int age;
|
||||
};
|
||||
|
||||
TEST(sqlite, test_operations) {
|
||||
const auto people1 = std::vector<Person>(
|
||||
{Person{
|
||||
.id = 0, .first_name = "Homer", .last_name = "Simpson", .age = 45},
|
||||
Person{.id = 1, .first_name = "Bart", .last_name = "Simpson", .age = 10},
|
||||
Person{.id = 2, .first_name = "Lisa", .last_name = "Simpson", .age = 8},
|
||||
Person{
|
||||
.id = 3, .first_name = "Maggie", .last_name = "Simpson", .age = 0}});
|
||||
|
||||
using namespace sqlgen;
|
||||
|
||||
struct Children {
|
||||
int id_plus_age;
|
||||
int age_times_2;
|
||||
int id_plus_2_minus_age;
|
||||
int age_mod_3;
|
||||
int abs_age;
|
||||
double exp_age;
|
||||
double sqrt_age;
|
||||
size_t length_first_name;
|
||||
std::string full_name;
|
||||
std::string first_name_lower;
|
||||
std::string first_name_upper;
|
||||
std::string first_name_replaced;
|
||||
};
|
||||
|
||||
const auto get_children =
|
||||
select_from<Person>(
|
||||
("id"_c + "age"_c) | as<"id_plus_age">,
|
||||
("age"_c * 2) | as<"age_times_2">, ("age"_c % 3) | as<"age_mod_3">,
|
||||
abs("age"_c * (-1)) | as<"abs_age">,
|
||||
round(exp(cast<double>("age"_c)), 2) | as<"exp_age">,
|
||||
round(sqrt(cast<double>("age"_c)), 2) | as<"sqrt_age">,
|
||||
("id"_c + 2 - "age"_c) | as<"id_plus_2_minus_age">,
|
||||
length(trim("first_name"_c)) | as<"length_first_name">,
|
||||
concat(ltrim("first_name"_c), " ", rtrim("last_name"_c)) |
|
||||
as<"full_name">,
|
||||
upper(rtrim(concat("first_name"_c, " "))) | as<"first_name_upper">,
|
||||
lower(ltrim(concat(" ", "first_name"_c))) | as<"first_name_lower">,
|
||||
replace("first_name"_c, "Bart", "Hugo") | as<"first_name_replaced">) |
|
||||
where("age"_c < 18) | order_by("age"_c.desc()) |
|
||||
to<std::vector<Children>>;
|
||||
|
||||
const auto children = sqlite::connect()
|
||||
.and_then(write(std::ref(people1)))
|
||||
.and_then(get_children)
|
||||
.value();
|
||||
|
||||
const std::string expected =
|
||||
R"([{"id_plus_age":11,"age_times_2":20,"id_plus_2_minus_age":-7,"age_mod_3":1,"abs_age":10,"exp_age":22026.47,"sqrt_age":3.16,"length_first_name":4,"full_name":"Bart Simpson","first_name_lower":"bart","first_name_upper":"BART","first_name_replaced":"Hugo"},{"id_plus_age":10,"age_times_2":16,"id_plus_2_minus_age":-4,"age_mod_3":2,"abs_age":8,"exp_age":2980.96,"sqrt_age":2.83,"length_first_name":4,"full_name":"Lisa Simpson","first_name_lower":"lisa","first_name_upper":"LISA","first_name_replaced":"Lisa"},{"id_plus_age":3,"age_times_2":0,"id_plus_2_minus_age":5,"age_mod_3":0,"abs_age":0,"exp_age":1.0,"sqrt_age":0.0,"length_first_name":6,"full_name":"Maggie Simpson","first_name_lower":"maggie","first_name_upper":"MAGGIE","first_name_replaced":"Maggie"}])";
|
||||
|
||||
EXPECT_EQ(rfl::json::write(children), expected);
|
||||
}
|
||||
|
||||
} // namespace test_operations
|
||||
|
||||
62
tests/sqlite/test_operations_with_nullable.cpp
Normal file
62
tests/sqlite/test_operations_with_nullable.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <optional>
|
||||
#include <ranges>
|
||||
#include <rfl.hpp>
|
||||
#include <rfl/json.hpp>
|
||||
#include <sqlgen.hpp>
|
||||
#include <sqlgen/sqlite.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace test_operations_with_nullable {
|
||||
|
||||
struct Person {
|
||||
sqlgen::PrimaryKey<uint32_t> id;
|
||||
std::string first_name;
|
||||
std::optional<std::string> last_name;
|
||||
std::optional<int> age;
|
||||
};
|
||||
|
||||
TEST(sqlite, test_operations_with_nullable) {
|
||||
const auto people1 = std::vector<Person>(
|
||||
{Person{
|
||||
.id = 0, .first_name = "Homer", .last_name = "Simpson", .age = 45},
|
||||
Person{.id = 1, .first_name = "Bart", .last_name = "Simpson", .age = 10},
|
||||
Person{.id = 2, .first_name = "Hugo", .age = 10},
|
||||
Person{.id = 3, .first_name = "Lisa", .last_name = "Simpson", .age = 8},
|
||||
Person{
|
||||
.id = 4, .first_name = "Maggie", .last_name = "Simpson", .age = 0}});
|
||||
|
||||
using namespace sqlgen;
|
||||
|
||||
struct Children {
|
||||
std::optional<int> id_plus_age;
|
||||
std::optional<int> age_times_2;
|
||||
std::optional<int> id_plus_2_minus_age;
|
||||
std::optional<std::string> full_name;
|
||||
std::string last_name_or_none;
|
||||
};
|
||||
|
||||
const auto get_children =
|
||||
select_from<Person>(
|
||||
("id"_c + "age"_c) | as<"id_plus_age">,
|
||||
("age"_c * 2) | as<"age_times_2">,
|
||||
("id"_c + 2 - "age"_c) | as<"id_plus_2_minus_age">,
|
||||
concat(upper("last_name"_c), ", ", "first_name"_c) | as<"full_name">,
|
||||
coalesce(upper("last_name"_c), "none") | as<"last_name_or_none">) |
|
||||
where("age"_c < 18) | to<std::vector<Children>>;
|
||||
|
||||
const auto children = sqlite::connect()
|
||||
.and_then(write(std::ref(people1)))
|
||||
.and_then(get_children)
|
||||
.value();
|
||||
|
||||
const std::string expected =
|
||||
R"([{"id_plus_age":11,"age_times_2":20,"id_plus_2_minus_age":-7,"full_name":"SIMPSON, Bart","last_name_or_none":"SIMPSON"},{"id_plus_age":12,"age_times_2":20,"id_plus_2_minus_age":-6,"last_name_or_none":"none"},{"id_plus_age":11,"age_times_2":16,"id_plus_2_minus_age":-3,"full_name":"SIMPSON, Lisa","last_name_or_none":"SIMPSON"},{"id_plus_age":4,"age_times_2":0,"id_plus_2_minus_age":6,"full_name":"SIMPSON, Maggie","last_name_or_none":"SIMPSON"}])";
|
||||
|
||||
EXPECT_EQ(rfl::json::write(children), expected);
|
||||
}
|
||||
|
||||
} // namespace test_operations_with_nullable
|
||||
|
||||
@@ -33,7 +33,7 @@ TEST(sqlite, test_where) {
|
||||
using namespace sqlgen;
|
||||
|
||||
const auto query = sqlgen::read<std::vector<Person>> |
|
||||
where("age"_c < 18 and "first_name"_c != "Hugo") |
|
||||
where("age"_c < 18 and not("first_name"_c == "Hugo")) |
|
||||
order_by("age"_c);
|
||||
|
||||
const auto people2 = query(conn).value();
|
||||
|
||||
48
tests/sqlite/test_where_with_nullable_operations.cpp
Normal file
48
tests/sqlite/test_where_with_nullable_operations.cpp
Normal file
@@ -0,0 +1,48 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <optional>
|
||||
#include <rfl.hpp>
|
||||
#include <rfl/json.hpp>
|
||||
#include <sqlgen.hpp>
|
||||
#include <sqlgen/sqlite.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace test_where_with_nullable_operations {
|
||||
|
||||
struct Person {
|
||||
sqlgen::PrimaryKey<uint32_t> id;
|
||||
std::string first_name;
|
||||
std::string last_name;
|
||||
std::optional<int> age;
|
||||
};
|
||||
|
||||
TEST(sqlite, test_where_with_nullable_operations) {
|
||||
const auto people1 = std::vector<Person>(
|
||||
{Person{
|
||||
.id = 0, .first_name = "Homer", .last_name = "Simpson", .age = 45},
|
||||
Person{.id = 1, .first_name = "Bart", .last_name = "Simpson", .age = 10},
|
||||
Person{.id = 2, .first_name = "Lisa", .last_name = "Simpson", .age = 8},
|
||||
Person{
|
||||
.id = 3, .first_name = "Maggie", .last_name = "Simpson", .age = 0},
|
||||
Person{
|
||||
.id = 4, .first_name = "Hugo", .last_name = "Simpson", .age = 10}});
|
||||
|
||||
const auto conn = sqlgen::sqlite::connect();
|
||||
|
||||
sqlgen::write(conn, people1);
|
||||
|
||||
using namespace sqlgen;
|
||||
|
||||
const auto query = sqlgen::read<std::vector<Person>> |
|
||||
where("age"_c * 2 + 4 < 40 and "first_name"_c != "Hugo") |
|
||||
order_by("age"_c);
|
||||
|
||||
const auto people2 = query(conn).value();
|
||||
|
||||
const std::string expected =
|
||||
R"([{"id":3,"first_name":"Maggie","last_name":"Simpson","age":0},{"id":2,"first_name":"Lisa","last_name":"Simpson","age":8},{"id":1,"first_name":"Bart","last_name":"Simpson","age":10}])";
|
||||
|
||||
EXPECT_EQ(rfl::json::write(people2), expected);
|
||||
}
|
||||
|
||||
} // namespace test_where_with_nullable_operations
|
||||
47
tests/sqlite/test_where_with_operations.cpp
Normal file
47
tests/sqlite/test_where_with_operations.cpp
Normal file
@@ -0,0 +1,47 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <rfl.hpp>
|
||||
#include <rfl/json.hpp>
|
||||
#include <sqlgen.hpp>
|
||||
#include <sqlgen/sqlite.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace test_where_with_operations {
|
||||
|
||||
struct Person {
|
||||
sqlgen::PrimaryKey<uint32_t> id;
|
||||
std::string first_name;
|
||||
std::string last_name;
|
||||
int age;
|
||||
};
|
||||
|
||||
TEST(sqlite, test_where_with_operations) {
|
||||
const auto people1 = std::vector<Person>(
|
||||
{Person{
|
||||
.id = 0, .first_name = "Homer", .last_name = "Simpson", .age = 45},
|
||||
Person{.id = 1, .first_name = "Bart", .last_name = "Simpson", .age = 10},
|
||||
Person{.id = 2, .first_name = "Lisa", .last_name = "Simpson", .age = 8},
|
||||
Person{
|
||||
.id = 3, .first_name = "Maggie", .last_name = "Simpson", .age = 0},
|
||||
Person{
|
||||
.id = 4, .first_name = "Hugo", .last_name = "Simpson", .age = 10}});
|
||||
|
||||
const auto conn = sqlgen::sqlite::connect();
|
||||
|
||||
sqlgen::write(conn, people1);
|
||||
|
||||
using namespace sqlgen;
|
||||
|
||||
const auto query = sqlgen::read<std::vector<Person>> |
|
||||
where("age"_c * 2 + 4 < 40 and "first_name"_c != "Hugo") |
|
||||
order_by("age"_c);
|
||||
|
||||
const auto people2 = query(conn).value();
|
||||
|
||||
const std::string expected =
|
||||
R"([{"id":3,"first_name":"Maggie","last_name":"Simpson","age":0},{"id":2,"first_name":"Lisa","last_name":"Simpson","age":8},{"id":1,"first_name":"Bart","last_name":"Simpson","age":10}])";
|
||||
|
||||
EXPECT_EQ(rfl::json::write(people2), expected);
|
||||
}
|
||||
|
||||
} // namespace test_where_with_operations
|
||||
@@ -20,7 +20,8 @@
|
||||
"dependencies": [
|
||||
{
|
||||
"name": "sqlite3",
|
||||
"version>=": "3.49.1"
|
||||
"version>=": "3.49.1",
|
||||
"features": ["math"]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user