1 /*
2  * Copyright (C) 2024 Huawei Device Co., Ltd.
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 
16 #ifndef OHOS_MEDIA_DATATRANSFER_MEDIA_LIBRARY_DB_UPGRADE_H
17 #define OHOS_MEDIA_DATATRANSFER_MEDIA_LIBRARY_DB_UPGRADE_H
18 
19 #include <string>
20 
21 #include "rdb_store.h"
22 #include "db_upgrade_utils.h"
23 
24 namespace OHOS::Media {
25 namespace DataTransfer {
26 class MediaLibraryDbUpgrade {
27 public:
28     int32_t OnUpgrade(NativeRdb::RdbStore &store);
29 
30 private:
31     int32_t UpgradeAlbumPlugin(NativeRdb::RdbStore &store);
32     int32_t UpgradePhotoAlbum(NativeRdb::RdbStore &store);
33     int32_t UpgradePhotos(NativeRdb::RdbStore &store);
34     int32_t UpgradePhotoMap(NativeRdb::RdbStore &store);
35     int32_t MergeAlbumFromOldBundleNameToNewBundleName(NativeRdb::RdbStore &store);
36     int32_t UpgradePhotosBelongsToAlbum(NativeRdb::RdbStore &store);
37 
38 private:
39     int32_t AddOwnerAlbumIdColumn(NativeRdb::RdbStore &store);
40     int32_t AddlPathColumn(NativeRdb::RdbStore &store);
41     int32_t MoveSingleRelationshipToPhotos(NativeRdb::RdbStore &store);
42     int32_t UpdatelPathColumn(NativeRdb::RdbStore &store);
43     int32_t ExecSqlWithRetry(std::function<int32_t()> execSql);
44 
45 private:
46     DbUpgradeUtils dbUpgradeUtils_;
47 
48 private:
49     const std::string SQL_PHOTO_ALBUM_TABLE_ADD_LPATH_COLUMN = "ALTER TABLE PhotoAlbum ADD COLUMN lpath TEXT;";
50     const std::string SQL_PHOTO_ALBUM_TABLE_UPDATE_LPATH_COLUMN = "\
51         UPDATE PhotoAlbum \
52         SET lpath = \
53         ( \
54             SELECT \
55                 CASE \
56                     WHEN COALESCE(album_plugin.lpath, '') <> '' THEN album_plugin.lpath \
57                     WHEN COALESCE(plugin_v2.lpath, '') <> '' THEN plugin_v2.lpath \
58                     WHEN album_type = 2048 THEN '/Pictures/'||PA.album_name \
59                     WHEN album_type = 0 THEN '/Pictures/Users/'||PA.album_name \
60                     ELSE '/Pictures/其它' \
61                 END AS s_lpath \
62             FROM PhotoAlbum AS PA \
63                 LEFT JOIN album_plugin \
64                 ON COALESCE(PA.bundle_name, '') <> '' AND PA.bundle_name = album_plugin.bundle_name \
65                 LEFT JOIN album_plugin AS plugin_v2 \
66                 ON PA.album_type = 2048 AND PA.album_name = plugin_v2.album_name \
67             WHERE PhotoAlbum.album_id = PA.album_id \
68             ORDER BY \
69                 ( \
70                 CASE \
71                     WHEN COALESCE(album_plugin.lpath, '') <> '' THEN album_plugin.priority \
72                     WHEN COALESCE(plugin_v2.lpath, '') <> '' THEN plugin_v2.priority \
73                     ELSE 1 \
74                 END \
75                 ) DESC, s_lpath ASC \
76             LIMIT 1 \
77         ) \
78         WHERE COALESCE(PhotoAlbum.album_name, '') <> '' AND \
79             PhotoAlbum.album_subtype != 1024 AND \
80             COALESCE(PhotoAlbum.lpath, '') = '';";
81     const std::string SQL_PHOTOS_TABLE_ADD_OWNER_ALBUM_ID = "\
82         ALTER TABLE Photos ADD COLUMN owner_album_id INT DEFAULT 0;";
83     const std::string SQL_TEMP_PHOTO_MAP_TABLE_DROP = "DROP TABLE IF EXISTS temp_photo_map;";
84     // 使用 group 分组查找 Photos 的一条关联关系
85     const std::string SQL_TEMP_PHOTO_MAP_TABLE_CREATE = "\
86         CREATE TABLE IF NOT EXISTS temp_photo_map \
87         AS \
88         SELECT MIN(map_album) AS map_album, \
89             map_asset \
90         FROM PhotoMap \
91             INNER JOIN Photos \
92             ON PhotoMap.map_asset=Photos.file_id \
93         WHERE position IN (1, 3) AND \
94             COALESCE(owner_album_id, 0) = 0 \
95         GROUP BY map_asset;";
96     const std::vector<std::string> SQL_TEMP_PHOTO_MAP_TABLE_CREATE_INDEX_ARRAY = {
97         "CREATE INDEX IF NOT EXISTS unique_index_temp_photo_map_map_asset ON temp_photo_map ( \
98             map_asset \
99         );",
100         "CREATE UNIQUE INDEX IF NOT EXISTS unique_index_temp_photo_map_all ON temp_photo_map ( \
101             map_album, map_asset \
102         );"};
103     // owner_album_id 默认值是 0,使用 UNION 避免无查询结果时,被更新为 NULL
104     const std::string SQL_PHOTOS_TABLE_UPDATE_ALBUM_ID = "\
105         UPDATE Photos SET owner_album_id = \
106         ( \
107             SELECT map_album \
108             FROM temp_photo_map \
109             WHERE file_id=map_asset \
110             UNION \
111             SELECT 0 AS map_album \
112             ORDER BY map_album DESC \
113             LIMIT 1 \
114         ) \
115         WHERE COALESCE(owner_album_id, 0)=0;";
116     const std::string SQL_PHOTO_MAP_TABLE_DELETE_SINGLE_RELATIONSHIP = "\
117         DELETE FROM PhotoMap \
118         WHERE EXISTS \
119         ( \
120             SELECT 1 \
121             FROM temp_photo_map \
122             WHERE PhotoMap.map_album=temp_photo_map.map_album AND \
123                 PhotoMap.map_asset=temp_photo_map.map_asset \
124         );";
125     /* Clear the cache table. */
126     const std::string SQL_TEMP_ALBUM_BUNDLE_NAME_DELETE = "\
127         DROP TABLE IF EXISTS temp_album_bundle_name;";
128     /* Cache the mapping of old to new bundle names */
129     const std::string SQL_TEMP_ALBUM_BUNDLE_NAME_CREATE = "\
130         CREATE TABLE IF NOT EXISTS temp_album_bundle_name \
131         AS \
132         SELECT \
133             'com.huawei.ohos.screenrecorder' AS bundle_name_old, \
134             'com.huawei.hmos.screenrecorder' AS bundle_name_new \
135         UNION \
136         SELECT \
137             'com.huawei.ohos.screenshot' AS bundle_name_old, \
138             'com.huawei.hmos.screenshot' AS bundle_name_new \
139         ;";
140     /* Create the Album if it doesn't exist */
141     const std::string SQL_PHOTO_ALBUM_INSERT_NEW_ALBUM = "\
142         INSERT INTO PhotoAlbum( \
143             album_type, \
144             album_subtype, \
145             album_name, \
146             bundle_name, \
147             lpath, \
148             date_modified, \
149             date_added \
150         ) \
151         SELECT \
152             PA1.album_type, \
153             PA1.album_subtype, \
154             PA1.album_name, \
155             M.bundle_name_new AS bundle_name, \
156             CASE \
157                 WHEN COALESCE(album_plugin.lpath,'') <> '' THEN album_plugin.lpath \
158                 ELSE '/Pictures/'||PA1.album_name \
159             END AS lpath, \
160             strftime('%s000', 'now') AS date_modified, \
161             strftime('%s000', 'now') AS date_added \
162         FROM PhotoAlbum AS PA1 \
163             INNER JOIN temp_album_bundle_name AS M \
164             ON PA1.bundle_name=M.bundle_name_old \
165             LEFT JOIN PhotoAlbum AS PA2 \
166             ON M.bundle_name_new=PA2.bundle_name \
167             LEFT JOIN album_plugin \
168             ON M.bundle_name_new=album_plugin.bundle_name \
169         WHERE PA2.bundle_name IS NULL;";
170     /* Add the relationship in PhotoMap for new Album and Photo */
171     const std::string SQL_PHOTO_MAP_INSERT_NEW_ALBUM = "\
172         INSERT INTO PhotoMap( \
173             map_album, \
174             map_asset \
175         ) \
176         SELECT \
177             PA2.album_id AS album_id_new, \
178             PM.map_asset \
179         FROM PhotoAlbum AS PA1 \
180             INNER JOIN temp_album_bundle_name AS M \
181             ON PA1.bundle_name=M.bundle_name_old \
182             INNER JOIN PhotoAlbum AS PA2 \
183             ON M.bundle_name_new=PA2.bundle_name \
184             INNER JOIN PhotoMap AS PM \
185             ON PA1.album_id=PM.map_album \
186         EXCEPT \
187         SELECT PA.album_id, \
188             PM.map_asset \
189         FROM PhotoAlbum AS PA \
190             INNER JOIN temp_album_bundle_name AS M \
191             ON PA.bundle_name=M.bundle_name_new \
192             INNER JOIN PhotoMap AS PM \
193             ON PA.album_id=PM.map_album \
194         ; ";
195     /* Remove the relationship in PhotoMap for old Album and Photo */
196     const std::string SQL_PHOTO_MAP_DELETE_OLD_ALBUM = "\
197         DELETE FROM PhotoMap \
198         WHERE map_album IN \
199         ( \
200             SELECT \
201                 PA1.album_id \
202             FROM PhotoAlbum AS PA1 \
203                 INNER JOIN temp_album_bundle_name AS M \
204                 ON PA1.bundle_name=M.bundle_name_old \
205         );";
206     /* Replace the relationship in Photos for new Album and Photo */
207     const std::string SQL_PHOTOS_UPDATE_NEW_ALBUM = "\
208         UPDATE Photos \
209         SET owner_album_id=( \
210             SELECT PA2.album_id \
211             FROM PhotoAlbum AS PA1 \
212                 INNER JOIN temp_album_bundle_name AS M \
213                 ON PA1.bundle_name=M.bundle_name_old \
214                 INNER JOIN PhotoAlbum AS PA2 \
215                 ON M.bundle_name_new=PA2.bundle_name \
216             WHERE Photos.owner_album_id=PA1.album_id \
217         ) \
218         WHERE owner_album_id IN \
219         ( \
220             SELECT \
221                 PA1.album_id \
222             FROM PhotoAlbum AS PA1 \
223                 INNER JOIN temp_album_bundle_name AS M \
224                 ON PA1.bundle_name=M.bundle_name_old \
225         ); ";
226     /* Remove the relationship in Photos for old Album and Photo */
227     const std::string SQL_PHOTO_ALBUM_DELETE_OLD_ALBUM = "\
228         DELETE FROM PhotoAlbum \
229         WHERE album_id IN \
230         ( \
231             SELECT \
232                 PA.album_id \
233             FROM PhotoAlbum AS PA \
234                 INNER JOIN temp_album_bundle_name AS M \
235                 ON PA.bundle_name=M.bundle_name_old \
236         ); ";
237     const std::vector<std::string> SQL_MERGE_ALBUM_FROM_OLD_BUNDLE_NAME_TO_NEW_BUNDLE_NAME = {
238         SQL_TEMP_ALBUM_BUNDLE_NAME_DELETE,
239         SQL_TEMP_ALBUM_BUNDLE_NAME_CREATE,
240         SQL_PHOTO_ALBUM_INSERT_NEW_ALBUM,
241         SQL_PHOTO_MAP_INSERT_NEW_ALBUM,
242         SQL_PHOTO_MAP_DELETE_OLD_ALBUM,
243         SQL_PHOTOS_UPDATE_NEW_ALBUM,
244         SQL_PHOTO_ALBUM_DELETE_OLD_ALBUM,
245         SQL_TEMP_ALBUM_BUNDLE_NAME_DELETE,
246     };
247     /* Create the Album if it doesn't exist */
248     const std::string SQL_PHOTO_ALBUM_INSERT_OTHER_ALBUM = "\
249         INSERT INTO PhotoAlbum( \
250             album_type, \
251             album_subtype, \
252             album_name, \
253             bundle_name, \
254             lpath \
255             ) \
256         SELECT \
257             2048 AS album_type, \
258             2049 AS album_subtype, \
259             '其它' AS album_name, \
260             'com.other.album' AS bundle_name, \
261             '/Pictures/其它' AS lpath \
262         EXCEPT \
263         SELECT \
264             album_type, \
265             album_subtype, \
266             album_name, \
267             bundle_name, \
268             lpath \
269         FROM PhotoAlbum ;";
270     /* The Photo, doesn't belong to any album, should belongs to '其它' album */
271     const std::string SQL_PHOTOS_UPDATE_OTHER_ALBUM = "\
272         UPDATE Photos \
273         SET owner_album_id=( \
274             SELECT album_id \
275             FROM PhotoAlbum \
276             WHERE lpath='/Pictures/其它' \
277             LIMIT 1 \
278         ) \
279         WHERE file_id IN \
280         ( \
281             SELECT file_id \
282             FROM Photos \
283                 LEFT JOIN PhotoMap \
284                 ON Photos.file_id = PhotoMap.map_asset \
285             WHERE PhotoMap.map_asset IS NULL AND \
286                 COALESCE(Photos.owner_album_id, 0) = 0 AND \
287                 Photos.position IN (1, 3) \
288         );";
289     const std::vector<std::string> SQL_PHOTOS_NEED_TO_BELONGS_TO_ALBUM = {
290         SQL_PHOTO_ALBUM_INSERT_OTHER_ALBUM,
291         SQL_PHOTOS_UPDATE_OTHER_ALBUM,
292     };
293 };
294 }  // namespace DataTransfer
295 }  // namespace OHOS::Media
296 #endif  // OHOS_MEDIA_DATATRANSFER_MEDIA_LIBRARY_DB_UPGRADE_H