ORM C++
Loading...
Searching...
No Matches
database.hpp
1#pragma once
2
3#include <algorithm>
4#include <cassert>
5#include <cstddef>
6#include <limits>
7#include <map>
8#include <memory>
9#include <stdexcept>
10#include <string>
11#include <string_view>
12#include <type_traits>
13#include <utility>
14#include <variant>
15#include <vector>
16
17#include "database/BackendCapabilities.hpp"
18#include "database/BackendRuntime.hpp"
19#include "database/BackendType.hpp"
20#include "database/binding/Binding.hpp"
21#include "database/binding/CollectionBinding.hpp"
22#include "database/binding/ConversionError.hpp"
23#include "database/binding/ProjectionBinding.hpp"
24#include "database/binding/StatementBinding.hpp"
25#include "database/CommandGeneratorFactory.hpp"
26#include "database/DatabaseError.hpp"
27#include "database/RelationStatements.hpp"
28#include "database/Statement.hpp"
29#include "model/Schema.hpp"
30#include "projection_query.hpp"
31#include "query.hpp"
32#include "reflection/Reflection.hpp"
33#include "soci/soci.h"
34#include "soci/values.h"
35#include "update.hpp"
36
37namespace orm
38{
39namespace detail
40{
41inline auto bindStatementParameter(const db::BackendRuntime& runtime, soci::values& values,
42 const db::StatementParameter& parameter) -> void
43{
44 runtime.bind(values, parameter.name, parameter.getBoundValue());
45}
46
47inline auto bindStatementParameters(const db::BackendRuntime& runtime, soci::values& values,
48 const std::vector<db::StatementParameter>& parameters) -> void
49{
50 for (const auto& parameter : parameters)
51 {
52 bindStatementParameter(runtime, values, parameter);
53 }
54}
55
56auto bindModelParameters(const db::BackendRuntime& runtime, soci::values& targetValues,
57 const soci::values& serializedModel, model::ModelView model) -> std::size_t;
58auto normalizeAffectedRows(long long affectedRows) -> std::size_t;
59} // namespace detail
60
67{
68public:
73
77 explicit DatabaseCore(db::CommandGeneratorFactory factory);
78 DatabaseCore(const DatabaseCore&) = delete;
79 DatabaseCore(DatabaseCore&&) = delete;
80 auto operator=(const DatabaseCore&) -> DatabaseCore& = delete;
81 auto operator=(DatabaseCore&&) -> DatabaseCore& = delete;
82
88 auto connect(const std::string& connectionString) -> void;
89
93 auto connect(db::BackendType requestedBackend, const std::string& connectionString) -> void;
94
98 auto disconnect() -> void;
99
107protected:
108 template <typename SchemaType, typename T>
109 auto selectImpl(Query<T>& query) -> std::vector<T>
110 {
111 constexpr auto model = model::modelView<SchemaType, T>();
112 ensureQuerySupported(model, query.getData());
113 std::vector<T> result;
114 const auto statement = getCommandGenerator().select(model, query.getData());
115 ensureStatementWithinBindLimit(statement.parameters.size(), "select");
116
117 try
118 {
119 soci::values parameterValues;
120 detail::bindStatementParameters(getBackend().runtime(), parameterValues, statement.parameters);
121
122 auto readRows = [&]<bool JoinedValues>()
123 {
124 soci::rowset<db::binding::BindingPayload<T, SchemaType, JoinedValues>> preparedRowSet =
125 (sql.prepare << statement.sql, soci::use(parameterValues));
126
127 for (auto& payload : preparedRowSet)
128 {
129 result.push_back(std::move(payload.value));
130 }
131 };
132
133 if (query.getData().shouldJoin)
134 {
135 readRows.template operator()<true>();
136 }
137 else
138 {
139 readRows.template operator()<false>();
140 }
141
142 loadIncludedCollections<SchemaType>(model, query.getData(), result);
143 }
144 catch (const db::binding::ConversionError&)
145 {
146 throw DatabaseError{DatabaseErrorCode::Conversion, backendType, "select",
147 "A database result cannot be represented by the requested model"};
148 }
149 catch (const soci::soci_error& error)
150 {
151 throwTranslatedError(error, DatabaseErrorCode::Statement, "select");
152 }
153
154 return result;
155 }
156
165 template <typename SchemaType, typename Source, typename Result>
166 auto selectImpl(ProjectionQuery<Source, Result>& query) -> std::vector<Result>
167 {
168 constexpr auto model = model::modelView<SchemaType, Source>();
169 ensureQuerySupported(model, query.getData());
170 std::vector<Result> result;
171 const auto statement = getCommandGenerator().select(model, query.getData());
172 ensureStatementWithinBindLimit(statement.parameters.size(), "select projection");
173
174 try
175 {
176 soci::values parameterValues;
177 detail::bindStatementParameters(getBackend().runtime(), parameterValues, statement.parameters);
178
179 soci::rowset<db::binding::ProjectionPayload<Result>> preparedRowSet =
180 (sql.prepare << statement.sql, soci::use(parameterValues));
181
182 for (auto& payload : preparedRowSet)
183 {
184 result.push_back(std::move(payload.value));
185 }
186 }
187 catch (const db::binding::ConversionError&)
188 {
189 throw DatabaseError{DatabaseErrorCode::Conversion, backendType, "select projection",
190 "A database result cannot be represented by the requested projection"};
191 }
192 catch (const soci::soci_error& error)
193 {
194 throwTranslatedError(error, DatabaseErrorCode::Statement, "select projection");
195 }
196
197 return result;
198 }
199
206 template <typename SchemaType, typename T>
207 auto insertImpl(const std::vector<T>& objects) -> void
208 {
209 for (const auto& object : objects)
210 {
212 }
213 }
214
221 template <typename SchemaType, typename T>
222 auto insertImpl(T object) -> void
223 {
224 constexpr auto model = model::modelView<SchemaType, T>();
225 ensureModelSupported(model, "insert");
226 requireCapability(getBackendCapabilities().mutations.insert, "insert", "insert is not supported");
227 const auto command = getCommandGenerator().insert(model);
228
229 const db::binding::BindingPayload<T, SchemaType> payload{};
230
231 payload.value = std::move(object);
232
233 try
234 {
235 soci::values serializedModel;
236 auto indicator = soci::i_ok;
237 soci::type_conversion<db::binding::BindingPayload<T, SchemaType>>::to_base(payload, serializedModel,
238 indicator);
239
240 soci::values parameterValues;
241 const auto parameterCount =
242 detail::bindModelParameters(getBackend().runtime(), parameterValues, serializedModel, model);
243 ensureStatementWithinBindLimit(parameterCount, "insert");
244
245 if (parameterCount == 0)
246 {
247 sql << command;
248 }
249 else
250 {
251 sql << command, soci::use(parameterValues);
252 }
253 }
254 catch (const db::binding::ConversionError&)
255 {
256 throw DatabaseError{DatabaseErrorCode::Conversion, backendType, "insert",
257 "A model value cannot be represented by the selected backend"};
258 }
259 catch (const soci::soci_error& error)
260 {
261 throwTranslatedError(error, DatabaseErrorCode::Statement, "insert");
262 }
263 }
264
272 template <typename SchemaType, typename T>
273 auto updateImpl(const Update<T>& update) -> std::size_t
274 {
275 constexpr auto model = model::modelView<SchemaType, T>();
276 ensureModelSupported(model, "update");
277 requireCapability(getBackendCapabilities().mutations.update, "update", "update is not supported");
278 const auto statement = getCommandGenerator().update(model, update.getData());
279
280 return executeMutation(statement, "update");
281 }
282
290 template <typename SchemaType, typename T>
291 auto removeImpl(const query::Predicate& predicate) -> std::size_t
292 {
293 constexpr auto model = model::modelView<SchemaType, T>();
294 ensureModelSupported(model, "remove");
295 requireCapability(getBackendCapabilities().mutations.remove, "remove", "remove is not supported");
296 const auto statement = getCommandGenerator().remove(model, predicate);
297
298 return executeMutation(statement, "remove");
299 }
300
306 template <typename SchemaType, typename T>
307 auto createTableImpl() -> void
308 {
309 constexpr auto model = model::modelView<SchemaType, T>();
310 ensureModelSupported(model, "create table");
311 requireCapability(getBackendCapabilities().schema.createTableIfNotExists, "create table",
312 "idempotent table creation is not supported");
313 const auto command = getCommandGenerator().createTable(model);
314 executeSql(command, "create table");
315 }
316
322 template <typename SchemaType, typename T>
323 auto deleteTableImpl() -> void
324 {
325 constexpr auto model = model::modelView<SchemaType, T>();
326 ensureModelSupported(model, "drop table");
327 requireCapability(getBackendCapabilities().schema.dropTableIfExists, "drop table",
328 "idempotent table removal is not supported");
329 const auto command = getCommandGenerator().dropTable(model);
330 executeSql(command, "drop table");
331 }
332
339 template <typename SchemaType, typename T>
341 {
342 constexpr auto owner = model::modelView<SchemaType, T>();
343 const auto ownsJunctionTable =
344 std::ranges::any_of(owner->relations,
345 [](const auto& relation)
346 {
347 return relation.kind == model::RelationKind::ManyToMany and
348 relation.junction.isConfigured() and relation.junction.owningSide;
349 });
350
351 if (not ownsJunctionTable)
352 {
353 return;
354 }
355
356 ensureModelSupported(owner, "create relation tables");
357 ensureRelationTableEndpointsExist(owner);
358
359 for (const auto& command : db::relations::createTableStatements(getBackend().dialect(), owner))
360 {
361 executeSql(command, "create relation table");
362 }
363 }
364
368 template <typename SchemaType, typename T>
370 {
371 constexpr auto owner = model::modelView<SchemaType, T>();
372 const auto ownsJunctionTable =
373 std::ranges::any_of(owner->relations,
374 [](const auto& relation)
375 {
376 return relation.kind == model::RelationKind::ManyToMany and
377 relation.junction.isConfigured() and relation.junction.owningSide;
378 });
379
380 if (not ownsJunctionTable)
381 {
382 return;
383 }
384
385 ensureModelSupported(owner, "drop relation tables");
386 requireCapability(getBackendCapabilities().relations.junctionTables, "drop relation tables",
387 "junction tables are not supported");
388 requireCapability(getBackendCapabilities().schema.dropTableIfExists, "drop relation tables",
389 "idempotent table removal is not supported");
390
391 for (const auto& command : db::relations::dropTableStatements(getBackend().dialect(), owner))
392 {
393 executeSql(command, "drop relation table");
394 }
395 }
396
401 template <typename SchemaType, typename Owner, typename Target>
402 auto linkImpl(const Owner& owner, std::string_view relationField, const Target& target) -> std::size_t
403 {
404 constexpr auto ownerDescriptor = model::modelView<SchemaType, Owner>();
405 const auto* relation = ownerDescriptor.findRelation(relationField);
406
407 if (relation == nullptr or relation->kind == model::RelationKind::ToOne)
408 {
409 throw std::invalid_argument{"Unknown collection relation: " + std::string{relationField}};
410 }
411
412 const auto targetDescriptor = ownerDescriptor.resolveTarget(*relation);
413 if (targetDescriptor == nullptr or targetDescriptor->type != model::typeId<Target>())
414 {
415 throw std::invalid_argument{"Relation target type does not match mapping: " + std::string{relationField}};
416 }
417
418 ensureModelSupported(ownerDescriptor, "link relation");
419 ensureModelSupported(*targetDescriptor, "link relation");
420 const auto ownerKey = db::binding::getPrimaryKey<SchemaType>(owner);
421 const auto targetKey = db::binding::getPrimaryKey<SchemaType>(target);
422
423 if (relation->kind == model::RelationKind::OneToMany)
424 {
425 requireCapability(getBackendCapabilities().relations.oneToMany, "link relation",
426 "one-to-many relations are not supported");
427 }
428 else
429 {
430 requireCapability(getBackendCapabilities().relations.manyToMany, "link relation",
431 "many-to-many relations are not supported");
432 requireCapability(getBackendCapabilities().mutations.atomicInsertIfAbsent, "link relation",
433 "idempotent relation links are not supported");
434 }
435 requireCapability(relation->kind == model::RelationKind::OneToMany ? getBackendCapabilities().mutations.update :
436 getBackendCapabilities().mutations.insert,
437 "link relation", "relation mutations are not supported");
438
439 if (not relationEndpointExists(ownerDescriptor, ownerKey) or
440 not relationEndpointExists(*targetDescriptor, targetKey))
441 {
442 throw std::invalid_argument{"Cannot link relation endpoints that do not exist"};
443 }
444
445 return executeMutation(
446 db::relations::linkStatement(getBackend().dialect(), ownerDescriptor, *relation, ownerKey, targetKey),
447 "link relation");
448 }
449
454 template <typename SchemaType, typename Owner, typename Target>
455 auto unlinkImpl(const Owner& owner, std::string_view relationField, const Target& target) -> std::size_t
456 {
457 constexpr auto ownerDescriptor = model::modelView<SchemaType, Owner>();
458 const auto* relation = ownerDescriptor.findRelation(relationField);
459
460 if (relation == nullptr or relation->kind == model::RelationKind::ToOne)
461 {
462 throw std::invalid_argument{"Unknown collection relation: " + std::string{relationField}};
463 }
464
465 const auto targetDescriptor = ownerDescriptor.resolveTarget(*relation);
466 if (targetDescriptor == nullptr or targetDescriptor->type != model::typeId<Target>())
467 {
468 throw std::invalid_argument{"Relation target type does not match mapping: " + std::string{relationField}};
469 }
470
471 ensureModelSupported(ownerDescriptor, "unlink relation");
472 ensureModelSupported(*targetDescriptor, "unlink relation");
473 requireCapability(relation->kind == model::RelationKind::OneToMany ?
474 getBackendCapabilities().relations.oneToMany :
475 getBackendCapabilities().relations.manyToMany,
476 "unlink relation", "the requested collection relation is not supported");
477 requireCapability(relation->kind == model::RelationKind::OneToMany ? getBackendCapabilities().mutations.update :
478 getBackendCapabilities().mutations.remove,
479 "unlink relation", "relation mutations are not supported");
480
481 return executeMutation(db::relations::unlinkStatement(getBackend().dialect(), ownerDescriptor, *relation,
482 db::binding::getPrimaryKey<SchemaType>(owner),
483 db::binding::getPrimaryKey<SchemaType>(target)),
484 "unlink relation");
485 }
486
487public:
493 [[nodiscard]] auto getBackendType() const noexcept -> db::BackendType;
494
498 [[nodiscard]] auto isConnected() const noexcept -> bool;
499
504 [[nodiscard]] auto getBackendCapabilities() const -> const db::BackendCapabilities&;
505
509 auto beginTransaction() -> void;
510
514 auto commitTransaction() -> void;
515
519 auto rollbackTransaction() -> void;
520
521private:
522 template <typename SchemaType, typename Owner, typename Target, bool JoinedValues>
523 auto appendCollectionRows(const db::Statement& statement,
524 std::map<db::binding::PrimaryKey, std::vector<Target>>& groupedTargets) -> void
525 {
526 ensureStatementWithinBindLimit(statement.parameters.size(), "include collection");
527 soci::values parameterValues;
528 detail::bindStatementParameters(getBackend().runtime(), parameterValues, statement.parameters);
529 soci::rowset<db::binding::CollectionPayload<Owner, Target, SchemaType, JoinedValues>> preparedRowSet =
530 (sql.prepare << statement.sql, soci::use(parameterValues));
531
532 for (auto& payload : preparedRowSet)
533 {
534 groupedTargets[payload.ownerKey].push_back(std::move(payload.value));
535 }
536 }
537
538 template <typename SchemaType, typename Owner>
539 auto loadIncludedCollections(model::ModelView ownerDescriptor, const query::SelectSpec& queryData,
540 std::vector<Owner>& owners) -> void
541 {
542 if (queryData.includes.empty())
543 {
544 return;
545 }
546
547 for (const auto& includedRelation : queryData.includes)
548 {
549 const auto* relation = ownerDescriptor.findRelation(includedRelation);
550
551 if (relation == nullptr or relation->kind == model::RelationKind::ToOne)
552 {
553 throw std::invalid_argument{"Unknown collection relation: " + includedRelation};
554 }
555 }
556
557 if (owners.empty())
558 {
559 return;
560 }
561
562 constexpr auto reflectedFields = reflection::fields<Owner>();
563 auto ownerFields = reflection::fieldPointers(owners.front());
564 auto loadField = [this, &queryData, &owners, ownerDescriptor, &reflectedFields](auto fieldIndex, auto* field)
565 {
566 using collection_t = std::decay_t<decltype(*field)>;
567
568 if constexpr (orm::is_relation_collection_v<collection_t>)
569 {
570 const auto relationName = std::string{reflectedFields[fieldIndex].name};
571
572 if (std::ranges::find(queryData.includes, relationName) == queryData.includes.end())
573 {
574 return;
575 }
576
577 const auto* relation = ownerDescriptor.findRelation(relationName);
578 assert(relation != nullptr);
579
580 loadCollectionField<SchemaType, decltype(fieldIndex)::value, Owner, collection_t>(
581 ownerDescriptor, queryData, owners, *relation);
582 }
583 };
584
585 utils::constexpr_for_tuple(ownerFields, loadField);
586 }
587
588 template <typename SchemaType, std::size_t FieldIndex, typename Owner, typename Collection>
589 auto loadCollectionField(model::ModelView ownerDescriptor, const query::SelectSpec& queryData,
590 std::vector<Owner>& owners, const model::RelationView& relation) -> void
591 {
592 using target_t = orm::relation_target_t<Collection>;
593
594 const auto targetDescriptor = ownerDescriptor.resolveTarget(relation);
595 if (targetDescriptor == nullptr or targetDescriptor->type != model::typeId<target_t>())
596 {
597 throw std::invalid_argument{"Collection wrapper target does not match relation metadata: " +
598 std::string{relation.fieldName}};
599 }
600
601 const auto ownerPrimaryKey = db::binding::getPrimaryKeyColumns(ownerDescriptor);
602 const auto runtimeLimits = getBackendRuntimeLimits();
603 const auto parameterBudget = runtimeLimits.maxBindParameters.value_or(std::numeric_limits<std::size_t>::max());
604
605 if (parameterBudget < ownerPrimaryKey.size())
606 {
607 throw DatabaseError{DatabaseErrorCode::UnsupportedFeature, backendType, "include collection",
608 "The backend bind-parameter limit is too small for the relation primary key"};
609 }
610
611 const auto batchSize = runtimeLimits.maxBindParameters.has_value() ?
612 std::max<std::size_t>(1, parameterBudget / ownerPrimaryKey.size()) :
613 owners.size();
614 std::map<db::binding::PrimaryKey, std::vector<target_t>> groupedTargets;
615
616 orm::Query<target_t> targetQuery;
617
618 if (not queryData.shouldJoin)
619 {
620 targetQuery.disableJoining();
621 }
622
623 const auto baseTargetStatement = getCommandGenerator().select(*targetDescriptor, targetQuery.getData());
624
625 assert(baseTargetStatement.parameters.empty());
626
627 for (std::size_t batchStart = 0; batchStart < owners.size(); batchStart += batchSize)
628 {
629 const auto batchEnd = std::min(owners.size(), batchStart + batchSize);
630 std::vector<db::binding::PrimaryKey> ownerKeys;
631 ownerKeys.reserve(batchEnd - batchStart);
632
633 for (auto ownerIndex = batchStart; ownerIndex < batchEnd; ++ownerIndex)
634 {
635 ownerKeys.push_back(db::binding::getPrimaryKey<SchemaType>(owners[ownerIndex]));
636 }
637
638 const auto statement =
639 db::relations::collectionSelectStatement(getBackend().dialect(), ownerDescriptor, relation,
640 baseTargetStatement.sql, ownerKeys, queryData.shouldJoin);
641 if (queryData.shouldJoin)
642 {
643 appendCollectionRows<SchemaType, Owner, target_t, true>(statement, groupedTargets);
644 }
645 else
646 {
647 appendCollectionRows<SchemaType, Owner, target_t, false>(statement, groupedTargets);
648 }
649 }
650
651 for (auto& owner : owners)
652 {
653 const auto ownerKey = db::binding::getPrimaryKey<SchemaType>(owner);
654 auto ownerFields = reflection::fieldPointers(owner);
655 auto* collection = std::get<FieldIndex>(ownerFields);
656 const auto targets = groupedTargets.find(ownerKey);
657
658 if (targets == groupedTargets.end())
659 {
660 collection->setLoaded({});
661 }
662 else
663 {
664 collection->setLoaded(targets->second);
665 }
666 }
667 }
668
669 auto executeMutation(const db::Statement& statement, std::string_view operation) -> std::size_t;
670 auto executeSql(std::string_view statement, std::string_view operation) -> void;
671 auto relationEndpointExists(model::ModelView model, const db::binding::PrimaryKey& key) -> bool;
672 auto tableExists(std::string_view tableName) -> bool;
673 auto ensureRelationTableEndpointsExist(model::ModelView owner) -> void;
674 [[nodiscard]] auto getBackend() const -> const db::BackendProvider&;
675 [[nodiscard]] auto getCommandGenerator() const -> const db::CommandGenerator&;
676 [[nodiscard]] auto getBackendRuntimeLimits() -> db::BackendRuntimeLimits;
677 auto ensureStatementWithinBindLimit(std::size_t parameterCount, std::string_view operation) -> void;
678 auto ensureModelSupported(model::ModelView model, std::string_view operation) const -> void;
679 auto ensureQuerySupported(model::ModelView model, const query::SelectSpec& spec) const -> void;
680 auto ensureAffectedRowsAvailable(std::string_view operation) const -> void;
681 auto requireCapability(bool supported, std::string_view operation, std::string_view message) const -> void;
682 [[noreturn]] auto throwTranslatedError(const soci::soci_error& error, DatabaseErrorCode fallback,
683 std::string_view operation) -> void;
684
685 soci::session sql;
686 std::unique_ptr<soci::transaction> transaction;
687 bool transactionFailed = false;
688 db::BackendType backendType;
689 db::CommandGeneratorFactory commandGeneratorFactory;
690 const db::BackendProvider* backend = nullptr;
691};
692
702template <typename SchemaType>
703class Database final : public DatabaseCore
704{
705 static_assert(requires { SchemaType::view; }, "Database requires an orm::Schema<...> type");
706
707 template <typename T>
708 static consteval auto requireSchemaModel() -> void
709 {
710 model::requireSchemaModel<SchemaType, T>();
711 }
712
713public:
715
716 template <typename T>
717 using Payload = db::binding::BindingPayload<T, SchemaType>;
718
719 template <typename T>
720 using ProjectionPayload = db::binding::ProjectionPayload<T>;
721
722 template <typename T>
723 auto select(Query<T>& query) -> std::vector<T>
724 {
725 requireSchemaModel<T>();
726 return this->template selectImpl<SchemaType>(query);
727 }
728
729 template <typename Source, typename Result>
730 auto select(ProjectionQuery<Source, Result>& query) -> std::vector<Result>
731 {
732 requireSchemaModel<Source>();
733 return this->template selectImpl<SchemaType>(query);
734 }
735
736 template <typename T>
737 auto insert(const std::vector<T>& objects) -> void
738 {
739 requireSchemaModel<T>();
740 this->template insertImpl<SchemaType>(objects);
741 }
742
743 template <typename T>
744 auto insert(T object) -> void
745 {
746 requireSchemaModel<T>();
747 this->template insertImpl<SchemaType>(std::move(object));
748 }
749
750 template <typename T>
751 auto update(const Update<T>& update) -> std::size_t
752 {
753 requireSchemaModel<T>();
754 return this->template updateImpl<SchemaType>(update);
755 }
756
757 template <typename T>
758 auto remove(const query::Predicate& predicate) -> std::size_t
759 {
760 requireSchemaModel<T>();
761 return this->template removeImpl<SchemaType, T>(predicate);
762 }
763
764 template <typename T>
765 auto createTable() -> void
766 {
767 requireSchemaModel<T>();
768 this->template createTableImpl<SchemaType, T>();
769 }
770
771 template <typename T>
772 auto deleteTable() -> void
773 {
774 requireSchemaModel<T>();
775 this->template deleteTableImpl<SchemaType, T>();
776 }
777
778 template <typename T>
779 auto createRelationTables() -> void
780 {
781 requireSchemaModel<T>();
783 }
784
785 template <typename T>
786 auto deleteRelationTables() -> void
787 {
788 requireSchemaModel<T>();
790 }
791
792 template <typename Owner, typename Target>
793 auto link(const Owner& owner, std::string_view relationField, const Target& target) -> std::size_t
794 {
795 requireSchemaModel<Owner>();
796 requireSchemaModel<Target>();
797 return this->template linkImpl<SchemaType>(owner, relationField, target);
798 }
799
800 template <typename Owner, typename Target>
801 auto unlink(const Owner& owner, std::string_view relationField, const Target& target) -> std::size_t
802 {
803 requireSchemaModel<Owner>();
804 requireSchemaModel<Target>();
805 return this->template unlinkImpl<SchemaType>(owner, relationField, target);
806 }
807};
808} // namespace orm
auto selectImpl(Query< T > &query) -> std::vector< T >
Executes a select query and returns the result.
Definition database.hpp:109
auto unlinkImpl(const Owner &owner, std::string_view relationField, const Target &target) -> std::size_t
Removes a OneToMany/ManyToMany association.
Definition database.hpp:455
auto removeImpl(const query::Predicate &predicate) -> std::size_t
Executes a delete query for rows matching a predicate.
Definition database.hpp:291
auto deleteTableImpl() -> void
Execute a delete table query for a model.
Definition database.hpp:323
auto disconnect() -> void
Disconnects from the database.
auto isConnected() const noexcept -> bool
Returns whether a backend session is currently open.
auto updateImpl(const Update< T > &update) -> std::size_t
Executes an update query.
Definition database.hpp:273
auto insertImpl(const std::vector< T > &objects) -> void
Executes a insert query for multiple objects.
Definition database.hpp:207
DatabaseCore(db::CommandGeneratorFactory factory)
Constructs a database with an application-supplied backend registry.
auto connect(const std::string &connectionString) -> void
Connects to a database.
auto insertImpl(T object) -> void
Executes a insert query for a single object.
Definition database.hpp:222
auto rollbackTransaction() -> void
Rollbacks a transaction.
auto getBackendCapabilities() const -> const db::BackendCapabilities &
Returns the capabilities advertised by the connected backend.
auto selectImpl(ProjectionQuery< Source, Result > &query) -> std::vector< Result >
Executes a projection select query and returns DTO results.
Definition database.hpp:166
auto connect(db::BackendType requestedBackend, const std::string &connectionString) -> void
Connects using an explicitly selected registered backend.
DatabaseCore()
Constructs a new Database object.
auto beginTransaction() -> void
Starts a transaction.
auto commitTransaction() -> void
Commits a transaction.
auto deleteRelationTablesImpl() -> void
Drops junction tables owned by a model's ManyToMany mappings.
Definition database.hpp:369
auto createRelationTablesImpl() -> void
Creates junction tables owned by a model's ManyToMany mappings.
Definition database.hpp:340
auto linkImpl(const Owner &owner, std::string_view relationField, const Target &target) -> std::size_t
Creates or changes a OneToMany/ManyToMany association.
Definition database.hpp:402
auto getBackendType() const noexcept -> db::BackendType
Get the backend type of the database.
auto createTableImpl() -> void
Execute a create table query for a model.
Definition database.hpp:307
Database facade bound to one closed compile-time model schema.
Definition database.hpp:704
DatabaseCore()
Constructs a new Database object.
A select query that returns a user-defined DTO instead of the full source model.
Definition projection_query.hpp:149
A template class representing a select query in the ORM framework.
Definition query.hpp:26
Definition update.hpp:16