676 [[nodiscard]] SqlSearchCondition& DerivedSearchCondition() noexcept;
678 enum class WhereJunctor : uint8_t
686 WhereJunctor m_nextWhereJunctor = WhereJunctor::Where;
687 bool m_nextIsNot =
false;
689 void AppendWhereJunctor();
692 template <
typename ColumnName>
693 requires(std::same_as<ColumnName, SqlQualifiedTableColumnName> || std::convertible_to<ColumnName, std::string_view>
694 || std::convertible_to<ColumnName, std::string>)
695 void AppendColumnName(ColumnName
const& columnName);
698 template <
typename LiteralType>
699 void AppendLiteralValue(LiteralType
const& value);
702 template <
typename LiteralType,
typename TargetType>
703 void PopulateLiteralValueInto(LiteralType
const& value, TargetType& target);
705 template <
typename LiteralType>
706 detail::RawSqlCondition PopulateSqlSetExpression(LiteralType
const& values);
708 enum class JoinType : uint8_t
717 [[nodiscard]] Derived& Join(JoinType joinType,
718 TableName
auto joinTable,
719 std::string_view joinColumnName,
720 SqlQualifiedTableColumnName onOtherColumn);
723 [[nodiscard]] Derived& Join(JoinType joinType,
724 TableName
auto joinTable,
725 std::string_view joinColumnName,
726 std::string_view onMainTableColumn);
729 template <
typename OnChainCallable>
730 [[nodiscard]] Derived& Join(JoinType joinType, TableName
auto joinTable, OnChainCallable
const& onClauseBuilder);
733enum class SqlResultOrdering : uint8_t
741 enum class SelectType : std::uint8_t
752 SelectType selectType = SelectType::Undefined;
753 SqlQueryFormatter
const* formatter =
nullptr;
755 bool distinct =
false;
756 SqlSearchCondition searchCondition {};
766 std::vector<std::string> projectedFieldNames;
773 bool projectionHasWildcard =
false;
784 size_t limit = (std::numeric_limits<size_t>::max)();
786 [[nodiscard]] LIGHTWEIGHT_API std::string
ToSql()
const;
791 [[nodiscard]] std::span<std::string const> ProjectedFieldNames() const noexcept
793 return projectedFieldNames;
797 [[nodiscard]]
bool ProjectionHasWildcard() const noexcept
799 return projectionHasWildcard;
816template <
typename Derived>
821 Derived& Distinct()
noexcept;
825 SqlResultOrdering ordering = SqlResultOrdering::ASCENDING);
828 Derived& OrderBy(std::string_view columnName, SqlResultOrdering ordering = SqlResultOrdering::ASCENDING);
833 Derived& GroupBy(std::string_view columnName);
853 template <
typename T>
854 [[nodiscard]] Derived& Having(
SqlFieldExpression const& aggregate, std::string_view binaryOp, T
const& value);
857 template <std::
size_t N>
858 [[nodiscard]] Derived& Having(
SqlFieldExpression const& aggregate, std::string_view binaryOp,
char const (&value)[N]);
865 [[nodiscard]] Derived& Having(std::string_view rawSqlCondition);
874 template <
typename Callable>
875 requires std::invocable<Callable, Derived&>
876 [[nodiscard]] Derived& Having(Callable
const& group);
886 template <
typename T>
887 [[nodiscard]] Derived& OrHaving(
SqlFieldExpression const& aggregate, std::string_view binaryOp, T
const& value);
894 [[nodiscard]] Derived& OrHaving(std::string_view rawSqlCondition);
901 template <
typename Callable>
902 requires std::invocable<Callable, Derived&>
903 [[nodiscard]] Derived& OrHaving(Callable
const& group);
905 using ComposedQuery = detail::ComposedQuery;
910 [[nodiscard]]
bool HasHavingClause()
const noexcept
912 return _nextHavingTerm != NextHavingTerm::OpensClause;
918 mutable ComposedQuery _query {};
922 enum class NextHavingTerm : std::uint8_t
930 void AppendGroupBySeparator();
933 void AppendHavingJunctor();
935 NextHavingTerm _nextHavingTerm = NextHavingTerm::OpensClause;
938template <
typename Derived>
939inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlBasicSelectQueryBuilder<Derived>::Distinct() noexcept
941 _query.distinct =
true;
942 return static_cast<Derived&
>(*this);
945template <
typename Derived>
946inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlBasicSelectQueryBuilder<Derived>::OrderBy(std::string_view columnName,
947 SqlResultOrdering ordering)
949 if (_query.orderBy.empty())
950 _query.orderBy +=
"\n ORDER BY ";
952 _query.orderBy +=
", ";
954 _query.orderBy +=
'"';
955 _query.orderBy += columnName;
956 _query.orderBy +=
'"';
958 if (ordering == SqlResultOrdering::DESCENDING)
959 _query.orderBy +=
" DESC";
960 else if (ordering == SqlResultOrdering::ASCENDING)
961 _query.orderBy +=
" ASC";
963 return static_cast<Derived&
>(*this);
966template <
typename Derived>
967inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlBasicSelectQueryBuilder<Derived>::OrderBy(
968 SqlQualifiedTableColumnName
const& columnName, SqlResultOrdering ordering)
970 if (_query.orderBy.empty())
971 _query.orderBy +=
"\n ORDER BY ";
973 _query.orderBy +=
", ";
975 _query.orderBy +=
'"';
976 _query.orderBy += columnName.tableName;
977 _query.orderBy +=
"\".\"";
978 _query.orderBy += columnName.columnName;
979 _query.orderBy +=
'"';
981 if (ordering == SqlResultOrdering::DESCENDING)
982 _query.orderBy +=
" DESC";
983 else if (ordering == SqlResultOrdering::ASCENDING)
984 _query.orderBy +=
" ASC";
986 return static_cast<Derived&
>(*this);
989template <
typename Derived>
990inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlBasicSelectQueryBuilder<Derived>::GroupBy(std::string_view columnName)
992 AppendGroupBySeparator();
994 _query.groupBy +=
'"';
995 _query.groupBy += columnName;
996 _query.groupBy +=
'"';
998 return static_cast<Derived&
>(*this);
1001template <
typename Derived>
1002inline LIGHTWEIGHT_FORCE_INLINE Derived& SqlBasicSelectQueryBuilder<Derived>::GroupBy(
1003 SqlQualifiedTableColumnName
const& columnName)
1005 AppendGroupBySeparator();
1007 _query.groupBy +=
'"';
1008 _query.groupBy += columnName.tableName;
1009 _query.groupBy +=
"\".\"";
1010 _query.groupBy += columnName.columnName;
1011 _query.groupBy +=
'"';
1013 return static_cast<Derived&
>(*this);
1016template <
typename Derived>
1017inline void SqlBasicSelectQueryBuilder<Derived>::AppendGroupBySeparator()
1019 if (HasHavingClause())
1020 throw std::logic_error {
"GroupBy() was called after Having(); the GROUP BY columns must come first." };
1022 if (_query.groupBy.empty())
1023 _query.groupBy +=
"\n GROUP BY ";
1025 _query.groupBy +=
", ";
1028template <
typename Derived>
1029inline void SqlBasicSelectQueryBuilder<Derived>::AppendHavingJunctor()
1031 if (_query.groupBy.empty())
1032 throw std::logic_error {
"Having() requires a preceding GroupBy()." };
1034 auto const pending = this->TakePendingConnective();
1036 switch (_nextHavingTerm)
1038 case NextHavingTerm::OpensClause:
1039 _query.groupBy +=
"\n HAVING ";
1041 case NextHavingTerm::OpensGroup:
1043 case NextHavingTerm::Continues:
1044 _query.groupBy += pending.isOr ?
" OR " :
" AND ";
1049 _query.groupBy +=
"NOT ";
1051 _nextHavingTerm = NextHavingTerm::Continues;
1054template <
typename Derived>
1055template <
typename T>
1056inline Derived& SqlBasicSelectQueryBuilder<Derived>::Having(SqlFieldExpression
const& aggregate,
1057 std::string_view binaryOp,
1060 AppendHavingJunctor();
1062 _query.groupBy += aggregate.expression;
1063 _query.groupBy +=
' ';
1064 _query.groupBy += binaryOp;
1065 _query.groupBy +=
' ';
1067 auto*
const inputBindings = _query.searchCondition.inputBindings;
1068 auto const bindingsBefore = inputBindings !=
nullptr ? inputBindings->size() : 0;
1069 detail::AppendConditionValue(value, _query.groupBy, inputBindings, 0, this->DerivedFormatter());
1070 if (inputBindings !=
nullptr)
1071 _query.searchCondition.havingBindingCount += inputBindings->size() - bindingsBefore;
1073 return static_cast<Derived&
>(*this);
1076template <
typename Derived>
1077template <std::
size_t N>
1078inline Derived& SqlBasicSelectQueryBuilder<Derived>::Having(SqlFieldExpression
const& aggregate,
1079 std::string_view binaryOp,
1080 char const (&value)[N])
1082 return Having(aggregate, binaryOp, std::string_view { value, N - 1 });
1085template <
typename Derived>
1086inline Derived& SqlBasicSelectQueryBuilder<Derived>::Having(std::string_view rawSqlCondition)
1088 AppendHavingJunctor();
1089 _query.groupBy += rawSqlCondition;
1090 return static_cast<Derived&
>(*this);
1093template <
typename Derived>
1094template <
typename Callable>
1095 requires std::invocable<Callable, Derived&>
1096inline Derived& SqlBasicSelectQueryBuilder<Derived>::Having(Callable
const& group)
1098 auto const originalSize = _query.groupBy.size();
1099 auto const originalNextTerm = _nextHavingTerm;
1101 AppendHavingJunctor();
1102 _nextHavingTerm = NextHavingTerm::OpensGroup;
1103 _query.groupBy +=
'(';
1105 auto const sizeBeforeGroup = _query.groupBy.size();
1106 (void) group(
static_cast<Derived&
>(*
this));
1108 if (_query.groupBy.size() == sizeBeforeGroup)
1111 _query.groupBy.resize(originalSize);
1112 _nextHavingTerm = originalNextTerm;
1115 _query.groupBy +=
')';
1117 return static_cast<Derived&
>(*this);
1120template <
typename Derived>
1121template <
typename T>
1122inline Derived& SqlBasicSelectQueryBuilder<Derived>::OrHaving(SqlFieldExpression
const& aggregate,
1123 std::string_view binaryOp,
1126 return this->Or().Having(aggregate, binaryOp, value);
1129template <
typename Derived>
1130inline Derived& SqlBasicSelectQueryBuilder<Derived>::OrHaving(std::string_view rawSqlCondition)
1132 return this->Or().Having(rawSqlCondition);
1135template <
typename Derived>
1136template <
typename Callable>
1137 requires std::invocable<Callable, Derived&>
1138inline Derived& SqlBasicSelectQueryBuilder<Derived>::OrHaving(Callable
const& group)
1140 return this->Or().Having(group);
1143template <
typename Derived>
1146 m_nextWhereJunctor = WhereJunctor::And;
1147 return static_cast<Derived&
>(*this);
1150template <
typename Derived>
1153 m_nextWhereJunctor = WhereJunctor::Or;
1154 return static_cast<Derived&
>(*this);
1157template <
typename Derived>
1160 m_nextIsNot = !m_nextIsNot;
1161 return static_cast<Derived&
>(*this);
1165template <
typename Derived>
1166template <
typename ColumnName,
typename T>
1169 if constexpr (detail::OneOf<T, SqlNullType, std::nullopt_t>)
1173 m_nextIsNot =
false;
1174 return Where(columnName,
"IS NOT", value);
1177 return Where(columnName,
"IS", value);
1180 return Where(columnName,
"=", value);
1184template <
typename Derived>
1185template <
typename ColumnName,
typename T>
1189 return Or().Where(columnName, value);
1193template <
typename Derived>
1194template <
typename Callable>
1195 requires std::invocable<Callable, SqlWhereClauseBuilder<Derived>&>
1198 return Or().Where(callable);
1202template <
typename Derived>
1203template <
typename Callable>
1204 requires std::invocable<Callable, SqlWhereClauseBuilder<Derived>&>
1207 auto& condition = DerivedSearchCondition().condition;
1209 auto const originalSize = condition.size();
1211 AppendWhereJunctor();
1212 m_nextWhereJunctor = WhereJunctor::Null;
1215 auto const sizeBeforeCallable = condition.size();
1217 (void) callable(*
this);
1219 if (condition.size() == sizeBeforeCallable)
1220 condition.resize(originalSize);
1224 return static_cast<Derived&
>(*this);
1228template <
typename Derived>
1229template <
typename ColumnName, std::ranges::input_range InputRange>
1231 InputRange
const& values)
1238 if (std::ranges::empty(values))
1239 return WhereRaw(
"1 = 0");
1240 return Where(columnName,
"IN", PopulateSqlSetExpression(values));
1244template <
typename Derived>
1245template <
typename ColumnName,
typename T>
1247 std::initializer_list<T>
const& values)
1250 if (values.begin() == values.end())
1251 return WhereRaw(
"1 = 0");
1252 return Where(columnName,
"IN", PopulateSqlSetExpression(values));
1256template <
typename Derived>
1257template <
typename ColumnName,
typename SubSelectQuery>
1258 requires(std::is_invocable_r_v<std::string,
decltype(&SubSelectQuery::ToSql), SubSelectQuery
const&>)
1260 SubSelectQuery
const& subSelectQuery)
1262 return Where(columnName,
"IN", detail::RawSqlCondition {
"(" + subSelectQuery.ToSql() +
")" });
1266template <
typename Derived>
1267template <
typename ColumnName>
1270 return Where(columnName,
"IS NOT", detail::RawSqlCondition {
"NULL" });
1274template <
typename Derived>
1275template <
typename ColumnName>
1278 return Where(columnName,
"IS", detail::RawSqlCondition {
"NULL" });
1282template <
typename Derived>
1283template <
typename ColumnName,
typename T>
1287 if constexpr (detail::OneOf<T, SqlNullType, std::nullopt_t>)
1288 return Where(columnName,
"IS NOT", value);
1290 return Where(columnName,
"!=", value);
1294template <
typename Derived>
1295template <
typename ColumnName>
1298 return Where(columnName,
"=",
true);
1302template <
typename Derived>
1303template <
typename ColumnName>
1306 return Where(columnName,
"=",
false);
1320template <
typename T>
1321struct WhereConditionLiteralType
1323 constexpr static bool needsQuotes = !std::is_integral_v<T> && !std::is_floating_point_v<T> && !std::same_as<T, bool>
1324 && !std::same_as<T, SqlWildcardType>;
1328template <
typename Derived>
1329template <
typename ColumnName, std::
size_t N>
1331 std::string_view binaryOp,
1332 char const (&value)[N])
1334 return Where(columnName, binaryOp, std::string_view { value, N - 1 });
1338template <
typename Derived>
1339template <
typename ColumnName,
typename T>
1341 std::string_view binaryOp,
1344 auto& searchCondition = DerivedSearchCondition();
1346 AppendWhereJunctor();
1347 AppendColumnName(columnName);
1348 searchCondition.condition +=
' ';
1349 searchCondition.condition += binaryOp;
1350 searchCondition.condition +=
' ';
1351 AppendLiteralValue(value);
1353 return static_cast<Derived&
>(*this);
1357template <
typename Derived>
1358template <
typename ColumnName,
typename SubSelectQuery>
1359 requires(std::is_invocable_r_v<std::string,
decltype(&SubSelectQuery::ToSql), SubSelectQuery
const&>)
1361 std::string_view binaryOp,
1362 SubSelectQuery
const& value)
1364 return Where(columnName, binaryOp, detail::RawSqlCondition {
"(" + value.ToSql() +
")" });
1368template <
typename Derived>
1369template <
typename ColumnName,
typename T>
1371 std::string_view binaryOp,
1374 return Or().
Where(columnName, binaryOp, value);
1377template <
typename Derived>
1379 std::string_view joinColumnName,
1382 return Join(JoinType::INNER, joinTable, joinColumnName, onOtherColumn);
1385template <
typename Derived>
1387 std::string_view joinColumnName,
1388 std::string_view onMainTableColumn)
1390 return Join(JoinType::INNER, joinTable, joinColumnName, onMainTableColumn);
1393template <
typename Derived>
1394template <auto LeftField, auto RightField>
1397#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
1398 return Join(JoinType::INNER,
1400 FieldNameOf<LeftField>,
1406 FieldNameOf<LeftField>,
1411template <
typename Derived>
1412template <
typename OnChainCallable>
1413 requires std::invocable<OnChainCallable, SqlJoinConditionBuilder>
1416 return Join(JoinType::INNER, joinTable, onClauseBuilder);
1419template <
typename Derived>
1423 return Join(JoinType::LEFT, joinTable, joinColumnName, onOtherColumn);
1426template <
typename Derived>
1428 std::string_view joinColumnName,
1429 std::string_view onMainTableColumn)
1431 return Join(JoinType::LEFT, joinTable, joinColumnName, onMainTableColumn);
1434template <
typename Derived>
1435template <
typename OnChainCallable>
1436 requires std::invocable<OnChainCallable, SqlJoinConditionBuilder>
1439 return Join(JoinType::LEFT, joinTable, onClauseBuilder);
1442template <
typename Derived>
1446 return Join(JoinType::RIGHT, joinTable, joinColumnName, onOtherColumn);
1449template <
typename Derived>
1451 std::string_view joinColumnName,
1452 std::string_view onMainTableColumn)
1454 return Join(JoinType::RIGHT, joinTable, joinColumnName, onMainTableColumn);
1457template <
typename Derived>
1458template <
typename OnChainCallable>
1459 requires std::invocable<OnChainCallable, SqlJoinConditionBuilder>
1462 return Join(JoinType::RIGHT, joinTable, onClauseBuilder);
1465template <
typename Derived>
1469 return Join(JoinType::FULL, joinTable, joinColumnName, onOtherColumn);
1472template <
typename Derived>
1474 std::string_view joinColumnName,
1475 std::string_view onMainTableColumn)
1477 return Join(JoinType::FULL, joinTable, joinColumnName, onMainTableColumn);
1480template <
typename Derived>
1481template <
typename OnChainCallable>
1482 requires std::invocable<OnChainCallable, SqlJoinConditionBuilder>
1485 return Join(JoinType::FULL, joinTable, onClauseBuilder);
1499 template <
typename Derived,
typename T>
1500 class [[nodiscard]] ConditionalWhereBuilder
1504 constexpr ConditionalWhereBuilder(Derived& builder, std::optional<T>
const& value)
noexcept:
1505 _builder { builder },
1515 template <
typename ColumnName>
1516 [[nodiscard]] Derived& ThenWhere(ColumnName
const& column)
const
1518 if (_value.has_value())
1519 return _builder.Where(column, *_value);
1541 template <
typename ColumnName>
1542 [[nodiscard]] Derived& ThenWhere(ColumnName
const& column, std::string_view binaryOp)
const
1544 if (_value.has_value())
1545 return _builder.Where(column, binaryOp, *_value);
1551 std::optional<T>
const& _value;
1557template <
typename Derived>
1558template <
typename T>
1561 return detail::ConditionalWhereBuilder<Derived, T> {
static_cast<Derived&
>(*this), value };
1564template <
typename Derived>
1567 AppendWhereJunctor();
1569 auto& condition = DerivedSearchCondition().condition;
1570 condition += sqlConditionExpression;
1572 return static_cast<Derived&
>(*this);
1575template <
typename Derived>
1578 auto const pending =
PendingConnective { .
isOr = m_nextWhereJunctor == WhereJunctor::Or, .isNot = m_nextIsNot };
1580 m_nextIsNot =
false;
1581 if (m_nextWhereJunctor == WhereJunctor::And || m_nextWhereJunctor == WhereJunctor::Or)
1582 m_nextWhereJunctor = DerivedSearchCondition().condition.empty() ? WhereJunctor::Where : WhereJunctor::And;
1587template <
typename Derived>
1590 return static_cast<Derived*
>(
this)->SearchCondition();
1593template <
typename Derived>
1596 return static_cast<Derived const*
>(
this)->Formatter();