6#include "../SqlQueryFormatter.hpp"
50 std::convertible_to<T, std::string_view> || std::convertible_to<T, std::string> || std::same_as<T, AliasedTableName>;
55 struct RawSqlCondition
57 std::string condition;
69 template <
typename LiteralType,
typename TargetType>
70 void AppendLiteralValueInto(LiteralType
const& value, TargetType& target, SqlQueryFormatter
const& formatter);
79template <Reflection::StringLiteral columnLiteral>
80constexpr SqlQualifiedTableColumnName QualifiedColumnName = []()
consteval {
84 !std::ranges::any_of(columnLiteral,
85 [](
char c)
consteval {
return c ==
'\\' || c ==
'[' || c ==
']' || c ==
'"' || c ==
'\''; }),
86 "QualifiedColumnName should not contain symbols \\ [ ] \" '");
89 static_assert(std::ranges::count(columnLiteral,
'.') == 1,
90 "QualifiedColumnName requires a column name with a single '.' to separate table and column name");
91 constexpr auto column = columnLiteral.sv();
92 auto dotPos = column.find(
'.');
93 return SqlQualifiedTableColumnName { .
tableName = column.substr(0, dotPos), .columnName = column.substr(dotPos + 1) };
99 template <
typename ColumnName>
100 std::string MakeSqlColumnName(ColumnName
const& columnName)
102 using namespace std::string_view_literals;
105 if constexpr (std::is_same_v<ColumnName, SqlQualifiedTableColumnName>)
107 output.reserve(columnName.tableName.size() + columnName.columnName.size() + 5);
109 output += columnName.tableName;
110 output += R
"(".")"sv;
111 output += columnName.columnName;
114 else if constexpr (std::is_same_v<ColumnName, SqlWildcardType>)
121 output += columnName;
127 template <
typename T>
128 std::string MakeEscapedSqlString(T
const& value)
130 std::string escapedValue;
131 escapedValue +=
'\'';
133 for (
auto const ch: value)
137 escapedValue +=
'\'';
140 escapedValue +=
'\'';
146struct [[nodiscard]] SqlSearchCondition
148 std::string tableName;
149 std::string tableAlias;
150 std::string tableJoins;
151 std::string condition;
152 std::vector<SqlVariant>* inputBindings =
nullptr;
159 std::size_t joinBindingCount = 0;
180 SqlSearchCondition* searchCondition,
182 _referenceTable { referenceTable },
183 _searchCondition { *searchCondition },
184 _formatter { *formatter }
191 return Operator(joinColumnName, onOtherColumn,
"AND");
197 return Operator(joinColumnName, onOtherColumn,
"OR");
206 AppendReferenceColumn(joinColumnName);
207 Condition() +=
" = ";
208 detail::AppendLiteralValueInto(onOtherColumn, Condition(), _formatter);
218 template <
typename T>
221 return ValueOperator(joinColumnName,
"=", value,
"AND");
225 template <
typename T>
228 return ValueOperator(joinColumnName, binaryOp, value,
"AND");
232 template <
typename T>
235 return ValueOperator(joinColumnName,
"=", value,
"OR");
239 template <
typename T>
242 return ValueOperator(joinColumnName, binaryOp, value,
"OR");
248 return NullTest(joinColumnName, NullSense::Null,
"AND");
254 return NullTest(joinColumnName, NullSense::NotNull,
"AND");
260 return NullTest(joinColumnName, NullSense::Null,
"OR");
266 return NullTest(joinColumnName, NullSense::NotNull,
"OR");
273 template <
typename Callable>
274 requires std::invocable<Callable, SqlJoinConditionBuilder&>
277 return Group(build,
"AND");
281 template <
typename Callable>
282 requires std::invocable<Callable, SqlJoinConditionBuilder&>
285 return Group(build,
"OR");
289 [[nodiscard]] std::string& Condition() noexcept
291 return _searchCondition.tableJoins;
294 void AppendJunctor(std::string_view op)
299 Condition() += std::format(
" {} ", op);
302 void AppendReferenceColumn(std::string_view joinColumnName)
305 Condition() += _referenceTable;
306 Condition() +=
"\".\"";
307 Condition() += joinColumnName;
311 template <
typename T>
313 std::string_view binaryOp,
318 AppendReferenceColumn(joinColumnName);
319 Condition() += std::format(
" {} ", binaryOp);
321 if (_searchCondition.inputBindings !=
nullptr)
325 _searchCondition.inputBindings->insert(_searchCondition.inputBindings->begin()
326 +
static_cast<std::ptrdiff_t
>(_searchCondition.joinBindingCount),
327 SqlVariant { value });
328 ++_searchCondition.joinBindingCount;
331 detail::AppendLiteralValueInto(value, Condition(), _formatter);
336 enum class NullSense : std::uint8_t
345 AppendReferenceColumn(joinColumnName);
346 Condition() += sense == NullSense::Null ?
" IS NULL" :
" IS NOT NULL";
350 template <
typename Callable>
355 auto const sizeBeforeJunctor = Condition().size();
356 bool const wasFirstCall = _firstCall;
360 auto const sizeBeforeParen = Condition().size();
368 if (Condition().size() == sizeBeforeParen + 1)
370 Condition().resize(sizeBeforeJunctor);
371 _firstCall = wasFirstCall;
379 std::string_view _referenceTable;
380 SqlSearchCondition& _searchCondition;
381 SqlQueryFormatter
const& _formatter;
382 bool _firstCall =
true;
390template <
typename Derived>
395 [[nodiscard]] Derived&
And() noexcept;
398 [[nodiscard]] Derived& Or() noexcept;
401 [[nodiscard]] Derived& Not() noexcept;
404 [[nodiscard]] Derived& WhereRaw(std::string_view sqlConditionExpression);
430 template <typename T>
431 [[nodiscard]] auto If(std::optional<T> const& value) noexcept;
434 template <typename ColumnName, typename T>
435 [[nodiscard]] Derived& Where(ColumnName const& columnName, std::string_view binaryOp, T const& value);
438 template <typename ColumnName, typename SubSelectQuery>
439 requires(std::is_invocable_r_v<std::
string, decltype(&SubSelectQuery::ToSql), SubSelectQuery const&>)
440 [[nodiscard]] Derived& Where(ColumnName const& columnName, std::string_view binaryOp, SubSelectQuery const& value);
443 template <typename ColumnName, typename T>
444 [[nodiscard]] Derived& OrWhere(ColumnName const& columnName, std::string_view binaryOp, T const& value);
447 template <typename ColumnName, std::
size_t N>
448 Derived& Where(ColumnName const& columnName, std::string_view binaryOp,
char const (&value)[N]);
451 template <typename ColumnName, typename T>
452 [[nodiscard]] Derived& Where(ColumnName const& columnName, T const& value);
455 template <typename ColumnName, typename T>
456 [[nodiscard]] Derived& OrWhere(ColumnName const& columnName, T const& value);
459 template <typename Callable>
461 [[nodiscard]] Derived& Where(Callable const& callable);
464 template <typename Callable>
466 [[nodiscard]] Derived& OrWhere(Callable const& callable);
469 template <typename ColumnName, std::ranges::input_range InputRange>
470 [[nodiscard]] Derived& WhereIn(ColumnName const& columnName, InputRange const& values);
473 template <typename ColumnName, typename T>
474 [[nodiscard]] Derived& WhereIn(ColumnName const& columnName, std::initializer_list<T> const& values);
477 template <typename ColumnName, typename SubSelectQuery>
478 requires(std::is_invocable_r_v<std::
string, decltype(&SubSelectQuery::ToSql), SubSelectQuery const&>)
479 [[nodiscard]] Derived& WhereIn(ColumnName const& columnName, SubSelectQuery const& subSelectQuery);
482 template <typename ColumnName>
483 [[nodiscard]] Derived& WhereNull(ColumnName const& columnName);
486 template <typename ColumnName>
487 [[nodiscard]] Derived& WhereNotNull(ColumnName const& columnName);
490 template <typename ColumnName, typename T>
491 [[nodiscard]] Derived& WhereNotEqual(ColumnName const& columnName, T const& value);
494 template <typename ColumnName>
495 [[nodiscard]] Derived& WhereTrue(ColumnName const& columnName);
498 template <typename ColumnName>
499 [[nodiscard]] Derived& WhereFalse(ColumnName const& columnName);
506 [[nodiscard]] Derived& InnerJoin(TableName auto joinTable,
507 std::string_view joinColumnName,
511 [[nodiscard]] Derived& InnerJoin(TableName auto joinTable,
512 std::string_view joinColumnName,
513 std::string_view onMainTableColumn);
516 template <typename OnChainCallable>
518 [[nodiscard]] Derived& InnerJoin(TableName auto joinTable, OnChainCallable const& onClauseBuilder);
532 template <auto LeftField, auto RightField>
533 [[nodiscard]] Derived& InnerJoin();
536 [[nodiscard]] Derived& LeftOuterJoin(TableName auto joinTable,
537 std::string_view joinColumnName,
541 [[nodiscard]] Derived& LeftOuterJoin(TableName auto joinTable,
542 std::string_view joinColumnName,
543 std::string_view onMainTableColumn);
546 template <typename OnChainCallable>
548 [[nodiscard]] Derived& LeftOuterJoin(TableName auto joinTable, OnChainCallable const& onClauseBuilder);
551 [[nodiscard]] Derived& RightOuterJoin(TableName auto joinTable,
552 std::string_view joinColumnName,
556 [[nodiscard]] Derived& RightOuterJoin(TableName auto joinTable,
557 std::string_view joinColumnName,
558 std::string_view onMainTableColumn);
561 template <typename OnChainCallable>
563 [[nodiscard]] Derived& RightOuterJoin(TableName auto joinTable, OnChainCallable const& onClauseBuilder);
566 [[nodiscard]] Derived& FullOuterJoin(TableName auto joinTable,
567 std::string_view joinColumnName,
571 [[nodiscard]] Derived& FullOuterJoin(TableName auto joinTable,
572 std::string_view joinColumnName,
573 std::string_view onMainTableColumn);
576 template <typename OnChainCallable>
578 [[nodiscard]] Derived& FullOuterJoin(TableName auto joinTable, OnChainCallable const& onClauseBuilder);
581 SqlSearchCondition& SearchCondition() noexcept;
584 enum class WhereJunctor : uint8_t
592 WhereJunctor m_nextWhereJunctor = WhereJunctor::Where;
593 bool m_nextIsNot =
false;
595 void AppendWhereJunctor();
598 template <
typename ColumnName>
599 requires(std::same_as<ColumnName, SqlQualifiedTableColumnName> || std::convertible_to<ColumnName, std::string_view>
600 || std::convertible_to<ColumnName, std::string>)
601 void AppendColumnName(ColumnName
const& columnName);
604 template <
typename LiteralType>
605 void AppendLiteralValue(LiteralType
const& value);
608 template <
typename LiteralType,
typename TargetType>
609 void PopulateLiteralValueInto(LiteralType
const& value, TargetType& target);
611 template <
typename LiteralType>
612 detail::RawSqlCondition PopulateSqlSetExpression(LiteralType
const& values);
614 enum class JoinType : uint8_t
623 [[nodiscard]] Derived& Join(JoinType joinType,
624 TableName
auto joinTable,
625 std::string_view joinColumnName,
626 SqlQualifiedTableColumnName onOtherColumn);
629 [[nodiscard]] Derived& Join(JoinType joinType,
630 TableName
auto joinTable,
631 std::string_view joinColumnName,
632 std::string_view onMainTableColumn);
635 template <
typename OnChainCallable>
636 [[nodiscard]] Derived& Join(JoinType joinType, TableName
auto joinTable, OnChainCallable
const& onClauseBuilder);
639enum class SqlResultOrdering : uint8_t
647 enum class SelectType : std::uint8_t
658 SelectType selectType = SelectType::Undefined;
659 SqlQueryFormatter
const* formatter =
nullptr;
661 bool distinct =
false;
662 SqlSearchCondition searchCondition {};
672 std::vector<std::string> projectedFieldNames;
679 bool projectionHasWildcard =
false;
685 size_t limit = (std::numeric_limits<size_t>::max)();
687 [[nodiscard]] LIGHTWEIGHT_API std::string
ToSql()
const;
692 [[nodiscard]] std::span<std::string const> ProjectedFieldNames() const noexcept
694 return projectedFieldNames;
698 [[nodiscard]]
bool ProjectionHasWildcard() const noexcept
700 return projectionHasWildcard;
705template <
typename Derived>
706class [[nodiscard]] SqlBasicSelectQueryBuilder:
public SqlWhereClauseBuilder<Derived>
710 Derived& Distinct() noexcept;
713 Derived& OrderBy(SqlQualifiedTableColumnName const& columnName,
714 SqlResultOrdering ordering = SqlResultOrdering::ASCENDING);
717 Derived& OrderBy(std::string_view columnName, SqlResultOrdering ordering = SqlResultOrdering::ASCENDING);
720 Derived& GroupBy(std::string_view columnName);
723 Derived& GroupBy(SqlQualifiedTableColumnName const& columnName);
725 using ComposedQuery = detail::ComposedQuery;
731 mutable ComposedQuery _query {};
734template <
typename Derived>
735inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlBasicSelectQueryBuilder<Derived>::Distinct() noexcept
737 _query.distinct =
true;
738 return static_cast<Derived&
>(*this);
741template <
typename Derived>
742inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlBasicSelectQueryBuilder<Derived>::OrderBy(std::string_view columnName,
743 SqlResultOrdering ordering)
745 if (_query.orderBy.empty())
746 _query.orderBy +=
"\n ORDER BY ";
748 _query.orderBy +=
", ";
750 _query.orderBy +=
'"';
751 _query.orderBy += columnName;
752 _query.orderBy +=
'"';
754 if (ordering == SqlResultOrdering::DESCENDING)
755 _query.orderBy +=
" DESC";
756 else if (ordering == SqlResultOrdering::ASCENDING)
757 _query.orderBy +=
" ASC";
759 return static_cast<Derived&
>(*this);
762template <
typename Derived>
763inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlBasicSelectQueryBuilder<Derived>::OrderBy(
764 SqlQualifiedTableColumnName
const& columnName, SqlResultOrdering ordering)
766 if (_query.orderBy.empty())
767 _query.orderBy +=
"\n ORDER BY ";
769 _query.orderBy +=
", ";
771 _query.orderBy +=
'"';
772 _query.orderBy += columnName.tableName;
773 _query.orderBy +=
"\".\"";
774 _query.orderBy += columnName.columnName;
775 _query.orderBy +=
'"';
777 if (ordering == SqlResultOrdering::DESCENDING)
778 _query.orderBy +=
" DESC";
779 else if (ordering == SqlResultOrdering::ASCENDING)
780 _query.orderBy +=
" ASC";
782 return static_cast<Derived&
>(*this);
785template <
typename Derived>
786inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlBasicSelectQueryBuilder<Derived>::GroupBy(std::string_view columnName)
788 if (_query.groupBy.empty())
789 _query.groupBy +=
"\n GROUP BY ";
791 _query.groupBy +=
", ";
793 _query.groupBy +=
'"';
794 _query.groupBy += columnName;
795 _query.groupBy +=
'"';
797 return static_cast<Derived&
>(*this);
800template <
typename Derived>
801inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlBasicSelectQueryBuilder<Derived>::GroupBy(
802 SqlQualifiedTableColumnName
const& columnName)
804 if (_query.groupBy.empty())
805 _query.groupBy +=
"\n GROUP BY ";
807 _query.groupBy +=
", ";
809 _query.groupBy +=
'"';
810 _query.groupBy += columnName.tableName;
811 _query.groupBy +=
"\".\"";
812 _query.groupBy += columnName.columnName;
813 _query.groupBy +=
'"';
815 return static_cast<Derived&
>(*this);
818template <
typename Derived>
821 m_nextWhereJunctor = WhereJunctor::And;
822 return static_cast<Derived&
>(*this);
825template <
typename Derived>
828 m_nextWhereJunctor = WhereJunctor::Or;
829 return static_cast<Derived&
>(*this);
832template <
typename Derived>
835 m_nextIsNot = !m_nextIsNot;
836 return static_cast<Derived&
>(*this);
840template <
typename Derived>
841template <
typename ColumnName,
typename T>
844 if constexpr (detail::OneOf<T, SqlNullType, std::nullopt_t>)
849 return Where(columnName,
"IS NOT", value);
852 return Where(columnName,
"IS", value);
855 return Where(columnName,
"=", value);
859template <
typename Derived>
860template <
typename ColumnName,
typename T>
864 return Or().Where(columnName, value);
868template <
typename Derived>
869template <
typename Callable>
870 requires std::invocable<Callable, SqlWhereClauseBuilder<Derived>&>
873 return Or().Where(callable);
877template <
typename Derived>
878template <
typename Callable>
879 requires std::invocable<Callable, SqlWhereClauseBuilder<Derived>&>
882 auto& condition = SearchCondition().condition;
884 auto const originalSize = condition.size();
886 AppendWhereJunctor();
887 m_nextWhereJunctor = WhereJunctor::Null;
890 auto const sizeBeforeCallable = condition.size();
892 (void) callable(*
this);
894 if (condition.size() == sizeBeforeCallable)
895 condition.resize(originalSize);
899 return static_cast<Derived&
>(*this);
903template <
typename Derived>
904template <
typename ColumnName, std::ranges::input_range InputRange>
906 InputRange
const& values)
913 if (std::ranges::empty(values))
914 return WhereRaw(
"1 = 0");
915 return Where(columnName,
"IN", PopulateSqlSetExpression(values));
919template <
typename Derived>
920template <
typename ColumnName,
typename T>
922 std::initializer_list<T>
const& values)
925 if (values.begin() == values.end())
926 return WhereRaw(
"1 = 0");
927 return Where(columnName,
"IN", PopulateSqlSetExpression(values));
931template <
typename Derived>
932template <
typename ColumnName,
typename SubSelectQuery>
933 requires(std::is_invocable_r_v<std::string,
decltype(&SubSelectQuery::ToSql), SubSelectQuery
const&>)
935 SubSelectQuery
const& subSelectQuery)
937 return Where(columnName,
"IN", detail::RawSqlCondition {
"(" + subSelectQuery.ToSql() +
")" });
941template <
typename Derived>
942template <
typename ColumnName>
945 return Where(columnName,
"IS NOT", detail::RawSqlCondition {
"NULL" });
949template <
typename Derived>
950template <
typename ColumnName>
953 return Where(columnName,
"IS", detail::RawSqlCondition {
"NULL" });
957template <
typename Derived>
958template <
typename ColumnName,
typename T>
962 if constexpr (detail::OneOf<T, SqlNullType, std::nullopt_t>)
963 return Where(columnName,
"IS NOT", value);
965 return Where(columnName,
"!=", value);
969template <
typename Derived>
970template <
typename ColumnName>
973 return Where(columnName,
"=",
true);
977template <
typename Derived>
978template <
typename ColumnName>
981 return Where(columnName,
"=",
false);
996struct WhereConditionLiteralType
998 constexpr static bool needsQuotes = !std::is_integral_v<T> && !std::is_floating_point_v<T> && !std::same_as<T, bool>
999 && !std::same_as<T, SqlWildcardType>;
1003template <
typename Derived>
1004template <
typename ColumnName, std::
size_t N>
1006 std::string_view binaryOp,
1007 char const (&value)[N])
1009 return Where(columnName, binaryOp, std::string_view { value, N - 1 });
1013template <
typename Derived>
1014template <
typename ColumnName,
typename T>
1016 std::string_view binaryOp,
1019 auto& searchCondition = SearchCondition();
1021 AppendWhereJunctor();
1022 AppendColumnName(columnName);
1023 searchCondition.condition +=
' ';
1024 searchCondition.condition += binaryOp;
1025 searchCondition.condition +=
' ';
1026 AppendLiteralValue(value);
1028 return static_cast<Derived&
>(*this);
1032template <
typename Derived>
1033template <
typename ColumnName,
typename SubSelectQuery>
1034 requires(std::is_invocable_r_v<std::string,
decltype(&SubSelectQuery::ToSql), SubSelectQuery
const&>)
1036 std::string_view binaryOp,
1037 SubSelectQuery
const& value)
1039 return Where(columnName, binaryOp, detail::RawSqlCondition {
"(" + value.ToSql() +
")" });
1043template <
typename Derived>
1044template <
typename ColumnName,
typename T>
1046 std::string_view binaryOp,
1049 return Or().
Where(columnName, binaryOp, value);
1052template <
typename Derived>
1054 std::string_view joinColumnName,
1057 return Join(JoinType::INNER, joinTable, joinColumnName, onOtherColumn);
1060template <
typename Derived>
1062 std::string_view joinColumnName,
1063 std::string_view onMainTableColumn)
1065 return Join(JoinType::INNER, joinTable, joinColumnName, onMainTableColumn);
1068template <
typename Derived>
1069template <auto LeftField, auto RightField>
1072#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
1073 return Join(JoinType::INNER,
1075 FieldNameOf<LeftField>,
1081 FieldNameOf<LeftField>,
1086template <
typename Derived>
1087template <
typename OnChainCallable>
1088 requires std::invocable<OnChainCallable, SqlJoinConditionBuilder>
1091 return Join(JoinType::INNER, joinTable, onClauseBuilder);
1094template <
typename Derived>
1098 return Join(JoinType::LEFT, joinTable, joinColumnName, onOtherColumn);
1101template <
typename Derived>
1103 std::string_view joinColumnName,
1104 std::string_view onMainTableColumn)
1106 return Join(JoinType::LEFT, joinTable, joinColumnName, onMainTableColumn);
1109template <
typename Derived>
1110template <
typename OnChainCallable>
1111 requires std::invocable<OnChainCallable, SqlJoinConditionBuilder>
1114 return Join(JoinType::LEFT, joinTable, onClauseBuilder);
1117template <
typename Derived>
1121 return Join(JoinType::RIGHT, joinTable, joinColumnName, onOtherColumn);
1124template <
typename Derived>
1126 std::string_view joinColumnName,
1127 std::string_view onMainTableColumn)
1129 return Join(JoinType::RIGHT, joinTable, joinColumnName, onMainTableColumn);
1132template <
typename Derived>
1133template <
typename OnChainCallable>
1134 requires std::invocable<OnChainCallable, SqlJoinConditionBuilder>
1137 return Join(JoinType::RIGHT, joinTable, onClauseBuilder);
1140template <
typename Derived>
1144 return Join(JoinType::FULL, joinTable, joinColumnName, onOtherColumn);
1147template <
typename Derived>
1149 std::string_view joinColumnName,
1150 std::string_view onMainTableColumn)
1152 return Join(JoinType::FULL, joinTable, joinColumnName, onMainTableColumn);
1155template <
typename Derived>
1156template <
typename OnChainCallable>
1157 requires std::invocable<OnChainCallable, SqlJoinConditionBuilder>
1160 return Join(JoinType::FULL, joinTable, onClauseBuilder);
1174 template <
typename Derived,
typename T>
1175 class [[nodiscard]] ConditionalWhereBuilder
1179 constexpr ConditionalWhereBuilder(Derived& builder, std::optional<T>
const& value)
noexcept:
1180 _builder { builder },
1190 template <
typename ColumnName>
1191 [[nodiscard]] Derived& ThenWhere(ColumnName
const& column)
const
1193 if (_value.has_value())
1194 return _builder.Where(column, *_value);
1216 template <
typename ColumnName>
1217 [[nodiscard]] Derived& ThenWhere(ColumnName
const& column, std::string_view binaryOp)
const
1219 if (_value.has_value())
1220 return _builder.Where(column, binaryOp, *_value);
1226 std::optional<T>
const& _value;
1232template <
typename Derived>
1233template <
typename T>
1236 return detail::ConditionalWhereBuilder<Derived, T> {
static_cast<Derived&
>(*this), value };
1239template <
typename Derived>
1242 AppendWhereJunctor();
1244 auto& condition = SearchCondition().condition;
1245 condition += sqlConditionExpression;
1247 return static_cast<Derived&
>(*this);
1250template <
typename Derived>
1253 return static_cast<Derived*
>(
this)->SearchCondition();
1256template <
typename Derived>
1257inline LIGHTWEIGHT_FORCE_INLINE SqlQueryFormatter
const& SqlWhereClauseBuilder<Derived>::Formatter() const noexcept
1259 return static_cast<Derived const*
>(
this)->Formatter();
1262template <
typename Derived>
1263inline LIGHTWEIGHT_FORCE_INLINE
void SqlWhereClauseBuilder<Derived>::AppendWhereJunctor()
1265 using namespace std::string_view_literals;
1267 auto& condition = SearchCondition().condition;
1269 switch (m_nextWhereJunctor)
1271 case WhereJunctor::Null:
1273 case WhereJunctor::Where:
1274 condition +=
"\n WHERE "sv;
1276 case WhereJunctor::And:
1277 condition +=
" AND "sv;
1279 case WhereJunctor::Or:
1280 condition +=
" OR "sv;
1286 condition +=
"NOT "sv;
1287 m_nextIsNot =
false;
1290 m_nextWhereJunctor = WhereJunctor::And;
1294template <
typename Derived>
1295template <
typename ColumnName>
1296 requires(std::same_as<ColumnName, SqlQualifiedTableColumnName> || std::convertible_to<ColumnName, std::string_view>
1297 || std::convertible_to<ColumnName, std::string>)
1300 SearchCondition().condition += detail::MakeSqlColumnName(columnName);
1304template <
typename Derived>
1305template <
typename LiteralType>
1308 auto& searchCondition = SearchCondition();
1310 if constexpr (std::is_same_v<LiteralType, SqlQualifiedTableColumnName>
1311 || detail::OneOf<LiteralType, SqlNullType, std::nullopt_t> || std::is_same_v<LiteralType, SqlWildcardType>
1312 || std::is_same_v<LiteralType, detail::RawSqlCondition>)
1314 PopulateLiteralValueInto(value, searchCondition.condition);
1316 else if (searchCondition.inputBindings)
1318 searchCondition.condition +=
'?';
1319 searchCondition.inputBindings->emplace_back(value);
1321 else if constexpr (std::is_same_v<LiteralType, bool>)
1323 searchCondition.condition += Formatter().BooleanLiteral(value);
1325 else if constexpr (!WhereConditionLiteralType<LiteralType>::needsQuotes)
1327 searchCondition.condition += std::format(
"{}", value);
1331 searchCondition.condition += detail::MakeEscapedSqlString(std::format(
"{}", value));
1338 template <
typename LiteralType,
typename TargetType>
1339 void AppendLiteralValueInto(LiteralType
const& value, TargetType& target,
SqlQueryFormatter const& formatter)
1341 if constexpr (std::is_same_v<LiteralType, SqlQualifiedTableColumnName>)
1344 target += value.tableName;
1346 target += value.columnName;
1349 else if constexpr (detail::OneOf<LiteralType, SqlNullType, std::nullopt_t>)
1353 else if constexpr (std::is_same_v<LiteralType, SqlWildcardType>)
1357 else if constexpr (std::is_same_v<LiteralType, detail::RawSqlCondition>)
1359 target += value.condition;
1361 else if constexpr (std::is_same_v<LiteralType, bool>)
1365 else if constexpr (!WhereConditionLiteralType<LiteralType>::needsQuotes)
1367 target += std::format(
"{}", value);
1371 target += detail::MakeEscapedSqlString(std::format(
"{}", value));
1378template <
typename Derived>
1379template <
typename LiteralType,
typename TargetType>
1383 detail::AppendLiteralValueInto(value, target, Formatter());
1386template <
typename Derived>
1387template <
typename LiteralType>
1390 using namespace std::string_view_literals;
1392 using ValueType = std::ranges::range_value_t<LiteralType>;
1398 constexpr bool isBindable =
1399 !(std::is_same_v<ValueType, SqlQualifiedTableColumnName> || detail::OneOf<ValueType, SqlNullType, std::nullopt_t>
1400 || std::is_same_v<ValueType, SqlWildcardType> || std::is_same_v<ValueType, detail::RawSqlCondition>);
1402 auto& searchCondition = SearchCondition();
1404 std::ostringstream fragment;
1410 auto const asBindable = [](
auto const& value) ->
decltype(
auto) {
1411 using Decayed = std::decay_t<
decltype(value)>;
1412 if constexpr (detail::OneOf<Decayed, char*, char const*>)
1413 return std::string_view { value };
1414 else if constexpr (detail::OneOf<Decayed, char16_t*, char16_t const*>)
1415 return std::u16string_view { value };
1422 bool bindValues = searchCondition.inputBindings !=
nullptr;
1423 if constexpr (std::ranges::sized_range<LiteralType>)
1427 auto const appendValue = [&](
auto const& value) {
1428 if constexpr (isBindable)
1433 searchCondition.inputBindings->emplace_back(asBindable(value));
1437 std::string valueString;
1438 PopulateLiteralValueInto(value, valueString);
1439 fragment << valueString;
1443#if !defined(__cpp_lib_ranges_enumerate)
1445 for (
auto const& value: values)
1449 for (
auto const&& [index, value]: values | std::views::enumerate)
1458 return detail::RawSqlCondition { fragment.str() };
1461template <
typename Derived>
1462inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::Join(JoinType joinType,
1463 TableName
auto joinTable,
1464 std::string_view joinColumnName,
1465 SqlQualifiedTableColumnName onOtherColumn)
1467 static constexpr std::array<std::string_view, 4> JoinTypeStrings = {
1474 if constexpr (std::is_same_v<std::remove_cvref_t<
decltype(joinTable)>, AliasedTableName>)
1476 SearchCondition().tableJoins += std::format(
"\n"
1477 R
"( {0} JOIN "{1}" AS "{2}" ON "{2}"."{3}" = "{4}"."{5}")",
1478 JoinTypeStrings[static_cast<std::size_t
>(joinType)],
1479 joinTable.tableName,
1482 onOtherColumn.tableName,
1483 onOtherColumn.columnName);
1487 SearchCondition().tableJoins += std::format(
"\n"
1488 R
"( {0} JOIN "{1}" ON "{1}"."{2}" = "{3}"."{4}")",
1489 JoinTypeStrings[static_cast<std::size_t
>(joinType)],
1492 onOtherColumn.tableName,
1493 onOtherColumn.columnName);
1495 return static_cast<Derived&
>(*this);
1498template <
typename Derived>
1499inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlWhereClauseBuilder<Derived>::Join(JoinType joinType,
1500 TableName
auto joinTable,
1501 std::string_view joinColumnName,
1502 std::string_view onMainTableColumn)
1504 return Join(joinType,
1507 SqlQualifiedTableColumnName { .tableName = SearchCondition().tableName, .columnName = onMainTableColumn });
1511template <
typename Derived>
1512template <
typename Callable>
1514 TableName
auto joinTable,
1515 Callable
const& onClauseBuilder)
1517 static constexpr std::array<std::string_view, 4> JoinTypeStrings = {
1524 size_t const originalSize = SearchCondition().tableJoins.size();
1525 SearchCondition().tableJoins +=
1526 std::format(
"\n {0} JOIN \"{1}\" ON ", JoinTypeStrings[
static_cast<std::size_t
>(joinType)], joinTable);
1527 size_t const sizeBefore = SearchCondition().tableJoins.size();
1529 size_t const sizeAfter = SearchCondition().tableJoins.size();
1530 if (sizeBefore == sizeAfter)
1531 SearchCondition().tableJoins.resize(originalSize);
1533 return static_cast<Derived&
>(*this);
Query builder for building JOIN conditions.
SqlJoinConditionBuilder & On(std::string_view joinColumnName, SqlQualifiedTableColumnName onOtherColumn)
Adds an AND join condition.
SqlJoinConditionBuilder & OrOnValue(std::string_view joinColumnName, T const &value)
Adds an OR join condition testing the joined column against value for equality.
SqlJoinConditionBuilder & OnNull(std::string_view joinColumnName)
Adds an AND join condition testing the joined column for NULL.
SqlJoinConditionBuilder & OrOnNull(std::string_view joinColumnName)
Adds an OR join condition testing the joined column for NULL.
SqlJoinConditionBuilder & OnValue(std::string_view joinColumnName, std::string_view binaryOp, T const &value)
Adds an AND join condition testing the joined column against value with binaryOp.
SqlJoinConditionBuilder & OnValue(std::string_view joinColumnName, T const &value)
SqlJoinConditionBuilder & OnNotNull(std::string_view joinColumnName)
Adds an AND join condition testing the joined column for NOT NULL.
SqlJoinConditionBuilder & OrOnGroup(Callable const &build)
Adds an OR group of join conditions, in parentheses.
SqlJoinConditionBuilder(std::string_view referenceTable, SqlSearchCondition *searchCondition, SqlQueryFormatter const *formatter) noexcept
SqlJoinConditionBuilder & Operator(std::string_view joinColumnName, SqlQualifiedTableColumnName onOtherColumn, std::string_view op)
Adds a join condition with a custom operator.
SqlJoinConditionBuilder & OrOnNotNull(std::string_view joinColumnName)
Adds an OR join condition testing the joined column for NOT NULL.
SqlJoinConditionBuilder & OrOnValue(std::string_view joinColumnName, std::string_view binaryOp, T const &value)
Adds an OR join condition testing the joined column against value with binaryOp.
SqlJoinConditionBuilder & OrOn(std::string_view joinColumnName, SqlQualifiedTableColumnName onOtherColumn)
Adds an OR join condition.
SqlJoinConditionBuilder & OnGroup(Callable const &build)
Derived & FullOuterJoin(TableName auto joinTable, std::string_view joinColumnName, SqlQualifiedTableColumnName onOtherColumn)
Constructs an FULL OUTER JOIN clause.
Derived & WhereTrue(ColumnName const &columnName)
Constructs or extends a WHERE clause to test for a value being true.
Derived & WhereIn(ColumnName const &columnName, InputRange const &values)
Constructs or extends an WHERE/OR clause to test for a value, satisfying std::ranges::input_range.
Derived & WhereNotNull(ColumnName const &columnName)
Constructs or extends a WHERE clause to test for a value being not null.
Derived & Where(ColumnName const &columnName, std::string_view binaryOp, T const &value)
Constructs or extends a WHERE clause to test for a binary operation.
Derived & WhereRaw(std::string_view sqlConditionExpression)
Constructs or extends a raw WHERE clause.
Derived & LeftOuterJoin(TableName auto joinTable, std::string_view joinColumnName, SqlQualifiedTableColumnName onOtherColumn)
Constructs an LEFT OUTER JOIN clause.
Derived & Not() noexcept
Indicates, that the next WHERE clause should be negated.
Derived & WhereNull(ColumnName const &columnName)
Constructs or extends an WHERE/OR clause to test for a value to be NULL.
Derived & WhereNotEqual(ColumnName const &columnName, T const &value)
Constructs or extends a WHERE clause to test for a value being equal to another column.
Derived & RightOuterJoin(TableName auto joinTable, std::string_view joinColumnName, SqlQualifiedTableColumnName onOtherColumn)
Constructs an RIGHT OUTER JOIN clause.
Derived & OrWhere(ColumnName const &columnName, std::string_view binaryOp, T const &value)
Constructs or extends a WHERE/OR clause to test for a binary operation.
auto If(std::optional< T > const &value) noexcept
Starts a conditional WHERE chain driven by a std::optional<T> value.
Derived & And() noexcept
Indicates, that the next WHERE clause should be AND-ed (default).
Derived & Or() noexcept
Indicates, that the next WHERE clause should be OR-ed.
Derived & WhereFalse(ColumnName const &columnName)
Constructs or extends a WHERE clause to test for a value being false.
constexpr std::string_view RecordTableName
Holds the SQL tabl ename for the given record type.
LIGHTWEIGHT_API std::vector< std::string > ToSql(SqlQueryFormatter const &formatter, SqlMigrationPlanElement const &element)
constexpr std::size_t SqlMaxBoundSetSize
Largest IN-set that WhereIn passes as bound parameters.
Name of table in a SQL query, where the table's name is aliased.
std::string_view alias
The alias for the table.
std::weak_ordering operator<=>(AliasedTableName const &) const =default
Three-way comparison operator.
std::string_view tableName
The table name.
SqlQualifiedTableColumnName represents a column name qualified with a table name.
std::string_view tableName
The table name.
SqlWildcardType is a placeholder for an explicit wildcard input parameter in a SQL query.