132 return std::make_shared<DataMapper>(std::move(connectionString), PrivateTag {});
154 template <
typename Record>
155 static std::string
Inspect(Record
const& record);
158 template <
typename Record>
162 template <
typename FirstRecord,
typename... MoreRecords>
166 template <
typename Record>
170 template <
typename FirstRecord,
typename... MoreRecords>
178 template <
typename Record>
179 RecordPrimaryKeyType<Record>
Create(Record& record);
186 template <
typename Record>
187 RecordPrimaryKeyType<Record>
CreateExplicit(Record
const& record);
193 template <
typename Record,
typename... PrimaryKeyTypes>
194 std::optional<Record>
QuerySingle(PrimaryKeyTypes&&... primaryKeys);
203 template <
typename Record,
typename... PrimaryKeyTypes>
207 template <
typename Record,
typename... InputParameters>
208 std::vector<Record>
Query(SqlSelectQueryBuilder::ComposedQuery
const& selectQuery, InputParameters&&... inputParameters);
239 template <
typename Record,
typename... InputParameters>
240 std::vector<Record>
Query(std::string_view sqlQueryString, InputParameters&&... inputParameters);
266 template <
typename ElementMask,
typename Record,
typename... InputParameters>
267 std::vector<Record>
Query(SqlSelectQueryBuilder::ComposedQuery
const& selectQuery, InputParameters&&... inputParameters);
293 template <
typename First,
typename Second,
typename... Rest,
DataMapperOptions QueryOptions = {}>
295 std::vector<std::tuple<First, Second, Rest...>>
Query(SqlSelectQueryBuilder::ComposedQuery
const& selectQuery);
298 template <
typename FirstRecord,
typename NextRecord,
DataMapperOptions QueryOptions = {}>
299 requires DataMapperRecord<FirstRecord> && DataMapperRecord<NextRecord>
300 SqlAllFieldsQueryBuilder<std::tuple<FirstRecord, NextRecord>, QueryOptions>
Query()
304 auto const emplaceRecordsFrom = [&fields]<
typename Record>() {
305 Reflection::EnumerateMembers<Record>([&fields]<
size_t I,
typename Field>() {
308 fields += std::format(R
"("{}"."{}")", RecordTableName<Record>, FieldNameAt<I, Record>);
312 emplaceRecordsFrom.template operator()<FirstRecord>();
313 emplaceRecordsFrom.template operator()<NextRecord>();
315 return SqlAllFieldsQueryBuilder<std::tuple<FirstRecord, NextRecord>, QueryOptions>(*
this, std::move(fields));
330 template <
typename Record, DataMapperOptions QueryOptions = {}>
334 Reflection::EnumerateMembers<Record>([&fields]<
size_t I,
typename Field>() {
338 fields += RecordTableName<Record>;
340 fields += FieldNameAt<I, Record>;
347 template <
typename Record>
348 void Update(Record& record);
351 template <
typename Record>
352 std::size_t
Delete(Record
const& record);
357 return _connection.
Query(tableName);
361 template <
typename Record>
362 bool IsModified(Record
const& record)
const noexcept;
365 template <
typename Record>
369 template <
typename Record>
376 template <
typename Record>
386 template <
typename Record,
typename... Args>
389 template <
typename Record,
typename ValueType>
390 void SetId(Record& record, ValueType&&
id);
392 template <
typename Record,
size_t InitialOffset = 1>
393 Record& BindOutputColumns(Record& record);
395 template <
typename Record,
size_t InitialOffset = 1>
396 Record& BindOutputColumns(Record& record,
SqlStatement* stmt);
398 template <
typename ElementMask,
typename Record,
size_t InitialOffset = 1>
399 Record& BindOutputColumns(Record& record);
401 template <
typename ElementMask,
typename Record,
size_t InitialOffset = 1>
402 Record& BindOutputColumns(Record& record,
SqlStatement* stmt);
404 template <
typename FieldType>
405 std::optional<typename FieldType::ReferencedRecord> LoadBelongsTo(
typename FieldType::ValueType value);
407 template <
size_t FieldIndex,
typename Record,
typename OtherRecord>
410 template <
typename ReferencedRecord,
typename ThroughRecord,
typename Record>
413 template <
typename ReferencedRecord,
typename ThroughRecord,
typename Record>
416 template <
size_t FieldIndex,
typename Record,
typename OtherRecord,
typename Callable>
417 void CallOnHasMany(Record& record, Callable
const& callback);
419 template <
typename ReferencedRecord,
typename ThroughRecord,
typename Record,
typename Callable>
420 void CallOnHasManyThrough(Record& record, Callable
const& callback);
430 template <
typename FieldType>
431 constexpr bool CanSafelyBindOutputColumn(SqlServerType sqlServerType)
noexcept
433 if (sqlServerType != SqlServerType::MICROSOFT_SQL)
440 if constexpr (IsField<FieldType>)
442 if constexpr (detail::OneOf<
typename FieldType::ValueType,
448 || IsSqlDynamicString<typename FieldType::ValueType>
449 || IsSqlDynamicBinary<typename FieldType::ValueType>)
458 template <DataMapperRecord Record>
459 constexpr bool CanSafelyBindOutputColumns(SqlServerType sqlServerType)
noexcept
461 if (sqlServerType != SqlServerType::MICROSOFT_SQL)
465 Reflection::EnumerateMembers<Record>([&result]<
size_t I,
typename Field>() {
466 if constexpr (IsField<Field>)
468 if constexpr (detail::OneOf<
typename Field::ValueType,
474 || IsSqlDynamicString<typename Field::ValueType>
475 || IsSqlDynamicBinary<typename Field::ValueType>)
485 template <
typename Record>
486 void BindAllOutputColumnsWithOffset(SqlResultCursor& reader, Record& record, SQLSMALLINT startOffset)
488 Reflection::EnumerateMembers(record,
489 [reader = &reader, i = startOffset]<
size_t I,
typename Field>(Field& field)
mutable {
490 if constexpr (IsField<Field>)
492 reader->BindOutputColumn(i++, &field.MutableValue());
494 else if constexpr (IsBelongsTo<Field>)
496 reader->BindOutputColumn(i++, &field.MutableValue());
498 else if constexpr (SqlOutputColumnBinder<Field>)
500 reader->BindOutputColumn(i++, &field);
505 template <
typename Record>
506 void BindAllOutputColumns(SqlResultCursor& reader, Record& record)
508 BindAllOutputColumnsWithOffset(reader, record, 1);
514 template <
typename ElementMask,
typename Record>
515 void GetAllColumns(SqlResultCursor& reader, Record& record, SQLUSMALLINT indexFromQuery = 0)
517 Reflection::EnumerateMembers<ElementMask>(
518 record, [reader = &reader, &indexFromQuery]<
size_t I,
typename Field>(Field& field)
mutable {
520 if constexpr (IsField<Field>)
523 field.MutableValue() =
524 reader->GetNullableColumn<
typename Field::ValueType::value_type>(indexFromQuery);
526 field.MutableValue() = reader->GetColumn<
typename Field::ValueType>(indexFromQuery);
528 else if constexpr (SqlGetColumnNativeType<Field>)
530 if constexpr (IsOptionalBelongsTo<Field>)
531 field = reader->GetNullableColumn<
typename Field::BaseType>(indexFromQuery);
533 field = reader->GetColumn<Field>(indexFromQuery);
538 template <
typename Record>
539 void GetAllColumns(SqlResultCursor& reader, Record& record, SQLUSMALLINT indexFromQuery = 0)
541 return GetAllColumns<std::make_integer_sequence<size_t, Reflection::CountMembers<Record>>, Record>(
542 reader, record, indexFromQuery);
545 template <
typename FirstRecord,
typename SecondRecord>
547 void GetAllColumns(SqlResultCursor& reader, std::tuple<FirstRecord, SecondRecord>& record)
549 auto& [firstRecord, secondRecord] = record;
551 Reflection::EnumerateMembers(firstRecord, [reader = &reader]<
size_t I,
typename Field>(Field& field)
mutable {
552 if constexpr (IsField<Field>)
555 field.MutableValue() = reader->GetNullableColumn<
typename Field::ValueType::value_type>(I + 1);
557 field.MutableValue() = reader->GetColumn<
typename Field::ValueType>(I + 1);
559 else if constexpr (SqlGetColumnNativeType<Field>)
562 field = reader->GetNullableColumn<
typename Field::BaseType>(I + 1);
564 field = reader->GetColumn<Field>(I + 1);
568 Reflection::EnumerateMembers(secondRecord, [reader = &reader]<
size_t I,
typename Field>(Field& field)
mutable {
569 if constexpr (IsField<Field>)
572 field.MutableValue() = reader->GetNullableColumn<
typename Field::ValueType::value_type>(
573 Reflection::CountMembers<FirstRecord> + I + 1);
575 field.MutableValue() =
576 reader->GetColumn<
typename Field::ValueType>(Reflection::CountMembers<FirstRecord> + I + 1);
578 else if constexpr (SqlGetColumnNativeType<Field>)
582 reader->GetNullableColumn<
typename Field::BaseType>(Reflection::CountMembers<FirstRecord> + I + 1);
584 field = reader->GetColumn<Field>(Reflection::CountMembers<FirstRecord> + I + 1);
589 template <
typename Record>
590 bool ReadSingleResult(SqlServerType sqlServerType, SqlResultCursor& reader, Record& record)
592 auto const outputColumnsBound = CanSafelyBindOutputColumns<Record>(sqlServerType);
594 if (outputColumnsBound)
595 BindAllOutputColumns(reader, record);
597 if (!reader.FetchRow())
600 if (!outputColumnsBound)
601 GetAllColumns(reader, record);
607template <
typename Record,
typename Derived, DataMapperOptions QueryOptions>
608inline SqlCoreDataMapperQueryBuilder<Record, Derived, QueryOptions>::SqlCoreDataMapperQueryBuilder(
609 DataMapper& dm, std::string fields)
noexcept:
611 _formatter { dm.Connection().QueryFormatter() },
612 _fields { std::move(fields) }
616template <
typename Record,
typename Derived, DataMapperOptions QueryOptions>
620 stmt.ExecuteDirect(_formatter.SelectCount(this->_query.distinct,
621 RecordTableName<Record>,
622 this->_query.searchCondition.tableAlias,
623 this->_query.searchCondition.tableJoins,
624 this->_query.searchCondition.condition));
625 auto reader = stmt.GetResultCursor();
626 if (reader.FetchRow())
627 return reader.GetColumn<
size_t>(1);
631template <
typename Record,
typename Derived, DataMapperOptions QueryOptions>
636 auto const query = _formatter.SelectFirst(this->_query.distinct,
638 RecordTableName<Record>,
639 this->_query.searchCondition.tableAlias,
640 this->_query.searchCondition.tableJoins,
641 this->_query.searchCondition.condition,
642 this->_query.orderBy,
645 stmt.ExecuteDirect(query);
646 if (
SqlResultCursor reader = stmt.GetResultCursor(); reader.FetchRow())
651template <
typename Record,
typename Derived, DataMapperOptions QueryOptions>
655 auto records = std::vector<Record> {};
657 stmt.ExecuteDirect(_formatter.SelectAll(this->_query.distinct,
659 RecordTableName<Record>,
660 this->_query.searchCondition.tableAlias,
661 this->_query.searchCondition.tableJoins,
662 this->_query.searchCondition.condition,
663 this->_query.orderBy,
664 this->_query.groupBy));
665 Derived::ReadResults(stmt.Connection().ServerType(), stmt.GetResultCursor(), &records);
671 if constexpr (QueryOptions.loadRelations)
673 for (
auto& record: records)
675 _dm.ConfigureRelationAutoLoading(record);
682template <
typename Record,
typename Derived, DataMapperOptions QueryOptions>
684#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
685 requires(is_aggregate_type(parent_of(
Field)))
687 requires std::is_member_object_pointer_v<
decltype(
Field)>
692 auto result = std::vector<value_type> {};
695 stmt.ExecuteDirect(_formatter.SelectAll(this->_query.distinct,
696 FullyQualifiedNamesOf<Field>.string_view(),
697 RecordTableName<Record>,
698 this->_query.searchCondition.tableAlias,
699 this->_query.searchCondition.tableJoins,
700 this->_query.searchCondition.condition,
701 this->_query.orderBy,
702 this->_query.groupBy));
704 auto const outputColumnsBound = detail::CanSafelyBindOutputColumn<value_type>(stmt.Connection().ServerType());
707 auto& value = result.emplace_back();
708 if (outputColumnsBound)
709 reader.BindOutputColumn(1, &value);
717 if (!outputColumnsBound)
724template <
typename Record,
typename Derived, DataMapperOptions QueryOptions>
725template <
auto... ReferencedFields>
726 requires(
sizeof...(ReferencedFields) >= 2)
729 auto records = std::vector<Record> {};
730 auto stmt = SqlStatement { _dm.Connection() };
732 stmt.ExecuteDirect(_formatter.SelectAll(this->_query.distinct,
733 FullyQualifiedNamesOf<ReferencedFields...>.string_view(),
734 RecordTableName<Record>,
735 this->_query.searchCondition.tableAlias,
736 this->_query.searchCondition.tableJoins,
737 this->_query.searchCondition.condition,
738 this->_query.orderBy,
739 this->_query.groupBy));
741 auto const outputColumnsBound = detail::CanSafelyBindOutputColumns<Record>(stmt.Connection().ServerType());
742 SqlResultCursor reader = stmt.GetResultCursor();
745 auto& record = records.emplace_back();
746 if (outputColumnsBound)
747#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
748 reader.BindOutputColumns(&(record.[:ReferencedFields:])...);
750 reader.BindOutputColumns(&(record.*ReferencedFields)...);
752 if (!reader.FetchRow())
757 if (!outputColumnsBound)
759 using ElementMask = std::integer_sequence<size_t, MemberIndexOf<ReferencedFields>...>;
760 detail::GetAllColumns<ElementMask>(reader, record);
767template <
typename Record,
typename Derived, DataMapperOptions QueryOptions>
770 std::optional<Record> record {};
772 stmt.ExecuteDirect(_formatter.SelectFirst(this->_query.distinct,
774 RecordTableName<Record>,
775 this->_query.searchCondition.tableAlias,
776 this->_query.searchCondition.tableJoins,
777 this->_query.searchCondition.condition,
778 this->_query.orderBy,
780 Derived::ReadResult(stmt.Connection().ServerType(), stmt.GetResultCursor(), &record);
781 if constexpr (QueryOptions.loadRelations)
784 _dm.ConfigureRelationAutoLoading(record.value());
789template <
typename Record,
typename Derived, DataMapperOptions QueryOptions>
791#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
792 requires(is_aggregate_type(parent_of(
Field)))
794 requires std::is_member_object_pointer_v<
decltype(
Field)>
798 auto constexpr count = 1;
800 stmt.ExecuteDirect(_formatter.SelectFirst(this->_query.distinct,
801 FullyQualifiedNamesOf<Field>.string_view(),
802 RecordTableName<Record>,
803 this->_query.searchCondition.tableAlias,
804 this->_query.searchCondition.tableJoins,
805 this->_query.searchCondition.condition,
806 this->_query.orderBy,
808 if (
SqlResultCursor reader = stmt.GetResultCursor(); reader.FetchRow())
813template <
typename Record,
typename Derived, DataMapperOptions QueryOptions>
814template <
auto... ReferencedFields>
815 requires(
sizeof...(ReferencedFields) >= 2)
818 auto optionalRecord = std::optional<Record> {};
820 auto stmt = SqlStatement { _dm.Connection() };
821 stmt.ExecuteDirect(_formatter.SelectFirst(this->_query.distinct,
822 FullyQualifiedNamesOf<ReferencedFields...>.string_view(),
823 RecordTableName<Record>,
824 this->_query.searchCondition.tableAlias,
825 this->_query.searchCondition.tableJoins,
826 this->_query.searchCondition.condition,
827 this->_query.orderBy,
830 auto& record = optionalRecord.emplace();
831 SqlResultCursor reader = stmt.GetResultCursor();
832 auto const outputColumnsBound = detail::CanSafelyBindOutputColumns<Record>(stmt.Connection().ServerType());
833 if (outputColumnsBound)
834#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
835 reader.BindOutputColumns(&(record.[:ReferencedFields:])...);
837 reader.BindOutputColumns(&(record.*ReferencedFields)...);
839 if (!reader.FetchRow())
841 if (!outputColumnsBound)
843 using ElementMask = std::integer_sequence<size_t, MemberIndexOf<ReferencedFields>...>;
844 detail::GetAllColumns<ElementMask>(reader, record);
847 if constexpr (QueryOptions.loadRelations)
848 _dm.ConfigureRelationAutoLoading(record);
850 return optionalRecord;
853template <
typename Record,
typename Derived, DataMapperOptions QueryOptions>
856 auto records = std::vector<Record> {};
859 stmt.ExecuteDirect(_formatter.SelectFirst(this->_query.distinct,
861 RecordTableName<Record>,
862 this->_query.searchCondition.tableAlias,
863 this->_query.searchCondition.tableJoins,
864 this->_query.searchCondition.condition,
865 this->_query.orderBy,
867 Derived::ReadResults(stmt.Connection().ServerType(), stmt.GetResultCursor(), &records);
869 if constexpr (QueryOptions.loadRelations)
871 for (
auto& record: records)
872 _dm.ConfigureRelationAutoLoading(record);
877template <
typename Record,
typename Derived, DataMapperOptions QueryOptions>
880 auto records = std::vector<Record> {};
882 records.reserve(limit);
884 _formatter.SelectRange(this->_query.distinct,
886 RecordTableName<Record>,
887 this->_query.searchCondition.tableAlias,
888 this->_query.searchCondition.tableJoins,
889 this->_query.searchCondition.condition,
890 !this->_query.orderBy.empty()
891 ? this->_query.orderBy
892 : std::format(
" ORDER BY \"{}\" ASC",
FieldNameAt<RecordPrimaryKeyIndex<Record>, Record>),
893 this->_query.groupBy,
896 Derived::ReadResults(stmt.Connection().ServerType(), stmt.GetResultCursor(), &records);
897 if constexpr (QueryOptions.loadRelations)
899 for (
auto& record: records)
900 _dm.ConfigureRelationAutoLoading(record);
905template <
typename Record,
typename Derived, DataMapperOptions QueryOptions>
906template <
auto... ReferencedFields>
909 auto records = std::vector<Record> {};
910 auto stmt = SqlStatement { _dm.Connection() };
911 records.reserve(limit);
913 _formatter.SelectRange(this->_query.distinct,
914 FullyQualifiedNamesOf<ReferencedFields...>.string_view(),
915 RecordTableName<Record>,
916 this->_query.searchCondition.tableAlias,
917 this->_query.searchCondition.tableJoins,
918 this->_query.searchCondition.condition,
919 !this->_query.orderBy.empty()
920 ? this->_query.orderBy
921 : std::format(
" ORDER BY \"{}\" ASC",
FieldNameAt<RecordPrimaryKeyIndex<Record>, Record>),
922 this->_query.groupBy,
926 auto const outputColumnsBound = detail::CanSafelyBindOutputColumns<Record>(stmt.Connection().ServerType());
927 SqlResultCursor reader = stmt.GetResultCursor();
930 auto& record = records.emplace_back();
931 if (outputColumnsBound)
932#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
933 reader.BindOutputColumns(&(record.[:ReferencedFields:])...);
935 reader.BindOutputColumns(&(record.*ReferencedFields)...);
937 if (!reader.FetchRow())
942 if (!outputColumnsBound)
944 using ElementMask = std::integer_sequence<size_t, MemberIndexOf<ReferencedFields>...>;
945 detail::GetAllColumns<ElementMask>(reader, record);
949 if constexpr (QueryOptions.loadRelations)
951 for (
auto& record: records)
952 _dm.ConfigureRelationAutoLoading(record);
958template <
typename Record,
typename Derived, DataMapperOptions QueryOptions>
959template <
auto... ReferencedFields>
962 auto records = std::vector<Record> {};
963 auto stmt = SqlStatement { _dm.Connection() };
965 stmt.ExecuteDirect(_formatter.SelectFirst(this->_query.distinct,
966 FullyQualifiedNamesOf<ReferencedFields...>.string_view(),
967 RecordTableName<Record>,
968 this->_query.searchCondition.tableAlias,
969 this->_query.searchCondition.tableJoins,
970 this->_query.searchCondition.condition,
971 this->_query.orderBy,
974 auto const outputColumnsBound = detail::CanSafelyBindOutputColumns<Record>(stmt.Connection().ServerType());
975 SqlResultCursor reader = stmt.GetResultCursor();
978 auto& record = records.emplace_back();
979 if (outputColumnsBound)
980#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
981 reader.BindOutputColumns(&(record.[:ReferencedFields:])...);
983 reader.BindOutputColumns(&(record.*ReferencedFields)...);
985 if (!reader.FetchRow())
990 if (!outputColumnsBound)
992 using ElementMask = std::integer_sequence<size_t, MemberIndexOf<ReferencedFields>...>;
993 detail::GetAllColumns<ElementMask>(reader, record);
997 if constexpr (QueryOptions.loadRelations)
999 for (
auto& record: records)
1000 _dm.ConfigureRelationAutoLoading(record);
1006template <
typename Record, DataMapperOptions QueryOptions>
1007void SqlAllFieldsQueryBuilder<Record, QueryOptions>::ReadResults(SqlServerType sqlServerType,
1008 SqlResultCursor reader,
1009 std::vector<Record>* records)
1013 Record& record = records->emplace_back();
1014 if (!detail::ReadSingleResult(sqlServerType, reader, record))
1016 records->pop_back();
1022template <
typename Record, DataMapperOptions QueryOptions>
1023void SqlAllFieldsQueryBuilder<Record, QueryOptions>::ReadResult(SqlServerType sqlServerType,
1024 SqlResultCursor reader,
1025 std::optional<Record>* optionalRecord)
1027 Record& record = optionalRecord->emplace();
1028 if (!detail::ReadSingleResult(sqlServerType, reader, record))
1029 optionalRecord->reset();
1032template <
typename FirstRecord,
typename SecondRecord, DataMapperOptions QueryOptions>
1033void SqlAllFieldsQueryBuilder<std::tuple<FirstRecord, SecondRecord>, QueryOptions>::ReadResults(
1034 SqlServerType sqlServerType, SqlResultCursor reader, std::vector<RecordType>* records)
1038 auto& record = records->emplace_back();
1039 auto& [firstRecord, secondRecord] = record;
1041 using FirstRecordType = std::remove_cvref_t<
decltype(firstRecord)>;
1042 using SecondRecordType = std::remove_cvref_t<
decltype(secondRecord)>;
1044 auto const outputColumnsBoundFirst = detail::CanSafelyBindOutputColumns<FirstRecordType>(sqlServerType);
1045 auto const outputColumnsBoundSecond = detail::CanSafelyBindOutputColumns<SecondRecordType>(sqlServerType);
1046 auto const canSafelyBindAll = outputColumnsBoundFirst && outputColumnsBoundSecond;
1048 if (canSafelyBindAll)
1050 detail::BindAllOutputColumnsWithOffset(reader, firstRecord, 1);
1051 detail::BindAllOutputColumnsWithOffset(reader, secondRecord, 1 + Reflection::CountMembers<FirstRecord>);
1054 if (!reader.FetchRow())
1056 records->pop_back();
1060 if (!canSafelyBindAll)
1061 detail::GetAllColumns(reader, record);
1065template <
typename Record>
1071 Reflection::CallOnMembers(record, [&str]<
typename Name,
typename Value>(Name
const& name, Value
const& value) {
1077 if constexpr (Value::IsOptional)
1079 if (!value.Value().has_value())
1081 str += std::format(
"{} {} := <nullopt>", Reflection::TypeNameOf<Value>, name);
1085 str += std::format(
"{} {} := {}", Reflection::TypeNameOf<Value>, name, value.Value().value());
1088 else if constexpr (IsBelongsTo<Value>)
1090 str += std::format(
"{} {} := {}", Reflection::TypeNameOf<Value>, name, value.Value());
1092 else if constexpr (std::same_as<typename Value::ValueType, char>)
1097 str += std::format(
"{} {} := {}", Reflection::TypeNameOf<Value>, name, value.InspectValue());
1100 else if constexpr (!IsHasMany<Value> && !IsHasManyThrough<Value> && !IsHasOneThrough<Value> && !IsBelongsTo<Value>)
1101 str += std::format(
"{} {} := {}", Reflection::TypeNameOf<Value>, name, value);
1103 return "{\n" + std::move(str) +
"\n}";
1106template <
typename Record>
1112 auto createTable = migration.
CreateTable(RecordTableName<Record>);
1113#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
1114 constexpr auto ctx = std::meta::access_context::current();
1115 template for (
constexpr auto el: define_static_array(nonstatic_data_members_of(^^Record, ctx)))
1117 using FieldType =
typename[:std::meta::type_of(el):];
1120 if constexpr (IsAutoIncrementPrimaryKey<FieldType>)
1121 createTable.PrimaryKeyWithAutoIncrement(std::string(FieldNameOf<el>),
1122 SqlColumnTypeDefinitionOf<typename FieldType::ValueType>);
1123 else if constexpr (FieldType::IsPrimaryKey)
1124 createTable.PrimaryKey(std::string(FieldNameOf<el>),
1125 SqlColumnTypeDefinitionOf<typename FieldType::ValueType>);
1126 else if constexpr (IsBelongsTo<FieldType>)
1128 constexpr size_t referencedFieldIndex = []()
constexpr ->
size_t {
1129 auto index = size_t(-1);
1130 Reflection::EnumerateMembers<typename FieldType::ReferencedRecord>(
1131 [&index]<
size_t J,
typename ReferencedFieldType>()
constexpr ->
void {
1132 if constexpr (IsField<ReferencedFieldType>)
1133 if constexpr (ReferencedFieldType::IsPrimaryKey)
1138 createTable.ForeignKey(
1139 std::string(FieldNameOf<el>),
1140 SqlColumnTypeDefinitionOf<typename FieldType::ValueType>,
1142 .
tableName = std::string { RecordTableName<typename FieldType::ReferencedRecord> },
1143 .columnName = std::string { FieldNameOf<FieldType::ReferencedField> } });
1145 else if constexpr (FieldType::IsMandatory)
1146 createTable.RequiredColumn(std::string(FieldNameOf<el>),
1147 SqlColumnTypeDefinitionOf<typename FieldType::ValueType>);
1149 createTable.Column(std::string(FieldNameOf<el>), SqlColumnTypeDefinitionOf<typename FieldType::ValueType>);
1154 Reflection::EnumerateMembers<Record>([&]<
size_t I,
typename FieldType>() {
1157 if constexpr (IsAutoIncrementPrimaryKey<FieldType>)
1158 createTable.PrimaryKeyWithAutoIncrement(std::string(FieldNameAt<I, Record>),
1159 SqlColumnTypeDefinitionOf<typename FieldType::ValueType>);
1160 else if constexpr (FieldType::IsPrimaryKey)
1161 createTable.PrimaryKey(std::string(FieldNameAt<I, Record>),
1162 SqlColumnTypeDefinitionOf<typename FieldType::ValueType>);
1163 else if constexpr (IsBelongsTo<FieldType>)
1165 constexpr size_t referencedFieldIndex = []()
constexpr ->
size_t {
1166 auto index = size_t(-1);
1167 Reflection::EnumerateMembers<typename FieldType::ReferencedRecord>(
1168 [&index]<
size_t J,
typename ReferencedFieldType>()
constexpr ->
void {
1169 if constexpr (IsField<ReferencedFieldType>)
1170 if constexpr (ReferencedFieldType::IsPrimaryKey)
1175 createTable.ForeignKey(
1176 std::string(FieldNameAt<I, Record>),
1177 SqlColumnTypeDefinitionOf<typename FieldType::ValueType>,
1179 .
tableName = std::string { RecordTableName<typename FieldType::ReferencedRecord> },
1181 std::string { FieldNameAt<referencedFieldIndex, typename FieldType::ReferencedRecord> } });
1183 else if constexpr (FieldType::IsMandatory)
1184 createTable.RequiredColumn(std::string(FieldNameAt<I, Record>),
1185 SqlColumnTypeDefinitionOf<typename FieldType::ValueType>);
1187 createTable.Column(std::string(FieldNameAt<I, Record>),
1188 SqlColumnTypeDefinitionOf<typename FieldType::ValueType>);
1192 return migration.GetPlan().ToSql();
1195template <
typename FirstRecord,
typename... MoreRecords>
1198 std::vector<std::string> output;
1199 auto const append = [&output](
auto const& sql) {
1200 output.insert(output.end(), sql.begin(), sql.end());
1202 append(CreateTableString<FirstRecord>(serverType));
1203 (append(CreateTableString<MoreRecords>(serverType)), ...);
1207template <
typename Record>
1212 auto const sqlQueryStrings = CreateTableString<Record>(_connection.
ServerType());
1213 for (
auto const& sqlQueryString: sqlQueryStrings)
1217template <
typename FirstRecord,
typename... MoreRecords>
1220 CreateTable<FirstRecord>();
1221 (CreateTable<MoreRecords>(), ...);
1224template <
typename Record>
1229 auto query = _connection.
Query(RecordTableName<Record>).
Insert(
nullptr);
1231#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
1232 constexpr auto ctx = std::meta::access_context::current();
1233 template for (
constexpr auto el: define_static_array(nonstatic_data_members_of(^^Record, ctx)))
1235 using FieldType =
typename[:std::meta::type_of(el):];
1236 if constexpr (SqlInputParameterBinder<FieldType> && !IsAutoIncrementPrimaryKey<FieldType>)
1237 query.Set(FieldNameOf<el>, SqlWildcard);
1240 Reflection::EnumerateMembers(record, [&query]<
auto I,
typename FieldType>(FieldType
const& ) {
1241 if constexpr (SqlInputParameterBinder<FieldType> && !IsAutoIncrementPrimaryKey<FieldType>)
1242 query.Set(FieldNameAt<I, Record>, SqlWildcard);
1248#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
1250 template for (
constexpr auto el: define_static_array(nonstatic_data_members_of(^^Record, ctx)))
1252 using FieldType =
typename[:std::meta::type_of(el):];
1253 if constexpr (SqlInputParameterBinder<FieldType> && !IsAutoIncrementPrimaryKey<FieldType>)
1254 _stmt.BindInputParameter(i++, record.[:el:], std::meta::identifier_of(el));
1257 Reflection::CallOnMembers(
1259 [
this, i = SQLSMALLINT { 1 }]<
typename Name,
typename FieldType>(Name
const& name, FieldType
const& field)
mutable {
1260 if constexpr (SqlInputParameterBinder<FieldType> && !IsAutoIncrementPrimaryKey<FieldType>)
1261 _stmt.BindInputParameter(i++, field, name);
1266 if constexpr (HasAutoIncrementPrimaryKey<Record>)
1267 return { _stmt.
LastInsertId(RecordTableName<Record>) };
1268 else if constexpr (HasPrimaryKey<Record>)
1270 RecordPrimaryKeyType<Record>
const* primaryKey =
nullptr;
1271#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
1272 template for (
constexpr auto el: define_static_array(nonstatic_data_members_of(^^Record, ctx)))
1274 using FieldType =
typename[:std::meta::type_of(el):];
1275 if constexpr (IsField<FieldType>)
1277 if constexpr (FieldType::IsPrimaryKey)
1279 primaryKey = &record.[:el:].Value();
1284 Reflection::EnumerateMembers(record, [&]<
size_t I,
typename FieldType>(FieldType& field) {
1285 if constexpr (IsField<FieldType>)
1287 if constexpr (FieldType::IsPrimaryKey)
1289 primaryKey = &field.Value();
1298template <
typename Record>
1301 static_assert(!std::is_const_v<Record>);
1306#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
1307 constexpr auto ctx = std::meta::access_context::current();
1308 template for (
constexpr auto el: define_static_array(nonstatic_data_members_of(^^Record, ctx)))
1310 using FieldType =
typename[:std::meta::type_of(el):];
1311 if constexpr (IsField<FieldType>)
1312 if constexpr (FieldType::IsPrimaryKey)
1313 if constexpr (FieldType::IsAutoAssignPrimaryKey)
1315 if (!record.[:el:].IsModified())
1317 using ValueType =
typename FieldType::ValueType;
1318 if constexpr (std::same_as<ValueType, SqlGuid>)
1320 if (!record.[:el:].Value())
1323 else if constexpr (
requires { ValueType {} + 1; })
1325 auto maxId =
SqlStatement { _connection }.ExecuteDirectScalar<ValueType>(std::format(
1326 R
"sql(SELECT MAX("{}") FROM "{}")sql", FieldNameOf<el>, RecordTableName<Record>));
1327 record.[:el:] = maxId.value_or(ValueType {}) + 1;
1333 CallOnPrimaryKey(record, [&]<
size_t PrimaryKeyIndex,
typename PrimaryKeyType>(PrimaryKeyType& primaryKeyField) {
1334 if constexpr (PrimaryKeyType::IsAutoAssignPrimaryKey)
1336 if (!primaryKeyField.IsModified())
1338 using ValueType =
typename PrimaryKeyType::ValueType;
1339 if constexpr (std::same_as<ValueType, SqlGuid>)
1341 if (!primaryKeyField.Value())
1344 else if constexpr (
requires { ValueType {} + 1; })
1346 auto maxId =
SqlStatement { _connection }.ExecuteDirectScalar<ValueType>(
1347 std::format(R
"sql(SELECT MAX("{}") FROM "{}")sql",
1348 FieldNameAt<PrimaryKeyIndex, Record>,
1349 RecordTableName<Record>));
1350 primaryKeyField = maxId.value_or(ValueType {}) + 1;
1359 if constexpr (HasAutoIncrementPrimaryKey<Record>)
1360 SetId(record, _stmt.
LastInsertId(RecordTableName<Record>));
1365 if constexpr (HasPrimaryKey<Record>)
1369template <
typename Record>
1374 bool modified =
false;
1376#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
1377 auto constexpr ctx = std::meta::access_context::current();
1378 template for (
constexpr auto el: define_static_array(nonstatic_data_members_of(^^Record, ctx)))
1380 if constexpr (
requires { record.[:el:].IsModified(); })
1382 modified = modified || record.[:el:].IsModified();
1386 Reflection::CallOnMembers(record, [&modified](
auto const& ,
auto const& field) {
1387 if constexpr (
requires { field.IsModified(); })
1389 modified = modified || field.IsModified();
1397template <
typename Record>
1402 auto query = _connection.
Query(RecordTableName<Record>).
Update();
1404#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
1405 auto constexpr ctx = std::meta::access_context::current();
1406 template for (
constexpr auto el: define_static_array(nonstatic_data_members_of(^^Record, ctx)))
1408 using FieldType =
typename[:std::meta::type_of(el):];
1411 if (record.[:el:].IsModified())
1412 query.Set(FieldNameOf<el>, SqlWildcard);
1413 if constexpr (IsPrimaryKey<FieldType>)
1414 std::ignore = query.Where(FieldNameOf<el>, SqlWildcard);
1418 Reflection::CallOnMembersWithoutName(record, [&query]<
size_t I,
typename FieldType>(FieldType
const& field) {
1419 if (field.IsModified())
1420 query.Set(FieldNameAt<I, Record>, SqlWildcard);
1423 if constexpr (IsPrimaryKey<Reflection::MemberTypeOf<I, Record>>)
1424 std::ignore = query.Where(FieldNameAt<I, Record>, SqlWildcard);
1431#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
1432 template for (
constexpr auto el: define_static_array(nonstatic_data_members_of(^^Record, ctx)))
1434 if (record.[:el:].IsModified())
1436 _stmt.BindInputParameter(i++, record.[:el:].Value(), FieldNameOf<el>);
1440 template for (
constexpr auto el: define_static_array(nonstatic_data_members_of(^^Record, ctx)))
1442 using FieldType =
typename[:std::meta::type_of(el):];
1443 if constexpr (FieldType::IsPrimaryKey)
1445 _stmt.BindInputParameter(i++, record.[:el:].Value(), FieldNameOf<el>);
1450 Reflection::CallOnMembersWithoutName(record, [
this, &i]<
size_t I,
typename FieldType>(FieldType
const& field) {
1451 if (field.IsModified())
1452 _stmt.BindInputParameter(i++, field.Value(), FieldNameAt<I, Record>);
1456 Reflection::CallOnMembersWithoutName(record, [
this, &i]<
size_t I,
typename FieldType>(FieldType
const& field) {
1457 if constexpr (IsPrimaryKey<Reflection::MemberTypeOf<I, Record>>)
1458 _stmt.BindInputParameter(i++, field.Value(), FieldNameAt<I, Record>);
1467template <
typename Record>
1472 auto query = _connection.
Query(RecordTableName<Record>).
Delete();
1474#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
1475 auto constexpr ctx = std::meta::access_context::current();
1476 template for (
constexpr auto el: define_static_array(nonstatic_data_members_of(^^Record, ctx)))
1478 using FieldType =
typename[:std::meta::type_of(el):];
1479 if constexpr (FieldType::IsPrimaryKey)
1480 std::ignore = query.Where(FieldNameOf<el>, SqlWildcard);
1483 Reflection::CallOnMembersWithoutName(record, [&query]<
size_t I,
typename FieldType>(FieldType
const& ) {
1484 if constexpr (IsPrimaryKey<Reflection::MemberTypeOf<I, Record>>)
1485 std::ignore = query.Where(FieldNameAt<I, Record>, SqlWildcard);
1491#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
1493 template for (
constexpr auto el: define_static_array(nonstatic_data_members_of(^^Record, ctx)))
1495 using FieldType =
typename[:std::meta::type_of(el):];
1496 if constexpr (FieldType::IsPrimaryKey)
1498 _stmt.BindInputParameter(i++, record.[:el:].Value(), FieldNameOf<el>);
1503 Reflection::CallOnMembersWithoutName(
1504 record, [
this, i = SQLSMALLINT { 1 }]<
size_t I,
typename FieldType>(FieldType
const& field)
mutable {
1505 if constexpr (IsPrimaryKey<Reflection::MemberTypeOf<I, Record>>)
1506 _stmt.BindInputParameter(i++, field.Value(), FieldNameAt<I, Record>);
1515template <
typename Record,
typename... PrimaryKeyTypes>
1520 auto queryBuilder = _connection.
Query(RecordTableName<Record>).
Select();
1522 Reflection::EnumerateMembers<Record>([&]<
size_t I,
typename FieldType>() {
1525 queryBuilder.Field(FieldNameAt<I, Record>);
1527 if constexpr (FieldType::IsPrimaryKey)
1528 std::ignore = queryBuilder.Where(FieldNameAt<I, Record>, SqlWildcard);
1532 _stmt.
Prepare(queryBuilder.First());
1533 _stmt.
Execute(std::forward<PrimaryKeyTypes>(primaryKeys)...);
1535 auto resultRecord = std::optional<Record> { Record {} };
1538 return std::nullopt;
1540 return resultRecord;
1543template <
typename Record,
typename... PrimaryKeyTypes>
1546 auto record = QuerySingleWithoutRelationAutoLoading<Record>(std::forward<PrimaryKeyTypes>(primaryKeys)...);
1552template <
typename Record,
typename... Args>
1557 Reflection::EnumerateMembers<Record>([&]<
size_t I,
typename FieldType>() {
1559 selectQuery.
Field(SqlQualifiedTableColumnName { RecordTableName<Record>, FieldNameAt<I, Record> });
1562 _stmt.
Execute(std::forward<Args>(args)...);
1564 auto resultRecord = std::optional<Record> { Record {} };
1567 return std::nullopt;
1568 return resultRecord;
1573template <
typename Record,
typename... InputParameters>
1575 SqlSelectQueryBuilder::ComposedQuery
const& selectQuery, InputParameters&&... inputParameters)
1577 static_assert(DataMapperRecord<Record> || std::same_as<Record, SqlVariantRow>,
"Record must satisfy DataMapperRecord");
1579 return Query<Record>(selectQuery.ToSql(), std::forward<InputParameters>(inputParameters)...);
1582template <
typename Record,
typename... InputParameters>
1583std::vector<Record>
DataMapper::Query(std::string_view sqlQueryString, InputParameters&&... inputParameters)
1585 auto result = std::vector<Record> {};
1586 if constexpr (std::same_as<Record, SqlVariantRow>)
1588 _stmt.
Prepare(sqlQueryString);
1589 _stmt.
Execute(std::forward<InputParameters>(inputParameters)...);
1593 auto& record = result.emplace_back();
1594 record.reserve(numResultColumns);
1595 for (
auto const i: std::views::iota(1U, numResultColumns + 1))
1603 bool const canSafelyBindOutputColumns = detail::CanSafelyBindOutputColumns<Record>(_stmt.
Connection().
ServerType());
1605 _stmt.
Prepare(sqlQueryString);
1606 _stmt.
Execute(std::forward<InputParameters>(inputParameters)...);
1612 auto& record = result.emplace_back();
1614 if (canSafelyBindOutputColumns)
1615 BindOutputColumns(record);
1617 if (!reader.FetchRow())
1620 if (!canSafelyBindOutputColumns)
1621 detail::GetAllColumns(reader, record);
1627 for (
auto& record: result)
1634template <
typename First,
typename Second,
typename... Rest,
DataMapperOptions QueryOptions>
1636std::vector<std::tuple<First, Second, Rest...>>
DataMapper::Query(SqlSelectQueryBuilder::ComposedQuery
const& selectQuery)
1638 using value_type = std::tuple<First, Second, Rest...>;
1639 auto result = std::vector<value_type> {};
1641 _stmt.
Prepare(selectQuery.ToSql());
1645 constexpr auto calculateOffset = []<
size_t I,
typename Tuple>() {
1648 if constexpr (I > 0)
1650 [&]<
size_t... Indices>(std::index_sequence<Indices...>) {
1651 ((Indices < I ? (offset += Reflection::CountMembers<std::tuple_element_t<Indices, Tuple>>) : 0), ...);
1652 }(std::make_index_sequence<I> {});
1657 auto const BindElements = [&](
auto& record) {
1658 Reflection::template_for<0, std::tuple_size_v<value_type>>([&]<
auto I>() {
1659 using TupleElement = std::decay_t<std::tuple_element_t<I, value_type>>;
1660 auto& element = std::get<I>(record);
1661 constexpr size_t offset = calculateOffset.template operator()<I, value_type>();
1662 this->BindOutputColumns<TupleElement, offset>(element);
1666 auto const GetElements = [&](
auto& record) {
1667 Reflection::template_for<0, std::tuple_size_v<value_type>>([&]<
auto I>() {
1668 auto& element = std::get<I>(record);
1669 constexpr size_t offset = calculateOffset.template operator()<I, value_type>();
1670 detail::GetAllColumns(reader, element, offset - 1);
1674 bool const canSafelyBindOutputColumns = [&]() {
1676 Reflection::template_for<0, std::tuple_size_v<value_type>>([&]<
auto I>() {
1677 using TupleElement = std::decay_t<std::tuple_element_t<I, value_type>>;
1685 auto& record = result.emplace_back();
1687 if (canSafelyBindOutputColumns)
1688 BindElements(record);
1690 if (!reader.FetchRow())
1693 if (!canSafelyBindOutputColumns)
1694 GetElements(record);
1700 if constexpr (QueryOptions.loadRelations)
1703 for (
auto& record: result)
1705 Reflection::template_for<0, std::tuple_size_v<value_type>>([&]<
auto I>() {
1706 auto& element = std::get<I>(record);
1715template <
typename ElementMask,
typename Record,
typename... InputParameters>
1717 InputParameters&&... inputParameters)
1721 _stmt.
Prepare(selectQuery.ToSql());
1722 _stmt.
Execute(std::forward<InputParameters>(inputParameters)...);
1724 auto records = std::vector<Record> {};
1727 bool const canSafelyBindOutputColumns = detail::CanSafelyBindOutputColumns<Record>(_stmt.
Connection().
ServerType());
1733 auto& record = records.emplace_back();
1735 if (canSafelyBindOutputColumns)
1736 BindOutputColumns<ElementMask>(record);
1738 if (!reader.FetchRow())
1741 if (!canSafelyBindOutputColumns)
1742 detail::GetAllColumns<ElementMask>(reader, record);
1748 for (
auto& record: records)
1754template <
typename Record>
1757 static_assert(!std::is_const_v<Record>);
1760 Reflection::EnumerateMembers(record, []<
size_t I,
typename FieldType>(FieldType& field) {
1761 if constexpr (
requires { field.SetModified(
false); })
1763 field.SetModified(
false);
1768template <
typename Record,
typename Callable>
1769inline LIGHTWEIGHT_FORCE_INLINE
void CallOnPrimaryKey(Record& record, Callable
const& callable)
1773 Reflection::EnumerateMembers(record, [&]<
size_t I,
typename FieldType>(FieldType& field) {
1774 if constexpr (IsField<FieldType>)
1776 if constexpr (FieldType::IsPrimaryKey)
1778 return callable.template operator()<I, FieldType>(field);
1784template <
typename Record,
typename Callable>
1785inline LIGHTWEIGHT_FORCE_INLINE
void CallOnPrimaryKey(Callable
const& callable)
1787 static_assert(DataMapperRecord<Record>,
"Record must satisfy DataMapperRecord");
1789 Reflection::EnumerateMembers<Record>([&]<
size_t I,
typename FieldType>() {
1790 if constexpr (IsField<FieldType>)
1792 if constexpr (FieldType::IsPrimaryKey)
1794 return callable.template operator()<I, FieldType>();
1800template <
typename Record,
typename Callable>
1801inline LIGHTWEIGHT_FORCE_INLINE
void CallOnBelongsTo(Callable
const& callable)
1803 static_assert(DataMapperRecord<Record>,
"Record must satisfy DataMapperRecord");
1805 Reflection::EnumerateMembers<Record>([&]<
size_t I,
typename FieldType>() {
1806 if constexpr (IsBelongsTo<FieldType>)
1808 return callable.template operator()<I, FieldType>();
1813template <
typename FieldType>
1814std::optional<typename FieldType::ReferencedRecord> DataMapper::LoadBelongsTo(
typename FieldType::ValueType value)
1816 using ReferencedRecord =
typename FieldType::ReferencedRecord;
1818 std::optional<ReferencedRecord> record { std::nullopt };
1820#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
1821 auto constexpr ctx = std::meta::access_context::current();
1822 template for (
constexpr auto el: define_static_array(nonstatic_data_members_of(^^ReferencedRecord, ctx)))
1824 using BelongsToFieldType =
typename[:std::meta::type_of(el):];
1825 if constexpr (IsField<BelongsToFieldType>)
1826 if constexpr (BelongsToFieldType::IsPrimaryKey)
1828 if (
auto result = QuerySingle<ReferencedRecord>(value); result)
1829 record = std::move(result);
1832 std::format(
"Loading BelongsTo failed for {}", RecordTableName<ReferencedRecord>));
1836 CallOnPrimaryKey<ReferencedRecord>([&]<
size_t PrimaryKeyIndex,
typename PrimaryKeyType>() {
1837 if (
auto result = QuerySingle<ReferencedRecord>(value); result)
1838 record = std::move(result);
1841 std::format(
"Loading BelongsTo failed for {}", RecordTableName<ReferencedRecord>));
1847template <
size_t FieldIndex,
typename Record,
typename OtherRecord,
typename Callable>
1848void DataMapper::CallOnHasMany(Record& record, Callable
const& callback)
1850 static_assert(DataMapperRecord<Record>,
"Record must satisfy DataMapperRecord");
1851 static_assert(DataMapperRecord<OtherRecord>,
"OtherRecord must satisfy DataMapperRecord");
1853 using FieldType = HasMany<OtherRecord>;
1854 using ReferencedRecord =
typename FieldType::ReferencedRecord;
1856 CallOnPrimaryKey(record, [&]<
size_t PrimaryKeyIndex,
typename PrimaryKeyType>(PrimaryKeyType
const& primaryKeyField) {
1857 auto query = _connection.
Query(RecordTableName<ReferencedRecord>)
1859 .Build([&](
auto& query) {
1860 Reflection::EnumerateMembers<ReferencedRecord>(
1861 [&]<
size_t ReferencedFieldIndex,
typename ReferencedFieldType>() {
1862 if constexpr (FieldWithStorage<ReferencedFieldType>)
1864 query.Field(FieldNameAt<ReferencedFieldIndex, ReferencedRecord>);
1868 .Where(FieldNameAt<FieldIndex, ReferencedRecord>, SqlWildcard);
1869 callback(query, primaryKeyField);
1873template <
size_t FieldIndex,
typename Record,
typename OtherRecord>
1874void DataMapper::LoadHasMany(Record& record, HasMany<OtherRecord>& field)
1876 static_assert(DataMapperRecord<Record>,
"Record must satisfy DataMapperRecord");
1877 static_assert(DataMapperRecord<OtherRecord>,
"OtherRecord must satisfy DataMapperRecord");
1879 CallOnHasMany<FieldIndex, Record, OtherRecord>(record, [&](SqlSelectQueryBuilder selectQuery,
auto& primaryKeyField) {
1880 field.Emplace(detail::ToSharedPtrList(Query<OtherRecord>(selectQuery.All(), primaryKeyField.Value())));
1884template <
typename ReferencedRecord,
typename ThroughRecord,
typename Record>
1885void DataMapper::LoadHasOneThrough(Record& record, HasOneThrough<ReferencedRecord, ThroughRecord>& field)
1887 static_assert(DataMapperRecord<Record>,
"Record must satisfy DataMapperRecord");
1888 static_assert(DataMapperRecord<ThroughRecord>,
"ThroughRecord must satisfy DataMapperRecord");
1891 CallOnPrimaryKey(record, [&]<
size_t PrimaryKeyIndex,
typename PrimaryKeyType>(PrimaryKeyType
const& primaryKeyField) {
1893 CallOnBelongsTo<ThroughRecord>([&]<
size_t ThroughBelongsToIndex,
typename ThroughBelongsToType>() {
1895 CallOnPrimaryKey<ThroughRecord>([&]<
size_t ThroughPrimaryKeyIndex,
typename ThroughPrimaryKeyType>() {
1897 CallOnBelongsTo<ReferencedRecord>([&]<
size_t ReferencedKeyIndex,
typename ReferencedKeyType>() {
1902 _connection.
Query(RecordTableName<ReferencedRecord>)
1904 .Build([&](
auto& query) {
1905 Reflection::EnumerateMembers<ReferencedRecord>(
1906 [&]<
size_t ReferencedFieldIndex,
typename ReferencedFieldType>() {
1907 if constexpr (FieldWithStorage<ReferencedFieldType>)
1909 query.Field(SqlQualifiedTableColumnName {
1910 RecordTableName<ReferencedRecord>,
1911 FieldNameAt<ReferencedFieldIndex, ReferencedRecord> });
1915 .InnerJoin(RecordTableName<ThroughRecord>,
1916 FieldNameAt<ThroughPrimaryKeyIndex, ThroughRecord>,
1917 FieldNameAt<ReferencedKeyIndex, ReferencedRecord>)
1918 .InnerJoin(RecordTableName<Record>,
1919 FieldNameAt<PrimaryKeyIndex, Record>,
1920 SqlQualifiedTableColumnName { RecordTableName<ThroughRecord>,
1921 FieldNameAt<ThroughBelongsToIndex, ThroughRecord> })
1923 SqlQualifiedTableColumnName {
1924 RecordTableName<Record>,
1925 FieldNameAt<PrimaryKeyIndex, ThroughRecord>,
1928 if (
auto link = QuerySingle<ReferencedRecord>(std::move(query), primaryKeyField.Value()); link)
1930 field.EmplaceRecord(std::make_shared<ReferencedRecord>(std::move(*link)));
1938template <
typename ReferencedRecord,
typename ThroughRecord,
typename Record,
typename Callable>
1939void DataMapper::CallOnHasManyThrough(Record& record, Callable
const& callback)
1941 static_assert(DataMapperRecord<Record>,
"Record must satisfy DataMapperRecord");
1944 CallOnPrimaryKey(record, [&]<
size_t PrimaryKeyIndex,
typename PrimaryKeyType>(PrimaryKeyType
const& primaryKeyField) {
1946 CallOnBelongsTo<ThroughRecord>([&]<
size_t ThroughBelongsToRecordIndex,
typename ThroughBelongsToRecordType>() {
1947 using ThroughBelongsToRecordFieldType = Reflection::MemberTypeOf<ThroughBelongsToRecordIndex, ThroughRecord>;
1948 if constexpr (std::is_same_v<typename ThroughBelongsToRecordFieldType::ReferencedRecord, Record>)
1951 CallOnBelongsTo<ThroughRecord>(
1952 [&]<
size_t ThroughBelongsToReferenceRecordIndex,
typename ThroughBelongsToReferenceRecordType>() {
1953 using ThroughBelongsToReferenceRecordFieldType =
1954 Reflection::MemberTypeOf<ThroughBelongsToReferenceRecordIndex, ThroughRecord>;
1955 if constexpr (std::is_same_v<
typename ThroughBelongsToReferenceRecordFieldType::ReferencedRecord,
1958 auto query = _connection.
Query(RecordTableName<ReferencedRecord>)
1960 .Build([&](
auto& query) {
1961 Reflection::EnumerateMembers<ReferencedRecord>(
1962 [&]<
size_t ReferencedFieldIndex,
typename ReferencedFieldType>() {
1963 if constexpr (FieldWithStorage<ReferencedFieldType>)
1965 query.Field(SqlQualifiedTableColumnName {
1966 RecordTableName<ReferencedRecord>,
1967 FieldNameAt<ReferencedFieldIndex, ReferencedRecord> });
1971 .InnerJoin(RecordTableName<ThroughRecord>,
1972 FieldNameAt<ThroughBelongsToReferenceRecordIndex, ThroughRecord>,
1973 SqlQualifiedTableColumnName { RecordTableName<ReferencedRecord>,
1974 FieldNameAt<PrimaryKeyIndex, Record> })
1976 SqlQualifiedTableColumnName {
1977 RecordTableName<ThroughRecord>,
1978 FieldNameAt<ThroughBelongsToRecordIndex, ThroughRecord>,
1981 callback(query, primaryKeyField);
1989template <
typename ReferencedRecord,
typename ThroughRecord,
typename Record>
1990void DataMapper::LoadHasManyThrough(Record& record, HasManyThrough<ReferencedRecord, ThroughRecord>& field)
1992 static_assert(DataMapperRecord<Record>,
"Record must satisfy DataMapperRecord");
1994 CallOnHasManyThrough<ReferencedRecord, ThroughRecord>(
1995 record, [&](SqlSelectQueryBuilder& selectQuery,
auto& primaryKeyField) {
1996 field.Emplace(detail::ToSharedPtrList(Query<ReferencedRecord>(selectQuery.All(), primaryKeyField.Value())));
2000template <
typename Record>
2003 static_assert(!std::is_const_v<Record>);
2006#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
2007 constexpr auto ctx = std::meta::access_context::current();
2008 template for (
constexpr auto el: define_static_array(nonstatic_data_members_of(^^Record, ctx)))
2010 using FieldType =
typename[:std::meta::type_of(el):];
2011 if constexpr (IsBelongsTo<FieldType>)
2013 auto& field = record.[:el:];
2014 field = LoadBelongsTo<FieldType>(field.Value());
2016 else if constexpr (IsHasMany<FieldType>)
2018 LoadHasMany<el>(record, record.[:el:]);
2020 else if constexpr (IsHasOneThrough<FieldType>)
2022 LoadHasOneThrough(record, record.[:el:]);
2024 else if constexpr (IsHasManyThrough<FieldType>)
2026 LoadHasManyThrough(record, record.[:el:]);
2030 Reflection::EnumerateMembers(record, [&]<
size_t FieldIndex,
typename FieldType>(FieldType& field) {
2031 if constexpr (IsBelongsTo<FieldType>)
2033 field = LoadBelongsTo<FieldType>(field.Value());
2035 else if constexpr (IsHasMany<FieldType>)
2037 LoadHasMany<FieldIndex>(record, field);
2039 else if constexpr (IsHasOneThrough<FieldType>)
2041 LoadHasOneThrough(record, field);
2043 else if constexpr (IsHasManyThrough<FieldType>)
2045 LoadHasManyThrough(record, field);
2051template <
typename Record,
typename ValueType>
2052inline LIGHTWEIGHT_FORCE_INLINE
void DataMapper::SetId(Record& record, ValueType&&
id)
2057#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
2059 auto constexpr ctx = std::meta::access_context::current();
2060 template for (
constexpr auto el: define_static_array(nonstatic_data_members_of(^^Record, ctx)))
2062 using FieldType =
typename[:std::meta::type_of(el):];
2063 if constexpr (IsField<FieldType>)
2065 if constexpr (FieldType::IsPrimaryKey)
2067 record.[:el:] = std::forward<ValueType>(
id);
2072 Reflection::EnumerateMembers(record, [&]<
size_t I,
typename FieldType>(FieldType& field) {
2073 if constexpr (IsField<FieldType>)
2075 if constexpr (FieldType::IsPrimaryKey)
2077 field = std::forward<FieldType>(
id);
2084template <
typename Record,
size_t InitialOffset>
2085inline LIGHTWEIGHT_FORCE_INLINE Record& DataMapper::BindOutputColumns(Record& record)
2087 static_assert(DataMapperRecord<Record>,
"Record must satisfy DataMapperRecord");
2088 BindOutputColumns<Record, InitialOffset>(record, &_stmt);
2092template <
typename Record,
size_t InitialOffset>
2093Record& DataMapper::BindOutputColumns(Record& record, SqlStatement* stmt)
2095 return BindOutputColumns<std::make_integer_sequence<size_t, Reflection::CountMembers<Record>>, Record, InitialOffset>(
2099template <
typename ElementMask,
typename Record,
size_t InitialOffset>
2100inline LIGHTWEIGHT_FORCE_INLINE Record& DataMapper::BindOutputColumns(Record& record)
2102 static_assert(DataMapperRecord<Record>,
"Record must satisfy DataMapperRecord");
2103 return BindOutputColumns<ElementMask, Record, InitialOffset>(record, &_stmt);
2106template <
typename ElementMask,
typename Record,
size_t InitialOffset>
2107Record& DataMapper::BindOutputColumns(Record& record, SqlStatement* stmt)
2109 static_assert(DataMapperRecord<Record>,
"Record must satisfy DataMapperRecord");
2110 static_assert(!std::is_const_v<Record>);
2111 assert(stmt !=
nullptr);
2113#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
2114 auto constexpr ctx = std::meta::access_context::current();
2115 SQLSMALLINT i = SQLSMALLINT { InitialOffset };
2116 template for (
constexpr auto index: define_static_array(template_arguments_of(^^ElementMask)) | std::views::drop(1))
2118 constexpr auto el = nonstatic_data_members_of(^^Record, ctx)[[:index:]];
2119 using FieldType =
typename[:std::meta::type_of(el):];
2120 if constexpr (IsField<FieldType>)
2122 stmt->BindOutputColumn(i++, &record.[:el:].MutableValue());
2124 else if constexpr (SqlOutputColumnBinder<FieldType>)
2126 stmt->BindOutputColumn(i++, &record.[:el:]);
2130 Reflection::EnumerateMembers<ElementMask>(
2131 record, [stmt, i = SQLSMALLINT { InitialOffset }]<
size_t I,
typename Field>(Field& field)
mutable {
2132 if constexpr (IsField<Field>)
2134 stmt->BindOutputColumn(i++, &field.MutableValue());
2136 else if constexpr (SqlOutputColumnBinder<Field>)
2138 stmt->BindOutputColumn(i++, &field);
2146template <
typename Record>
2151 auto self = shared_from_this();
2152 auto const callback = [&]<
size_t FieldIndex,
typename FieldType>(FieldType& field) {
2153 if constexpr (IsBelongsTo<FieldType>)
2155 field.SetAutoLoader(
typename FieldType::Loader {
2156 .loadReference = [self, value = field.Value()]() -> std::optional<typename FieldType::ReferencedRecord> {
2157 return self->LoadBelongsTo<FieldType>(value);
2161 else if constexpr (IsHasMany<FieldType>)
2163 using ReferencedRecord =
typename FieldType::ReferencedRecord;
2166 .count = [self, &record]() ->
size_t {
2168 self->CallOnHasMany<FieldIndex, Record, ReferencedRecord>(
2170 self->_stmt.Prepare(selectQuery.Count());
2171 self->_stmt.Execute(primaryKeyField.Value());
2172 if (self->_stmt.FetchRow())
2173 count = self->_stmt.GetColumn<
size_t>(1);
2174 self->_stmt.CloseCursor();
2178 .all = [self, &record, &hasMany]() { self->LoadHasMany<FieldIndex>(record, hasMany); },
2180 [self, &record](
auto const& each) {
2181 self->CallOnHasMany<FieldIndex, Record, ReferencedRecord>(
2184 stmt.
Prepare(selectQuery.All());
2185 stmt.Execute(primaryKeyField.Value());
2187 auto referencedRecord = ReferencedRecord {};
2188 self->BindOutputColumns(referencedRecord, &stmt);
2189 self->ConfigureRelationAutoLoading(referencedRecord);
2191 while (stmt.FetchRow())
2193 each(referencedRecord);
2194 self->BindOutputColumns(referencedRecord, &stmt);
2200 else if constexpr (IsHasOneThrough<FieldType>)
2202 using ReferencedRecord =
typename FieldType::ReferencedRecord;
2203 using ThroughRecord =
typename FieldType::ThroughRecord;
2207 [self, &record, &hasOneThrough]() {
2208 self->LoadHasOneThrough<ReferencedRecord, ThroughRecord>(record, hasOneThrough);
2212 else if constexpr (IsHasManyThrough<FieldType>)
2214 using ReferencedRecord =
typename FieldType::ReferencedRecord;
2215 using ThroughRecord =
typename FieldType::ThroughRecord;
2218 .count = [self, &record]() ->
size_t {
2221 self->CallOnHasManyThrough<ReferencedRecord, ThroughRecord>(
2223 self->_stmt.Prepare(selectQuery.Count());
2224 self->_stmt.Execute(primaryKeyField.Value());
2225 if (self->_stmt.FetchRow())
2226 count = self->_stmt.GetColumn<
size_t>(1);
2227 self->_stmt.CloseCursor();
2232 [self, &record, &hasManyThrough]() {
2234 self->LoadHasManyThrough(record, hasManyThrough);
2237 [self, &record](
auto const& each) {
2239 self->CallOnHasManyThrough<ReferencedRecord, ThroughRecord>(
2242 stmt.
Prepare(selectQuery.All());
2243 stmt.Execute(primaryKeyField.Value());
2245 auto referencedRecord = ReferencedRecord {};
2246 self->BindOutputColumns(referencedRecord, &stmt);
2247 self->ConfigureRelationAutoLoading(referencedRecord);
2249 while (stmt.FetchRow())
2251 each(referencedRecord);
2252 self->BindOutputColumns(referencedRecord, &stmt);
2260#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
2261 constexpr auto ctx = std::meta::access_context::current();
2263 Reflection::template_for<0, nonstatic_data_members_of(^^Record, ctx).size()>([&callback, &record]<
auto I>() {
2264 constexpr auto localctx = std::meta::access_context::current();
2265 constexpr auto members = define_static_array(nonstatic_data_members_of(^^Record, localctx));
2266 using FieldType =
typename[:std::meta::type_of(members[I]):];
2267 callback.template operator()<I, FieldType>(record.[:members[I]:]);
2270 Reflection::EnumerateMembers(record, callback);