| Flutter Impeller |

archive.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "impeller/archivist/archive.h"
6
7 #include <iterator>
8
9 #include "flutter/fml/logging.h"
10 #include "impeller/archivist/archive_class_registration.h"
11 #include "impeller/archivist/archive_database.h"
12 #include "impeller/archivist/archive_location.h"
13 #include "impeller/base/validation.h"
14
15 namespace impeller {
16
17 Archive::Archive(const std::string& path)
18 : database_(std::make_unique<ArchiveDatabase>(path)) {}
19
20 Archive::~Archive() {
21 FML_DCHECK(transaction_count_ == 0)
22 << "There must be no pending transactions";
23 }
24
25 bool Archive::IsValid() const {
26 return database_->IsValid();
27 }
28
29 std::optional<int64_t /* row id */> Archive::ArchiveInstance(
30 const ArchiveDef& definition,
31 const Archivable& archivable) {
32 if (!IsValid()) {
33 return std::nullopt;
34 }
35
36 auto transaction = database_->CreateTransaction(transaction_count_);
37
38 const auto* registration =
39 database_->GetRegistrationForDefinition(definition);
40
41 if (registration == nullptr) {
42 return std::nullopt;
43 }
44
45 auto statement = registration->CreateInsertStatement();
46
47 if (!statement.IsValid() || !statement.Reset()) {
48 /*
49 * Must be able to reset the statement for a new write
50 */
51 return std::nullopt;
52 }
53
54 auto primary_key = archivable.GetPrimaryKey();
55
56 /*
57 * The lifecycle of the archive item is tied to this scope and there is no
58 * way for the user to create an instance of an archive item. So its safe
59 * for its members to be references. It does not manage the lifetimes of
60 * anything.
61 */
62 ArchiveLocation item(*this, statement, *registration, primary_key);
63
64 /*
65 * If the item provides its own primary key, we need to bind it now.
66 * Otherwise, one will be automatically assigned to it.
67 */
68 if (primary_key.has_value() &&
69 !statement.WriteValue(ArchiveClassRegistration::kPrimaryKeyIndex,
70 primary_key.value())) {
71 return std::nullopt;
72 }
73
74 if (!archivable.Write(item)) {
75 return std::nullopt;
76 }
77
78 if (statement.Execute() != ArchiveStatement::Result::kDone) {
79 return std::nullopt;
80 }
81
82 int64_t lastInsert = database_->GetLastInsertRowID();
83
84 if (primary_key.has_value() &&
85 lastInsert != static_cast<int64_t>(primary_key.value())) {
86 return std::nullopt;
87 }
88
89 /*
90 * If any of the nested calls fail, we would have already checked for the
91 * failure and returned.
92 */
93 transaction.MarkWritesAsReadyForCommit();
94
95 return lastInsert;
96 }
97
98 bool Archive::UnarchiveInstance(const ArchiveDef& definition,
99 PrimaryKey name,
100 Archivable& archivable) {
101 UnarchiveStep stepper = [&archivable](ArchiveLocation& item) {
102 archivable.Read(item);
103 return false /* no-more after single read */;
104 };
105
106 return UnarchiveInstances(definition, stepper, name) == 1;
107 }
108
109 size_t Archive::UnarchiveInstances(const ArchiveDef& definition,
110 const Archive::UnarchiveStep& stepper,
111 PrimaryKey primary_key) {
112 if (!IsValid()) {
113 return 0;
114 }
115
116 const auto* registration =
117 database_->GetRegistrationForDefinition(definition);
118
119 if (registration == nullptr) {
120 return 0;
121 }
122
123 const bool isQueryingSingle = primary_key.has_value();
124
125 auto statement = registration->CreateQueryStatement(isQueryingSingle);
126
127 if (!statement.IsValid() || !statement.Reset()) {
128 return 0;
129 }
130
131 if (isQueryingSingle) {
132 /*
133 * If a single statement is being queried for, bind the primary key as a
134 * statement argument.
135 */
136 if (!statement.WriteValue(ArchiveClassRegistration::kPrimaryKeyIndex,
137 primary_key.value())) {
138 return 0;
139 }
140 }
141
142 if (statement.GetColumnCount() !=
143 registration->GetMemberCount() + 1 /* primary key */) {
144 return 0;
145 }
146
147 /*
148 * Acquire a transaction but never mark it successful since we will never
149 * be committing any writes to the database during unarchiving.
150 */
151 auto transaction = database_->CreateTransaction(transaction_count_);
152
153 size_t itemsRead = 0;
154
155 while (statement.Execute() == ArchiveStatement::Result::kRow) {
156 itemsRead++;
157
158 /*
159 * Prepare a fresh archive item for the given statement
160 */
161 ArchiveLocation item(*this, statement, *registration, primary_key);
162
163 if (!stepper(item)) {
164 break;
165 }
166
167 if (isQueryingSingle) {
168 break;
169 }
170 }
171
172 return itemsRead;
173 }
174
175 } // namespace impeller
impeller::Archivable
Instances of Archivables can be read from and written to a persistent archive.
Definition: archivable.h:27
archive_location.h
impeller::ArchiveDef
Definition: archivable.h:14
impeller::PrimaryKey
std::optional< int64_t > PrimaryKey
Definition: archivable.h:21
impeller::Archive::UnarchiveStep
std::function< bool(ArchiveLocation &)> UnarchiveStep
Definition: archive.h:44
validation.h
impeller::Archivable::Write
virtual bool Write(ArchiveLocation &item) const =0
impeller::ArchiveClassRegistration::kPrimaryKeyIndex
static constexpr size_t kPrimaryKeyIndex
Definition: archive_class_registration.h:18
impeller::Archive::~Archive
~Archive()
Definition: archive.cc:20
impeller::ArchiveStatement::Result::kDone
@ kDone
impeller::ArchiveDatabase
A handle to the underlying database connection for an archive.
Definition: archive_database.h:23
impeller::Archive::IsValid
bool IsValid() const
Definition: archive.cc:25
archive_class_registration.h
impeller::Archivable::GetPrimaryKey
virtual PrimaryKey GetPrimaryKey() const =0
archive_database.h
archive.h
std
Definition: comparable.h:98
impeller::ArchiveStatement::Result::kRow
@ kRow
impeller::Archive::Archive
Archive(const std::string &path)
Definition: archive.cc:17
impeller::Archive::ArchiveLocation
friend class ArchiveLocation
Definition: archive.h:57
impeller
Definition: aiks_context.cc:10