31template <TableType Table, ConditionExpr Condition>
42template <ConditionExpr Condition>
43auto on(Condition cond) {
58template <
typename Columns,
typename Tables = std::tuple<>,
typename Joins = std::tuple<>,
59 typename Where = std::nullopt_t,
typename GroupBys = std::tuple<>,
60 typename OrderBys = std::tuple<>,
typename HavingCond = std::nullopt_t,
61 typename LimitVal = std::nullopt_t,
typename OffsetVal = std::nullopt_t,
62 bool IsDistinct = false>
86 explicit SelectQuery(Columns columns, Tables tables = {}, Joins joins = {},
87 Where
where = std::nullopt, GroupBys group_bys = {}, OrderBys order_bys = {},
88 HavingCond
having = std::nullopt, LimitVal
limit = std::nullopt,
89 OffsetVal
offset = std::nullopt)
90 : columns_(
std::move(columns)), tables_(
std::move(tables)), joins_(
std::move(joins)),
91 where_(
std::move(
where)), group_bys_(
std::move(group_bys)),
102 if constexpr (IsDistinct) {
110 if constexpr (!is_empty_tuple<Tables>()) {
114 [&](
const auto&... tables) { ((ss << (i++ > 0 ?
", " :
"") << tables.table_name), ...); },
119 if constexpr (!is_empty_tuple<Joins>()) {
121 [&](
const auto&
join) {
125 case JoinType::Inner:
131 case JoinType::Right:
137 case JoinType::Cross:
142 ss <<
join.table.table_name;
146 ss <<
join.condition.to_sql();
153 if constexpr (!std::is_same_v<Where, std::nullopt_t>) {
154 if (where_.has_value()) {
155 ss <<
" WHERE " << where_.value().to_sql();
160 if constexpr (!is_empty_tuple<GroupBys>()) {
165 if constexpr (!std::is_same_v<HavingCond, std::nullopt_t>) {
166 if (having_.has_value()) {
167 ss <<
" HAVING " << having_.value().to_sql();
172 if constexpr (!is_empty_tuple<OrderBys>()) {
177 if constexpr (!std::is_same_v<LimitVal, std::nullopt_t>) {
178 if constexpr (std::is_same_v<LimitVal, Value<int>>) {
179 ss <<
" LIMIT " << limit_.to_sql();
180 }
else if (limit_.has_value()) {
181 ss <<
" LIMIT " << limit_.value().to_sql();
186 if constexpr (!std::is_same_v<OffsetVal, std::nullopt_t>) {
187 if constexpr (std::is_same_v<OffsetVal, Value<int>>) {
188 ss <<
" OFFSET " << offset_.to_sql();
189 }
else if (offset_.has_value()) {
190 ss <<
" OFFSET " << offset_.value().to_sql();
200 std::vector<std::string> params;
203 if constexpr (!is_empty_tuple<Columns>()) {
205 params.insert(params.end(), column_params.begin(), column_params.end());
211 if constexpr (!is_empty_tuple<Joins>()) {
213 [&](
const auto&... joins) {
216 auto join_params = joins.condition.bind_params();
217 params.insert(params.end(), join_params.begin(), join_params.end());
218 }
catch (
const std::exception& e) {
220 std::print(
"Error collecting join params: {}", e.what());
229 if constexpr (!std::is_same_v<Where, std::nullopt_t>) {
230 if (where_.has_value()) {
231 auto where_params = where_.value().bind_params();
232 params.insert(params.end(), where_params.begin(), where_params.end());
237 if constexpr (!is_empty_tuple<GroupBys>()) {
239 params.insert(params.end(), group_params.begin(), group_params.end());
243 if constexpr (!std::is_same_v<HavingCond, std::nullopt_t>) {
244 if (having_.has_value()) {
245 auto having_params = having_.value().bind_params();
246 params.insert(params.end(), having_params.begin(), having_params.end());
251 if constexpr (!is_empty_tuple<OrderBys>()) {
253 params.insert(params.end(), order_params.begin(), order_params.end());
257 if constexpr (!std::is_same_v<LimitVal, std::nullopt_t>) {
258 if constexpr (std::is_same_v<LimitVal, Value<int>>) {
259 auto limit_params = limit_.bind_params();
260 params.insert(params.end(), limit_params.begin(), limit_params.end());
261 }
else if (limit_.has_value()) {
262 auto limit_params = limit_.value().bind_params();
263 params.insert(params.end(), limit_params.begin(), limit_params.end());
268 if constexpr (!std::is_same_v<OffsetVal, std::nullopt_t>) {
269 if constexpr (std::is_same_v<OffsetVal, Value<int>>) {
270 auto offset_params = offset_.bind_params();
271 params.insert(params.end(), offset_params.begin(), offset_params.end());
272 }
else if (offset_.has_value()) {
273 auto offset_params = offset_.value().bind_params();
274 params.insert(params.end(), offset_params.begin(), offset_params.end());
285 template <TableType T>
286 auto from(
const T& table)
const {
287 using new_tables_type =
decltype(std::tuple_cat(tables_, std::tuple<T>()));
288 return SelectQuery<Columns, new_tables_type, Joins, Where, GroupBys, OrderBys, HavingCond,
289 LimitVal, OffsetVal, IsDistinct>(
290 columns_, std::tuple_cat(tables_, std::tuple<T>(table)), joins_, where_, group_bys_,
291 order_bys_, having_, limit_, offset_);
299 auto from(
const Args&... tables)
const {
300 using new_tables_type =
decltype(std::tuple_cat(tables_, std::tuple<Args...>()));
301 return SelectQuery<Columns, new_tables_type, Joins, Where, GroupBys, OrderBys, HavingCond,
302 LimitVal, OffsetVal, IsDistinct>(
303 columns_, std::tuple_cat(tables_, std::tuple<Args...>(tables...)), joins_, where_,
304 group_bys_, order_bys_, having_, limit_, offset_);
314 template <TableType Table, ConditionExpr Condition>
317 using new_joins_type =
decltype(std::tuple_cat(
318 joins_, std::tuple<JoinSpec>{
JoinSpec{table, cond, type}}));
320 return SelectQuery<Columns, Tables, new_joins_type, Where, GroupBys, OrderBys, HavingCond,
321 LimitVal, OffsetVal, IsDistinct>(
323 std::tuple_cat(joins_, std::tuple<JoinSpec>{
JoinSpec{table, cond, type}}), where_,
324 group_bys_, order_bys_, having_, limit_, offset_);
333 template <TableType Table, ConditionExpr Condition>
334 auto left_join(
const Table& table,
const Condition& cond)
const {
344 template <TableType Table, ConditionExpr Condition>
345 auto right_join(
const Table& table,
const Condition& cond)
const {
355 template <TableType Table, ConditionExpr Condition>
356 auto full_join(
const Table& table,
const Condition& cond)
const {
364 template <TableType Table>
369 std::string to_sql()
const override {
return "1=1"; }
370 std::vector<std::string> bind_params()
const override {
return {}; }
380 template <ConditionExpr Condition>
381 auto where(
const Condition& cond)
const {
383 HavingCond, LimitVal, OffsetVal, IsDistinct>(
384 columns_, tables_, joins_, std::optional<Condition>(cond), group_bys_, order_bys_, having_,
392 template <
typename... Args>
394 auto transform_arg = []<
typename T>(
const T& arg) {
402 using new_group_bys_type =
decltype(std::tuple_cat(
403 group_bys_, std::make_tuple(transform_arg(std::declval<Args>())...)));
405 return SelectQuery<Columns, Tables, Joins, Where, new_group_bys_type, OrderBys, HavingCond,
406 LimitVal, OffsetVal, IsDistinct>(
407 columns_, tables_, joins_, where_,
408 std::tuple_cat(group_bys_, std::make_tuple(transform_arg(args)...)), order_bys_, having_,
416 template <ConditionExpr Condition>
417 auto having(
const Condition& cond)
const {
419 LimitVal, OffsetVal, IsDistinct>(
420 columns_, tables_, joins_, where_, group_bys_, order_bys_, std::optional<Condition>(cond),
430 using new_order_bys_type =
decltype(std::tuple_cat(order_bys_, std::tuple<Args...>{args...}));
432 return SelectQuery<Columns, Tables, Joins, Where, GroupBys, new_order_bys_type, HavingCond,
433 LimitVal, OffsetVal, IsDistinct>(
434 columns_, tables_, joins_, where_, group_bys_,
435 std::tuple_cat(order_bys_, std::tuple<Args...>{args...}), having_, limit_, offset_);
442 template <
typename T>
449 std::is_arithmetic_v<ColumnType> || std::same_as<ColumnType, std::string> ||
450 std::same_as<ColumnType, std::string_view>,
451 "ORDER BY can only be used with comparable columns (numeric types, strings). "
452 "Complex types, boolean columns, or non-comparable types cannot be used for ordering.");
463 return SelectQuery<Columns, Tables, Joins, Where, GroupBys, OrderBys, HavingCond,
464 decltype(limit_expr), OffsetVal, IsDistinct>(
465 columns_, tables_, joins_, where_, group_bys_, order_bys_, having_, std::move(limit_expr),
475 return SelectQuery<Columns, Tables, Joins, Where, GroupBys, OrderBys, HavingCond, LimitVal,
476 decltype(offset_expr), IsDistinct>(columns_, tables_, joins_, where_,
477 group_bys_, order_bys_, having_, limit_,
478 std::move(offset_expr));
500template <
typename... Args>
511 auto transform_arg = []<
typename T>(
const T& arg) {
519 return SelectQuery(std::make_tuple(transform_arg(args)...));
527template <
typename... Args>
536template <SqlExpr Expr>
541 std::string
to_sql()
const override {
return expr_.to_sql() +
" DESC"; }
543 std::vector<std::string>
bind_params()
const override {
return expr_.bind_params(); }
553template <SqlExpr Expr>
559 requires ColumnType<T>
568template <SqlExpr Expr>
573 std::string
to_sql()
const override {
return expr_.to_sql() +
" ASC"; }
575 std::vector<std::string>
bind_params()
const override {
return expr_.bind_params(); }
585template <SqlExpr Expr>
595 requires ColumnType<T>
601template <TableType Table>
606 std::string to_sql()
const override {
return "*"; }
608 std::vector<std::string> bind_params()
const override {
return {}; }
618template <TableType Table>
631template <
typename... Args>
636 std::tuple<>, std::tuple<>, std::nullopt_t, std::nullopt_t, std::nullopt_t,
640 return SelectQuery<std::tuple<Args...>, std::tuple<>, std::tuple<>, std::nullopt_t,
641 std::tuple<>, std::tuple<>, std::nullopt_t, std::nullopt_t, std::nullopt_t,
642 true>(std::tuple<Args...>(args...));
645 auto transform_arg = []<
typename T>(
const T& arg) {
654 std::tuple<>, std::nullopt_t, std::tuple<>, std::tuple<>, std::nullopt_t,
655 std::nullopt_t, std::nullopt_t,
true>(
656 std::make_tuple(transform_arg(args)...));
664template <SqlExpr... Args>
673template <TableType Table>
678 std::string to_sql()
const override {
return "*"; }
680 std::vector<std::string> bind_params()
const override {
return {}; }
685 std::tuple<>, std::tuple<>, std::nullopt_t, std::nullopt_t, std::nullopt_t,
686 true>(std::tuple<StarExpression>())
693template <TableType Table>
Helper for creating an ascending order by expression.
std::vector< std::string > bind_params() const override
std::string to_sql() const override
Column reference expression.
Helper for creating a descending order by expression.
DescendingExpr(Expr expr)
std::vector< std::string > bind_params() const override
std::string to_sql() const override
Base SELECT query builder.
auto cross_join(const Table &table) const
Add a CROSS JOIN clause to the query.
auto right_join(const Table &table, const Condition &cond) const
Add a RIGHT JOIN clause to the query.
auto limit(int limit) const
Add a LIMIT clause to the query.
auto from(const Args &... tables) const
Add a FROM clause with multiple tables.
auto where(const Condition &cond) const
Add a WHERE clause to the query.
std::string to_sql() const
Generate the SQL for this SELECT query.
auto order_by(const T &column) const
Add an ORDER BY clause to the query.
auto left_join(const Table &table, const Condition &cond) const
Add a LEFT JOIN clause to the query.
SelectQuery(Columns columns, Tables tables={}, Joins joins={}, Where where=std::nullopt, GroupBys group_bys={}, OrderBys order_bys={}, HavingCond having=std::nullopt, LimitVal limit=std::nullopt, OffsetVal offset=std::nullopt)
Constructor for the SELECT query builder.
auto full_join(const Table &table, const Condition &cond) const
Add a FULL JOIN clause to the query.
std::vector< std::string > bind_params() const
Get the bind parameters for this SELECT query.
auto join(const Table &table, const Condition &cond, JoinType type=JoinType::Inner) const
Add a JOIN clause to the query.
auto from(const T &table) const
Add a FROM clause to the query.
auto having(const Condition &cond) const
Add a HAVING clause to the query.
auto order_by(const Args &... args) const
Add an ORDER BY clause to the query.
auto group_by(const Args &... args) const
Add a GROUP BY clause to the query.
auto offset(int offset) const
Add an OFFSET clause to the query.
Represents a literal value in a SQL query.
Represents a column in a database table.
Concept for column types.
Concept for SQL expression components.
Concept for database table types.
std::string tuple_to_sql(const Tuple &tuple, const char *separator)
Helper to convert a tuple of expressions to SQL.
auto asc(Expr expr)
Create an ascending order by expression.
JoinType
Types of JOIN operations.
auto desc(Expr expr)
Create a descending order by expression.
auto select_expr(const schema::column< TableT, Name, T, Modifiers... > &col, Args &&... args)
static void apply_tuple(Func &&func, const Tuple &tuple)
Helper to apply a function to each element of a tuple.
auto select_distinct(const Args &... args)
Create a SELECT DISTINCT query with the specified columns or expressions.
auto select_distinct_all()
Create a SELECT DISTINCT * query without requiring a table instance.
auto to_expr(const C &col, std::string_view table_name="")
Helper to wrap a schema column in a SQL expression.
auto select(const Args &... args)
Create a column reference from a member pointer without requiring a table instance.
std::vector< std::string > tuple_bind_params(const Tuple &tuple)
Helper to collect bind parameters from a tuple of expressions.
auto from(const SelectQuery< Columns > &query, const Table &table)
FROM extension for schema tables with automatic adapter creation.
auto select_all()
Create a SELECT * query without requiring a table instance.
auto on(Condition cond)
Create a join condition with the ON clause.
auto select_distinct_expr(const Args &... args)
Create a SELECT DISTINCT query with the specified column expressions.
Base class for SQL expressions.