6#include "../SqlQueryFormatter.hpp"
29static constexpr inline auto SqlWildcard = SqlWildcardType {};
32struct AliasedTableName
34 std::string_view tableName;
35 std::string_view alias;
37 std::weak_ordering operator<=>(AliasedTableName
const&)
const =
default;
42 std::convertible_to<T, std::string_view> || std::convertible_to<T, std::string> || std::same_as<T, AliasedTableName>;
47 struct RawSqlCondition
49 std::string condition;
56struct SqlQualifiedTableColumnName
58 std::string_view tableName;
59 std::string_view columnName;
67template <Reflection::StringLiteral columnLiteral>
68constexpr SqlQualifiedTableColumnName QualifiedColumnName = []()
consteval {
72 !std::ranges::any_of(columnLiteral,
73 [](
char c)
consteval {
return c ==
'\\' || c ==
'[' || c ==
']' || c ==
'"' || c ==
'\''; }),
74 "QualifiedColumnName should not contain symbols \\ [ ] \" '");
77 static_assert(std::ranges::count(columnLiteral,
'.') == 1,
78 "QualifiedColumnName requires a column name with a single '.' to separate table and column name");
79 constexpr auto column = columnLiteral.sv();
80 auto dotPos = column.find(
'.');
81 return SqlQualifiedTableColumnName { .tableName = column.substr(0, dotPos), .columnName = column.substr(dotPos + 1) };
87 template <
typename ColumnName>
88 std::string MakeSqlColumnName(ColumnName
const& columnName)
90 using namespace std::string_view_literals;
93 if constexpr (std::is_same_v<ColumnName, SqlQualifiedTableColumnName>)
95 output.reserve(columnName.tableName.size() + columnName.columnName.size() + 5);
97 output += columnName.tableName;
99 output += columnName.columnName;
102 else if constexpr (std::is_same_v<ColumnName, SqlRawColumnNameView>)
104 output += columnName.value;
106 else if constexpr (std::is_same_v<ColumnName, SqlWildcardType>)
113 output += columnName;
119 template <
typename T>
120 std::string MakeEscapedSqlString(T
const& value)
122 std::string escapedValue;
123 escapedValue +=
'\'';
125 for (
auto const ch: value)
129 escapedValue +=
'\'';
132 escapedValue +=
'\'';
138struct [[nodiscard]] SqlSearchCondition
140 std::string tableName;
141 std::string tableAlias;
142 std::string tableJoins;
143 std::string condition;
144 std::vector<SqlVariant>* inputBindings =
nullptr;
149class SqlJoinConditionBuilder
152 explicit SqlJoinConditionBuilder(std::string_view referenceTable, std::string* condition)
noexcept:
153 _referenceTable { referenceTable },
154 _condition { *condition }
158 SqlJoinConditionBuilder& On(std::string_view joinColumnName, SqlQualifiedTableColumnName onOtherColumn)
160 return Operator(joinColumnName, onOtherColumn,
"AND");
163 SqlJoinConditionBuilder& OrOn(std::string_view joinColumnName, SqlQualifiedTableColumnName onOtherColumn)
165 return Operator(joinColumnName, onOtherColumn,
"OR");
168 SqlJoinConditionBuilder& Operator(std::string_view joinColumnName,
169 SqlQualifiedTableColumnName onOtherColumn,
173 _firstCall = !_firstCall;
175 _condition += std::format(
" {} ", op);
178 _condition += _referenceTable;
179 _condition +=
"\".\"";
180 _condition += joinColumnName;
181 _condition +=
"\" = \"";
182 _condition += onOtherColumn.tableName;
183 _condition +=
"\".\"";
184 _condition += onOtherColumn.columnName;
191 std::string_view _referenceTable;
192 std::string& _condition;
193 bool _firstCall =
true;
201template <
typename Derived>
202class [[nodiscard]] SqlWhereClauseBuilder
206 [[nodiscard]] Derived& And() noexcept;
209 [[nodiscard]] Derived& Or() noexcept;
212 [[nodiscard]] Derived& Not() noexcept;
215 [[nodiscard]] Derived& WhereRaw(std::string_view sqlConditionExpression);
218 template <typename ColumnName, typename T>
219 [[nodiscard]] Derived& Where(ColumnName const& columnName, std::string_view binaryOp, T const& value);
222 template <typename ColumnName, typename SubSelectQuery>
223 requires(std::is_invocable_r_v<std::
string, decltype(&SubSelectQuery::ToSql), SubSelectQuery const&>)
224 [[nodiscard]] Derived& Where(ColumnName const& columnName, std::string_view binaryOp, SubSelectQuery const& value);
227 template <typename ColumnName, typename T>
228 [[nodiscard]] Derived& OrWhere(ColumnName const& columnName, std::string_view binaryOp, T const& value);
231 template <typename ColumnName, std::
size_t N>
232 Derived& Where(ColumnName const& columnName, std::string_view binaryOp,
char const (&value)[N]);
235 template <typename ColumnName, typename T>
236 [[nodiscard]] Derived& Where(ColumnName const& columnName, T const& value);
239 template <typename ColumnName, typename T>
240 [[nodiscard]] Derived& OrWhere(ColumnName const& columnName, T const& value);
243 template <typename Callable>
244 requires std::invocable<Callable, SqlWhereClauseBuilder<Derived>&>
245 [[nodiscard]] Derived& Where(Callable const& callable);
248 template <typename Callable>
249 requires std::invocable<Callable, SqlWhereClauseBuilder<Derived>&>
250 [[nodiscard]] Derived& OrWhere(Callable const& callable);
253 template <typename ColumnName, std::ranges::input_range InputRange>
254 [[nodiscard]] Derived& WhereIn(ColumnName const& columnName, InputRange const& values);
257 template <typename ColumnName, typename T>
258 [[nodiscard]] Derived& WhereIn(ColumnName const& columnName, std::initializer_list<T> const& values);
261 template <typename ColumnName, typename SubSelectQuery>
262 requires(std::is_invocable_r_v<std::
string, decltype(&SubSelectQuery::ToSql), SubSelectQuery const&>)
263 [[nodiscard]] Derived& WhereIn(ColumnName const& columnName, SubSelectQuery const& subSelectQuery);
266 template <typename ColumnName>
267 [[nodiscard]] Derived& WhereNull(ColumnName const& columnName);
270 template <typename ColumnName>
271 [[nodiscard]] Derived& WhereNotNull(ColumnName const& columnName);
274 template <typename ColumnName, typename T>
275 [[nodiscard]] Derived& WhereNotEqual(ColumnName const& columnName, T const& value);
278 template <typename ColumnName>
279 [[nodiscard]] Derived& WhereTrue(ColumnName const& columnName);
282 template <typename ColumnName>
283 [[nodiscard]] Derived& WhereFalse(ColumnName const& columnName);
286 template <typename LeftColumn, typename RightColumn>
287 [[nodiscard]] Derived& WhereColumn(LeftColumn const& left, std::string_view binaryOp, RightColumn const& right);
294 [[nodiscard]] Derived& InnerJoin(TableName auto joinTable,
295 std::string_view joinColumnName,
296 SqlQualifiedTableColumnName onOtherColumn);
299 [[nodiscard]] Derived& InnerJoin(TableName auto joinTable,
300 std::string_view joinColumnName,
301 std::string_view onMainTableColumn);
304 template <typename OnChainCallable>
305 requires std::invocable<OnChainCallable, SqlJoinConditionBuilder>
306 [[nodiscard]] Derived& InnerJoin(TableName auto joinTable, OnChainCallable const& onClauseBuilder);
319 template <auto LeftField, auto RightField>
320 [[nodiscard]] Derived& InnerJoin();
323 [[nodiscard]] Derived& LeftOuterJoin(TableName auto joinTable,
324 std::string_view joinColumnName,
325 SqlQualifiedTableColumnName onOtherColumn);
328 [[nodiscard]] Derived& LeftOuterJoin(TableName auto joinTable,
329 std::string_view joinColumnName,
330 std::string_view onMainTableColumn);
333 template <typename OnChainCallable>
334 requires std::invocable<OnChainCallable, SqlJoinConditionBuilder>
335 [[nodiscard]] Derived& LeftOuterJoin(TableName auto joinTable, OnChainCallable const& onClauseBuilder);
338 [[nodiscard]] Derived& RightOuterJoin(TableName auto joinTable,
339 std::string_view joinColumnName,
340 SqlQualifiedTableColumnName onOtherColumn);
343 [[nodiscard]] Derived& RightOuterJoin(TableName auto joinTable,
344 std::string_view joinColumnName,
345 std::string_view onMainTableColumn);
348 template <typename OnChainCallable>
349 requires std::invocable<OnChainCallable, SqlJoinConditionBuilder>
350 [[nodiscard]] Derived& RightOuterJoin(TableName auto joinTable, OnChainCallable const& onClauseBuilder);
353 [[nodiscard]] Derived& FullOuterJoin(TableName auto joinTable,
354 std::string_view joinColumnName,
355 SqlQualifiedTableColumnName onOtherColumn);
358 [[nodiscard]] Derived& FullOuterJoin(TableName auto joinTable,
359 std::string_view joinColumnName,
360 std::string_view onMainTableColumn);
363 template <typename OnChainCallable>
364 requires std::invocable<OnChainCallable, SqlJoinConditionBuilder>
365 [[nodiscard]] Derived& FullOuterJoin(TableName auto joinTable, OnChainCallable const& onClauseBuilder);
368 SqlSearchCondition& SearchCondition() noexcept;
369 [[nodiscard]] SqlQueryFormatter const& Formatter() const noexcept;
371 enum class WhereJunctor : uint8_t
379 WhereJunctor m_nextWhereJunctor = WhereJunctor::Where;
380 bool m_nextIsNot =
false;
382 void AppendWhereJunctor();
384 template <
typename ColumnName>
385 requires(std::same_as<ColumnName, SqlQualifiedTableColumnName> || std::convertible_to<ColumnName, std::string_view>
386 || std::same_as<ColumnName, SqlRawColumnNameView> || std::convertible_to<ColumnName, std::string>)
387 void AppendColumnName(ColumnName
const& columnName);
389 template <
typename LiteralType>
390 void AppendLiteralValue(LiteralType
const& value);
392 template <
typename LiteralType,
typename TargetType>
393 void PopulateLiteralValueInto(LiteralType
const& value, TargetType& target);
395 template <
typename LiteralType>
396 detail::RawSqlCondition PopulateSqlSetExpression(LiteralType
const& values);
398 enum class JoinType : uint8_t
407 [[nodiscard]] Derived& Join(JoinType joinType,
408 TableName
auto joinTable,
409 std::string_view joinColumnName,
410 SqlQualifiedTableColumnName onOtherColumn);
413 [[nodiscard]] Derived& Join(JoinType joinType,
414 TableName
auto joinTable,
415 std::string_view joinColumnName,
416 std::string_view onMainTableColumn);
419 template <
typename OnChainCallable>
420 [[nodiscard]] Derived& Join(JoinType joinType, TableName
auto joinTable, OnChainCallable
const& onClauseBuilder);
423enum class SqlResultOrdering : uint8_t
431 enum class SelectType : std::uint8_t
442 SelectType selectType = SelectType::Undefined;
443 SqlQueryFormatter
const* formatter =
nullptr;
445 bool distinct =
false;
446 SqlSearchCondition searchCondition {};
454 size_t limit = (std::numeric_limits<size_t>::max)();
456 [[nodiscard]] LIGHTWEIGHT_API std::string ToSql()
const;
460template <
typename Derived>
461class [[nodiscard]] SqlBasicSelectQueryBuilder:
public SqlWhereClauseBuilder<Derived>
465 Derived& Distinct() noexcept;
468 Derived& OrderBy(SqlQualifiedTableColumnName const& columnName,
469 SqlResultOrdering ordering = SqlResultOrdering::ASCENDING);
472 Derived& OrderBy(std::string_view columnName, SqlResultOrdering ordering = SqlResultOrdering::ASCENDING);
475 Derived& GroupBy(std::string_view columnName);
477 using ComposedQuery = detail::ComposedQuery;
480 ComposedQuery _query {};
483template <
typename Derived>
484inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlBasicSelectQueryBuilder<Derived>::Distinct() noexcept
486 _query.distinct =
true;
487 return static_cast<Derived&
>(*this);
490template <
typename Derived>
491inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlBasicSelectQueryBuilder<Derived>::OrderBy(std::string_view columnName,
492 SqlResultOrdering ordering)
494 if (_query.orderBy.empty())
495 _query.orderBy +=
"\n ORDER BY ";
497 _query.orderBy +=
", ";
499 _query.orderBy +=
'"';
500 _query.orderBy += columnName;
501 _query.orderBy +=
'"';
503 if (ordering == SqlResultOrdering::DESCENDING)
504 _query.orderBy +=
" DESC";
505 else if (ordering == SqlResultOrdering::ASCENDING)
506 _query.orderBy +=
" ASC";
508 return static_cast<Derived&
>(*this);
511template <
typename Derived>
512inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlBasicSelectQueryBuilder<Derived>::OrderBy(
513 SqlQualifiedTableColumnName
const& columnName, SqlResultOrdering ordering)
515 if (_query.orderBy.empty())
516 _query.orderBy +=
"\n ORDER BY ";
518 _query.orderBy +=
", ";
520 _query.orderBy +=
'"';
521 _query.orderBy += columnName.tableName;
522 _query.orderBy +=
"\".\"";
523 _query.orderBy += columnName.columnName;
524 _query.orderBy +=
'"';
526 if (ordering == SqlResultOrdering::DESCENDING)
527 _query.orderBy +=
" DESC";
528 else if (ordering == SqlResultOrdering::ASCENDING)
529 _query.orderBy +=
" ASC";
531 return static_cast<Derived&
>(*this);
534template <
typename Derived>
535inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlBasicSelectQueryBuilder<Derived>::GroupBy(std::string_view columnName)
537 if (_query.groupBy.empty())
538 _query.groupBy +=
"\n GROUP BY ";
540 _query.groupBy +=
", ";
542 _query.groupBy +=
'"';
543 _query.groupBy += columnName;
544 _query.groupBy +=
'"';
546 return static_cast<Derived&
>(*this);
549template <
typename Derived>
550inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::And() noexcept
552 m_nextWhereJunctor = WhereJunctor::And;
553 return static_cast<Derived&
>(*this);
556template <
typename Derived>
557inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::Or() noexcept
559 m_nextWhereJunctor = WhereJunctor::Or;
560 return static_cast<Derived&
>(*this);
563template <
typename Derived>
564inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::Not() noexcept
566 m_nextIsNot = !m_nextIsNot;
567 return static_cast<Derived&
>(*this);
570template <
typename Derived>
571template <
typename ColumnName,
typename T>
572inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::Where(ColumnName
const& columnName, T
const& value)
574 if constexpr (detail::OneOf<T, SqlNullType, std::nullopt_t>)
579 return Where(columnName,
"IS NOT", value);
582 return Where(columnName,
"IS", value);
585 return Where(columnName,
"=", value);
588template <
typename Derived>
589template <
typename ColumnName,
typename T>
590inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::OrWhere(ColumnName
const& columnName,
593 return Or().Where(columnName, value);
596template <
typename Derived>
597template <
typename Callable>
598 requires std::invocable<Callable, SqlWhereClauseBuilder<Derived>&>
599inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::OrWhere(Callable
const& callable)
601 return Or().Where(callable);
604template <
typename Derived>
605template <
typename Callable>
606 requires std::invocable<Callable, SqlWhereClauseBuilder<Derived>&>
607inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::Where(Callable
const& callable)
609 auto& condition = SearchCondition().condition;
611 auto const originalSize = condition.size();
613 AppendWhereJunctor();
614 m_nextWhereJunctor = WhereJunctor::Null;
617 auto const sizeBeforeCallable = condition.size();
619 (void) callable(*
this);
621 if (condition.size() == sizeBeforeCallable)
622 condition.resize(originalSize);
626 return static_cast<Derived&
>(*this);
629template <
typename Derived>
630template <
typename ColumnName, std::ranges::input_range InputRange>
631inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::WhereIn(ColumnName
const& columnName,
632 InputRange
const& values)
634 return Where(columnName,
"IN", PopulateSqlSetExpression(values));
637template <
typename Derived>
638template <
typename ColumnName,
typename T>
639inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::WhereIn(ColumnName
const& columnName,
640 std::initializer_list<T>
const& values)
642 return Where(columnName,
"IN", PopulateSqlSetExpression(values));
645template <
typename Derived>
646template <
typename ColumnName,
typename SubSelectQuery>
647 requires(std::is_invocable_r_v<std::string,
decltype(&SubSelectQuery::ToSql), SubSelectQuery
const&>)
648inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::WhereIn(ColumnName
const& columnName,
649 SubSelectQuery
const& subSelectQuery)
651 return Where(columnName,
"IN", detail::RawSqlCondition {
"(" + subSelectQuery.ToSql() +
")" });
654template <
typename Derived>
655template <
typename ColumnName>
656inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::WhereNotNull(ColumnName
const& columnName)
658 return Where(columnName,
"IS NOT", detail::RawSqlCondition {
"NULL" });
661template <
typename Derived>
662template <
typename ColumnName>
663inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::WhereNull(ColumnName
const& columnName)
665 return Where(columnName,
"IS", detail::RawSqlCondition {
"NULL" });
668template <
typename Derived>
669template <
typename ColumnName,
typename T>
670inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::WhereNotEqual(ColumnName
const& columnName,
673 if constexpr (detail::OneOf<T, SqlNullType, std::nullopt_t>)
674 return Where(columnName,
"IS NOT", value);
676 return Where(columnName,
"!=", value);
679template <
typename Derived>
680template <
typename ColumnName>
681inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::WhereTrue(ColumnName
const& columnName)
683 return Where(columnName,
"=",
true);
686template <
typename Derived>
687template <
typename ColumnName>
688inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::WhereFalse(ColumnName
const& columnName)
690 return Where(columnName,
"=",
false);
693template <
typename Derived>
694template <
typename LeftColumn,
typename RightColumn>
695inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::WhereColumn(LeftColumn
const& left,
696 std::string_view binaryOp,
697 RightColumn
const& right)
699 AppendWhereJunctor();
701 AppendColumnName(left);
702 SearchCondition().condition +=
' ';
703 SearchCondition().condition += binaryOp;
704 SearchCondition().condition +=
' ';
705 AppendColumnName(right);
707 return static_cast<Derived&
>(*this);
711struct WhereConditionLiteralType
713 constexpr static bool needsQuotes = !std::is_integral_v<T> && !std::is_floating_point_v<T> && !std::same_as<T, bool>
714 && !std::same_as<T, SqlWildcardType>;
717template <
typename Derived>
718template <
typename ColumnName, std::
size_t N>
719inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::Where(ColumnName
const& columnName,
720 std::string_view binaryOp,
721 char const (&value)[N])
723 return Where(columnName, binaryOp, std::string_view { value, N - 1 });
726template <
typename Derived>
727template <
typename ColumnName,
typename T>
728inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::Where(ColumnName
const& columnName,
729 std::string_view binaryOp,
732 auto& searchCondition = SearchCondition();
734 AppendWhereJunctor();
735 AppendColumnName(columnName);
736 searchCondition.condition +=
' ';
737 searchCondition.condition += binaryOp;
738 searchCondition.condition +=
' ';
739 AppendLiteralValue(value);
741 return static_cast<Derived&
>(*this);
744template <
typename Derived>
745template <
typename ColumnName,
typename SubSelectQuery>
746 requires(std::is_invocable_r_v<std::string,
decltype(&SubSelectQuery::ToSql), SubSelectQuery
const&>)
747inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::Where(ColumnName
const& columnName,
748 std::string_view binaryOp,
749 SubSelectQuery
const& value)
751 return Where(columnName, binaryOp, detail::RawSqlCondition {
"(" + value.ToSql() +
")" });
754template <
typename Derived>
755template <
typename ColumnName,
typename T>
756inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::OrWhere(ColumnName
const& columnName,
757 std::string_view binaryOp,
760 return Or().Where(columnName, binaryOp, value);
763template <
typename Derived>
764inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::InnerJoin(TableName
auto joinTable,
765 std::string_view joinColumnName,
766 SqlQualifiedTableColumnName onOtherColumn)
768 return Join(JoinType::INNER, joinTable, joinColumnName, onOtherColumn);
771template <
typename Derived>
772inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::InnerJoin(TableName
auto joinTable,
773 std::string_view joinColumnName,
774 std::string_view onMainTableColumn)
776 return Join(JoinType::INNER, joinTable, joinColumnName, onMainTableColumn);
779template <
typename Derived>
780template <auto LeftField, auto RightField>
781Derived& SqlWhereClauseBuilder<Derived>::InnerJoin()
783#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
784 return Join(JoinType::INNER,
786 FieldNameOf<LeftField>,
787 SqlQualifiedTableColumnName { RecordTableName<MemberClassType<RightField>>, FieldNameOf<RightField> });
792 FieldNameOf<LeftField>,
793 SqlQualifiedTableColumnName { RecordTableName<Reflection::MemberClassType<RightField>>, FieldNameOf<RightField> });
797template <
typename Derived>
798template <
typename OnChainCallable>
799 requires std::invocable<OnChainCallable, SqlJoinConditionBuilder>
800Derived& SqlWhereClauseBuilder<Derived>::InnerJoin(TableName
auto joinTable, OnChainCallable
const& onClauseBuilder)
802 return Join(JoinType::INNER, joinTable, onClauseBuilder);
805template <
typename Derived>
806inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::LeftOuterJoin(
807 TableName
auto joinTable, std::string_view joinColumnName, SqlQualifiedTableColumnName onOtherColumn)
809 return Join(JoinType::LEFT, joinTable, joinColumnName, onOtherColumn);
812template <
typename Derived>
813inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::LeftOuterJoin(TableName
auto joinTable,
814 std::string_view joinColumnName,
815 std::string_view onMainTableColumn)
817 return Join(JoinType::LEFT, joinTable, joinColumnName, onMainTableColumn);
820template <
typename Derived>
821template <
typename OnChainCallable>
822 requires std::invocable<OnChainCallable, SqlJoinConditionBuilder>
823Derived& SqlWhereClauseBuilder<Derived>::LeftOuterJoin(TableName
auto joinTable, OnChainCallable
const& onClauseBuilder)
825 return Join(JoinType::LEFT, joinTable, onClauseBuilder);
828template <
typename Derived>
829inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::RightOuterJoin(
830 TableName
auto joinTable, std::string_view joinColumnName, SqlQualifiedTableColumnName onOtherColumn)
832 return Join(JoinType::RIGHT, joinTable, joinColumnName, onOtherColumn);
835template <
typename Derived>
836inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::RightOuterJoin(TableName
auto joinTable,
837 std::string_view joinColumnName,
838 std::string_view onMainTableColumn)
840 return Join(JoinType::RIGHT, joinTable, joinColumnName, onMainTableColumn);
843template <
typename Derived>
844template <
typename OnChainCallable>
845 requires std::invocable<OnChainCallable, SqlJoinConditionBuilder>
846Derived& SqlWhereClauseBuilder<Derived>::RightOuterJoin(TableName
auto joinTable, OnChainCallable
const& onClauseBuilder)
848 return Join(JoinType::RIGHT, joinTable, onClauseBuilder);
851template <
typename Derived>
852inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::FullOuterJoin(
853 TableName
auto joinTable, std::string_view joinColumnName, SqlQualifiedTableColumnName onOtherColumn)
855 return Join(JoinType::FULL, joinTable, joinColumnName, onOtherColumn);
858template <
typename Derived>
859inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::FullOuterJoin(TableName
auto joinTable,
860 std::string_view joinColumnName,
861 std::string_view onMainTableColumn)
863 return Join(JoinType::FULL, joinTable, joinColumnName, onMainTableColumn);
866template <
typename Derived>
867template <
typename OnChainCallable>
868 requires std::invocable<OnChainCallable, SqlJoinConditionBuilder>
869Derived& SqlWhereClauseBuilder<Derived>::FullOuterJoin(TableName
auto joinTable, OnChainCallable
const& onClauseBuilder)
871 return Join(JoinType::FULL, joinTable, onClauseBuilder);
874template <
typename Derived>
875inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::WhereRaw(std::string_view sqlConditionExpression)
877 AppendWhereJunctor();
879 auto& condition = SearchCondition().condition;
880 condition += sqlConditionExpression;
882 return static_cast<Derived&
>(*this);
885template <
typename Derived>
886inline LIGHTWEIGHT_FORCE_INLINE SqlSearchCondition& SqlWhereClauseBuilder<Derived>::SearchCondition() noexcept
888 return static_cast<Derived*
>(
this)->SearchCondition();
891template <
typename Derived>
892inline LIGHTWEIGHT_FORCE_INLINE SqlQueryFormatter
const& SqlWhereClauseBuilder<Derived>::Formatter() const noexcept
894 return static_cast<Derived const*
>(
this)->Formatter();
897template <
typename Derived>
898inline LIGHTWEIGHT_FORCE_INLINE
void SqlWhereClauseBuilder<Derived>::AppendWhereJunctor()
900 using namespace std::string_view_literals;
902 auto& condition = SearchCondition().condition;
904 switch (m_nextWhereJunctor)
906 case WhereJunctor::Null:
908 case WhereJunctor::Where:
909 condition +=
"\n WHERE "sv;
911 case WhereJunctor::And:
912 condition +=
" AND "sv;
914 case WhereJunctor::Or:
915 condition +=
" OR "sv;
921 condition +=
"NOT "sv;
925 m_nextWhereJunctor = WhereJunctor::And;
928template <
typename Derived>
929template <
typename ColumnName>
930 requires(std::same_as<ColumnName, SqlQualifiedTableColumnName> || std::convertible_to<ColumnName, std::string_view>
931 || std::same_as<ColumnName, SqlRawColumnNameView> || std::convertible_to<ColumnName, std::string>)
932inline LIGHTWEIGHT_FORCE_INLINE
void SqlWhereClauseBuilder<Derived>::AppendColumnName(ColumnName
const& columnName)
934 SearchCondition().condition += detail::MakeSqlColumnName(columnName);
937template <
typename Derived>
938template <
typename LiteralType>
939inline LIGHTWEIGHT_FORCE_INLINE
void SqlWhereClauseBuilder<Derived>::AppendLiteralValue(LiteralType
const& value)
941 auto& searchCondition = SearchCondition();
943 if constexpr (std::is_same_v<LiteralType, SqlQualifiedTableColumnName>
944 || detail::OneOf<LiteralType, SqlNullType, std::nullopt_t> || std::is_same_v<LiteralType, SqlWildcardType>
945 || std::is_same_v<LiteralType, detail::RawSqlCondition>)
947 PopulateLiteralValueInto(value, searchCondition.condition);
949 else if (searchCondition.inputBindings)
951 searchCondition.condition +=
'?';
952 searchCondition.inputBindings->emplace_back(value);
954 else if constexpr (std::is_same_v<LiteralType, bool>)
956 searchCondition.condition += Formatter().BooleanLiteral(value);
958 else if constexpr (!WhereConditionLiteralType<LiteralType>::needsQuotes)
960 searchCondition.condition += std::format(
"{}", value);
964 searchCondition.condition += detail::MakeEscapedSqlString(std::format(
"{}", value));
968template <
typename Derived>
969template <
typename LiteralType,
typename TargetType>
970inline LIGHTWEIGHT_FORCE_INLINE
void SqlWhereClauseBuilder<Derived>::PopulateLiteralValueInto(LiteralType
const& value,
973 if constexpr (std::is_same_v<LiteralType, SqlQualifiedTableColumnName>)
976 target += value.tableName;
978 target += value.columnName;
981 else if constexpr (detail::OneOf<LiteralType, SqlNullType, std::nullopt_t>)
985 else if constexpr (std::is_same_v<LiteralType, SqlWildcardType>)
989 else if constexpr (std::is_same_v<LiteralType, detail::RawSqlCondition>)
991 target += value.condition;
993 else if constexpr (std::is_same_v<LiteralType, bool>)
995 target += Formatter().BooleanLiteral(value);
997 else if constexpr (!WhereConditionLiteralType<LiteralType>::needsQuotes)
999 target += std::format(
"{}", value);
1003 target += detail::MakeEscapedSqlString(std::format(
"{}", value));
1007template <
typename Derived>
1008template <
typename LiteralType>
1009detail::RawSqlCondition SqlWhereClauseBuilder<Derived>::PopulateSqlSetExpression(LiteralType
const& values)
1011 using namespace std::string_view_literals;
1012 std::ostringstream fragment;
1014#if !defined(__cpp_lib_ranges_enumerate)
1016 for (
auto const& value: values)
1020 for (
auto const&& [index, value]: values | std::views::enumerate)
1026 std::string valueString;
1027 PopulateLiteralValueInto(value, valueString);
1028 fragment << valueString;
1031 return detail::RawSqlCondition { fragment.str() };
1034template <
typename Derived>
1035inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::Join(JoinType joinType,
1036 TableName
auto joinTable,
1037 std::string_view joinColumnName,
1038 SqlQualifiedTableColumnName onOtherColumn)
1040 static constexpr std::array<std::string_view, 4> JoinTypeStrings = {
1047 if constexpr (std::is_same_v<std::remove_cvref_t<
decltype(joinTable)>, AliasedTableName>)
1049 SearchCondition().tableJoins += std::format(
"\n"
1050 R
"( {0} JOIN "{1}" AS "{2}" ON "{2}"."{3}" = "{4}"."{5}")",
1051 JoinTypeStrings[static_cast<std::size_t
>(joinType)],
1052 joinTable.tableName,
1055 onOtherColumn.tableName,
1056 onOtherColumn.columnName);
1060 SearchCondition().tableJoins += std::format(
"\n"
1061 R
"( {0} JOIN "{1}" ON "{1}"."{2}" = "{3}"."{4}")",
1062 JoinTypeStrings[static_cast<std::size_t
>(joinType)],
1065 onOtherColumn.tableName,
1066 onOtherColumn.columnName);
1068 return static_cast<Derived&
>(*this);
1071template <
typename Derived>
1072inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::Join(JoinType joinType,
1073 TableName
auto joinTable,
1074 std::string_view joinColumnName,
1075 std::string_view onMainTableColumn)
1077 return Join(joinType,
1080 SqlQualifiedTableColumnName { .tableName = SearchCondition().tableName, .columnName = onMainTableColumn });
1083template <
typename Derived>
1084template <
typename Callable>
1085inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::Join(JoinType joinType,
1086 TableName
auto joinTable,
1087 Callable
const& onClauseBuilder)
1089 static constexpr std::array<std::string_view, 4> JoinTypeStrings = {
1096 size_t const originalSize = SearchCondition().tableJoins.size();
1097 SearchCondition().tableJoins +=
1098 std::format(
"\n {0} JOIN \"{1}\" ON ", JoinTypeStrings[
static_cast<std::size_t
>(joinType)], joinTable);
1099 size_t const sizeBefore = SearchCondition().tableJoins.size();
1100 onClauseBuilder(SqlJoinConditionBuilder { joinTable, &SearchCondition().tableJoins });
1101 size_t const sizeAfter = SearchCondition().tableJoins.size();
1102 if (sizeBefore == sizeAfter)
1103 SearchCondition().tableJoins.resize(originalSize);
1105 return static_cast<Derived&
>(*this);
constexpr std::string_view RecordTableName
Holds the SQL tabl ename for the given record type.