5#include "../SqlConnection.hpp"
6#include "../SqlQueryFormatter.hpp"
7#include "../SqlStatement.hpp"
9#include "BelongsTo.hpp"
18 template <
typename FieldType>
19 constexpr bool CanSafelyBindOutputColumn(SqlServerType sqlServerType)
noexcept
21 if (sqlServerType != SqlServerType::MICROSOFT_SQL)
28 if constexpr (IsField<FieldType>)
30 if constexpr (detail::OneOf<
typename FieldType::ValueType,
36 || IsSqlDynamicString<typename FieldType::ValueType>
37 || IsSqlDynamicBinary<typename FieldType::ValueType>)
46 template <DataMapperRecord Record>
47 constexpr bool CanSafelyBindOutputColumns(SqlServerType sqlServerType)
noexcept
49 if (sqlServerType != SqlServerType::MICROSOFT_SQL)
53 Reflection::EnumerateMembers<Record>([&result]<
size_t I,
typename Field>() {
54 if constexpr (IsField<Field>)
56 if constexpr (detail::OneOf<
typename Field::ValueType,
62 || IsSqlDynamicString<typename Field::ValueType>
63 || IsSqlDynamicBinary<typename Field::ValueType>)
73 template <
typename Record>
74 void BindAllOutputColumnsWithOffset(SqlResultCursor& reader, Record& record, SQLSMALLINT startOffset)
76 Reflection::EnumerateMembers(record,
77 [reader = &reader, i = startOffset]<
size_t I,
typename Field>(Field& field)
mutable {
78 if constexpr (IsField<Field>)
80 reader->BindOutputColumn(i++, &field.MutableValue());
82 else if constexpr (IsBelongsTo<Field>)
84 reader->BindOutputColumn(i++, &field.MutableValue());
86 else if constexpr (SqlOutputColumnBinder<Field>)
88 reader->BindOutputColumn(i++, &field);
93 template <
typename Record>
94 void BindAllOutputColumns(SqlResultCursor& reader, Record& record)
96 BindAllOutputColumnsWithOffset(reader, record, 1);
102 template <
typename ElementMask,
typename Record>
103 void GetAllColumns(SqlResultCursor& reader, Record& record, SQLUSMALLINT indexFromQuery = 0)
105 Reflection::EnumerateMembers<ElementMask>(
106 record, [reader = &reader, &indexFromQuery]<
size_t I,
typename Field>(Field& field)
mutable {
108 if constexpr (IsField<Field>)
111 field.MutableValue() =
112 reader->GetNullableColumn<
typename Field::ValueType::value_type>(indexFromQuery);
114 field.MutableValue() = reader->GetColumn<
typename Field::ValueType>(indexFromQuery);
116 else if constexpr (SqlGetColumnNativeType<Field>)
118 if constexpr (IsOptionalBelongsTo<Field>)
119 field = reader->GetNullableColumn<
typename Field::BaseType>(indexFromQuery);
121 field = reader->GetColumn<Field>(indexFromQuery);
126 template <
typename Record>
127 void GetAllColumns(SqlResultCursor& reader, Record& record, SQLUSMALLINT indexFromQuery = 0)
129 return GetAllColumns<std::make_integer_sequence<size_t, Reflection::CountMembers<Record>>, Record>(
130 reader, record, indexFromQuery);
133 template <
typename FirstRecord,
typename SecondRecord>
135 void GetAllColumns(SqlResultCursor& reader, std::tuple<FirstRecord, SecondRecord>& record)
137 auto& [firstRecord, secondRecord] = record;
139 Reflection::EnumerateMembers(firstRecord, [reader = &reader]<
size_t I,
typename Field>(Field& field)
mutable {
140 if constexpr (IsField<Field>)
143 field.MutableValue() = reader->GetNullableColumn<
typename Field::ValueType::value_type>(I + 1);
145 field.MutableValue() = reader->GetColumn<
typename Field::ValueType>(I + 1);
147 else if constexpr (SqlGetColumnNativeType<Field>)
150 field = reader->GetNullableColumn<
typename Field::BaseType>(I + 1);
152 field = reader->GetColumn<Field>(I + 1);
156 Reflection::EnumerateMembers(secondRecord, [reader = &reader]<
size_t I,
typename Field>(Field& field)
mutable {
157 if constexpr (IsField<Field>)
160 field.MutableValue() = reader->GetNullableColumn<
typename Field::ValueType::value_type>(
161 Reflection::CountMembers<FirstRecord> + I + 1);
163 field.MutableValue() =
164 reader->GetColumn<
typename Field::ValueType>(Reflection::CountMembers<FirstRecord> + I + 1);
166 else if constexpr (SqlGetColumnNativeType<Field>)
170 reader->GetNullableColumn<
typename Field::BaseType>(Reflection::CountMembers<FirstRecord> + I + 1);
172 field = reader->GetColumn<Field>(Reflection::CountMembers<FirstRecord> + I + 1);
177 template <
typename Record>
178 bool ReadSingleResult(SqlServerType sqlServerType, SqlResultCursor& reader, Record& record)
180 auto const outputColumnsBound = CanSafelyBindOutputColumns<Record>(sqlServerType);
182 if (outputColumnsBound)
183 BindAllOutputColumns(reader, record);
185 if (!reader.FetchRow())
188 if (!outputColumnsBound)
189 GetAllColumns(reader, record);
198template <
typename Record,
typename Derived>
207 friend class SqlWhereClauseBuilder<Derived>;
209 LIGHTWEIGHT_FORCE_INLINE SqlSearchCondition& SearchCondition()
noexcept
211 return this->_query.searchCondition;
214 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE
SqlQueryFormatter const& Formatter()
const noexcept
223 _fields { std::move(fields) }
232 RecordTableName<Record>,
233 this->_query.searchCondition.tableAlias,
234 this->_query.searchCondition.tableJoins,
235 this->_query.searchCondition.condition));
237 if (reader.FetchRow())
243 [[nodiscard]] std::vector<Record>
All()
245 auto records = std::vector<Record> {};
248 RecordTableName<Record>,
249 this->_query.searchCondition.tableAlias,
250 this->_query.searchCondition.tableJoins,
251 this->_query.searchCondition.condition,
252 this->_query.orderBy,
253 this->_query.groupBy));
270 template <auto Field>
271 [[nodiscard]]
auto All() -> std::vector<ReferencedFieldTypeOf<Field>>
274 auto result = std::vector<value_type> {};
277 FullyQualifiedNamesOf<Field>.string_view(),
278 RecordTableName<Record>,
279 this->_query.searchCondition.tableAlias,
280 this->_query.searchCondition.tableJoins,
281 this->_query.searchCondition.condition,
282 this->_query.orderBy,
283 this->_query.groupBy));
285 auto const outputColumnsBound = detail::CanSafelyBindOutputColumn<value_type>(_stmt.
Connection().
ServerType());
288 auto& value = result.emplace_back();
289 if (outputColumnsBound)
290 reader.BindOutputColumn(1, &value);
298 if (!outputColumnsBound)
318 template <
auto... ReferencedFields>
319 requires(
sizeof...(ReferencedFields) >= 2)
320 [[nodiscard]]
auto All() -> std::vector<Record>
322 auto records = std::vector<Record> {};
324 _stmt.ExecuteDirect(_formatter.SelectAll(this->_query.distinct,
325 FullyQualifiedNamesOf<ReferencedFields...>.string_view(),
326 RecordTableName<Record>,
327 this->_query.searchCondition.tableAlias,
328 this->_query.searchCondition.tableJoins,
329 this->_query.searchCondition.condition,
330 this->_query.orderBy,
331 this->_query.groupBy));
333 auto const outputColumnsBound = detail::CanSafelyBindOutputColumns<Record>(_stmt.Connection().ServerType());
337 auto& record = records.emplace_back();
338 if (outputColumnsBound)
339#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
349 if (!outputColumnsBound)
351 using ElementMask = std::integer_sequence<size_t, MemberIndexOf<ReferencedFields>...>;
352 detail::GetAllColumns<ElementMask>(reader, record);
360 [[nodiscard]] std::optional<Record>
First()
362 std::optional<Record> record {};
363 _stmt.ExecuteDirect(_formatter.SelectFirst(this->_query.distinct,
365 RecordTableName<Record>,
366 this->_query.searchCondition.tableAlias,
367 this->_query.searchCondition.tableJoins,
368 this->_query.searchCondition.condition,
369 this->_query.orderBy,
371 Derived::ReadResult(_stmt.Connection().ServerType(), _stmt.GetResultCursor(), &record);
380 template <auto Field>
381 [[nodiscard]]
auto First() -> std::optional<ReferencedFieldTypeOf<Field>>
383 auto constexpr count = 1;
384 _stmt.ExecuteDirect(_formatter.SelectFirst(this->_query.distinct,
385 FullyQualifiedNamesOf<Field>.string_view(),
386 RecordTableName<Record>,
387 this->_query.searchCondition.tableAlias,
388 this->_query.searchCondition.tableJoins,
389 this->_query.searchCondition.condition,
390 this->_query.orderBy,
392 if (
SqlResultCursor reader = _stmt.GetResultCursor(); reader.FetchRow())
402 template <
auto... ReferencedFields>
403 requires(
sizeof...(ReferencedFields) >= 2)
404 [[nodiscard]]
auto First() -> std::optional<Record>
406 auto optionalRecord = std::optional<Record> {};
408 _stmt.ExecuteDirect(_formatter.SelectFirst(this->_query.distinct,
409 FullyQualifiedNamesOf<ReferencedFields...>.string_view(),
410 RecordTableName<Record>,
411 this->_query.searchCondition.tableAlias,
412 this->_query.searchCondition.tableJoins,
413 this->_query.searchCondition.condition,
414 this->_query.orderBy,
417 auto& record = optionalRecord.emplace();
419 auto const outputColumnsBound = detail::CanSafelyBindOutputColumns<Record>(_stmt.Connection().ServerType());
420 if (outputColumnsBound)
421#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
428 if (!outputColumnsBound)
430 using ElementMask = std::integer_sequence<size_t, MemberIndexOf<ReferencedFields>...>;
431 detail::GetAllColumns<ElementMask>(reader, record);
434 return optionalRecord;
438 [[nodiscard]] std::vector<Record>
First(
size_t n)
440 auto records = std::vector<Record> {};
442 _stmt.ExecuteDirect(_formatter.SelectFirst(this->_query.distinct,
444 RecordTableName<Record>,
445 this->_query.searchCondition.tableAlias,
446 this->_query.searchCondition.tableJoins,
447 this->_query.searchCondition.condition,
448 this->_query.orderBy,
450 Derived::ReadResults(_stmt.Connection().ServerType(), _stmt.GetResultCursor(), &records);
454 template <
auto... ReferencedFields>
455 [[nodiscard]] std::vector<Record> First(
size_t n)
457 auto records = std::vector<Record> {};
459 _stmt.ExecuteDirect(_formatter.SelectFirst(this->_query.distinct,
460 FullyQualifiedNamesOf<ReferencedFields...>.string_view(),
461 RecordTableName<Record>,
462 this->_query.searchCondition.tableAlias,
463 this->_query.searchCondition.tableJoins,
464 this->_query.searchCondition.condition,
465 this->_query.orderBy,
468 auto const outputColumnsBound = detail::CanSafelyBindOutputColumns<Record>(_stmt.Connection().ServerType());
469 SqlResultCursor reader = _stmt.GetResultCursor();
472 auto& record = records.emplace_back();
473 if (outputColumnsBound)
474#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
475 reader.BindOutputColumns(&(record.[:ReferencedFields:])...);
477 reader.BindOutputColumns(&(record.*ReferencedFields)...);
479 if (!reader.FetchRow())
484 if (!outputColumnsBound)
486 using ElementMask = std::integer_sequence<size_t, MemberIndexOf<ReferencedFields>...>;
487 detail::GetAllColumns<ElementMask>(reader, record);
495 [[nodiscard]] std::vector<Record>
Range(
size_t offset,
size_t limit)
497 auto records = std::vector<Record> {};
498 records.reserve(limit);
499 _stmt.ExecuteDirect(_formatter.SelectRange(
500 this->_query.distinct,
502 RecordTableName<Record>,
503 this->_query.searchCondition.tableAlias,
504 this->_query.searchCondition.tableJoins,
505 this->_query.searchCondition.condition,
506 !this->_query.orderBy.empty()
507 ? this->_query.orderBy
508 : std::format(
" ORDER BY \"{}\" ASC",
FieldNameAt<RecordPrimaryKeyIndex<Record>, Record>),
509 this->_query.groupBy,
512 Derived::ReadResults(_stmt.Connection().ServerType(), _stmt.GetResultCursor(), &records);
516 template <
auto... ReferencedFields>
517 [[nodiscard]] std::vector<Record> Range(
size_t offset,
size_t limit)
519 auto records = std::vector<Record> {};
520 records.reserve(limit);
521 _stmt.ExecuteDirect(_formatter.SelectRange(
522 this->_query.distinct,
523 FullyQualifiedNamesOf<ReferencedFields...>.string_view(),
524 RecordTableName<Record>,
525 this->_query.searchCondition.tableAlias,
526 this->_query.searchCondition.tableJoins,
527 this->_query.searchCondition.condition,
528 !this->_query.orderBy.empty()
529 ? this->_query.orderBy
530 : std::format(
" ORDER BY \"{}\" ASC", FieldNameAt<RecordPrimaryKeyIndex<Record>, Record>),
531 this->_query.groupBy,
535 auto const outputColumnsBound = detail::CanSafelyBindOutputColumns<Record>(_stmt.Connection().ServerType());
536 SqlResultCursor reader = _stmt.GetResultCursor();
539 auto& record = records.emplace_back();
540 if (outputColumnsBound)
541#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
542 reader.BindOutputColumns(&(record.[:ReferencedFields:])...);
544 reader.BindOutputColumns(&(record.*ReferencedFields)...);
546 if (!reader.FetchRow())
551 if (!outputColumnsBound)
553 using ElementMask = std::integer_sequence<size_t, MemberIndexOf<ReferencedFields>...>;
554 detail::GetAllColumns<ElementMask>(reader, record);
565template <
typename Record>
578 static void ReadResults(SqlServerType sqlServerType,
SqlResultCursor reader, std::vector<Record>* records)
582 Record& record = records->emplace_back();
583 if (!detail::ReadSingleResult(sqlServerType, reader, record))
591 static void ReadResult(SqlServerType sqlServerType,
SqlResultCursor reader, std::optional<Record>* optionalRecord)
593 Record& record = optionalRecord->emplace();
594 if (!detail::ReadSingleResult(sqlServerType, reader, record))
595 optionalRecord->reset();
603template <
typename FirstRecord,
typename SecondRecord>
606 SqlAllFieldsQueryBuilder<std::tuple<FirstRecord, SecondRecord>>>
609 using RecordType = std::tuple<FirstRecord, SecondRecord>;
618 static void ReadResults(SqlServerType sqlServerType, SqlResultCursor reader, std::vector<RecordType>* records)
622 auto& record = records->emplace_back();
623 auto& [firstRecord, secondRecord] = record;
625 using FirstRecordType = std::remove_cvref_t<
decltype(firstRecord)>;
626 using SecondRecordType = std::remove_cvref_t<
decltype(secondRecord)>;
628 auto const outputColumnsBoundFirst = detail::CanSafelyBindOutputColumns<FirstRecordType>(sqlServerType);
629 auto const outputColumnsBoundSecond = detail::CanSafelyBindOutputColumns<SecondRecordType>(sqlServerType);
630 auto const canSafelyBindAll = outputColumnsBoundFirst && outputColumnsBoundSecond;
632 if (canSafelyBindAll)
634 detail::BindAllOutputColumnsWithOffset(reader, firstRecord, 1);
635 detail::BindAllOutputColumnsWithOffset(reader, secondRecord, 1 + Reflection::CountMembers<FirstRecord>);
638 if (!reader.FetchRow())
644 if (!canSafelyBindAll)
645 detail::GetAllColumns(reader, record);
Main API for mapping records to and from the database using high level C++ syntax.
Represents a query builder that retrieves all fields of a record.
SqlServerType ServerType() const noexcept
Retrieves the type of the server.
SqlQueryFormatter const & QueryFormatter() const noexcept
Retrieves a query formatter suitable for the SQL server being connected.
std::vector< Record > First(size_t n)
Executes a SELECT query for the first n records found and returns them.
std::optional< Record > First()
Executes a SELECT query for the first record found and returns it.
auto First() -> std::optional< Record >
Executes a SELECT query for the first record found and returns it with only the specified fields popu...
std::vector< Record > Range(size_t offset, size_t limit)
Executes a SELECT query for a range of records and returns them.
size_t Count()
Executes a SELECT COUNT query and returns the number of records found.
auto First() -> std::optional< ReferencedFieldTypeOf< Field > >
Executes the query to get a single scalar value from the first record found.
auto All() -> std::vector< ReferencedFieldTypeOf< Field > >
Executes a SELECT query and returns all records found for the specified field.
auto All() -> std::vector< Record >
Executes a SELECT query and returns all records found for the specified field, only having the specif...
std::vector< Record > All()
Executes a SELECT query and returns all records found.
API for reading an SQL query result set.
LIGHTWEIGHT_FORCE_INLINE bool GetColumn(SQLUSMALLINT column, T *result) const
LIGHTWEIGHT_FORCE_INLINE void BindOutputColumns(Args *... args)
LIGHTWEIGHT_FORCE_INLINE bool FetchRow()
Fetches the next row of the result set.
High level API for (prepared) raw SQL statements.
LIGHTWEIGHT_API SqlConnection & Connection() noexcept
Retrieves the connection associated with this statement.
SqlResultCursor GetResultCursor() noexcept
Retrieves the result cursor for reading an SQL query result.
LIGHTWEIGHT_API void ExecuteDirect(std::string_view const &query, std::source_location location=std::source_location::current())
Executes the given query directly.
typename std::remove_cvref_t< decltype(std::declval< MemberClassType< decltype(Field)> >().*Field)>::ValueType ReferencedFieldTypeOf
Retrieves the type of a member field in a record.
constexpr std::string_view FieldNameAt
Returns the SQL field name of the given field index in the record.
static constexpr auto IsOptional
Indicates if the field is optional, i.e., it can be NULL.