16template <SqlExpr Expr>
19 FunctionExpr(std::string name, Expr expr) : func_name_(
std::move(name)), expr_(
std::move(expr)) {}
21 std::string
to_sql()
const override {
return func_name_ +
"(" + expr_.to_sql() +
")"; }
23 std::vector<std::string>
bind_params()
const override {
return expr_.bind_params(); }
25 std::string
column_name()
const override {
return func_name_ +
"(" + expr_.column_name() +
")"; }
28 if constexpr (std::is_base_of_v<ColumnExpression, Expr>) {
30 return expr_.table_name();
37 std::string func_name_;
46 std::string
to_sql()
const override {
return func_name_ +
"()"; }
48 std::vector<std::string>
bind_params()
const override {
return {}; }
50 std::string
column_name()
const override {
return func_name_ +
"()"; }
55 std::string func_name_;
61 std::string
to_sql()
const override {
return "COUNT(*)"; }
63 std::vector<std::string>
bind_params()
const override {
return {}; }
65 std::string
column_name()
const override {
return "COUNT(*)"; }
80template <SqlExpr Expr>
87 requires ColumnType<T>
96template <SqlExpr Expr>
103 requires ColumnType<T>
109namespace aggregate_checking {
113concept Summable = std::is_arithmetic_v<std::remove_cvref_t<T>> &&
114 !std::same_as<std::remove_cvref_t<T>,
bool>;
122concept Comparable = std::is_arithmetic_v<std::remove_cvref_t<T>> ||
123 std::same_as<std::remove_cvref_t<T>, std::string> ||
124 std::same_as<std::remove_cvref_t<T>, std::string_view>;
145template <SqlExpr Expr>
152 requires ColumnType<T>
156 "SUM can only be used with numeric columns (int, long, float, double, etc.). "
157 "Boolean columns are not summable.");
165template <SqlExpr Expr>
172 requires ColumnType<T>
176 "AVG can only be used with numeric columns (int, long, float, double, etc.). "
177 "Boolean and string columns cannot be averaged.");
185template <SqlExpr Expr>
192 requires ColumnType<T>
196 "MIN can only be used with comparable columns (numeric types or strings). "
197 "Boolean columns cannot be compared for MIN/MAX.");
205template <SqlExpr Expr>
212 requires ColumnType<T>
216 "MAX can only be used with comparable columns (numeric types or strings). "
217 "Boolean columns cannot be compared for MIN/MAX.");
225template <SqlExpr Expr>
230 std::string
to_sql()
const override {
return "DISTINCT " + expr_.to_sql(); }
232 std::vector<std::string>
bind_params()
const override {
return expr_.bind_params(); }
235 if constexpr (std::is_base_of_v<ColumnExpression, Expr>) {
236 return "DISTINCT_" + expr_.column_name();
238 return "DISTINCT_EXPR";
243 if constexpr (std::is_base_of_v<ColumnExpression, Expr>) {
244 return expr_.table_name();
258template <SqlExpr Expr>
265 requires ColumnType<T>
274template <SqlExpr Expr>
281 requires ColumnType<T>
285 std::same_as<column_type, std::string> || std::same_as<column_type, std::string_view> ||
286 std::same_as<column_type, const char*>,
287 "LOWER can only be used with string columns (std::string, std::string_view, const char*). "
288 "Numeric and boolean columns cannot be converted to lowercase.");
296template <SqlExpr Expr>
303 requires ColumnType<T>
307 std::same_as<column_type, std::string> || std::same_as<column_type, std::string_view> ||
308 std::same_as<column_type, const char*>,
309 "UPPER can only be used with string columns (std::string, std::string_view, const char*). "
310 "Numeric and boolean columns cannot be converted to uppercase.");
318template <SqlExpr Expr>
325 requires ColumnType<T>
334template <SqlExpr Expr>
341 requires ColumnType<T>
354template <SqlExpr First, SqlExpr Second, SqlExpr... Rest>
359 rest_(
std::make_tuple(
std::move(rest)...)) {}
362 std::stringstream ss;
363 ss <<
"COALESCE(" << first_.to_sql() <<
", " << second_.to_sql();
365 std::apply([&](
const auto&... items) { ((ss <<
", " << items.to_sql()), ...); }, rest_);
372 std::vector<std::string> params;
374 auto first_params = first_.bind_params();
375 params.insert(params.end(), first_params.begin(), first_params.end());
377 auto second_params = second_.bind_params();
378 params.insert(params.end(), second_params.begin(), second_params.end());
381 [&](
const auto&... items) {
382 ((params.insert(params.end(), items.bind_params().begin(), items.bind_params().end())),
397 std::tuple<Rest...> rest_;
408template <SqlExpr First, SqlExpr Second, SqlExpr... Rest>
409auto coalesce(First first, Second second, Rest... rest) {
415template <
typename T, SqlExpr Second, SqlExpr... Rest>
416 requires ColumnType<T>
422template <
typename T1,
typename T2, SqlExpr... Rest>
423 requires ColumnType<T1> && ColumnType<T2>
424auto coalesce(
const T1& column1,
const T2& column2, Rest... rest) {
430 requires ColumnType<T>
437 requires ColumnType<T>
443template <
typename T1,
typename T2>
444 requires ColumnType<T1> && ColumnType<T2>
445auto coalesce(
const T1& column1,
const T2& column2,
const char* str) {
452 using WhenThenPair = std::pair<std::unique_ptr<SqlExpression>, std::unique_ptr<SqlExpression>>;
454 explicit CaseExpr(std::vector<WhenThenPair>&& when_thens,
455 std::unique_ptr<SqlExpression> else_expr)
456 : when_thens_(
std::move(when_thens)), else_expr_(
std::move(else_expr)) {}
467 std::string sql =
"CASE";
469 for (
const auto& [when_cond, then_val] : when_thens_) {
471 std::string cond = when_cond->to_sql();
473 if (cond.size() >= 2 && cond.front() ==
'(' && cond.back() ==
')') {
474 cond = cond.substr(1, cond.size() - 2);
476 sql += cond +
") THEN " + then_val->to_sql();
480 sql +=
" ELSE " + else_expr_->to_sql();
488 std::vector<std::string> params;
491 for (
const auto& [when_cond, then_val] : when_thens_) {
493 auto when_params = when_cond->bind_params();
494 params.insert(params.end(), when_params.begin(), when_params.end());
497 auto then_params = then_val->bind_params();
498 params.insert(params.end(), then_params.begin(), then_params.end());
503 auto else_params = else_expr_->bind_params();
504 params.insert(params.end(), else_params.begin(), else_params.end());
515 std::vector<WhenThenPair> when_thens_;
516 std::unique_ptr<SqlExpression> else_expr_;
520template <
typename ResultType =
void>
523 using WhenThenPair = std::pair<std::unique_ptr<SqlExpression>, std::unique_ptr<SqlExpression>>;
526 std::vector<WhenThenPair> when_thens_;
527 std::unique_ptr<SqlExpression> else_expr_;
534 : when_thens_(std::move(other.when_thens_)), else_expr_(std::move(other.else_expr_)) {}
538 if (
this != &other) {
539 when_thens_ = std::move(other.when_thens_);
540 else_expr_ = std::move(other.else_expr_);
550 template <
typename OtherType>
552 : when_thens_(
std::move(other.when_thens_)), else_expr_(
std::move(other.else_expr_)) {}
554 template <SqlExpr Then>
556 if constexpr (std::same_as<ResultType, void>) {
558 when_thens_.emplace_back(
559 std::make_unique<std::remove_cvref_t<
decltype(when_cond)>>(when_cond),
560 std::make_unique<Then>(then));
564 static_assert(std::same_as<std::remove_cvref_t<Then>, std::remove_cvref_t<ResultType>>,
565 "All WHEN/THEN branches in CASE expressions must return the same type. "
566 "Mixed types in CASE branches can lead to runtime type errors.");
568 when_thens_.emplace_back(
569 std::make_unique<std::remove_cvref_t<
decltype(when_cond)>>(when_cond),
570 std::make_unique<Then>(then));
571 return std::move(*
this);
604 template <SqlExpr Else>
607 if constexpr (!std::same_as<ResultType, void>) {
608 static_assert(std::same_as<std::remove_cvref_t<Else>, std::remove_cvref_t<ResultType>>,
609 "ELSE clause type must match the WHEN/THEN branch types in CASE expressions.");
612 else_expr_ = std::make_unique<Else>(else_expr);
613 return std::move(*
this);
641 auto build() {
return CaseExpr(std::move(when_thens_), std::move(else_expr_)); }
662 auto expr_ptr = std::make_unique<CaseExpr>(std::move(expr));
std::string to_sql() const override
CaseExpr & operator=(CaseExpr &&)=default
std::string table_name() const override
std::pair< std::unique_ptr< SqlExpression >, std::unique_ptr< SqlExpression > > WhenThenPair
CaseExpr(CaseExpr &&)=default
std::vector< std::string > bind_params() const override
CaseExpr & operator=(const CaseExpr &)=delete
std::string column_name() const override
CaseExpr(const CaseExpr &)=delete
CaseExpr(std::vector< WhenThenPair > &&when_thens, std::unique_ptr< SqlExpression > else_expr)
std::vector< std::string > bind_params() const override
std::string table_name() const override
CoalesceExpr(First first, Second second, Rest... rest)
std::string to_sql() const override
std::string column_name() const override
Base class for column expressions.
Expression representing COUNT(*) in SQL.
std::string to_sql() const override
std::vector< std::string > bind_params() const override
std::string table_name() const override
std::string column_name() const override
DISTINCT qualifier for an expression.
std::string table_name() const override
std::string to_sql() const override
std::vector< std::string > bind_params() const override
std::string column_name() const override
Base class for SQL function expressions.
FunctionExpr(std::string name, Expr expr)
std::string to_sql() const override
std::string column_name() const override
std::string table_name() const override
std::vector< std::string > bind_params() const override
Base class for SQL function expressions with no arguments.
std::string table_name() const override
std::string to_sql() const override
NullaryFunctionExpr(std::string name)
std::string column_name() const override
std::vector< std::string > bind_params() const override
auto when(const ConditionExpr auto &when_cond, const Then &then)
auto when(const ConditionExpr auto &when_cond, int then)
std::pair< std::unique_ptr< SqlExpression >, std::unique_ptr< SqlExpression > > WhenThenPair
auto when(const ConditionExpr auto &when_cond, const char *then)
TypedCaseBuilder(TypedCaseBuilder &&other) noexcept
auto else_(int else_value)
auto else_(const Else &else_expr)
TypedCaseBuilder & operator=(TypedCaseBuilder &&other) noexcept
auto else_(const std::string &else_value)
auto else_(float else_value)
TypedCaseBuilder & operator=(const TypedCaseBuilder &)=delete
TypedCaseBuilder(const TypedCaseBuilder &)=delete
auto else_(bool else_value)
auto when(const ConditionExpr auto &when_cond, float then)
auto when(const ConditionExpr auto &when_cond, const std::string &then)
auto when(const ConditionExpr auto &when_cond, double then)
auto when(const ConditionExpr auto &when_cond, bool then)
auto when(const ConditionExpr auto &when_cond, long then)
auto else_(long else_value)
TypedCaseBuilder(TypedCaseBuilder< OtherType > &&other)
auto else_(const char *else_value)
auto else_(double else_value)
Represents a column in a database table.
Concept for a condition expression.
Concept for comparable types suitable for MIN/MAX.
Concept for types suitable for COUNT (any type is countable)
Concept for numeric types suitable for SUM and AVG.
typename extract_column_type< T >::type extract_column_type_t
auto min(Expr expr)
MIN aggregate function.
auto second(const T &date_column)
Get the second from a timestamp.
auto avg(Expr expr)
AVG aggregate function.
auto trim(Expr expr)
TRIM string function.
auto max(Expr expr)
MAX aggregate function.
auto sum(Expr expr)
SUM aggregate function.
auto length(Expr expr)
LENGTH string function.
auto as(const Expr &expr, std::string alias)
Create an aliased column expression.
auto case_()
Create a CASE expression with type checking.
auto to_expr(const C &col, std::string_view table_name="")
Helper to wrap a schema column in a SQL expression.
auto count_distinct(Expr expr)
COUNT(DISTINCT expr) aggregate function.
auto count(Expr expr)
COUNT aggregate function.
auto coalesce(First first, Second second, Rest... rest)
Create a COALESCE expression.
auto lower(Expr expr)
LOWER string function.
auto distinct(Expr expr)
Create a DISTINCT expression.
auto val(const char *str)
Helper to create a value expression from a string literal.
auto upper(Expr expr)
UPPER string function.
auto count_all()
COUNT(*) aggregate function.
Helper to extract column type for checking.
Compile-time string type that can be used as template non-type parameter in C++20.