xref: /aosp_15_r20/external/icing/icing/file/version-util.h (revision 8b6cd535a057e39b3b86660c4aa06c99747c2136)
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 // - Version 5: M-2025-02. Schema is compatible with v1, v2, v3 and v4.
41 inline static constexpr int32_t kVersion = 5;
42 inline static constexpr int32_t kVersionOne = 1;
43 inline static constexpr int32_t kVersionTwo = 2;
44 inline static constexpr int32_t kVersionThree = 3;
45 inline static constexpr int32_t kVersionFour = 4;
46 inline static constexpr int32_t kVersionFive = 5;
47 
48 // Version at which v2 version file is introduced.
49 inline static constexpr int32_t kFirstV2Version = kVersionFour;
50 
51 // Version at which the database field is introduced to the schema proto.
52 inline static constexpr int32_t kSchemaDatabaseVersion = kVersionFive;
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   bool needs_embedding_index_rebuild = false;
101 
102   DerivedFilesRebuildResult() = default;
103 
DerivedFilesRebuildResultDerivedFilesRebuildResult104   explicit DerivedFilesRebuildResult(
105       bool needs_document_store_derived_files_rebuild_in,
106       bool needs_schema_store_derived_files_rebuild_in,
107       bool needs_term_index_rebuild_in, bool needs_integer_index_rebuild_in,
108       bool needs_qualified_id_join_index_rebuild_in,
109       bool needs_embedding_index_rebuild_in)
110       : needs_document_store_derived_files_rebuild(
111             needs_document_store_derived_files_rebuild_in),
112         needs_schema_store_derived_files_rebuild(
113             needs_schema_store_derived_files_rebuild_in),
114         needs_term_index_rebuild(needs_term_index_rebuild_in),
115         needs_integer_index_rebuild(needs_integer_index_rebuild_in),
116         needs_qualified_id_join_index_rebuild(
117             needs_qualified_id_join_index_rebuild_in),
118         needs_embedding_index_rebuild(needs_embedding_index_rebuild_in) {}
119 
IsRebuildNeededDerivedFilesRebuildResult120   bool IsRebuildNeeded() const {
121     return needs_document_store_derived_files_rebuild ||
122            needs_schema_store_derived_files_rebuild ||
123            needs_term_index_rebuild || needs_integer_index_rebuild ||
124            needs_qualified_id_join_index_rebuild ||
125            needs_embedding_index_rebuild;
126   }
127 
128   bool operator==(const DerivedFilesRebuildResult& other) const {
129     return needs_document_store_derived_files_rebuild ==
130                other.needs_document_store_derived_files_rebuild &&
131            needs_schema_store_derived_files_rebuild ==
132                other.needs_schema_store_derived_files_rebuild &&
133            needs_term_index_rebuild == other.needs_term_index_rebuild &&
134            needs_integer_index_rebuild == other.needs_integer_index_rebuild &&
135            needs_qualified_id_join_index_rebuild ==
136                other.needs_qualified_id_join_index_rebuild &&
137            needs_embedding_index_rebuild == other.needs_embedding_index_rebuild;
138   }
139 
CombineWithOtherRebuildResultOrDerivedFilesRebuildResult140   void CombineWithOtherRebuildResultOr(const DerivedFilesRebuildResult& other) {
141     needs_document_store_derived_files_rebuild |=
142         other.needs_document_store_derived_files_rebuild;
143     needs_schema_store_derived_files_rebuild |=
144         other.needs_schema_store_derived_files_rebuild;
145     needs_term_index_rebuild |= other.needs_term_index_rebuild;
146     needs_integer_index_rebuild |= other.needs_integer_index_rebuild;
147     needs_qualified_id_join_index_rebuild |=
148         other.needs_qualified_id_join_index_rebuild;
149     needs_embedding_index_rebuild |= other.needs_embedding_index_rebuild;
150   }
151 };
152 
153 // There are two icing version files:
154 // 1. V1 version file contains version and max_version info of the existing
155 //    data.
156 // 2. V2 version file writes the version information using
157 //    FileBackedProto<IcingSearchEngineVersionProto>. This contains information
158 //    about the version's enabled trunk stable features in addition to the
159 //    version numbers written for V1.
160 //
161 // Both version files must be written to maintain backwards compatibility.
MakeVersionFilePath(std::string_view version_file_dir,std::string_view version_file_name)162 inline std::string MakeVersionFilePath(std::string_view version_file_dir,
163                                        std::string_view version_file_name) {
164   return absl_ports::StrCat(version_file_dir, "/", version_file_name);
165 }
166 
167 // Returns a VersionInfo from a given IcingSearchEngineVersionProto.
GetVersionInfoFromProto(const IcingSearchEngineVersionProto & version_proto)168 inline VersionInfo GetVersionInfoFromProto(
169     const IcingSearchEngineVersionProto& version_proto) {
170   return VersionInfo(version_proto.version(), version_proto.max_version());
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 whether the schema database migration is required.
253 //
254 // This is true if the previous version is less than the version at which the
255 // database field is introduced, or if the schema database feature was
256 // not enabled in the previous version.
257 bool SchemaDatabaseMigrationRequired(
258     const IcingSearchEngineVersionProto& prev_version_proto);
259 
260 // Returns the derived files rebuilds required for a given feature.
261 DerivedFilesRebuildResult GetFeatureDerivedFilesRebuildResult(
262     IcingSearchEngineFeatureInfoProto::FlaggedFeatureType feature);
263 
264 // Constructs the IcingSearchEngineFeatureInfoProto for a given feature.
265 IcingSearchEngineFeatureInfoProto GetFeatureInfoProto(
266     IcingSearchEngineFeatureInfoProto::FlaggedFeatureType feature);
267 
268 // Populates the enabled_features field for an IcingSearchEngineFeatureInfoProto
269 // based on icing's initialization flag options.
270 //
271 // All enabled features are converted into an IcingSearchEngineFeatureInfoProto
272 // and returned in IcingSearchEngineVersionProto::enabled_features. A conversion
273 // should be added for each trunk stable feature flag defined in
274 // IcingSearchEngineOptions.
275 void AddEnabledFeatures(const IcingSearchEngineOptions& options,
276                         IcingSearchEngineVersionProto* version_proto);
277 
278 }  // namespace version_util
279 
280 }  // namespace lib
281 }  // namespace icing
282 
283 #endif  // ICING_FILE_VERSION_UTIL_H_
284