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