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[[nodiscard]] auto hasOwningJunction(model::ModelView owner) -> bool;
60[[nodiscard]] auto requireCollectionTarget(model::ModelView owner, const model::RelationView& relation,
61 model::TypeId expectedType) -> model::ModelView;
62} // namespace detail
63
70{
71public:
76
80 explicit DatabaseCore(db::CommandGeneratorFactory factory);
81 DatabaseCore(const DatabaseCore&) = delete;
82 DatabaseCore(DatabaseCore&&) = delete;
83 auto operator=(const DatabaseCore&) -> DatabaseCore& = delete;
84 auto operator=(DatabaseCore&&) -> DatabaseCore& = delete;
85
91 auto connect(const std::string& connectionString) -> void;
92
96 auto connect(db::BackendType requestedBackend, const std::string& connectionString) -> void;
97
101 auto disconnect() -> void;
102
110protected:
111 template <typename SchemaType, typename T>
112 auto selectImpl(Query<T>& query) -> std::vector<T>
113 {
114 constexpr auto model = model::modelView<SchemaType, T>();
115 ensureQuerySupported(model, query.getData());
116 std::vector<T> result;
117 const auto statement = getCommandGenerator().select(model, query.getData());
118 ensureStatementWithinBindLimit(statement.parameters.size(), "select");
119
120 try
121 {
122 soci::values parameterValues;
123 detail::bindStatementParameters(getBackend().runtime(), parameterValues, statement.parameters);
124
125 auto readRows = [&]<bool JoinedValues>()
126 {
127 soci::rowset<db::binding::BindingPayload<T, SchemaType, JoinedValues>> preparedRowSet =
128 (sql.prepare << statement.sql, soci::use(parameterValues));
129
130 for (auto& payload : preparedRowSet)
131 {
132 result.push_back(std::move(payload.value));
133 }
134 };
135
136 if (query.getData().shouldJoin)
137 {
138 readRows.template operator()<true>();
139 }
140 else
141 {
142 readRows.template operator()<false>();
143 }
144
145 loadIncludedCollections<SchemaType>(model, query.getData(), result);
146 }
147 catch (const db::binding::ConversionError&)
148 {
149 throw DatabaseError{DatabaseErrorCode::Conversion, backendType, "select",
150 "A database result cannot be represented by the requested model"};
151 }
152 catch (const soci::soci_error& error)
153 {
154 throwTranslatedError(error, DatabaseErrorCode::Statement, "select");
155 }
156
157 return result;
158 }
159
168 template <typename SchemaType, typename Source, typename Result>
169 auto selectImpl(ProjectionQuery<Source, Result>& query) -> std::vector<Result>
170 {
171 constexpr auto model = model::modelView<SchemaType, Source>();
172 ensureQuerySupported(model, query.getData());
173 std::vector<Result> result;
174 const auto statement = getCommandGenerator().select(model, query.getData());
175 ensureStatementWithinBindLimit(statement.parameters.size(), "select projection");
176
177 try
178 {
179 soci::values parameterValues;
180 detail::bindStatementParameters(getBackend().runtime(), parameterValues, statement.parameters);
181
182 soci::rowset<db::binding::ProjectionPayload<Result>> preparedRowSet =
183 (sql.prepare << statement.sql, soci::use(parameterValues));
184
185 for (auto& payload : preparedRowSet)
186 {
187 result.push_back(std::move(payload.value));
188 }
189 }
190 catch (const db::binding::ConversionError&)
191 {
192 throw DatabaseError{DatabaseErrorCode::Conversion, backendType, "select projection",
193 "A database result cannot be represented by the requested projection"};
194 }
195 catch (const soci::soci_error& error)
196 {
197 throwTranslatedError(error, DatabaseErrorCode::Statement, "select projection");
198 }
199
200 return result;
201 }
202
209 template <typename SchemaType, typename T>
210 auto insertImpl(const std::vector<T>& objects) -> void
211 {
212 for (const auto& object : objects)
213 {
215 }
216 }
217
224 template <typename SchemaType, typename T>
225 auto insertImpl(T object) -> void
226 {
227 constexpr auto model = model::modelView<SchemaType, T>();
228 ensureModelSupported(model, "insert");
229 requireCapability(getBackendCapabilities().mutations.insert, "insert", "insert is not supported");
230 const auto command = getCommandGenerator().insert(model);
231
232 const db::binding::BindingPayload<T, SchemaType> payload{};
233
234 payload.value = std::move(object);
235
236 try
237 {
238 soci::values serializedModel;
239 auto indicator = soci::i_ok;
240 soci::type_conversion<db::binding::BindingPayload<T, SchemaType>>::to_base(payload, serializedModel,
241 indicator);
242
243 soci::values parameterValues;
244 const auto parameterCount =
245 detail::bindModelParameters(getBackend().runtime(), parameterValues, serializedModel, model);
246 ensureStatementWithinBindLimit(parameterCount, "insert");
247
248 if (parameterCount == 0)
249 {
250 sql << command;
251 }
252 else
253 {
254 sql << command, soci::use(parameterValues);
255 }
256 }
257 catch (const db::binding::ConversionError&)
258 {
259 throw DatabaseError{DatabaseErrorCode::Conversion, backendType, "insert",
260 "A model value cannot be represented by the selected backend"};
261 }
262 catch (const soci::soci_error& error)
263 {
264 throwTranslatedError(error, DatabaseErrorCode::Statement, "insert");
265 }
266 }
267
275 template <typename SchemaType, typename T>
276 auto updateImpl(const Update<T>& update) -> std::size_t
277 {
278 constexpr auto model = model::modelView<SchemaType, T>();
279 ensureModelSupported(model, "update");
280 requireCapability(getBackendCapabilities().mutations.update, "update", "update is not supported");
281 const auto statement = getCommandGenerator().update(model, update.getData());
282
283 return executeMutation(statement, "update");
284 }
285
293 template <typename SchemaType, typename T>
294 auto removeImpl(const query::Predicate& predicate) -> std::size_t
295 {
296 constexpr auto model = model::modelView<SchemaType, T>();
297 ensureModelSupported(model, "remove");
298 requireCapability(getBackendCapabilities().mutations.remove, "remove", "remove is not supported");
299 const auto statement = getCommandGenerator().remove(model, predicate);
300
301 return executeMutation(statement, "remove");
302 }
303
309 template <typename SchemaType, typename T>
310 auto createTableImpl() -> void
311 {
312 constexpr auto model = model::modelView<SchemaType, T>();
313 ensureModelSupported(model, "create table");
314 requireCapability(getBackendCapabilities().schema.createTableIfNotExists, "create table",
315 "idempotent table creation is not supported");
316 const auto command = getCommandGenerator().createTable(model);
317 executeSql(command, "create table");
318 }
319
325 template <typename SchemaType, typename T>
326 auto deleteTableImpl() -> void
327 {
328 constexpr auto model = model::modelView<SchemaType, T>();
329 ensureModelSupported(model, "drop table");
330 requireCapability(getBackendCapabilities().schema.dropTableIfExists, "drop table",
331 "idempotent table removal is not supported");
332 const auto command = getCommandGenerator().dropTable(model);
333 executeSql(command, "drop table");
334 }
335
342 template <typename SchemaType, typename T>
344 {
345 const auto owner = model::modelView<SchemaType, T>();
346 const auto ownsJunctionTable = detail::hasOwningJunction(owner);
347
348 if (not ownsJunctionTable)
349 {
350 return;
351 }
352
353 ensureModelSupported(owner, "create relation tables");
354 ensureRelationTableEndpointsExist(owner);
355
356 for (const auto& command : db::relations::createTableStatements(getBackend().dialect(), owner))
357 {
358 executeSql(command, "create relation table");
359 }
360 }
361
365 template <typename SchemaType, typename T>
367 {
368 const auto owner = model::modelView<SchemaType, T>();
369 const auto ownsJunctionTable = detail::hasOwningJunction(owner);
370
371 if (not ownsJunctionTable)
372 {
373 return;
374 }
375
376 ensureModelSupported(owner, "drop relation tables");
377 requireCapability(getBackendCapabilities().relations.junctionTables, "drop relation tables",
378 "junction tables are not supported");
379 requireCapability(getBackendCapabilities().schema.dropTableIfExists, "drop relation tables",
380 "idempotent table removal is not supported");
381
382 for (const auto& command : db::relations::dropTableStatements(getBackend().dialect(), owner))
383 {
384 executeSql(command, "drop relation table");
385 }
386 }
387
392 template <typename SchemaType, typename Owner, typename Target>
393 auto linkImpl(const Owner& owner, std::string_view relationField, const Target& target) -> std::size_t
394 {
395 constexpr auto ownerDescriptor = model::modelView<SchemaType, Owner>();
396 const auto* relation = ownerDescriptor.findRelation(relationField);
397
398 if (relation == nullptr or relation->kind == model::RelationKind::ToOne)
399 {
400 throw std::invalid_argument{"Unknown collection relation: " + std::string{relationField}};
401 }
402
403 const auto targetDescriptor = ownerDescriptor.resolveTarget(*relation);
404 if (targetDescriptor == nullptr or targetDescriptor->type != model::typeId<Target>())
405 {
406 throw std::invalid_argument{"Relation target type does not match mapping: " + std::string{relationField}};
407 }
408
409 ensureModelSupported(ownerDescriptor, "link relation");
410 ensureModelSupported(*targetDescriptor, "link relation");
411 const auto ownerKey = db::binding::getPrimaryKey<SchemaType>(owner);
412 const auto targetKey = db::binding::getPrimaryKey<SchemaType>(target);
413
414 if (relation->kind == model::RelationKind::OneToMany)
415 {
416 requireCapability(getBackendCapabilities().relations.oneToMany, "link relation",
417 "one-to-many relations are not supported");
418 }
419 else
420 {
421 requireCapability(getBackendCapabilities().relations.manyToMany, "link relation",
422 "many-to-many relations are not supported");
423 requireCapability(getBackendCapabilities().mutations.atomicInsertIfAbsent, "link relation",
424 "idempotent relation links are not supported");
425 }
426 requireCapability(relation->kind == model::RelationKind::OneToMany ? getBackendCapabilities().mutations.update :
427 getBackendCapabilities().mutations.insert,
428 "link relation", "relation mutations are not supported");
429
430 if (not relationEndpointExists(ownerDescriptor, ownerKey) or
431 not relationEndpointExists(*targetDescriptor, targetKey))
432 {
433 throw std::invalid_argument{"Cannot link relation endpoints that do not exist"};
434 }
435
436 return executeMutation(
437 db::relations::linkStatement(getBackend().dialect(), ownerDescriptor, *relation, ownerKey, targetKey),
438 "link relation");
439 }
440
445 template <typename SchemaType, typename Owner, typename Target>
446 auto unlinkImpl(const Owner& owner, std::string_view relationField, const Target& target) -> std::size_t
447 {
448 constexpr auto ownerDescriptor = model::modelView<SchemaType, Owner>();
449 const auto* relation = ownerDescriptor.findRelation(relationField);
450
451 if (relation == nullptr or relation->kind == model::RelationKind::ToOne)
452 {
453 throw std::invalid_argument{"Unknown collection relation: " + std::string{relationField}};
454 }
455
456 const auto targetDescriptor = ownerDescriptor.resolveTarget(*relation);
457 if (targetDescriptor == nullptr or targetDescriptor->type != model::typeId<Target>())
458 {
459 throw std::invalid_argument{"Relation target type does not match mapping: " + std::string{relationField}};
460 }
461
462 ensureModelSupported(ownerDescriptor, "unlink relation");
463 ensureModelSupported(*targetDescriptor, "unlink relation");
464 requireCapability(relation->kind == model::RelationKind::OneToMany ?
465 getBackendCapabilities().relations.oneToMany :
466 getBackendCapabilities().relations.manyToMany,
467 "unlink relation", "the requested collection relation is not supported");
468 requireCapability(relation->kind == model::RelationKind::OneToMany ? getBackendCapabilities().mutations.update :
469 getBackendCapabilities().mutations.remove,
470 "unlink relation", "relation mutations are not supported");
471
472 return executeMutation(db::relations::unlinkStatement(getBackend().dialect(), ownerDescriptor, *relation,
473 db::binding::getPrimaryKey<SchemaType>(owner),
474 db::binding::getPrimaryKey<SchemaType>(target)),
475 "unlink relation");
476 }
477
478public:
484 [[nodiscard]] auto getBackendType() const noexcept -> db::BackendType;
485
489 [[nodiscard]] auto isConnected() const noexcept -> bool;
490
495 [[nodiscard]] auto getBackendCapabilities() const -> const db::BackendCapabilities&;
496
500 auto beginTransaction() -> void;
501
505 auto commitTransaction() -> void;
506
510 auto rollbackTransaction() -> void;
511
512private:
513 template <typename SchemaType, typename Owner, typename Target, bool JoinedValues>
514 auto appendCollectionRows(const db::Statement& statement,
515 std::map<db::binding::PrimaryKey, std::vector<Target>>& groupedTargets) -> void
516 {
517 ensureStatementWithinBindLimit(statement.parameters.size(), "include collection");
518 soci::values parameterValues;
519 detail::bindStatementParameters(getBackend().runtime(), parameterValues, statement.parameters);
520 soci::rowset<db::binding::CollectionPayload<Owner, Target, SchemaType, JoinedValues>> preparedRowSet =
521 (sql.prepare << statement.sql, soci::use(parameterValues));
522
523 for (auto& payload : preparedRowSet)
524 {
525 groupedTargets[payload.ownerKey].push_back(std::move(payload.value));
526 }
527 }
528
529 template <typename SchemaType, typename Owner>
530 auto loadIncludedCollections(model::ModelView ownerDescriptor, const query::SelectSpec& queryData,
531 std::vector<Owner>& owners) -> void
532 {
533 if (queryData.includes.empty())
534 {
535 return;
536 }
537
538 for (const auto& includedRelation : queryData.includes)
539 {
540 const auto* relation = ownerDescriptor.findRelation(includedRelation);
541
542 if (relation == nullptr or relation->kind == model::RelationKind::ToOne)
543 {
544 throw std::invalid_argument{"Unknown collection relation: " + includedRelation};
545 }
546 }
547
548 if (owners.empty())
549 {
550 return;
551 }
552
553 constexpr auto reflectedFields = reflection::fields<Owner>();
554 auto ownerFields = reflection::fieldPointers(owners.front());
555 auto loadField = [this, &queryData, &owners, ownerDescriptor, &reflectedFields](auto fieldIndex, auto* field)
556 {
557 using collection_t = std::decay_t<decltype(*field)>;
558
559 if constexpr (orm::is_relation_collection_v<collection_t>)
560 {
561 const auto relationName = std::string{reflectedFields[fieldIndex].name};
562
563 if (std::ranges::find(queryData.includes, relationName) == queryData.includes.end())
564 {
565 return;
566 }
567
568 const auto* relation = ownerDescriptor.findRelation(relationName);
569 assert(relation != nullptr);
570
571 loadCollectionField<SchemaType, decltype(fieldIndex)::value, Owner, collection_t>(
572 ownerDescriptor, queryData, owners, *relation);
573 }
574 };
575
576 utils::constexpr_for_tuple(ownerFields, loadField);
577 }
578
579 template <typename SchemaType, std::size_t FieldIndex, typename Owner, typename Collection>
580 auto loadCollectionField(model::ModelView ownerDescriptor, const query::SelectSpec& queryData,
581 std::vector<Owner>& owners, const model::RelationView& relation) -> void
582 {
583 using target_t = orm::relation_target_t<Collection>;
584
585 const auto targetDescriptor =
586 detail::requireCollectionTarget(ownerDescriptor, relation, model::typeId<target_t>());
587
588 const auto ownerPrimaryKey = db::binding::getPrimaryKeyColumns(ownerDescriptor);
589 const auto runtimeLimits = getBackendRuntimeLimits();
590 const auto parameterBudget = runtimeLimits.maxBindParameters.value_or(std::numeric_limits<std::size_t>::max());
591
592 if (parameterBudget < ownerPrimaryKey.size())
593 {
594 throw DatabaseError{DatabaseErrorCode::UnsupportedFeature, backendType, "include collection",
595 "The backend bind-parameter limit is too small for the relation primary key"};
596 }
597
598 const auto batchSize = runtimeLimits.maxBindParameters.has_value() ?
599 std::max<std::size_t>(1, parameterBudget / ownerPrimaryKey.size()) :
600 owners.size();
601 std::map<db::binding::PrimaryKey, std::vector<target_t>> groupedTargets;
602
603 orm::Query<target_t> targetQuery;
604
605 if (not queryData.shouldJoin)
606 {
607 targetQuery.disableJoining();
608 }
609
610 const auto baseTargetStatement = getCommandGenerator().select(*targetDescriptor, targetQuery.getData());
611
612 assert(baseTargetStatement.parameters.empty());
613
614 for (std::size_t batchStart = 0; batchStart < owners.size(); batchStart += batchSize)
615 {
616 const auto batchEnd = std::min(owners.size(), batchStart + batchSize);
617 std::vector<db::binding::PrimaryKey> ownerKeys;
618 ownerKeys.reserve(batchEnd - batchStart);
619
620 for (auto ownerIndex = batchStart; ownerIndex < batchEnd; ++ownerIndex)
621 {
622 ownerKeys.push_back(db::binding::getPrimaryKey<SchemaType>(owners[ownerIndex]));
623 }
624
625 const auto statement =
626 db::relations::collectionSelectStatement(getBackend().dialect(), ownerDescriptor, relation,
627 baseTargetStatement.sql, ownerKeys, queryData.shouldJoin);
628 if (queryData.shouldJoin)
629 {
630 appendCollectionRows<SchemaType, Owner, target_t, true>(statement, groupedTargets);
631 }
632 else
633 {
634 appendCollectionRows<SchemaType, Owner, target_t, false>(statement, groupedTargets);
635 }
636 }
637
638 for (auto& owner : owners)
639 {
640 const auto ownerKey = db::binding::getPrimaryKey<SchemaType>(owner);
641 auto ownerFields = reflection::fieldPointers(owner);
642 auto* collection = std::get<FieldIndex>(ownerFields);
643 const auto targets = groupedTargets.find(ownerKey);
644
645 if (targets == groupedTargets.end())
646 {
647 collection->setLoaded({});
648 }
649 else
650 {
651 collection->setLoaded(targets->second);
652 }
653 }
654 }
655
656 auto executeMutation(const db::Statement& statement, std::string_view operation) -> std::size_t;
657 auto executeSql(std::string_view statement, std::string_view operation) -> void;
658 auto relationEndpointExists(model::ModelView model, const db::binding::PrimaryKey& key) -> bool;
659 auto tableExists(std::string_view tableName) -> bool;
660 auto ensureRelationTableEndpointsExist(model::ModelView owner) -> void;
661 [[nodiscard]] auto getBackend() const -> const db::BackendProvider&;
662 [[nodiscard]] auto getCommandGenerator() const -> const db::CommandGenerator&;
663 [[nodiscard]] auto getBackendRuntimeLimits() -> db::BackendRuntimeLimits;
664 auto ensureStatementWithinBindLimit(std::size_t parameterCount, std::string_view operation) -> void;
665 auto ensureModelSupported(model::ModelView model, std::string_view operation) const -> void;
666 auto ensureQuerySupported(model::ModelView model, const query::SelectSpec& spec) const -> void;
667 auto ensureAffectedRowsAvailable(std::string_view operation) const -> void;
668 auto requireCapability(bool supported, std::string_view operation, std::string_view message) const -> void;
669 [[noreturn]] auto throwTranslatedError(const soci::soci_error& error, DatabaseErrorCode fallback,
670 std::string_view operation) -> void;
671
672 soci::session sql;
673 std::unique_ptr<soci::transaction> transaction;
674 bool transactionFailed = false;
675 db::BackendType backendType;
676 db::CommandGeneratorFactory commandGeneratorFactory;
677 const db::BackendProvider* backend = nullptr;
678};
679
689template <typename SchemaType>
690class Database final : public DatabaseCore
691{
692 static_assert(requires { SchemaType::view; }, "Database requires an orm::Schema<...> type");
693
694 template <typename T>
695 static consteval auto requireSchemaModel() -> void
696 {
697 model::requireSchemaModel<SchemaType, T>();
698 }
699
700public:
702
703 template <typename T>
704 using Payload = db::binding::BindingPayload<T, SchemaType>;
705
706 template <typename T>
707 using ProjectionPayload = db::binding::ProjectionPayload<T>;
708
709 template <typename T>
710 auto select(Query<T>& query) -> std::vector<T>
711 {
712 requireSchemaModel<T>();
713 return this->template selectImpl<SchemaType>(query);
714 }
715
716 template <typename Source, typename Result>
717 auto select(ProjectionQuery<Source, Result>& query) -> std::vector<Result>
718 {
719 requireSchemaModel<Source>();
720 return this->template selectImpl<SchemaType>(query);
721 }
722
723 template <typename T>
724 auto insert(const std::vector<T>& objects) -> void
725 {
726 requireSchemaModel<T>();
727 this->template insertImpl<SchemaType>(objects);
728 }
729
730 template <typename T>
731 auto insert(T object) -> void
732 {
733 requireSchemaModel<T>();
734 this->template insertImpl<SchemaType>(std::move(object));
735 }
736
737 template <typename T>
738 auto update(const Update<T>& update) -> std::size_t
739 {
740 requireSchemaModel<T>();
741 return this->template updateImpl<SchemaType>(update);
742 }
743
744 template <typename T>
745 auto remove(const query::Predicate& predicate) -> std::size_t
746 {
747 requireSchemaModel<T>();
748 return this->template removeImpl<SchemaType, T>(predicate);
749 }
750
751 template <typename T>
752 auto createTable() -> void
753 {
754 requireSchemaModel<T>();
755 this->template createTableImpl<SchemaType, T>();
756 }
757
758 template <typename T>
759 auto deleteTable() -> void
760 {
761 requireSchemaModel<T>();
762 this->template deleteTableImpl<SchemaType, T>();
763 }
764
765 template <typename T>
766 auto createRelationTables() -> void
767 {
768 requireSchemaModel<T>();
770 }
771
772 template <typename T>
773 auto deleteRelationTables() -> void
774 {
775 requireSchemaModel<T>();
777 }
778
779 template <typename Owner, typename Target>
780 auto link(const Owner& owner, std::string_view relationField, const Target& target) -> std::size_t
781 {
782 requireSchemaModel<Owner>();
783 requireSchemaModel<Target>();
784 return this->template linkImpl<SchemaType>(owner, relationField, target);
785 }
786
787 template <typename Owner, typename Target>
788 auto unlink(const Owner& owner, std::string_view relationField, const Target& target) -> std::size_t
789 {
790 requireSchemaModel<Owner>();
791 requireSchemaModel<Target>();
792 return this->template unlinkImpl<SchemaType>(owner, relationField, target);
793 }
794};
795} // namespace orm
auto selectImpl(Query< T > &query) -> std::vector< T >
Executes a select query and returns the result.
Definition database.hpp:112
auto unlinkImpl(const Owner &owner, std::string_view relationField, const Target &target) -> std::size_t
Removes a OneToMany/ManyToMany association.
Definition database.hpp:446
auto removeImpl(const query::Predicate &predicate) -> std::size_t
Executes a delete query for rows matching a predicate.
Definition database.hpp:294
auto deleteTableImpl() -> void
Execute a delete table query for a model.
Definition database.hpp:326
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:276
auto insertImpl(const std::vector< T > &objects) -> void
Executes a insert query for multiple objects.
Definition database.hpp:210
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:225
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:169
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:366
auto createRelationTablesImpl() -> void
Creates junction tables owned by a model's ManyToMany mappings.
Definition database.hpp:343
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:393
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:310
Database facade bound to one closed compile-time model schema.
Definition database.hpp:691
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