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)
635 return static_cast<Derived&
>(*this);
636 return Where(columnName,
"IN", PopulateSqlSetExpression(values));
639template <
typename Derived>
640template <
typename ColumnName,
typename T>
641inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::WhereIn(ColumnName
const& columnName,
642 std::initializer_list<T>
const& values)
644 if (values.begin() == values.end())
645 return static_cast<Derived&
>(*this);
646 return Where(columnName,
"IN", PopulateSqlSetExpression(values));
649template <
typename Derived>
650template <
typename ColumnName,
typename SubSelectQuery>
651 requires(std::is_invocable_r_v<std::string,
decltype(&SubSelectQuery::ToSql), SubSelectQuery
const&>)
652inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::WhereIn(ColumnName
const& columnName,
653 SubSelectQuery
const& subSelectQuery)
655 return Where(columnName,
"IN", detail::RawSqlCondition {
"(" + subSelectQuery.ToSql() +
")" });
658template <
typename Derived>
659template <
typename ColumnName>
660inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::WhereNotNull(ColumnName
const& columnName)
662 return Where(columnName,
"IS NOT", detail::RawSqlCondition {
"NULL" });
665template <
typename Derived>
666template <
typename ColumnName>
667inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::WhereNull(ColumnName
const& columnName)
669 return Where(columnName,
"IS", detail::RawSqlCondition {
"NULL" });
672template <
typename Derived>
673template <
typename ColumnName,
typename T>
674inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::WhereNotEqual(ColumnName
const& columnName,
677 if constexpr (detail::OneOf<T, SqlNullType, std::nullopt_t>)
678 return Where(columnName,
"IS NOT", value);
680 return Where(columnName,
"!=", value);
683template <
typename Derived>
684template <
typename ColumnName>
685inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::WhereTrue(ColumnName
const& columnName)
687 return Where(columnName,
"=",
true);
690template <
typename Derived>
691template <
typename ColumnName>
692inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::WhereFalse(ColumnName
const& columnName)
694 return Where(columnName,
"=",
false);
697template <
typename Derived>
698template <
typename LeftColumn,
typename RightColumn>
699inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::WhereColumn(LeftColumn
const& left,
700 std::string_view binaryOp,
701 RightColumn
const& right)
703 AppendWhereJunctor();
705 AppendColumnName(left);
706 SearchCondition().condition +=
' ';
707 SearchCondition().condition += binaryOp;
708 SearchCondition().condition +=
' ';
709 AppendColumnName(right);
711 return static_cast<Derived&
>(*this);
715struct WhereConditionLiteralType
717 constexpr static bool needsQuotes = !std::is_integral_v<T> && !std::is_floating_point_v<T> && !std::same_as<T, bool>
718 && !std::same_as<T, SqlWildcardType>;
721template <
typename Derived>
722template <
typename ColumnName, std::
size_t N>
723inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::Where(ColumnName
const& columnName,
724 std::string_view binaryOp,
725 char const (&value)[N])
727 return Where(columnName, binaryOp, std::string_view { value, N - 1 });
730template <
typename Derived>
731template <
typename ColumnName,
typename T>
732inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::Where(ColumnName
const& columnName,
733 std::string_view binaryOp,
736 auto& searchCondition = SearchCondition();
738 AppendWhereJunctor();
739 AppendColumnName(columnName);
740 searchCondition.condition +=
' ';
741 searchCondition.condition += binaryOp;
742 searchCondition.condition +=
' ';
743 AppendLiteralValue(value);
745 return static_cast<Derived&
>(*this);
748template <
typename Derived>
749template <
typename ColumnName,
typename SubSelectQuery>
750 requires(std::is_invocable_r_v<std::string,
decltype(&SubSelectQuery::ToSql), SubSelectQuery
const&>)
751inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::Where(ColumnName
const& columnName,
752 std::string_view binaryOp,
753 SubSelectQuery
const& value)
755 return Where(columnName, binaryOp, detail::RawSqlCondition {
"(" + value.ToSql() +
")" });
758template <
typename Derived>
759template <
typename ColumnName,
typename T>
760inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::OrWhere(ColumnName
const& columnName,
761 std::string_view binaryOp,
764 return Or().Where(columnName, binaryOp, value);
767template <
typename Derived>
768inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::InnerJoin(TableName
auto joinTable,
769 std::string_view joinColumnName,
770 SqlQualifiedTableColumnName onOtherColumn)
772 return Join(JoinType::INNER, joinTable, joinColumnName, onOtherColumn);
775template <
typename Derived>
776inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::InnerJoin(TableName
auto joinTable,
777 std::string_view joinColumnName,
778 std::string_view onMainTableColumn)
780 return Join(JoinType::INNER, joinTable, joinColumnName, onMainTableColumn);
783template <
typename Derived>
784template <auto LeftField, auto RightField>
785Derived& SqlWhereClauseBuilder<Derived>::InnerJoin()
787#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
788 return Join(JoinType::INNER,
790 FieldNameOf<LeftField>,
791 SqlQualifiedTableColumnName { RecordTableName<MemberClassType<RightField>>, FieldNameOf<RightField> });
796 FieldNameOf<LeftField>,
797 SqlQualifiedTableColumnName { RecordTableName<Reflection::MemberClassType<RightField>>, FieldNameOf<RightField> });
801template <
typename Derived>
802template <
typename OnChainCallable>
803 requires std::invocable<OnChainCallable, SqlJoinConditionBuilder>
804Derived& SqlWhereClauseBuilder<Derived>::InnerJoin(TableName
auto joinTable, OnChainCallable
const& onClauseBuilder)
806 return Join(JoinType::INNER, joinTable, onClauseBuilder);
809template <
typename Derived>
810inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::LeftOuterJoin(
811 TableName
auto joinTable, std::string_view joinColumnName, SqlQualifiedTableColumnName onOtherColumn)
813 return Join(JoinType::LEFT, joinTable, joinColumnName, onOtherColumn);
816template <
typename Derived>
817inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::LeftOuterJoin(TableName
auto joinTable,
818 std::string_view joinColumnName,
819 std::string_view onMainTableColumn)
821 return Join(JoinType::LEFT, joinTable, joinColumnName, onMainTableColumn);
824template <
typename Derived>
825template <
typename OnChainCallable>
826 requires std::invocable<OnChainCallable, SqlJoinConditionBuilder>
827Derived& SqlWhereClauseBuilder<Derived>::LeftOuterJoin(TableName
auto joinTable, OnChainCallable
const& onClauseBuilder)
829 return Join(JoinType::LEFT, joinTable, onClauseBuilder);
832template <
typename Derived>
833inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::RightOuterJoin(
834 TableName
auto joinTable, std::string_view joinColumnName, SqlQualifiedTableColumnName onOtherColumn)
836 return Join(JoinType::RIGHT, joinTable, joinColumnName, onOtherColumn);
839template <
typename Derived>
840inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::RightOuterJoin(TableName
auto joinTable,
841 std::string_view joinColumnName,
842 std::string_view onMainTableColumn)
844 return Join(JoinType::RIGHT, joinTable, joinColumnName, onMainTableColumn);
847template <
typename Derived>
848template <
typename OnChainCallable>
849 requires std::invocable<OnChainCallable, SqlJoinConditionBuilder>
850Derived& SqlWhereClauseBuilder<Derived>::RightOuterJoin(TableName
auto joinTable, OnChainCallable
const& onClauseBuilder)
852 return Join(JoinType::RIGHT, joinTable, onClauseBuilder);
855template <
typename Derived>
856inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::FullOuterJoin(
857 TableName
auto joinTable, std::string_view joinColumnName, SqlQualifiedTableColumnName onOtherColumn)
859 return Join(JoinType::FULL, joinTable, joinColumnName, onOtherColumn);
862template <
typename Derived>
863inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::FullOuterJoin(TableName
auto joinTable,
864 std::string_view joinColumnName,
865 std::string_view onMainTableColumn)
867 return Join(JoinType::FULL, joinTable, joinColumnName, onMainTableColumn);
870template <
typename Derived>
871template <
typename OnChainCallable>
872 requires std::invocable<OnChainCallable, SqlJoinConditionBuilder>
873Derived& SqlWhereClauseBuilder<Derived>::FullOuterJoin(TableName
auto joinTable, OnChainCallable
const& onClauseBuilder)
875 return Join(JoinType::FULL, joinTable, onClauseBuilder);
878template <
typename Derived>
879inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::WhereRaw(std::string_view sqlConditionExpression)
881 AppendWhereJunctor();
883 auto& condition = SearchCondition().condition;
884 condition += sqlConditionExpression;
886 return static_cast<Derived&
>(*this);
889template <
typename Derived>
890inline LIGHTWEIGHT_FORCE_INLINE SqlSearchCondition& SqlWhereClauseBuilder<Derived>::SearchCondition() noexcept
892 return static_cast<Derived*
>(
this)->SearchCondition();
895template <
typename Derived>
896inline LIGHTWEIGHT_FORCE_INLINE SqlQueryFormatter
const& SqlWhereClauseBuilder<Derived>::Formatter() const noexcept
898 return static_cast<Derived const*
>(
this)->Formatter();
901template <
typename Derived>
902inline LIGHTWEIGHT_FORCE_INLINE
void SqlWhereClauseBuilder<Derived>::AppendWhereJunctor()
904 using namespace std::string_view_literals;
906 auto& condition = SearchCondition().condition;
908 switch (m_nextWhereJunctor)
910 case WhereJunctor::Null:
912 case WhereJunctor::Where:
913 condition +=
"\n WHERE "sv;
915 case WhereJunctor::And:
916 condition +=
" AND "sv;
918 case WhereJunctor::Or:
919 condition +=
" OR "sv;
925 condition +=
"NOT "sv;
929 m_nextWhereJunctor = WhereJunctor::And;
932template <
typename Derived>
933template <
typename ColumnName>
934 requires(std::same_as<ColumnName, SqlQualifiedTableColumnName> || std::convertible_to<ColumnName, std::string_view>
935 || std::same_as<ColumnName, SqlRawColumnNameView> || std::convertible_to<ColumnName, std::string>)
936inline LIGHTWEIGHT_FORCE_INLINE
void SqlWhereClauseBuilder<Derived>::AppendColumnName(ColumnName
const& columnName)
938 SearchCondition().condition += detail::MakeSqlColumnName(columnName);
941template <
typename Derived>
942template <
typename LiteralType>
943inline LIGHTWEIGHT_FORCE_INLINE
void SqlWhereClauseBuilder<Derived>::AppendLiteralValue(LiteralType
const& value)
945 auto& searchCondition = SearchCondition();
947 if constexpr (std::is_same_v<LiteralType, SqlQualifiedTableColumnName>
948 || detail::OneOf<LiteralType, SqlNullType, std::nullopt_t> || std::is_same_v<LiteralType, SqlWildcardType>
949 || std::is_same_v<LiteralType, detail::RawSqlCondition>)
951 PopulateLiteralValueInto(value, searchCondition.condition);
953 else if (searchCondition.inputBindings)
955 searchCondition.condition +=
'?';
956 searchCondition.inputBindings->emplace_back(value);
958 else if constexpr (std::is_same_v<LiteralType, bool>)
960 searchCondition.condition += Formatter().BooleanLiteral(value);
962 else if constexpr (!WhereConditionLiteralType<LiteralType>::needsQuotes)
964 searchCondition.condition += std::format(
"{}", value);
968 searchCondition.condition += detail::MakeEscapedSqlString(std::format(
"{}", value));
972template <
typename Derived>
973template <
typename LiteralType,
typename TargetType>
974inline LIGHTWEIGHT_FORCE_INLINE
void SqlWhereClauseBuilder<Derived>::PopulateLiteralValueInto(LiteralType
const& value,
977 if constexpr (std::is_same_v<LiteralType, SqlQualifiedTableColumnName>)
980 target += value.tableName;
982 target += value.columnName;
985 else if constexpr (detail::OneOf<LiteralType, SqlNullType, std::nullopt_t>)
989 else if constexpr (std::is_same_v<LiteralType, SqlWildcardType>)
993 else if constexpr (std::is_same_v<LiteralType, detail::RawSqlCondition>)
995 target += value.condition;
997 else if constexpr (std::is_same_v<LiteralType, bool>)
999 target += Formatter().BooleanLiteral(value);
1001 else if constexpr (!WhereConditionLiteralType<LiteralType>::needsQuotes)
1003 target += std::format(
"{}", value);
1007 target += detail::MakeEscapedSqlString(std::format(
"{}", value));
1011template <
typename Derived>
1012template <
typename LiteralType>
1013detail::RawSqlCondition SqlWhereClauseBuilder<Derived>::PopulateSqlSetExpression(LiteralType
const& values)
1015 using namespace std::string_view_literals;
1016 std::ostringstream fragment;
1018#if !defined(__cpp_lib_ranges_enumerate)
1020 for (
auto const& value: values)
1024 for (
auto const&& [index, value]: values | std::views::enumerate)
1030 std::string valueString;
1031 PopulateLiteralValueInto(value, valueString);
1032 fragment << valueString;
1035 return detail::RawSqlCondition { fragment.str() };
1038template <
typename Derived>
1039inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::Join(JoinType joinType,
1040 TableName
auto joinTable,
1041 std::string_view joinColumnName,
1042 SqlQualifiedTableColumnName onOtherColumn)
1044 static constexpr std::array<std::string_view, 4> JoinTypeStrings = {
1051 if constexpr (std::is_same_v<std::remove_cvref_t<
decltype(joinTable)>, AliasedTableName>)
1053 SearchCondition().tableJoins += std::format(
"\n"
1054 R
"( {0} JOIN "{1}" AS "{2}" ON "{2}"."{3}" = "{4}"."{5}")",
1055 JoinTypeStrings[static_cast<std::size_t
>(joinType)],
1056 joinTable.tableName,
1059 onOtherColumn.tableName,
1060 onOtherColumn.columnName);
1064 SearchCondition().tableJoins += std::format(
"\n"
1065 R
"( {0} JOIN "{1}" ON "{1}"."{2}" = "{3}"."{4}")",
1066 JoinTypeStrings[static_cast<std::size_t
>(joinType)],
1069 onOtherColumn.tableName,
1070 onOtherColumn.columnName);
1072 return static_cast<Derived&
>(*this);
1075template <
typename Derived>
1076inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::Join(JoinType joinType,
1077 TableName
auto joinTable,
1078 std::string_view joinColumnName,
1079 std::string_view onMainTableColumn)
1081 return Join(joinType,
1084 SqlQualifiedTableColumnName { .tableName = SearchCondition().tableName, .columnName = onMainTableColumn });
1087template <
typename Derived>
1088template <
typename Callable>
1089inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::Join(JoinType joinType,
1090 TableName
auto joinTable,
1091 Callable
const& onClauseBuilder)
1093 static constexpr std::array<std::string_view, 4> JoinTypeStrings = {
1100 size_t const originalSize = SearchCondition().tableJoins.size();
1101 SearchCondition().tableJoins +=
1102 std::format(
"\n {0} JOIN \"{1}\" ON ", JoinTypeStrings[
static_cast<std::size_t
>(joinType)], joinTable);
1103 size_t const sizeBefore = SearchCondition().tableJoins.size();
1104 onClauseBuilder(SqlJoinConditionBuilder { joinTable, &SearchCondition().tableJoins });
1105 size_t const sizeAfter = SearchCondition().tableJoins.size();
1106 if (sizeBefore == sizeAfter)
1107 SearchCondition().tableJoins.resize(originalSize);
1109 return static_cast<Derived&
>(*this);
constexpr std::string_view RecordTableName
Holds the SQL tabl ename for the given record type.