Lightweight 0.20261030.0
Loading...
Searching...
No Matches
Data Mapper

The data mapper is a high level API for mapping records to and from the database using high level C++ syntax. More...

Concepts

concept  Lightweight::ConnectionType
 Satisfied by Connection specializations.
 
concept  Lightweight::DataMapperRecord
 Represents a record type that can be used with the DataMapper.
 
concept  Lightweight::RelationSelector
 Constrains what may be used to single out one of several foreign keys into the same table.
 
concept  Lightweight::FieldWithStorage
 
concept  Lightweight::RecordColumnMember
 Requires that T maps onto a column of its record's table.
 
concept  Lightweight::HasDescription
 Satisfied when a Description specialization exists for the given record type.
 

Classes

class  Lightweight::BelongsTo< TheReferencedField, ColumnNameOverrideString, Nullable >
 Represents the many-to-one side of a foreign-key relationship. More...
 
struct  Lightweight::Connection< FromPtr, IntoPtr >
 One column pair of a composite foreign key: "this record's column references that one". More...
 
class  Lightweight::CompositeForeignKey< Connections >
 Represents a foreign key spanning several columns. More...
 
class  Lightweight::DataMapper
 Main API for mapping records to and from the database using high level C++ syntax. More...
 
class  Lightweight::SqlRequireLoadedError
 Raised by the relation shortcuts that cannot return a RelationResult - operator->, operator*, begin()/end(), At() and operator[] - when the relation is unavailable. More...
 
struct  Lightweight::Field< T, P1, P2 >
 Represents a single column in a table. More...
 
class  Lightweight::HasMany< OtherRecord, TheInverseSelector >
 This HasMany<OtherRecord> represents a simple one-to-many relationship between two records. More...
 
class  Lightweight::HasManyThrough< ReferencedRecordT, ThroughSpec, TheOwnerSelector, TheReferencedSelector >
 This API represents a many-to-many relationship between two records through a third record. More...
 
class  Lightweight::HasOneThrough< OtherTable, ThroughSpec, TheOwnerSelector, TheThroughSelector >
 Represents a one-to-one relationship through a join table. More...
 
class  Lightweight::SqlCoreDataMapperQueryBuilder< Record, Derived, QueryOptions >
 
class  Lightweight::SqlAllFieldsQueryBuilder< Record, QueryOptions, Execution >
 Represents a query builder that retrieves all fields of a record. More...
 
struct  Lightweight::Through< JoinRecordT >
 Marks the join record of a relationship that is reached through an intermediate table. More...
 
struct  Lightweight::RecordMemberList< MemberPointers >
 Compile-time list of a record's members, stored as pointers-to-member. More...
 
struct  Lightweight::Description< Record >
 Customization point providing pre-computed reflection metadata for a record type. More...
 

Typedefs

template<typename T >
using Lightweight::RelationResult = std::expected< T, RelationError >
 The outcome of a relation access: the requested value, or why it is unavailable.
 
template<auto Field>
using Lightweight::ReferencedFieldTypeOf = std::remove_cvref_t< decltype(std::declval< MemberClassType< decltype(Field)> >().*Field)>::ValueType
 Retrieves the type of a member field in a record.
 
template<size_t... Ints>
using Lightweight::SqlElements = std::integer_sequence< size_t, Ints... >
 Represents a sequence of indexes that can be used alongside Query() to retrieve only part of the record.
 
template<typename ThroughSpec >
using Lightweight::ThroughRecordOf = detail::ThroughRecordOfHelper< ThroughSpec >::type
 Resolves the join record of a through-relationship from its template argument.
 
template<typename Record >
using Lightweight::RecordPrimaryKeyTuple = detail::RecordPrimaryKeyTupleHelper< Record >::type
 The tuple of a record's primary key value types, in member declaration order.
 
template<std::size_t I, typename Record >
using Lightweight::RecordMemberTypeOf = detail::RecordMemberTypeOfDispatch< I, std::remove_cvref_t< Record >, HasDescription< Record > >::type
 Type of the member at index I — from the descriptor if present, else via reflection.
 

Enumerations

enum class  Lightweight::RelationError : std::uint8_t { RelationError::NotConfigured , RelationError::NotFound , RelationError::Outdated , RelationError::QueryFailed }
 Why a relation could not provide its record(s). More...
 
enum class  Lightweight::FieldTimestamp : uint8_t { FieldTimestamp::None , FieldTimestamp::CreatedAt , FieldTimestamp::UpdatedAt }
 Tells the data mapper that this field is a timestamp it maintains automatically, or not. More...
 

Functions

template<typename Record >
RecordPrimaryKeyTuple< Record > Lightweight::GetPrimaryKeyFields (Record const &record)
 Reads every primary key value of record, in member declaration order.
 
template<typename Record >
LIGHTWEIGHT_FORCE_INLINE RecordPrimaryKeyType< Record > Lightweight::GetPrimaryKeyField (Record const &record) noexcept
 
template<std::size_t I, typename Record >
constexpr decltype(auto) Lightweight::GetRecordMemberAt (Record &&record)
 Returns a reference to the member at index I — from the descriptor if present, else via reflection.
 
template<typename Record , typename Callable >
constexpr void Lightweight::EnumerateRecordMembers (Record &record, Callable &&callable)
 Invokes callable as callable<I>(member) for each member of record.
 
template<typename ElementMask , typename Record , typename Callable >
constexpr void Lightweight::EnumerateRecordMembers (Record &record, Callable &&callable)
 Invokes callable as callable<I>(member) for each member selected by ElementMask.
 
template<typename Record , typename Callable >
constexpr void Lightweight::EnumerateRecordMembers (Callable &&callable)
 Invokes callable as callable<I, MemberType>() for each member of Record.
 
template<typename Record , typename Callable , typename ResultType >
constexpr ResultType Lightweight::FoldRecordMembers (ResultType initialValue, Callable const &callable)
 Folds over a record's members as result = callable<I, MemberType>(result).
 

Variables

template<typename T >
constexpr bool Lightweight::IsCompositeForeignKey = detail::IsCompositeForeignKeyType<std::remove_cvref_t<T>>::value
 Whether T is a CompositeForeignKey.
 
constexpr std::nullopt_t Lightweight::AutoDetectRelation = std::nullopt
 Selector value meaning "resolve the relationship automatically; the match must be unique".
 
template<typename T >
constexpr bool Lightweight::IsThrough = detail::IsThroughType<std::remove_cvref_t<T>>::value
 
template<typename OwnerRecord , typename ChildRecord , auto Selector = AutoDetectRelation>
constexpr size_t Lightweight::InverseBelongsToIndexOf = detail::InverseBelongsToResolver<OwnerRecord, ChildRecord, Selector>::Index
 Member index, within ChildRecord, of the BelongsTo member that points back to OwnerRecord.
 
template<typename OwnerRecord , typename ChildRecord , auto Selector = AutoDetectRelation>
constexpr std::string_view Lightweight::InverseBelongsToFieldNameOf
 SQL column name of the foreign key that links ChildRecord back to OwnerRecord.
 
template<typename Record >
constexpr size_t Lightweight::RecordColumnCount
 Represents the number of members of a record that map onto a column of a result set.
 
template<typename Record >
constexpr size_t Lightweight::RecordStorageFieldCount
 
template<typename T >
constexpr bool Lightweight::HasPrimaryKey = detail::CheckFieldProperty<[]<typename Field>() { return IsPrimaryKey<Field>; }, T>
 Tests if the given record type does contain a primary key.
 
template<typename T >
constexpr bool Lightweight::HasAutoIncrementPrimaryKey
 Tests if the given record type does contain an auto increment primary key.
 
template<typename Record >
constexpr std::size_t Lightweight::RecordPrimaryKeyCount = std::tuple_size_v<RecordPrimaryKeyTuple<Record>>
 Number of members of Record marked as a primary key.
 
template<typename Record >
constexpr bool Lightweight::HasCompositePrimaryKey = RecordPrimaryKeyCount<Record> > 1
 Whether Record's identity spans more than one column.
 
template<typename Record >
constexpr std::size_t Lightweight::RecordMemberCount
 Number of members in a record — from the descriptor if present, else via reflection.
 
template<std::size_t I, typename Record >
constexpr std::string_view Lightweight::FieldNameAt = detail::FieldNameAt<I, Record>()
 Returns the SQL field name of the given field index in the record.
 
template<typename Record >
constexpr std::string_view Lightweight::RecordTableName = detail::RecordTableNameImpl<Record>::Value
 Holds the SQL tabl ename for the given record type.
 
template<auto ReferencedField>
constexpr auto Lightweight::FullyQualifiedNameOf
 Holds the fully qualified column reference (table + column) for the given field.
 

Detailed Description

The data mapper is a high level API for mapping records to and from the database using high level C++ syntax.

Typedef Documentation

◆ RelationResult

template<typename T >
using Lightweight::RelationResult = typedef std::expected<T, RelationError>

The outcome of a relation access: the requested value, or why it is unavailable.

Definition at line 44 of file Error.hpp.

◆ ReferencedFieldTypeOf

template<auto Field>
using Lightweight::ReferencedFieldTypeOf = typedef std::remove_cvref_t<decltype(std::declval<MemberClassType<decltype(Field)> >().*Field)>::ValueType

Retrieves the type of a member field in a record.

Field must be a member of the record type, and it must be a field type, e.g. Field<int> or BelongsTo<OtherRecord>.

using MyRecord = Record {
Field<int> value;
Field<std::optional<char>> optionalValue;
};
using MyFieldType = ReferencedFieldTypeOf<&MyRecord::value>; // Retrieves `int`
using MyOptionalFieldType = ReferencedFieldTypeOf<&MyRecord::optionalValue>; // Retrieves `std::optional<char>`
Represents a single column in a table.
Definition Field.hpp:154

Definition at line 491 of file Field.hpp.

◆ SqlElements

template<size_t... Ints>
using Lightweight::SqlElements = typedef std::integer_sequence<size_t, Ints...>

Represents a sequence of indexes that can be used alongside Query() to retrieve only part of the record.

Definition at line 25 of file Record.hpp.

◆ ThroughRecordOf

template<typename ThroughSpec >
using Lightweight::ThroughRecordOf = typedef detail::ThroughRecordOfHelper<ThroughSpec>::type

Resolves the join record of a through-relationship from its template argument.

Accepts both the Through<T> marker and the deprecated bare T spelling, yielding T either way.

Template Parameters
ThroughSpecEither Through<T> or, deprecated, T.
See also
Through

Definition at line 250 of file Record.hpp.

◆ RecordPrimaryKeyTuple

template<typename Record >
using Lightweight::RecordPrimaryKeyTuple = typedef detail::RecordPrimaryKeyTupleHelper<Record>::type

The tuple of a record's primary key value types, in member declaration order.

Unlike RecordPrimaryKeyType, which names a single field's type, this covers composite keys: for a record with several members marked PrimaryKey it is a tuple of all of them. For a single-key record it is a one-element tuple.

Added alongside the single-key helpers rather than replacing them, so no existing caller changes behaviour; only composite-aware code reaches for this.

Definition at line 559 of file Record.hpp.

◆ RecordMemberTypeOf

template<std::size_t I, typename Record >
using Lightweight::RecordMemberTypeOf = typedef detail::RecordMemberTypeOfDispatch<I, std::remove_cvref_t<Record>, HasDescription<Record> >::type

Type of the member at index I — from the descriptor if present, else via reflection.

Definition at line 169 of file Description.hpp.

Enumeration Type Documentation

◆ RelationError

enum class Lightweight::RelationError : std::uint8_t
strong

Why a relation could not provide its record(s).

Returned by the relation accessors (BelongsTo::Record(), HasMany::All(), HasMany::Count(), ...) through RelationResult instead of throwing.

Enumerator
NotConfigured 

No loader is installed and nothing was loaded: the record was built by hand, or read with DataMapperOptions { .loadRelations = false }.

NotFound 

There is nothing to load: the foreign key is NULL, or the referenced row does not exist.

Outdated 

The default connection string changed since the record was read (see SqlConnection::SetDefaultConnectionString), so the record belongs to a database the application has switched away from. No query is attempted. Re-read the record, or load its relations before switching.

QueryFailed 

The load query failed (lost connection, missing table, ...). The driver's diagnostic is reported through SqlLogger. Unlike the other errors this one is not remembered: the next access retries.

Definition at line 20 of file Error.hpp.

◆ FieldTimestamp

enum class Lightweight::FieldTimestamp : uint8_t
strong

Tells the data mapper that this field is a timestamp it maintains automatically, or not.

Pass it in one of a Field's two option slots, alone or together with a column-name override:

struct Article
{
Field<SqlDateTime, FieldTimestamp::UpdatedAt, SqlRealName { "updated_at" }> updatedAt;
};
Helper class, used to represent a real SQL column names as template arguments.

The value is read from the DataMapper's timestamp clock (see DataMapper::SetTimestampClock()), once per operation, so every timestamp written by one operation carries the same value. A value the caller set explicitly is never overwritten, which allows importing historical rows.

Only SqlDateTime and std::optional<SqlDateTime> fields may carry a timestamp marker.

Enumerator
None 

The field is not maintained by the data mapper.

CreatedAt 

The field holds the time the record was created.

Set when the record is inserted, unless the caller gave it a value. Never changed by an update. 
UpdatedAt 

The field holds the time the record was last written.

Set when the record is inserted, unless the caller gave it a value, and again whenever an update
writes any other column of the record, unless the caller modified this field in that change. 

Definition at line 70 of file Field.hpp.

Function Documentation

◆ GetPrimaryKeyFields()

template<typename Record >
RecordPrimaryKeyTuple< Record > Lightweight::GetPrimaryKeyFields ( Record const &  record)

Reads every primary key value of record, in member declaration order.

This is the order a primary key lookup binds its arguments in, so the returned tuple can be applied straight to QuerySingle/Update/Delete.

Parameters
recordRecord to read.
Returns
The key values as a tuple.

Definition at line 585 of file Record.hpp.

◆ GetPrimaryKeyField()

template<typename Record >
LIGHTWEIGHT_FORCE_INLINE RecordPrimaryKeyType< Record > Lightweight::GetPrimaryKeyField ( Record const &  record)
inlinenoexcept

Returns the primary key value of the record.

Selected by type: the first primary key member of type RecordPrimaryKeyType, preferring a generated one (AutoAssign or ServerSideAutoIncrement) over a PrimaryKey::Manual one of the same type, so that a Manual member declared earlier does not stand in for the key the mapper generated.

Definition at line 615 of file Record.hpp.

References Lightweight::EnumerateRecordMembers().

Referenced by Lightweight::DataMapper::ConfigureRelationAutoLoading(), and Lightweight::DataMapper::Create().

◆ GetRecordMemberAt()

template<std::size_t I, typename Record >
constexpr decltype(auto) Lightweight::GetRecordMemberAt ( Record &&  record)
constexpr

Returns a reference to the member at index I — from the descriptor if present, else via reflection.

Definition at line 174 of file Description.hpp.

◆ EnumerateRecordMembers() [1/3]

template<typename Record , typename Callable >
constexpr void Lightweight::EnumerateRecordMembers ( Record &  record,
Callable &&  callable 
)
constexpr

Invokes callable as callable<I>(member) for each member of record.

Mirrors Reflection::EnumerateMembers(object, callable) but reads the descriptor when present, avoiding the aggregate-decomposition (ToTuple) instantiation.

Definition at line 188 of file Description.hpp.

Referenced by Lightweight::DataMapper::ConfigureRelationAutoLoading(), Lightweight::DataMapper::Delete(), Lightweight::GetPrimaryKeyField(), Lightweight::DataMapper::LoadRelations(), Lightweight::DataMapper::SetId(), Lightweight::DataMapper::SetModifiedState(), and Lightweight::DataMapper::Update().

◆ EnumerateRecordMembers() [2/3]

template<typename ElementMask , typename Record , typename Callable >
constexpr void Lightweight::EnumerateRecordMembers ( Record &  record,
Callable &&  callable 
)
constexpr

Invokes callable as callable<I>(member) for each member selected by ElementMask.

Mirrors Reflection::EnumerateMembers<ElementMask>(object, callable) (partial-column queries) but reads the descriptor when present. ElementMask is a std::integer_sequence<std::size_t, ...>.

Definition at line 202 of file Description.hpp.

◆ EnumerateRecordMembers() [3/3]

template<typename Record , typename Callable >
constexpr void Lightweight::EnumerateRecordMembers ( Callable &&  callable)
constexpr

Invokes callable as callable<I, MemberType>() for each member of Record.

Mirrors Reflection::EnumerateMembers<Object>(callable) but reads the descriptor when present.

Definition at line 216 of file Description.hpp.

◆ FoldRecordMembers()

template<typename Record , typename Callable , typename ResultType >
constexpr ResultType Lightweight::FoldRecordMembers ( ResultType  initialValue,
Callable const &  callable 
)
constexpr

Folds over a record's members as result = callable<I, MemberType>(result).

Mirrors Reflection::FoldMembers<Object>(initialValue, callable) but reads the descriptor when present.

Definition at line 229 of file Description.hpp.

Variable Documentation

◆ IsCompositeForeignKey

template<typename T >
constexpr bool Lightweight::IsCompositeForeignKey = detail::IsCompositeForeignKeyType<std::remove_cvref_t<T>>::value
constexpr

Whether T is a CompositeForeignKey.

Definition at line 485 of file CompositeForeignKey.hpp.

◆ AutoDetectRelation

constexpr std::nullopt_t Lightweight::AutoDetectRelation = std::nullopt
inlineconstexpr

Selector value meaning "resolve the relationship automatically; the match must be unique".

This is the default for every relationship selector. Pass a SqlRealName instead to name the foreign key column explicitly, which is required when a record holds more than one foreign key into the same table.

Definition at line 138 of file Record.hpp.

◆ IsThrough

template<typename T >
constexpr bool Lightweight::IsThrough = detail::IsThroughType<std::remove_cvref_t<T>>::value
constexpr

Tests whether T is a Through marker.

Definition at line 200 of file Record.hpp.

◆ InverseBelongsToIndexOf

template<typename OwnerRecord , typename ChildRecord , auto Selector = AutoDetectRelation>
constexpr size_t Lightweight::InverseBelongsToIndexOf = detail::InverseBelongsToResolver<OwnerRecord, ChildRecord, Selector>::Index
constexpr

Member index, within ChildRecord, of the BelongsTo member that points back to OwnerRecord.

This is how HasMany, HasManyThrough and HasOneThrough locate their foreign key column: by matching the relationship type, never by member position. Using it is a hard compile-time error when ChildRecord declares no such BelongsTo, or when it declares more than one and Selector does not single out exactly one of them.

Template Parameters
OwnerRecordThe record being referenced (the "one" side of the relationship).
ChildRecordThe record holding the foreign key (the "many" side).
SelectorSingles out one of several foreign keys, see the RelationSelector concept.

Definition at line 370 of file Record.hpp.

◆ InverseBelongsToFieldNameOf

template<typename OwnerRecord , typename ChildRecord , auto Selector = AutoDetectRelation>
constexpr std::string_view Lightweight::InverseBelongsToFieldNameOf
constexpr
Initial value:
=
FieldNameAt<InverseBelongsToIndexOf<OwnerRecord, ChildRecord, Selector>, ChildRecord>

SQL column name of the foreign key that links ChildRecord back to OwnerRecord.

Template Parameters
OwnerRecordThe record being referenced (the "one" side of the relationship).
ChildRecordThe record holding the foreign key (the "many" side).
SelectorSingles out one of several foreign keys, see the RelationSelector concept.

Definition at line 380 of file Record.hpp.

◆ RecordColumnCount

template<typename Record >
constexpr size_t Lightweight::RecordColumnCount
constexpr
Initial value:
=
FoldRecordMembers<Record>(size_t { 0 }, []<size_t I, typename Field>(size_t const accum) constexpr {
return accum + 1;
else
return accum;
})
Requires that T maps onto a column of its record's table.
Definition Record.hpp:419

Represents the number of members of a record that map onto a column of a result set.

This is the width the record occupies in a projection built from the RecordColumnMember concept (e.g. SqlSelectQueryBuilder::Fields), and therefore the amount by which the index must be advanced to reach the first column of the record that follows it in a multi-record projection. It differs from RecordMemberCount exactly by the number of relation members (HasMany, HasManyThrough, HasOneThrough, ...), which have no column of their own.

Definition at line 431 of file Record.hpp.

◆ RecordStorageFieldCount

template<typename Record >
constexpr size_t Lightweight::RecordStorageFieldCount
constexpr
Initial value:
=
FoldRecordMembers<Record>(size_t { 0 }, []<size_t I, typename Field>(size_t const accum) constexpr {
if constexpr (FieldWithStorage<Field>)
return accum + 1;
else
return accum;
})

Represents the number of fields with storage in a record.

Definition at line 443 of file Record.hpp.

◆ HasPrimaryKey

template<typename T >
constexpr bool Lightweight::HasPrimaryKey = detail::CheckFieldProperty<[]<typename Field>() { return IsPrimaryKey<Field>; }, T>
constexpr

Tests if the given record type does contain a primary key.

Definition at line 471 of file Record.hpp.

◆ HasAutoIncrementPrimaryKey

template<typename T >
constexpr bool Lightweight::HasAutoIncrementPrimaryKey
constexpr
Initial value:
=
detail::CheckFieldProperty<[]<typename Field>() { return IsAutoIncrementPrimaryKey<Field>; }, T>

Tests if the given record type does contain an auto increment primary key.

Definition at line 477 of file Record.hpp.

◆ RecordPrimaryKeyCount

template<typename Record >
constexpr std::size_t Lightweight::RecordPrimaryKeyCount = std::tuple_size_v<RecordPrimaryKeyTuple<Record>>
constexpr

Number of members of Record marked as a primary key.

One for an ordinary record, more for a composite key, zero for a keyless record.

Definition at line 567 of file Record.hpp.

◆ HasCompositePrimaryKey

template<typename Record >
constexpr bool Lightweight::HasCompositePrimaryKey = RecordPrimaryKeyCount<Record> > 1
constexpr

Whether Record's identity spans more than one column.

Definition at line 573 of file Record.hpp.

◆ RecordMemberCount

template<typename Record >
constexpr std::size_t Lightweight::RecordMemberCount
constexpr
Initial value:
= []() constexpr {
if constexpr (HasDescription<Record>)
return Description<std::remove_cvref_t<Record>>::FieldCount;
else
return Reflection::CountMembers<Record>;
}()
Satisfied when a Description specialization exists for the given record type.

Number of members in a record — from the descriptor if present, else via reflection.

Definition at line 159 of file Description.hpp.

◆ FieldNameAt

template<std::size_t I, typename Record >
constexpr std::string_view Lightweight::FieldNameAt = detail::FieldNameAt<I, Record>()
inlineconstexpr

Returns the SQL field name of the given field index in the record.

Definition at line 285 of file Utils.hpp.

◆ RecordTableName

template<typename Record >
constexpr std::string_view Lightweight::RecordTableName = detail::RecordTableNameImpl<Record>::Value
constexpr

Holds the SQL tabl ename for the given record type.

Definition at line 291 of file Utils.hpp.

Referenced by Lightweight::SqlWhereClauseBuilder< Derived >::InnerJoin().

◆ FullyQualifiedNameOf

template<auto ReferencedField>
constexpr auto Lightweight::FullyQualifiedNameOf
inlineconstexpr
Initial value:
= SqlQualifiedTableColumnName {
.tableName = RecordTableName<MemberClassType<decltype(ReferencedField)>>,
.columnName = FieldNameOf<ReferencedField>,
}
constexpr std::string_view RecordTableName
Holds the SQL tabl ename for the given record type.
Definition Utils.hpp:291

Holds the fully qualified column reference (table + column) for the given field.

Template Parameters
ReferencedFieldA pointer-to-member identifying the field.
constexpr auto ref = FullyQualifiedNameOf<&Person::id>;
static_assert(ref.tableName == "Person");
static_assert(ref.columnName == "id");

The result is an SqlQualifiedTableColumnName accepted by every column-name entry point in the builder (Field, Fields, Where, OrderBy, GroupBy, Aggregate::*, joins). The builder applies the quoting.

Definition at line 367 of file Utils.hpp.