• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (C) 2023 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #ifndef ICING_FILE_VERSION_UTIL_H_
16 #define ICING_FILE_VERSION_UTIL_H_
17 
18 #include <cstdint>
19 #include <memory>
20 #include <string>
21 #include <string_view>
22 
23 #include "icing/text_classifier/lib3/utils/base/status.h"
24 #include "icing/text_classifier/lib3/utils/base/statusor.h"
25 #include "icing/absl_ports/str_cat.h"
26 #include "icing/file/filesystem.h"
27 #include "icing/proto/initialize.pb.h"
28 
29 namespace icing {
30 namespace lib {
31 
32 namespace version_util {
33 
34 // - Version 0: Android T base. Can be identified only by flash index magic.
35 // - Version 1: Android U base and M-2023-08.
36 // - Version 2: M-2023-09, M-2023-11, M-2024-01. Schema is compatible with v1.
37 //   (There were no M-2023-10, M-2023-12).
38 // - Version 3: M-2024-02. Schema is compatible with v1 and v2.
39 // - Version 4: Android V base. Schema is compatible with v1, v2 and v3.
40 //
41 // TODO(b/314816301): Bump kVersion to 4 for Android V rollout with v2 version
42 // detection
43 // LINT.IfChange(kVersion)
44 inline static constexpr int32_t kVersion = 4;
45 // LINT.ThenChange(//depot/google3/icing/schema/schema-store.cc:min_overlay_version_compatibility)
46 inline static constexpr int32_t kVersionOne = 1;
47 inline static constexpr int32_t kVersionTwo = 2;
48 inline static constexpr int32_t kVersionThree = 3;
49 inline static constexpr int32_t kVersionFour = 4;
50 
51 // Version at which v2 version file is introduced.
52 inline static constexpr int32_t kFirstV2Version = kVersionFour;
53 
54 inline static constexpr int kVersionZeroFlashIndexMagic = 0x6dfba6ae;
55 
56 inline static constexpr std::string_view kVersionFilenameV1 = "version";
57 inline static constexpr std::string_view kVersionFilenameV2 = "version2";
58 
59 struct VersionInfo {
60   int32_t version;
61   int32_t max_version;
62 
VersionInfoVersionInfo63   explicit VersionInfo(int32_t version_in, int32_t max_version_in)
64       : version(version_in), max_version(max_version_in) {}
65 
IsValidVersionInfo66   bool IsValid() const { return version >= 0 && max_version >= 0; }
67 
68   bool operator==(const VersionInfo& other) const {
69     return version == other.version && max_version == other.max_version;
70   }
71 } __attribute__((packed));
72 static_assert(sizeof(VersionInfo) == 8, "");
73 
74 enum class StateChange {
75   kUndetermined,
76   kCompatible,
77   kRollForward,
78   kRollBack,
79   kUpgrade,
80   kVersionZeroUpgrade,
81   kVersionZeroRollForward,
82 };
83 
84 // Contains information about which derived files need to be rebuild.
85 //
86 // These flags only reflect whether each component should be rebuilt, but do not
87 // handle any dependencies. The caller should handle the dependencies by
88 // themselves.
89 // e.g. - qualified id join index depends on document store derived files, but
90 //        it's possible to have needs_document_store_derived_files_rebuild =
91 //        true and needs_qualified_id_join_index_rebuild = false.
92 //      - The caller should know that join index should also be rebuilt in this
93 //        case even though needs_qualified_id_join_index_rebuild = false.
94 struct DerivedFilesRebuildResult {
95   bool needs_document_store_derived_files_rebuild = false;
96   bool needs_schema_store_derived_files_rebuild = false;
97   bool needs_term_index_rebuild = false;
98   bool needs_integer_index_rebuild = false;
99   bool needs_qualified_id_join_index_rebuild = false;
100 
101   DerivedFilesRebuildResult() = default;
102 
DerivedFilesRebuildResultDerivedFilesRebuildResult103   explicit DerivedFilesRebuildResult(
104       bool needs_document_store_derived_files_rebuild_in,
105       bool needs_schema_store_derived_files_rebuild_in,
106       bool needs_term_index_rebuild_in, bool needs_integer_index_rebuild_in,
107       bool needs_qualified_id_join_index_rebuild_in)
108       : needs_document_store_derived_files_rebuild(
109             needs_document_store_derived_files_rebuild_in),
110         needs_schema_store_derived_files_rebuild(
111             needs_schema_store_derived_files_rebuild_in),
112         needs_term_index_rebuild(needs_term_index_rebuild_in),
113         needs_integer_index_rebuild(needs_integer_index_rebuild_in),
114         needs_qualified_id_join_index_rebuild(
115             needs_qualified_id_join_index_rebuild_in) {}
116 
IsRebuildNeededDerivedFilesRebuildResult117   bool IsRebuildNeeded() const {
118     return needs_document_store_derived_files_rebuild ||
119            needs_schema_store_derived_files_rebuild ||
120            needs_term_index_rebuild || needs_integer_index_rebuild ||
121            needs_qualified_id_join_index_rebuild;
122   }
123 
124   bool operator==(const DerivedFilesRebuildResult& other) const {
125     return needs_document_store_derived_files_rebuild ==
126                other.needs_document_store_derived_files_rebuild &&
127            needs_schema_store_derived_files_rebuild ==
128                other.needs_schema_store_derived_files_rebuild &&
129            needs_term_index_rebuild == other.needs_term_index_rebuild &&
130            needs_integer_index_rebuild == other.needs_integer_index_rebuild &&
131            needs_qualified_id_join_index_rebuild ==
132                other.needs_qualified_id_join_index_rebuild;
133   }
134 
CombineWithOtherRebuildResultOrDerivedFilesRebuildResult135   void CombineWithOtherRebuildResultOr(const DerivedFilesRebuildResult& other) {
136     needs_document_store_derived_files_rebuild =
137         needs_document_store_derived_files_rebuild ||
138         other.needs_document_store_derived_files_rebuild;
139     needs_schema_store_derived_files_rebuild =
140         needs_schema_store_derived_files_rebuild ||
141         other.needs_schema_store_derived_files_rebuild;
142     needs_term_index_rebuild =
143         needs_term_index_rebuild || other.needs_term_index_rebuild;
144     needs_integer_index_rebuild =
145         needs_integer_index_rebuild || other.needs_integer_index_rebuild;
146     needs_qualified_id_join_index_rebuild =
147         needs_qualified_id_join_index_rebuild ||
148         other.needs_qualified_id_join_index_rebuild;
149   }
150 };
151 
152 // There are two icing version files:
153 // 1. V1 version file contains version and max_version info of the existing
154 //    data.
155 // 2. V2 version file writes the version information using
156 //    FileBackedProto<IcingSearchEngineVersionProto>. This contains information
157 //    about the version's enabled trunk stable features in addition to the
158 //    version numbers written for V1.
159 //
160 // Both version files must be written to maintain backwards compatibility.
MakeVersionFilePath(std::string_view version_file_dir,std::string_view version_file_name)161 inline std::string MakeVersionFilePath(std::string_view version_file_dir,
162                                        std::string_view version_file_name) {
163   return absl_ports::StrCat(version_file_dir, "/", version_file_name);
164 }
165 
166 // Returns a VersionInfo from a given IcingSearchEngineVersionProto.
GetVersionInfoFromProto(const IcingSearchEngineVersionProto & version_proto)167 inline VersionInfo GetVersionInfoFromProto(
168     const IcingSearchEngineVersionProto& version_proto) {
169   return VersionInfo(version_proto.version(), version_proto.max_version());
170 }
171 
172 
173 // Reads the IcingSearchEngineVersionProto from the version files of the
174 // existing data.
175 //
176 // This method reads both the v1 and v2 version files, and returns the v1
177 // version numbers in the absence of the v2 version file. If there is a mismatch
178 // between the v1 and v2 version numbers, or if the state is invalid (e.g. flash
179 // index header file is missing), then an invalid VersionInfo is returned.
180 //
181 // RETURNS:
182 //   - Existing data's IcingSearchEngineVersionProto on success
183 //   - INTERNAL_ERROR on I/O errors
184 libtextclassifier3::StatusOr<IcingSearchEngineVersionProto> ReadVersion(
185     const Filesystem& filesystem, const std::string& version_file_dir,
186     const std::string& index_base_dir);
187 
188 // Writes the v1 version file. V1 version file is written for all versions and
189 // contains only Icing's VersionInfo (version number and max_version)
190 //
191 // RETURNS:
192 //   - OK on success
193 //   - INTERNAL_ERROR on I/O errors
194 libtextclassifier3::Status WriteV1Version(const Filesystem& filesystem,
195                                           const std::string& version_file_dir,
196                                           const VersionInfo& version_info);
197 
198 // Writes the v2 version file. V2 version file writes the version information
199 // using FileBackedProto<IcingSearchEngineVersionProto>.
200 //
201 // REQUIRES: version_proto.version >= kFirstV2Version. We implement v2 version
202 // checking in kFirstV2Version, so callers will always use a version # greater
203 // than this.
204 //
205 // RETURNS:
206 //   - OK on success
207 //   - INTERNAL_ERROR on I/O errors
208 libtextclassifier3::Status WriteV2Version(
209     const Filesystem& filesystem, const std::string& version_file_dir,
210     std::unique_ptr<IcingSearchEngineVersionProto> version_proto);
211 
212 // Deletes Icing's version files from version_file_dir.
213 //
214 // Returns:
215 //   - OK on success
216 //   - INTERNAL_ERROR on I/O error
217 libtextclassifier3::Status DiscardVersionFiles(
218     const Filesystem& filesystem, std::string_view version_file_dir);
219 
220 // Determines the change state between the existing data version and the current
221 // code version.
222 //
223 // REQUIRES: curr_version > 0. We implement version checking in version 1, so
224 //   the callers (except unit tests) will always use a version # greater than 0.
225 //
226 // RETURNS: StateChange
227 StateChange GetVersionStateChange(const VersionInfo& existing_version_info,
228                                   int32_t curr_version = kVersion);
229 
230 // Determines the derived files that need to be rebuilt between Icing's existing
231 // data based on previous data version and enabled features, and the current
232 // code version and enabled features.
233 //
234 // REQUIRES: curr_version >= kFirstV2Version. We implement v2 version checking
235 // in kFirstV2Version, so callers will always use a version # greater than this.
236 //
237 // RETURNS: DerivedFilesRebuildResult
238 DerivedFilesRebuildResult CalculateRequiredDerivedFilesRebuild(
239     const IcingSearchEngineVersionProto& prev_version_proto,
240     const IcingSearchEngineVersionProto& curr_version_proto);
241 
242 // Determines whether Icing should rebuild all derived files.
243 // Sometimes it is not required to rebuild derived files when
244 // roll-forward/upgrading. This function "encodes" upgrade paths and checks if
245 // the roll-forward/upgrading requires derived files to be rebuilt or not.
246 //
247 // REQUIRES: curr_version > 0. We implement version checking in version 1, so
248 //   the callers (except unit tests) will always use a version # greater than 0.
249 bool ShouldRebuildDerivedFiles(const VersionInfo& existing_version_info,
250                                int32_t curr_version = kVersion);
251 
252 // Returns the derived files rebuilds required for a given feature.
253 DerivedFilesRebuildResult GetFeatureDerivedFilesRebuildResult(
254     IcingSearchEngineFeatureInfoProto::FlaggedFeatureType feature);
255 
256 // Constructs the IcingSearchEngineFeatureInfoProto for a given feature.
257 IcingSearchEngineFeatureInfoProto GetFeatureInfoProto(
258     IcingSearchEngineFeatureInfoProto::FlaggedFeatureType feature);
259 
260 // Populates the enabled_features field for an IcingSearchEngineFeatureInfoProto
261 // based on icing's initialization flag options.
262 //
263 // All enabled features are converted into an IcingSearchEngineFeatureInfoProto
264 // and returned in IcingSearchEngineVersionProto::enabled_features. A conversion
265 // should be added for each trunk stable feature flag defined in
266 // IcingSearchEngineOptions.
267 void AddEnabledFeatures(const IcingSearchEngineOptions& options,
268                         IcingSearchEngineVersionProto* version_proto);
269 
270 }  // namespace version_util
271 
272 }  // namespace lib
273 }  // namespace icing
274 
275 #endif  // ICING_FILE_VERSION_UTIL_H_
276