• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #define LOG_TAG "SqliteFunctionRegistry"
16 
17 #include "sqlite_default_function.h"
18 
19 #include <sys/stat.h>
20 
21 #include <vector>
22 
23 #include "logger.h"
24 #include "raw_data_parser.h"
25 #include "sqlite_connection.h"
26 #include "sqlite_errno.h"
27 #include "sqlite_utils.h"
28 
29 #if !defined(CROSS_PLATFORM)
30 #include "relational/relational_store_sqlite_ext.h"
31 #endif
32 
33 namespace OHOS {
34 namespace NativeRdb {
35 using namespace OHOS::Rdb;
36 
37 static constexpr int BACKUP_PAGES_PRE_STEP = 12800; // 1024 * 4 * 12800 == 50m
38 static constexpr int BACKUP_MAX_RETRY_COUNT = 10000;
39 static constexpr int BACKUP_MAX_TIME = 10000;
40 static constexpr int BACKUP_SLEEP_TIME = 1000;
41 
MergeAssets(sqlite3_context * ctx,int argc,sqlite3_value ** argv)42 void SqliteFunctionRegistry::MergeAssets(sqlite3_context *ctx, int argc, sqlite3_value **argv)
43 {
44     // 2 is the number of parameters
45     if (ctx == nullptr || argc != 2 || argv == nullptr) {
46         LOG_ERROR("Parameter does not meet restrictions. ctx: %{public}d, argc: %{public}d, argv: %{public}d",
47             ctx == nullptr, argc, argv == nullptr);
48         return;
49     }
50     std::map<std::string, ValueObject::Asset> assets;
51     auto data = static_cast<const uint8_t *>(sqlite3_value_blob(argv[0]));
52     if (data != nullptr) {
53         int len = sqlite3_value_bytes(argv[0]);
54         RawDataParser::ParserRawData(data, len, assets);
55     }
56     std::map<std::string, ValueObject::Asset> newAssets;
57     data = static_cast<const uint8_t *>(sqlite3_value_blob(argv[1]));
58     if (data != nullptr) {
59         int len = sqlite3_value_bytes(argv[1]);
60         RawDataParser::ParserRawData(data, len, newAssets);
61     }
62     CompAssets(assets, newAssets);
63     auto blob = RawDataParser::PackageRawData(assets);
64     sqlite3_result_blob(ctx, blob.data(), blob.size(), SQLITE_TRANSIENT);
65 }
66 
MergeAsset(sqlite3_context * ctx,int argc,sqlite3_value ** argv)67 void SqliteFunctionRegistry::MergeAsset(sqlite3_context *ctx, int argc, sqlite3_value **argv)
68 {
69     // 2 is the number of parameters
70     if (ctx == nullptr || argc != 2 || argv == nullptr) {
71         LOG_ERROR("Parameter does not meet restrictions. ctx: %{public}d, argc: %{public}d, argv: %{public}d",
72             ctx == nullptr, argc, argv == nullptr);
73         return;
74     }
75     ValueObject::Asset asset;
76     size_t size = 0;
77     auto data = static_cast<const uint8_t *>(sqlite3_value_blob(argv[0]));
78     if (data != nullptr) {
79         int len = sqlite3_value_bytes(argv[0]);
80         size = RawDataParser::ParserRawData(data, len, asset);
81     }
82     ValueObject::Asset newAsset;
83     data = static_cast<const uint8_t *>(sqlite3_value_blob(argv[1]));
84     if (data != nullptr) {
85         int len = sqlite3_value_bytes(argv[1]);
86         RawDataParser::ParserRawData(data, len, newAsset);
87     }
88 
89     if (size == 0) {
90         asset = std::move(newAsset);
91         if (asset.status != AssetValue::Status::STATUS_DELETE) {
92             asset.status = AssetValue::Status::STATUS_INSERT;
93         }
94     } else if (asset.name == newAsset.name) {
95         MergeAsset(asset, newAsset);
96     } else {
97         LOG_WARN("name change! old:%{public}s, new:%{public}s", SqliteUtils::Anonymous(asset.name).c_str(),
98             SqliteUtils::Anonymous(newAsset.name).c_str());
99     }
100     auto blob = RawDataParser::PackageRawData(asset);
101     sqlite3_result_blob(ctx, blob.data(), blob.size(), SQLITE_TRANSIENT);
102 }
103 
CompAssets(std::map<std::string,ValueObject::Asset> & assets,std::map<std::string,ValueObject::Asset> & newAssets)104 void SqliteFunctionRegistry::CompAssets(
105     std::map<std::string, ValueObject::Asset> &assets, std::map<std::string, ValueObject::Asset> &newAssets)
106 {
107     auto oldIt = assets.begin();
108     auto newIt = newAssets.begin();
109     for (; oldIt != assets.end() && newIt != newAssets.end();) {
110         if (oldIt->first == newIt->first) {
111             MergeAsset(oldIt->second, newIt->second);
112             oldIt++;
113             newIt = newAssets.erase(newIt);
114             continue;
115         }
116         if (oldIt->first < newIt->first) {
117             ++oldIt;
118             continue;
119         }
120         newIt++;
121     }
122     for (auto &[key, value] : newAssets) {
123         value.status = ValueObject::Asset::Status::STATUS_INSERT;
124         assets.insert(std::pair{ key, std::move(value) });
125     }
126 }
127 
MergeAsset(ValueObject::Asset & oldAsset,ValueObject::Asset & newAsset)128 void SqliteFunctionRegistry::MergeAsset(ValueObject::Asset &oldAsset, ValueObject::Asset &newAsset)
129 {
130     using Status = ValueObject::Asset::Status;
131     if (newAsset.status == Status::STATUS_DELETE) {
132         oldAsset.status = Status::STATUS_DELETE;
133         oldAsset.hash = "";
134         oldAsset.modifyTime = "";
135         oldAsset.size = "";
136         return;
137     }
138     auto status = static_cast<int32_t>(oldAsset.status);
139     switch (status) {
140         case Status::STATUS_UNKNOWN:  // fallthrough
141         case Status::STATUS_NORMAL:   // fallthrough
142         case Status::STATUS_ABNORMAL: // fallthrough
143         case Status::STATUS_INSERT:   // fallthrough
144         case Status::STATUS_UPDATE:   // fallthrough
145             if (oldAsset.modifyTime != newAsset.modifyTime || oldAsset.size != newAsset.size ||
146                 oldAsset.uri != newAsset.uri || oldAsset.path != newAsset.path) {
147                 if (oldAsset.modifyTime != newAsset.modifyTime || oldAsset.size != newAsset.size ||
148                     oldAsset.uri == newAsset.uri || oldAsset.path == newAsset.path) {
149                     oldAsset.expiresTime = newAsset.expiresTime;
150                     oldAsset.hash = newAsset.hash;
151                     oldAsset.status = Status::STATUS_UPDATE;
152                 }
153                 oldAsset.version = newAsset.version;
154                 oldAsset.uri = newAsset.uri;
155                 oldAsset.createTime = newAsset.createTime;
156                 oldAsset.modifyTime = newAsset.modifyTime;
157                 oldAsset.size = newAsset.size;
158                 oldAsset.path = newAsset.path;
159             }
160             return;
161         default:
162             return;
163     }
164 }
165 
SqliteResultError(sqlite3_context * ctx,const int & errCode,const std::string & errMsg)166 void SqliteFunctionRegistry::SqliteResultError(sqlite3_context *ctx, const int &errCode, const std::string &errMsg)
167 {
168     sqlite3_result_error(ctx, errMsg.c_str(), -1);
169     sqlite3_result_error_code(ctx, errCode);
170 }
171 
ImportDB(sqlite3_context * ctx,int argc,sqlite3_value ** argv)172 void SqliteFunctionRegistry::ImportDB(sqlite3_context *ctx, int argc, sqlite3_value **argv)
173 {
174     if (ctx == nullptr || argc != 1 || argv == nullptr) {
175         LOG_ERROR("Parameter does not meet restrictions. ctx: %{public}d, argc: %{public}d, argv: %{public}d",
176             ctx == nullptr, argc, argv == nullptr);
177         SqliteResultError(ctx, SQLITE_ERROR, "invalid param");
178         return;
179     }
180     std::string path;
181     auto data = static_cast<const char *>(sqlite3_value_blob(argv[0]));
182     if (data != nullptr) {
183         path = std::string(data, sqlite3_value_bytes(argv[0]));
184     }
185 
186     struct stat fileStat;
187     if (stat(path.c_str(), &fileStat) != 0) {
188         if (errno != ENOENT) {
189             LOG_ERROR(
190                 "File stat error. path: %{public}s, error: %{public}d", SqliteUtils::Anonymous(path).c_str(), errno);
191         }
192         SqliteResultError(ctx, SQLITE_CANTOPEN, "backup failed");
193         return;
194     }
195 
196     sqlite3 *sourceDbHandle = nullptr;
197     int32_t errCode =
198         sqlite3_open_v2(path.c_str(), &sourceDbHandle, SQLITE_OPEN_READONLY | SQLITE_OPEN_FULLMUTEX, nullptr);
199 
200     auto source = std::shared_ptr<sqlite3>(sourceDbHandle, [&ctx, &errCode, &path](auto *handle) {
201         sqlite3_close(handle);
202         if (errCode == SQLITE_OK || errCode == SQLITE_DONE) {
203             return;
204         }
205         LOG_ERROR("Error during backup. path: %{public}s, errCode: %{public}d, errno: %{public}d",
206             SqliteUtils::Anonymous(path).c_str(), errCode, errno);
207         SqliteResultError(ctx, errCode, "backup failed");
208     });
209     if (errCode != SQLITE_OK) {
210         return;
211     }
212     errCode = IntegrityCheck(sourceDbHandle);
213     if (errCode != SQLITE_OK) {
214         return;
215     }
216     errCode = BackUpDB(sourceDbHandle, sqlite3_context_db_handle(ctx));
217     if (errCode != SQLITE_DONE) {
218         return;
219     }
220     sqlite3_result_null(ctx);
221 }
222 
IntegrityCheck(sqlite3 * dbHandle)223 int32_t SqliteFunctionRegistry::IntegrityCheck(sqlite3 *dbHandle)
224 {
225     int32_t errCode = SQLITE_OK;
226 
227     errCode = sqlite3_exec(
228         dbHandle, "PRAGMA integrity_check",
229         [](void *data, int argc, char **argv, char **colNames) -> int {
230             if (argc > 0) {
231                 std::string result = argv[0] ? argv[0] : "";
232                 if (result == "ok") {
233                     return 0;
234                 }
235             }
236             return 1;
237         },
238         nullptr, nullptr);
239     if (errCode != SQLITE_OK) {
240         LOG_ERROR("Integrity check failed. error: %{public}d.", errCode);
241         return SQLITE_CORRUPT;
242     }
243     return SQLITE_OK;
244 }
245 
BackUpDB(sqlite3 * source,sqlite3 * dest)246 int32_t SqliteFunctionRegistry::BackUpDB(sqlite3 *source, sqlite3 *dest)
247 {
248     sqlite3_backup *pBackup = sqlite3_backup_init(dest, "main", source, "main");
249     if (pBackup == nullptr) {
250         return SQLITE_BUSY;
251     }
252 
253     int retryCount = 0;
254     int32_t errCode = SQLITE_OK;
255 
256     auto startTime = std::chrono::steady_clock::now();
257     do {
258         errCode = sqlite3_backup_step(pBackup, BACKUP_PAGES_PRE_STEP);
259         if (errCode == SQLITE_BUSY || errCode == SQLITE_LOCKED) {
260             retryCount++;
261         } else {
262             retryCount = 0;
263         }
264 
265         if (retryCount > BACKUP_MAX_RETRY_COUNT) {
266             sqlite3_backup_finish(pBackup);
267             return SQLITE_ERROR;
268         }
269 
270         auto now = std::chrono::steady_clock::now();
271         auto elapsedTime = now - startTime;
272         if (std::chrono::duration_cast<std::chrono::milliseconds>(elapsedTime).count() > BACKUP_MAX_TIME) {
273             startTime = now;
274             sqlite3_sleep(BACKUP_SLEEP_TIME);
275         }
276     } while (sqlite3_backup_pagecount(pBackup) != 0 &&
277              (errCode == SQLITE_OK || errCode == SQLITE_BUSY || errCode == SQLITE_LOCKED));
278     (void)sqlite3_backup_finish(pBackup);
279     if (errCode != SQLITE_DONE) {
280         LOG_ERROR("Backup failed! error:%{public}d.", errCode);
281     }
282     return errCode;
283 }
284 
285 } // namespace NativeRdb
286 } // namespace OHOS