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 "flutter/fml/logging.h"
11 
12 namespace impeller {
13 
14 Archive::Archive(const std::string& path)
15  : database_(std::make_unique<ArchiveDatabase>(path)) {}
16 
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