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