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 
6 
7 #include <iterator>
8 
9 #include "flutter/fml/logging.h"
14 
15 namespace impeller {
16 
17 Archive::Archive(const std::string& path)
18  : database_(std::make_unique<ArchiveDatabase>(path)) {}
19 
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