diff --git a/bundle.json b/bundle.json index b6cbc95a86c93f6e5cfd34c304df1df4e2ff1f36..f10fc9319362abb4b7f447cdfa9727f9bd564c83 100644 --- a/bundle.json +++ b/bundle.json @@ -79,7 +79,10 @@ "safwk", "samgr", "file_api", - "zlib" + "zlib", + "jsoncpp", + "openssl", + "sqlite" ] }, "build": { @@ -210,6 +213,35 @@ ], "header_base": "//foundation/distributeddatamgr/kv_store/databaseutils/include" } + }, + { + "name": "//foundation/distributeddatamgr/kv_store/frameworks/libs/distributeddb:distributeddb_client", + "header": { + "header_files": [ + "relational_store_client.h", + "relational_store_sqlite_ext.h", + "relational_store_manager.h" + ], + "header_base": "//foundation/distributeddatamgr/kv_store/frameworks/libs/distributeddb/interfaces/include/relational" + } + }, + { + "name": "//foundation/distributeddatamgr/kv_store/frameworks/libs/distributeddb:distributeddb_client", + "header": { + "header_files": [ + "store_observer.h" + ], + "header_base": "//foundation/distributeddatamgr/kv_store/frameworks/libs/distributeddb/interfaces/include" + } + }, + { + "name": "//foundation/distributeddatamgr/kv_store/frameworks/libs/distributeddb:distributeddb_client", + "header": { + "header_files": [ + "query.h" + ], + "header_base": "//foundation/distributeddatamgr/kv_store/frameworks/libs/distributeddb/include" + } } ], "test": [ diff --git a/frameworks/libs/distributeddb/BUILD.gn b/frameworks/libs/distributeddb/BUILD.gn index 31d0a2b23facfa71464455094942545e2ff386fd..cf6a67bb3f07a9e39b797895d82d9df3b0418385 100644 --- a/frameworks/libs/distributeddb/BUILD.gn +++ b/frameworks/libs/distributeddb/BUILD.gn @@ -91,6 +91,7 @@ group("build_module") { deps = [ ":customtokenizer", ":distributeddb", + ":distributeddb_client", ] } @@ -196,3 +197,100 @@ ohos_shared_library("customtokenizer") { innerapi_tags = [ "platformsdk_indirect" ] part_name = "kv_store" } + +config("distributeddb_client_public_config") { + visibility = [ "*:*" ] + include_dirs = [ + "interfaces/include/relational", + "interfaces/include", + "include", + ] +} + +config("distributeddb_client_config") { + visibility = [ ":*" ] + include_dirs = [ + "interfaces/src/relational", + "common/include/cloud", + "common/include", + "interfaces/include", + "interfaces/include/cloud", + "include", + "storage/include", + "communicator/include", + "common/include/relational", + "storage/src/sqlite", + "storage/src", + "storage/src/kv", + "syncer/src/device", + + "storage/src/sqlite/relational", + "storage/src/relational", + ] + + defines = [ + "_LARGEFILE64_SOURCE", + "_FILE_OFFSET_BITS=64", + "SQLITE_HAS_CODEC", + "SQLITE_ENABLE_JSON1", + "USING_HILOG_LOGGER", + "USE_SQLITE_SYMBOLS", + "USING_DB_JSON_EXTRACT_AUTOMATICALLY", + "JSONCPP_USE_BUILDER", + "OMIT_FLATBUFFER", + "OMIT_MULTI_VER", + "RELATIONAL_STORE", + "SQLITE_DISTRIBUTE_RELATIONAL", + "USE_DFX_ABILITY", + "SQLITE_ENABLE_DROPTABLE_CALLBACK", + "OPENSSL_SUPPRESS_DEPRECATED", + ] + if (is_debug) { + defines += [ "TRACE_SQLITE_EXECUTE" ] + } + if (is_ohos) { + defines += [ "USE_FFRT" ] + defines += [ "RDB_CLIENT" ] + } + if (kv_store_cloud) { + defines += [ "USE_DISTRIBUTEDDB_CLOUD" ] + } +} + +ohos_shared_library("distributeddb_client") { + branch_protector_ret = "pac_ret" + sanitize = { + ubsan = true + boundary_sanitize = true + cfi = true + cfi_cross_dso = true + debug = false + } + + sources = distributeddb_client_src + configs = [ ":distributeddb_client_config" ] + public_configs = [ ":distributeddb_client_public_config" ] + + use_exceptions = true + cflags_cc = [ + "-fvisibility=hidden", + "-Os", + "-D_FORTIFY_SOURCE=2", + ] + + external_deps = [ + "c_utils:utils", + "ffrt:libffrt", + "hilog:libhilog", + ] + + public_external_deps = [ + "openssl:libcrypto_shared", + "jsoncpp:jsoncpp", + "sqlite:sqlite", + ] + + subsystem_name = "distributeddatamgr" + innerapi_tags = [ "platformsdk_indirect" ] + part_name = "kv_store" +} diff --git a/frameworks/libs/distributeddb/common/include/cloud/cloud_db_constant.h b/frameworks/libs/distributeddb/common/include/cloud/cloud_db_constant.h index 464bcc6185bee0a1ba16416d484725bf7b5e99f3..5d3f97ab832bd3378b9f1ba3fce98fae470a5c2e 100644 --- a/frameworks/libs/distributeddb/common/include/cloud/cloud_db_constant.h +++ b/frameworks/libs/distributeddb/common/include/cloud/cloud_db_constant.h @@ -99,6 +99,7 @@ public: static constexpr const uint32_t ON_CHANGE_TRACKER = 0x1; static constexpr const uint32_t ON_CHANGE_P2P = 0x2; + static constexpr const uint32_t ON_CHANGE_KNOWLEDGE = 0x4; }; } // namespace DistributedDB #endif // CLOUD_DB_CONSTANT_H \ No newline at end of file diff --git a/frameworks/libs/distributeddb/common/include/concurrent_adapter.h b/frameworks/libs/distributeddb/common/include/concurrent_adapter.h index 8c436567d281fcddd252f71c696abf3d8f74aa00..cf3872742f41b235fa4b9f9a07bb4b0c019745e9 100644 --- a/frameworks/libs/distributeddb/common/include/concurrent_adapter.h +++ b/frameworks/libs/distributeddb/common/include/concurrent_adapter.h @@ -39,6 +39,7 @@ public: #ifdef USE_FFRT static void AdapterAutoLock(ffrt::mutex &mutex); static void AdapterAutoUnLock(ffrt::mutex &mutex); + static void Stop(); #else static void AdapterAutoLock(std::mutex &mutex); static void AdapterAutoUnLock(std::mutex &mutex); diff --git a/frameworks/libs/distributeddb/common/include/db_constant.h b/frameworks/libs/distributeddb/common/include/db_constant.h index acbc03a856fc8bd6d473bd0f63e2b34ed9a57f4b..6059dcf21089a9474ddb315e0a22ec6ba216e3dc 100644 --- a/frameworks/libs/distributeddb/common/include/db_constant.h +++ b/frameworks/libs/distributeddb/common/include/db_constant.h @@ -86,6 +86,7 @@ public: static constexpr const char *SCHEMA_KEY = "schemaKey"; static const std::string RELATIONAL_SCHEMA_KEY; static const std::string RELATIONAL_TRACKER_SCHEMA_KEY; + static constexpr const char *RDB_KNOWLEDGE_SCHEMA_KEY = "rdbKnowledgeSchemaKey"; static constexpr const char *RD_KV_COLLECTION_MODE = "{\"mode\" : \"kv\"}"; static constexpr const char *RD_KV_HASH_COLLECTION_MODE = "{\"mode\" : \"kv\",\"indextype\" : \"hash\"}"; diff --git a/frameworks/libs/distributeddb/common/include/log_print.h b/frameworks/libs/distributeddb/common/include/log_print.h index a7592f66251bebadb0a4b341e998a9ca2a42c742..7b3eb592b07f387176ff067fd87c0cd99f2e4631 100644 --- a/frameworks/libs/distributeddb/common/include/log_print.h +++ b/frameworks/libs/distributeddb/common/include/log_print.h @@ -36,6 +36,7 @@ public: virtual ~Logger() {}; static Logger *GetInstance(); + static void DeleteInstance(); static void RegisterLogger(Logger *logger); static void Log(Level level, const std::string &tag, const char *func, int line, const char *format, ...); diff --git a/frameworks/libs/distributeddb/common/include/relational/tracker_table.h b/frameworks/libs/distributeddb/common/include/relational/tracker_table.h index e46e1e8f4ac720644571a5edf259b0c353f2f81c..7e0f74f777f17b2769c9789dfdfda44efb2aced6 100644 --- a/frameworks/libs/distributeddb/common/include/relational/tracker_table.h +++ b/frameworks/libs/distributeddb/common/include/relational/tracker_table.h @@ -58,13 +58,17 @@ public: bool IsChanging(const TrackerSchema &schema); int ReBuildTempTrigger(sqlite3 *db, TriggerMode::TriggerModeEnum mode, const AfterBuildAction &action); void SetTrackerAction(bool isTrackerAction); - + void SetTriggerObserver(bool isTriggerObserver); + void SetKnowledgeTable(bool isKnowledgeTable); + std::string GetOnChangeType() const; private: std::string tableName_; std::string extendColName_; std::set extendColNames_; std::set trackerColNames_; bool isTrackerAction_ = false; + bool isTriggerObserver_ = true; + bool isKnowledgeTable_ = false; }; } // namespace DistributedDB diff --git a/frameworks/libs/distributeddb/common/include/schema_utils.h b/frameworks/libs/distributeddb/common/include/schema_utils.h index e1aec19db49141c9f0fe37b0ccee2bca502e0bd1..f9610033dfe3039f7be9fd3c10ada6afd78c850b 100644 --- a/frameworks/libs/distributeddb/common/include/schema_utils.h +++ b/frameworks/libs/distributeddb/common/include/schema_utils.h @@ -79,6 +79,8 @@ private: static int TransToString(const std::string &defaultContent, SchemaAttribute &outAttr); static int TransToBool(const std::string &defaultContent, SchemaAttribute &outAttr); + + static void TrimFiled(std::string &inString); }; } // namespace DistributedDB #endif // SCHEMA_UTILS_H \ No newline at end of file diff --git a/frameworks/libs/distributeddb/common/src/concurrent_adapter.cpp b/frameworks/libs/distributeddb/common/src/concurrent_adapter.cpp index 19198891fe52aa5c71b5e0dcb7497f4b1a8aab57..b0b99011f06f883b722378cb69ea585fd28fe512 100644 --- a/frameworks/libs/distributeddb/common/src/concurrent_adapter.cpp +++ b/frameworks/libs/distributeddb/common/src/concurrent_adapter.cpp @@ -62,6 +62,11 @@ void ConcurrentAdapter::AdapterAutoUnLock(ffrt::mutex &mutex) { mutex.unlock(); } + +void ConcurrentAdapter::Stop() +{ + ffrt::wait(); +} #else void ConcurrentAdapter::AdapterAutoLock(std::mutex &mutex) { diff --git a/frameworks/libs/distributeddb/common/src/db_common.cpp b/frameworks/libs/distributeddb/common/src/db_common.cpp index e735fed762f55427a90f107d7855ee70a497f7fb..cd5cf43153b5d1a49dac83c76552a166e7a44136 100644 --- a/frameworks/libs/distributeddb/common/src/db_common.cpp +++ b/frameworks/libs/distributeddb/common/src/db_common.cpp @@ -33,16 +33,9 @@ #include "query_sync_object.h" #include "hash.h" #include "runtime_context.h" -#include "value_hash_calc.h" namespace DistributedDB { namespace { - constexpr const int32_t HEAD_SIZE = 3; - constexpr const int32_t END_SIZE = 3; - constexpr const int32_t MIN_SIZE = HEAD_SIZE + END_SIZE + 3; - constexpr const char *REPLACE_CHAIN = "***"; - constexpr const char *DEFAULT_ANONYMOUS = "******"; - void RemoveFiles(const std::list &fileList, OS::FileType type) { for (const auto &item : fileList) { @@ -68,7 +61,6 @@ namespace { } } } - const std::string HEX_CHAR_MAP = "0123456789abcdef"; const std::string CAP_HEX_CHAR_MAP = "0123456789ABCDEF"; } @@ -87,12 +79,6 @@ int DBCommon::CreateDirectory(const std::string &directory) return E_OK; } -void DBCommon::StringToVector(const std::string &src, std::vector &dst) -{ - dst.resize(src.size()); - dst.assign(src.begin(), src.end()); -} - void DBCommon::VectorToString(const std::vector &src, std::string &dst) { dst.clear(); @@ -142,57 +128,6 @@ void DBCommon::PrintHexVector(const std::vector &data, int line, const return; } -std::string DBCommon::TransferHashString(const std::string &devName) -{ - if (devName.empty()) { - return ""; - } - std::vector devVect(devName.begin(), devName.end()); - std::vector hashVect; - int errCode = CalcValueHash(devVect, hashVect); - if (errCode != E_OK) { - return ""; - } - - return std::string(hashVect.begin(), hashVect.end()); -} - -std::string DBCommon::TransferStringToHex(const std::string &origStr) -{ - if (origStr.empty()) { - return ""; - } - - std::string tmp; - for (auto item : origStr) { - unsigned char currentByte = static_cast(item); - tmp.push_back(HEX_CHAR_MAP[currentByte >> 4]); // high 4 bits to one hex. - tmp.push_back(HEX_CHAR_MAP[currentByte & 0x0F]); // low 4 bits to one hex. - } - return tmp; -} - -int DBCommon::CalcValueHash(const std::vector &value, std::vector &hashValue) -{ - ValueHashCalc hashCalc; - int errCode = hashCalc.Initialize(); - if (errCode != E_OK) { - return -E_INTERNAL_ERROR; - } - - errCode = hashCalc.Update(value); - if (errCode != E_OK) { - return -E_INTERNAL_ERROR; - } - - errCode = hashCalc.GetResult(hashValue); - if (errCode != E_OK) { - return -E_INTERNAL_ERROR; - } - - return E_OK; -} - int DBCommon::CreateStoreDirectory(const std::string &directory, const std::string &identifierName, const std::string &subDir, bool isCreate) { @@ -346,19 +281,6 @@ std::string DBCommon::StringMasking(const std::string &oriStr, size_t remain) return oriStr; } -std::string DBCommon::StringMiddleMasking(const std::string &name) -{ - if (name.length() <= HEAD_SIZE) { - return DEFAULT_ANONYMOUS; - } - - if (name.length() < MIN_SIZE) { - return (name.substr(0, HEAD_SIZE) + REPLACE_CHAIN); - } - - return (name.substr(0, HEAD_SIZE) + REPLACE_CHAIN + name.substr(name.length() - END_SIZE, END_SIZE)); -} - std::string DBCommon::GetDistributedTableName(const std::string &device, const std::string &tableName) { if (!RuntimeContext::GetInstance()->ExistTranslateDevIdCallback()) { @@ -399,57 +321,6 @@ void DBCommon::GetDeviceFromName(const std::string &deviceTableName, std::string } } -std::string DBCommon::TrimSpace(const std::string &input) -{ - std::string res; - res.reserve(input.length()); - bool isPreSpace = true; - for (char c : input) { - if (std::isspace(c)) { - isPreSpace = true; - } else { - if (!res.empty() && isPreSpace) { - res += ' '; - } - res += c; - isPreSpace = false; - } - } - res.shrink_to_fit(); - return res; -} - -void DBCommon::RTrim(std::string &oriString) -{ - if (oriString.empty()) { - return; - } - oriString.erase(oriString.find_last_not_of(" ") + 1); -} - -namespace { -bool CharIn(char c, const std::string &pattern) -{ - return std::any_of(pattern.begin(), pattern.end(), [c] (char p) { - return c == p; - }); -} -} - -bool DBCommon::HasConstraint(const std::string &sql, const std::string &keyWord, const std::string &prePattern, - const std::string &nextPattern) -{ - size_t pos = 0; - while ((pos = sql.find(keyWord, pos)) != std::string::npos) { - if (pos >= 1 && CharIn(sql[pos - 1], prePattern) && ((pos + keyWord.length() == sql.length()) || - ((pos + keyWord.length() < sql.length()) && CharIn(sql[pos + keyWord.length()], nextPattern)))) { - return true; - } - pos++; - } - return false; -} - bool DBCommon::IsSameCipher(CipherType srcType, CipherType inputType) { // At present, the default type is AES-256-GCM. @@ -464,33 +335,6 @@ bool DBCommon::IsSameCipher(CipherType srcType, CipherType inputType) return false; } -std::string DBCommon::ToLowerCase(const std::string &str) -{ - std::string res(str.length(), ' '); - std::transform(str.begin(), str.end(), res.begin(), ::tolower); - return res; -} - -std::string DBCommon::ToUpperCase(const std::string &str) -{ - std::string res(str.length(), ' '); - std::transform(str.begin(), str.end(), res.begin(), ::toupper); - return res; -} - -bool DBCommon::CaseInsensitiveCompare(const std::string &first, const std::string &second) -{ - return (strcasecmp(first.c_str(), second.c_str()) == 0); -} - -bool DBCommon::CheckIsAlnumOrUnderscore(const std::string &text) -{ - auto iter = std::find_if_not(text.begin(), text.end(), [](char c) { - return (std::isalnum(c) || c == '_'); - }); - return iter == text.end(); -} - bool DBCommon::CheckQueryWithoutMultiTable(const Query &query) { if (!QuerySyncObject::GetQuerySyncObject(query).empty()) { @@ -804,11 +648,6 @@ bool DBCommon::CheckCloudSyncConfigValid(const CloudSyncConfig &config) return true; } -std::string DBCommon::GetCursorKey(const std::string &tableName) -{ - return std::string(DBConstant::RELATIONAL_PREFIX) + "cursor_" + ToLowerCase(tableName); -} - bool DBCommon::ConvertToUInt64(const std::string &str, uint64_t &value) { auto [ptr, errCode] = std::from_chars(str.data(), str.data() + str.size(), value); diff --git a/frameworks/libs/distributeddb/common/src/db_common_client.cpp b/frameworks/libs/distributeddb/common/src/db_common_client.cpp new file mode 100644 index 0000000000000000000000000000000000000000..60ea818bc393f7cdaa0ee9180e743aa4658d2402 --- /dev/null +++ b/frameworks/libs/distributeddb/common/src/db_common_client.cpp @@ -0,0 +1,182 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "db_common.h" +#include "value_hash_calc.h" + +namespace DistributedDB { +namespace { + constexpr const int32_t HEAD_SIZE = 3; + constexpr const int32_t END_SIZE = 3; + constexpr const int32_t MIN_SIZE = HEAD_SIZE + END_SIZE + 3; + constexpr const char *REPLACE_CHAIN = "***"; + constexpr const char *DEFAULT_ANONYMOUS = "******"; + + const std::string HEX_CHAR_MAP = "0123456789abcdef"; +} + +void DBCommon::StringToVector(const std::string &src, std::vector &dst) +{ + dst.resize(src.size()); + dst.assign(src.begin(), src.end()); +} + +std::string DBCommon::StringMiddleMasking(const std::string &name) +{ + if (name.length() <= HEAD_SIZE) { + return DEFAULT_ANONYMOUS; + } + + if (name.length() < MIN_SIZE) { + return (name.substr(0, HEAD_SIZE) + REPLACE_CHAIN); + } + + return (name.substr(0, HEAD_SIZE) + REPLACE_CHAIN + name.substr(name.length() - END_SIZE, END_SIZE)); +} + +void DBCommon::RTrim(std::string &oriString) +{ + if (oriString.empty()) { + return; + } + oriString.erase(oriString.find_last_not_of(" ") + 1); +} + +bool DBCommon::CheckIsAlnumOrUnderscore(const std::string &text) +{ + auto iter = std::find_if_not(text.begin(), text.end(), [](char c) { + return (std::isalnum(c) || c == '_'); + }); + return iter == text.end(); +} + +std::string DBCommon::ToLowerCase(const std::string &str) +{ + std::string res(str.length(), ' '); + std::transform(str.begin(), str.end(), res.begin(), ::tolower); + return res; +} + +std::string DBCommon::ToUpperCase(const std::string &str) +{ + std::string res(str.length(), ' '); + std::transform(str.begin(), str.end(), res.begin(), ::toupper); + return res; +} + +int DBCommon::CalcValueHash(const std::vector &value, std::vector &hashValue) +{ + ValueHashCalc hashCalc; + int errCode = hashCalc.Initialize(); + if (errCode != E_OK) { + return -E_INTERNAL_ERROR; + } + + errCode = hashCalc.Update(value); + if (errCode != E_OK) { + return -E_INTERNAL_ERROR; + } + + errCode = hashCalc.GetResult(hashValue); + if (errCode != E_OK) { + return -E_INTERNAL_ERROR; + } + + return E_OK; +} + + +namespace { +bool CharIn(char c, const std::string &pattern) +{ + return std::any_of(pattern.begin(), pattern.end(), [c] (char p) { + return c == p; + }); +} +} +bool DBCommon::HasConstraint(const std::string &sql, const std::string &keyWord, const std::string &prePattern, + const std::string &nextPattern) +{ + size_t pos = 0; + while ((pos = sql.find(keyWord, pos)) != std::string::npos) { + if (pos >= 1 && CharIn(sql[pos - 1], prePattern) && ((pos + keyWord.length() == sql.length()) || + ((pos + keyWord.length() < sql.length()) && CharIn(sql[pos + keyWord.length()], nextPattern)))) { + return true; + } + pos++; + } + return false; +} + +std::string DBCommon::TransferStringToHex(const std::string &origStr) +{ + if (origStr.empty()) { + return ""; + } + + std::string tmp; + for (auto item : origStr) { + unsigned char currentByte = static_cast(item); + tmp.push_back(HEX_CHAR_MAP[currentByte >> 4]); // high 4 bits to one hex. + tmp.push_back(HEX_CHAR_MAP[currentByte & 0x0F]); // low 4 bits to one hex. + } + return tmp; +} + +std::string DBCommon::GetCursorKey(const std::string &tableName) +{ + return std::string(DBConstant::RELATIONAL_PREFIX) + "cursor_" + ToLowerCase(tableName); +} + +std::string DBCommon::TrimSpace(const std::string &input) +{ + std::string res; + res.reserve(input.length()); + bool isPreSpace = true; + for (char c : input) { + if (std::isspace(c)) { + isPreSpace = true; + } else { + if (!res.empty() && isPreSpace) { + res += ' '; + } + res += c; + isPreSpace = false; + } + } + res.shrink_to_fit(); + return res; +} + +std::string DBCommon::TransferHashString(const std::string &devName) +{ + if (devName.empty()) { + return ""; + } + std::vector devVect(devName.begin(), devName.end()); + std::vector hashVect; + int errCode = CalcValueHash(devVect, hashVect); + if (errCode != E_OK) { + return ""; + } + + return std::string(hashVect.begin(), hashVect.end()); +} + +bool DBCommon::CaseInsensitiveCompare(const std::string &first, const std::string &second) +{ + return (strcasecmp(first.c_str(), second.c_str()) == 0); +} +} // namespace DistributedDB diff --git a/frameworks/libs/distributeddb/common/src/log_print.cpp b/frameworks/libs/distributeddb/common/src/log_print.cpp index 92a014de32a2cf40e7c48429aee7aa9a92d332b2..d1429b60c06bbbf115c8fc52fcd8472fcea8889f 100644 --- a/frameworks/libs/distributeddb/common/src/log_print.cpp +++ b/frameworks/libs/distributeddb/common/src/log_print.cpp @@ -27,6 +27,8 @@ namespace DistributedDB { Logger *Logger::logHandler = nullptr; const std::string Logger::PRIVATE_TAG = "s{private}"; +static std::mutex g_logInstanceLock; +static std::atomic g_logInstance = nullptr; class HiLogger : public Logger { public: @@ -65,17 +67,26 @@ public: Logger *Logger::GetInstance() { - static std::mutex logInstanceLock; - static std::atomic logInstance = nullptr; // For Double-Checked Locking, we need check logInstance twice - if (logInstance == nullptr) { - std::lock_guard lock(logInstanceLock); - if (logInstance == nullptr) { + if (g_logInstance == nullptr) { + std::lock_guard lock(g_logInstanceLock); + if (g_logInstance == nullptr) { // Here, we new logInstance to print log, if new failed, we can do nothing. - logInstance = new (std::nothrow) HiLogger; + g_logInstance = new (std::nothrow) HiLogger; } } - return logInstance; + return g_logInstance; +} + +void Logger::DeleteInstance() +{ + if (g_logInstance == nullptr) { + return; + } + std::lock_guard lock(g_logInstanceLock); + delete g_logInstance; + g_logInstance = nullptr; + logHandler = nullptr; } void Logger::RegisterLogger(Logger *logger) diff --git a/frameworks/libs/distributeddb/common/src/param_check_utils.cpp b/frameworks/libs/distributeddb/common/src/param_check_utils.cpp index 5fdf237a6473752a137c6151ccd77bc62fc78bb3..e5ec9c23e80a8b57c6c00b30cc0df5a2703dff6a 100644 --- a/frameworks/libs/distributeddb/common/src/param_check_utils.cpp +++ b/frameworks/libs/distributeddb/common/src/param_check_utils.cpp @@ -217,14 +217,6 @@ uint8_t ParamCheckUtils::GetValidCompressionRate(uint8_t compressionRate) return compressionRate; } -bool ParamCheckUtils::CheckRelationalTableName(const std::string &tableName) -{ - if (!DBCommon::CheckIsAlnumOrUnderscore(tableName)) { - return false; - } - return tableName.compare(0, DBConstant::SYSTEM_TABLE_PREFIX.size(), DBConstant::SYSTEM_TABLE_PREFIX) != 0; -} - bool ParamCheckUtils::CheckTableReference(const std::vector &tableReferenceProperty) { if (tableReferenceProperty.empty()) { diff --git a/frameworks/libs/distributeddb/common/src/param_check_utils_client.cpp b/frameworks/libs/distributeddb/common/src/param_check_utils_client.cpp new file mode 100644 index 0000000000000000000000000000000000000000..001fd29559935012854b73c1486aa1f125c3e3b2 --- /dev/null +++ b/frameworks/libs/distributeddb/common/src/param_check_utils_client.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "param_check_utils.h" + +#include "db_common.h" + +namespace DistributedDB { +bool ParamCheckUtils::CheckRelationalTableName(const std::string &tableName) +{ + if (!DBCommon::CheckIsAlnumOrUnderscore(tableName)) { + return false; + } + return tableName.compare(0, DBConstant::SYSTEM_TABLE_PREFIX.size(), DBConstant::SYSTEM_TABLE_PREFIX) != 0; +} +} // namespace DistributedDB \ No newline at end of file diff --git a/frameworks/libs/distributeddb/common/src/relational/tracker_table.cpp b/frameworks/libs/distributeddb/common/src/relational/tracker_table.cpp index 685740d916782bbfde4683e46e003d00b08644f3..6b7745777225d883cc0b9ef011bfdf733caea23b 100644 --- a/frameworks/libs/distributeddb/common/src/relational/tracker_table.cpp +++ b/frameworks/libs/distributeddb/common/src/relational/tracker_table.cpp @@ -81,7 +81,7 @@ const std::string TrackerTable::GetExtendAssignValSql(bool isDelete) const const std::string TrackerTable::GetDiffTrackerValSql() const { if (trackerColNames_.empty() || isTrackerAction_) { - return isTrackerAction_ ? "1" : "0"; + return isTrackerAction_ ? GetOnChangeType() : "0"; } std::string sql = " CASE WHEN ("; size_t index = 0; @@ -92,7 +92,7 @@ const std::string TrackerTable::GetDiffTrackerValSql() const } index++; } - sql += ") THEN 1 ELSE 0 END"; + sql += ") THEN " + GetOnChangeType() + " ELSE 0 END"; return sql; } @@ -213,7 +213,7 @@ const std::string TrackerTable::GetTempInsertTriggerSql(bool incFlag) const sql += "cursor=" + CloudStorageUtils::GetSelectIncCursorSql(tableName_) + " WHERE"; } sql += " hash_key = NEW.hash_key;\n"; - if (!IsEmpty()) { + if (!IsEmpty() && isTriggerObserver_) { sql += "SELECT server_observer('" + tableName_ + "', 1);"; } sql += "\nEND;"; @@ -244,7 +244,7 @@ const std::string TrackerTable::GetTempUpdateTriggerSql(bool incFlag) const sql += "cursor=" + CloudStorageUtils::GetSelectIncCursorSql(tableName_) + " WHERE"; } sql += " data_key = OLD." + std::string(DBConstant::SQLITE_INNER_ROWID) + ";\n"; - if (!IsEmpty()) { + if (!IsEmpty() && isTriggerObserver_) { sql += "SELECT server_observer('" + tableName_ + "', " + GetDiffTrackerValSql() + ");"; } sql += "\nEND;"; @@ -277,7 +277,7 @@ const std::string TrackerTable::GetTempDeleteTriggerSql(bool incFlag) const sql.pop_back(); } sql += " WHERE data_key = OLD." + std::string(DBConstant::SQLITE_INNER_ROWID) + ";\n"; - if (!IsEmpty()) { + if (!IsEmpty() && isTriggerObserver_) { sql += "SELECT server_observer('" + tableName_ + "', 1);"; } sql += "\nEND;"; @@ -382,5 +382,21 @@ void TrackerTable::SetTrackerAction(bool isTrackerAction) { isTrackerAction_ = isTrackerAction; } + +void TrackerTable::SetTriggerObserver(bool isTriggerObserver) +{ + isTriggerObserver_ = isTriggerObserver; +} + +void TrackerTable::SetKnowledgeTable(bool isKnowledgeTable) +{ + isKnowledgeTable_ = isKnowledgeTable; +} + +std::string TrackerTable::GetOnChangeType() const +{ + return isKnowledgeTable_ ? std::to_string(CloudDbConstant::ON_CHANGE_KNOWLEDGE) : + std::to_string(CloudDbConstant::ON_CHANGE_TRACKER); +} } #endif diff --git a/frameworks/libs/distributeddb/common/src/schema_utils.cpp b/frameworks/libs/distributeddb/common/src/schema_utils.cpp index f4814779bfd0e2ac6e09941f7a5b3117c9c298f8..284051c47a94531e4c13ad27aacc7fdcf039e881 100644 --- a/frameworks/libs/distributeddb/common/src/schema_utils.cpp +++ b/frameworks/libs/distributeddb/common/src/schema_utils.cpp @@ -31,14 +31,6 @@ namespace { { return (std::isalnum(character) || character == '_'); } - void TrimFiled(std::string &inString) - { - inString.erase(0, inString.find_first_not_of("\r\t ")); - size_t temp = inString.find_last_not_of("\r\t "); - if (temp < inString.size()) { - inString.erase(temp + 1); - } - } // TYPE, [NOT NULL,] [DEFAULT X] // DEFAULT at last @@ -449,13 +441,6 @@ int SchemaUtils::CheckFieldName(const FieldName &inName) return E_OK; } -std::string SchemaUtils::Strip(const std::string &inString) -{ - std::string stripRes = inString; - TrimFiled(stripRes); - return stripRes; -} - std::string SchemaUtils::StripNameSpace(const std::string &inFullName) { auto pos = inFullName.find_last_of('.'); @@ -465,22 +450,6 @@ std::string SchemaUtils::StripNameSpace(const std::string &inFullName) return inFullName.substr(pos + 1); } -std::string SchemaUtils::FieldTypeString(FieldType inType) -{ - static std::map fieldTypeMapString = { - {FieldType::LEAF_FIELD_NULL, "NULL"}, - {FieldType::LEAF_FIELD_BOOL, "BOOL"}, - {FieldType::LEAF_FIELD_INTEGER, "INTEGER"}, - {FieldType::LEAF_FIELD_LONG, "LONG"}, - {FieldType::LEAF_FIELD_DOUBLE, "DOUBLE"}, - {FieldType::LEAF_FIELD_STRING, "STRING"}, - {FieldType::LEAF_FIELD_ARRAY, "ARRAY"}, - {FieldType::LEAF_FIELD_OBJECT, "LEAF_OBJECT"}, - {FieldType::INTERNAL_FIELD_OBJECT, "INTERNAL_OBJECT"}, - }; - return fieldTypeMapString[inType]; -} - std::string SchemaUtils::SchemaTypeString(SchemaType inType) { static std::map schemaTypeMapString { @@ -515,50 +484,4 @@ void SchemaUtils::TransTrackerSchemaToLower(const TrackerSchema &srcSchema, Trac destSchema.trackerColNames.insert(DBCommon::ToLowerCase(srcName)); } } - -int SchemaUtils::ExtractJsonObj(const JsonObject &inJsonObject, const std::string &field, - JsonObject &out) -{ - FieldType fieldType; - auto fieldPath = FieldPath {field}; - int errCode = inJsonObject.GetFieldTypeByFieldPath(fieldPath, fieldType); - if (errCode != E_OK) { - LOGE("[SchemaUtils][ExtractJsonObj] Get schema %s fieldType failed: %d.", field.c_str(), errCode); - return -E_SCHEMA_PARSE_FAIL; - } - if (FieldType::INTERNAL_FIELD_OBJECT != fieldType) { - LOGE("[SchemaUtils][ExtractJsonObj] Expect %s Object but %s.", field.c_str(), - SchemaUtils::FieldTypeString(fieldType).c_str()); - return -E_SCHEMA_PARSE_FAIL; - } - errCode = inJsonObject.GetObjectByFieldPath(fieldPath, out); - if (errCode != E_OK) { - LOGE("[SchemaUtils][ExtractJsonObj] Get schema %s value failed: %d.", field.c_str(), errCode); - return -E_SCHEMA_PARSE_FAIL; - } - return E_OK; -} - -int SchemaUtils::ExtractJsonObjArray(const JsonObject &inJsonObject, const std::string &field, - std::vector &out) -{ - FieldType fieldType; - auto fieldPath = FieldPath {field}; - int errCode = inJsonObject.GetFieldTypeByFieldPath(fieldPath, fieldType); - if (errCode != E_OK) { - LOGE("[SchemaUtils][ExtractJsonObj] Get schema %s fieldType failed: %d.", field.c_str(), errCode); - return -E_SCHEMA_PARSE_FAIL; - } - if (FieldType::LEAF_FIELD_ARRAY != fieldType) { - LOGE("[SchemaUtils][ExtractJsonObj] Expect %s Object but %s.", field.c_str(), - SchemaUtils::FieldTypeString(fieldType).c_str()); - return -E_SCHEMA_PARSE_FAIL; - } - errCode = inJsonObject.GetObjectArrayByFieldPath(fieldPath, out); - if (errCode != E_OK) { - LOGE("[SchemaUtils][ExtractJsonObj] Get schema %s value failed: %d.", field.c_str(), errCode); - return -E_SCHEMA_PARSE_FAIL; - } - return E_OK; -} } // namespace DistributedDB diff --git a/frameworks/libs/distributeddb/common/src/schema_utils_client.cpp b/frameworks/libs/distributeddb/common/src/schema_utils_client.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1406683e9c1f919377777c1f670cce78a7204ab2 --- /dev/null +++ b/frameworks/libs/distributeddb/common/src/schema_utils_client.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "schema_utils.h" +namespace DistributedDB { +void SchemaUtils::TrimFiled(std::string &inString) +{ + inString.erase(0, inString.find_first_not_of("\r\t ")); + size_t temp = inString.find_last_not_of("\r\t "); + if (temp < inString.size()) { + inString.erase(temp + 1); + } +} + +std::string SchemaUtils::Strip(const std::string &inString) +{ + std::string stripRes = inString; + TrimFiled(stripRes); + return stripRes; +} + +std::string SchemaUtils::FieldTypeString(FieldType inType) +{ + static std::map fieldTypeMapString = { + {FieldType::LEAF_FIELD_NULL, "NULL"}, + {FieldType::LEAF_FIELD_BOOL, "BOOL"}, + {FieldType::LEAF_FIELD_INTEGER, "INTEGER"}, + {FieldType::LEAF_FIELD_LONG, "LONG"}, + {FieldType::LEAF_FIELD_DOUBLE, "DOUBLE"}, + {FieldType::LEAF_FIELD_STRING, "STRING"}, + {FieldType::LEAF_FIELD_ARRAY, "ARRAY"}, + {FieldType::LEAF_FIELD_OBJECT, "LEAF_OBJECT"}, + {FieldType::INTERNAL_FIELD_OBJECT, "INTERNAL_OBJECT"}, + }; + return fieldTypeMapString[inType]; +} + +int SchemaUtils::ExtractJsonObj(const JsonObject &inJsonObject, const std::string &field, + JsonObject &out) +{ + FieldType fieldType; + auto fieldPath = FieldPath {field}; + int errCode = inJsonObject.GetFieldTypeByFieldPath(fieldPath, fieldType); + if (errCode != E_OK) { + LOGE("[SchemaUtils][ExtractJsonObj] Get schema %s fieldType failed: %d.", field.c_str(), errCode); + return -E_SCHEMA_PARSE_FAIL; + } + if (FieldType::INTERNAL_FIELD_OBJECT != fieldType) { + LOGE("[SchemaUtils][ExtractJsonObj] Expect %s Object but %s.", field.c_str(), + SchemaUtils::FieldTypeString(fieldType).c_str()); + return -E_SCHEMA_PARSE_FAIL; + } + errCode = inJsonObject.GetObjectByFieldPath(fieldPath, out); + if (errCode != E_OK) { + LOGE("[SchemaUtils][ExtractJsonObj] Get schema %s value failed: %d.", field.c_str(), errCode); + return -E_SCHEMA_PARSE_FAIL; + } + return E_OK; +} + +int SchemaUtils::ExtractJsonObjArray(const JsonObject &inJsonObject, const std::string &field, + std::vector &out) +{ + FieldType fieldType; + auto fieldPath = FieldPath {field}; + int errCode = inJsonObject.GetFieldTypeByFieldPath(fieldPath, fieldType); + if (errCode != E_OK) { + LOGE("[SchemaUtils][ExtractJsonObj] Get schema %s fieldType failed: %d.", field.c_str(), errCode); + return -E_SCHEMA_PARSE_FAIL; + } + if (FieldType::LEAF_FIELD_ARRAY != fieldType) { + LOGE("[SchemaUtils][ExtractJsonObj] Expect %s Object but %s.", field.c_str(), + SchemaUtils::FieldTypeString(fieldType).c_str()); + return -E_SCHEMA_PARSE_FAIL; + } + errCode = inJsonObject.GetObjectArrayByFieldPath(fieldPath, out); + if (errCode != E_OK) { + LOGE("[SchemaUtils][ExtractJsonObj] Get schema %s value failed: %d.", field.c_str(), errCode); + return -E_SCHEMA_PARSE_FAIL; + } + return E_OK; +} +} // namespace DistributedDB diff --git a/frameworks/libs/distributeddb/distributeddb.gni b/frameworks/libs/distributeddb/distributeddb.gni index def61407b0d06055329272bcdbdb3397fd97ddc8..cf7e25ef21e04d32f205753cc86d84c0e2b21a9e 100644 --- a/frameworks/libs/distributeddb/distributeddb.gni +++ b/frameworks/libs/distributeddb/distributeddb.gni @@ -26,6 +26,7 @@ distributeddb_src = [ "${distributeddb_path}/common/src/data_compression.cpp", "${distributeddb_path}/common/src/data_value.cpp", "${distributeddb_path}/common/src/db_common.cpp", + "${distributeddb_path}/common/src/db_common_client.cpp", "${distributeddb_path}/common/src/db_base64_utils.cpp", "${distributeddb_path}/common/src/db_constant.cpp", "${distributeddb_path}/common/src/db_dfx_adapter.cpp", @@ -43,6 +44,7 @@ distributeddb_src = [ "${distributeddb_path}/common/src/log_print.cpp", "${distributeddb_path}/common/src/notification_chain.cpp", "${distributeddb_path}/common/src/param_check_utils.cpp", + "${distributeddb_path}/common/src/param_check_utils_client.cpp", "${distributeddb_path}/common/src/parcel.cpp", "${distributeddb_path}/common/src/performance_analysis.cpp", "${distributeddb_path}/common/src/platform_specific.cpp", @@ -62,6 +64,7 @@ distributeddb_src = [ "${distributeddb_path}/common/src/schema_negotiate.cpp", "${distributeddb_path}/common/src/schema_object.cpp", "${distributeddb_path}/common/src/schema_utils.cpp", + "${distributeddb_path}/common/src/schema_utils_client.cpp", "${distributeddb_path}/common/src/semaphore_utils.cpp", "${distributeddb_path}/common/src/task_pool.cpp", "${distributeddb_path}/common/src/task_pool_impl.cpp", @@ -96,10 +99,13 @@ distributeddb_src = [ "${distributeddb_path}/interfaces/src/relational/relational_store_changed_data_impl.cpp", "${distributeddb_path}/interfaces/src/relational/relational_store_delegate_impl.cpp", "${distributeddb_path}/interfaces/src/relational/relational_store_manager.cpp", + "${distributeddb_path}/interfaces/src/relational/relational_store_manager_client.cpp", "${distributeddb_path}/interfaces/src/relational/relational_store_sqlite_ext.cpp", + "${distributeddb_path}/interfaces/src/relational/knowledge_source_utils.cpp", "${distributeddb_path}/interfaces/src/runtime_config.cpp", "${distributeddb_path}/storage/src/cloud/cloud_meta_data.cpp", "${distributeddb_path}/storage/src/cloud/cloud_storage_utils.cpp", + "${distributeddb_path}/storage/src/cloud/cloud_storage_utils_client.cpp", "${distributeddb_path}/storage/src/cloud/cloud_upload_recorder.cpp", "${distributeddb_path}/storage/src/cloud/schema_mgr.cpp", "${distributeddb_path}/storage/src/data_transformer.cpp", @@ -171,6 +177,7 @@ distributeddb_src = [ "${distributeddb_path}/storage/src/sqlite/relational/sqlite_relational_store.cpp", "${distributeddb_path}/storage/src/sqlite/relational/sqlite_relational_store_connection.cpp", "${distributeddb_path}/storage/src/sqlite/relational/sqlite_relational_utils.cpp", + "${distributeddb_path}/storage/src/sqlite/relational/sqlite_relational_utils_client.cpp", "${distributeddb_path}/storage/src/sqlite/relational/sqlite_single_relational_storage_engine.cpp", "${distributeddb_path}/storage/src/sqlite/relational/sqlite_single_ver_relational_continue_token.cpp", "${distributeddb_path}/storage/src/sqlite/relational/sqlite_single_ver_relational_storage_executor.cpp", @@ -201,6 +208,7 @@ distributeddb_src = [ "${distributeddb_path}/storage/src/sqlite/sqlite_storage_engine.cpp", "${distributeddb_path}/storage/src/sqlite/sqlite_storage_executor.cpp", "${distributeddb_path}/storage/src/sqlite/sqlite_utils.cpp", + "${distributeddb_path}/storage/src/sqlite/sqlite_utils_client.cpp", "${distributeddb_path}/storage/src/sqlite/sqlite_utils_extend.cpp", "${distributeddb_path}/storage/src/storage_engine.cpp", "${distributeddb_path}/storage/src/storage_engine_manager.cpp", @@ -300,3 +308,30 @@ distributeddb_src_rd = [ "${distributeddb_path}/gaussdb_rd/src/oh_adapter/src/sqlite_store_executor_impl.cpp", "${distributeddb_path}/gaussdb_rd/src/oh_adapter/src/rd_sqlite_utils.cpp", ] + +distributeddb_client_src = [ + "${distributeddb_path}/common/src/cjson_object.cpp", + "${distributeddb_path}/common/src/concurrent_adapter.cpp", + "${distributeddb_path}/common/src/db_common_client.cpp", + "${distributeddb_path}/common/src/db_constant.cpp", + "${distributeddb_path}/common/src/json_object.cpp", + "${distributeddb_path}/common/src/log_print.cpp", + "${distributeddb_path}/common/src/param_check_utils_client.cpp", + "${distributeddb_path}/common/src/platform_specific.cpp", + "${distributeddb_path}/common/src/relational/relational_schema_object.cpp", + "${distributeddb_path}/common/src/relational/table_info.cpp", + "${distributeddb_path}/common/src/relational/tracker_table.cpp", + "${distributeddb_path}/common/src/schema_constant.cpp", + "${distributeddb_path}/common/src/schema_utils_client.cpp", + "${distributeddb_path}/interfaces/src/relational/knowledge_source_utils.cpp", + "${distributeddb_path}/interfaces/src/relational/relational_store_manager_client.cpp", + "${distributeddb_path}/interfaces/src/relational/relational_store_sqlite_ext.cpp", + "${distributeddb_path}/interfaces/src/kv_store_errno.cpp", + "${distributeddb_path}/storage/src/cloud/cloud_storage_utils_client.cpp", + "${distributeddb_path}/storage/src/sqlite/relational/cloud_sync_log_table_manager.cpp", + "${distributeddb_path}/storage/src/sqlite/relational/simple_tracker_log_table_manager.cpp", + "${distributeddb_path}/storage/src/sqlite/relational/split_device_log_table_manager.cpp", + "${distributeddb_path}/storage/src/sqlite/relational/sqlite_relational_utils_client.cpp", + "${distributeddb_path}/storage/src/sqlite/sqlite_log_table_manager.cpp", + "${distributeddb_path}/storage/src/sqlite/sqlite_utils_client.cpp", +] diff --git a/frameworks/libs/distributeddb/interfaces/include/relational/relational_store_client.h b/frameworks/libs/distributeddb/interfaces/include/relational/relational_store_client.h index 9942baeaa8e5a4f3ccbf3044316ac2710178bf8d..1be216ce362dad12eb744f657f7bed0f1fcdd602 100644 --- a/frameworks/libs/distributeddb/interfaces/include/relational/relational_store_client.h +++ b/frameworks/libs/distributeddb/interfaces/include/relational/relational_store_client.h @@ -58,4 +58,19 @@ DB_API void RegisterDbHook(sqlite3 *db); DB_API void UnregisterDbHook(sqlite3 *db); DB_API DistributedDB::DBStatus CreateDataChangeTempTrigger(sqlite3 *db); + +namespace DistributedDB { +struct KnowledgeSourceSchema { + std::string tableName; + std::set extendColNames; + std::set knowledgeColNames; +}; +} + +DB_API DistributedDB::DBStatus SetKnowledgeSourceSchema(sqlite3 *db, + const DistributedDB::KnowledgeSourceSchema &schema); + +DB_API DistributedDB::DBStatus CleanDeletedData(sqlite3 *db, const std::string &tableName, uint64_t cursor); + +DB_API void Clean(bool isOpenSslClean); #endif // RELATIONAL_STORE_CLIENT_H diff --git a/frameworks/libs/distributeddb/interfaces/include/store_types.h b/frameworks/libs/distributeddb/interfaces/include/store_types.h index a75a5b428c3e8f87cd47642d2fbc2127d46c6ffd..a5dd8fd813d4742f1b1946a9b09a392fa65637ac 100644 --- a/frameworks/libs/distributeddb/interfaces/include/store_types.h +++ b/frameworks/libs/distributeddb/interfaces/include/store_types.h @@ -258,6 +258,7 @@ static constexpr const char *SQLITE = "sqlite"; struct ChangeProperties { bool isTrackedDataChange = false; bool isP2pSyncDataChange = false; + bool isKnowledgeDataChange = false; }; enum IndexType : uint32_t { diff --git a/frameworks/libs/distributeddb/interfaces/src/relational/knowledge_source_utils.cpp b/frameworks/libs/distributeddb/interfaces/src/relational/knowledge_source_utils.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0df636c9d2afd29209b69bc1922f8001ac194228 --- /dev/null +++ b/frameworks/libs/distributeddb/interfaces/src/relational/knowledge_source_utils.cpp @@ -0,0 +1,322 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "knowledge_source_utils.h" + +#include "db_common.h" +#include "db_errno.h" +#include "simple_tracker_log_table_manager.h" +#include "sqlite_relational_utils.h" +#include "sqlite_utils.h" + +namespace DistributedDB { +namespace { +int GenLogForExistData(sqlite3 *db, const TableInfo &table, std::unique_ptr &manager) +{ + SQLiteRelationalUtils::GenLogParam param = {db, false, true}; + return SQLiteRelationalUtils::GeneLogInfoForExistedData("", table, manager, param); +} + +TrackerTable GetTrackerTable(const KnowledgeSourceSchema &schema) +{ + TrackerTable trackerTable; + trackerTable.SetTableName(schema.tableName); + trackerTable.SetExtendNames(schema.extendColNames); + trackerTable.SetTrackerNames(schema.knowledgeColNames); + trackerTable.SetTriggerObserver(false); + trackerTable.SetKnowledgeTable(true); + return trackerTable; +} + +TrackerSchema GetTrackerSchema(const KnowledgeSourceSchema &schema) +{ + TrackerSchema trackerSchema; + trackerSchema.tableName = schema.tableName; + trackerSchema.extendColNames = schema.extendColNames; + trackerSchema.trackerColNames = schema.knowledgeColNames; + return trackerSchema; +} +} + +int KnowledgeSourceUtils::SetKnowledgeSourceSchema(sqlite3 *db, const KnowledgeSourceSchema &schema) +{ + int errCode = SQLiteUtils::BeginTransaction(db, TransactType::IMMEDIATE); + if (errCode != E_OK) { + LOGE("Begin transaction failed %d when set knowledge schema", errCode); + return errCode; + } + errCode = SetKnowledgeSourceSchemaInner(db, schema); + int ret; + if (errCode != E_OK) { + ret = SQLiteUtils::RollbackTransaction(db); + } else { + ret = SQLiteUtils::CommitTransaction(db); + } + LOGI("Set knowledge source schema res %d, commit or rollback ret %d", errCode, ret); + return errCode == E_OK ? ret : errCode; +} + +int KnowledgeSourceUtils::CleanDeletedData(sqlite3 *db, const std::string &tableName, uint64_t cursor) +{ + bool isExist = false; + auto errCode = SQLiteUtils::CheckTableExists(db, DBCommon::GetLogTableName(tableName), isExist); + if (errCode != E_OK) { + return errCode; + } + if (!isExist) { + LOGI("Clean delete data with no exist log, ori table %s len %zu", + DBCommon::StringMiddleMasking(tableName).c_str(), tableName.size()); + return E_OK; + } + errCode = SQLiteRelationalUtils::CleanTrackerData(db, tableName, static_cast(cursor), true); + if (errCode != E_OK) { + return errCode; + } + auto count = sqlite3_changes64(db); + LOGI("Clean delete data success, table %s len %zu cursor %" PRIu64 " delete count %" PRId64, + DBCommon::StringMiddleMasking(tableName).c_str(), tableName.size(), cursor, count); + return errCode; +} + +int KnowledgeSourceUtils::SetKnowledgeSourceSchemaInner(sqlite3 *db, const KnowledgeSourceSchema &schema) +{ + bool isExist = false; + auto errCode = SQLiteUtils::CheckTableExists(db, schema.tableName, isExist); + if (errCode != E_OK) { + return errCode; + } + if (!isExist) { + LOGE("Set not exist table's knowledge schema"); + return -E_INVALID_ARGS; + } + errCode = InitMeta(db, schema.tableName); + if (errCode != E_OK) { + return errCode; + } + RelationalSchemaObject knowledgeSchema; + std::tie(errCode, knowledgeSchema) = GetKnowledgeSourceSchema(db); + if (errCode != E_OK) { + return errCode; + } + TableInfo tableInfo; + errCode = SQLiteUtils::AnalysisSchema(db, schema.tableName, tableInfo); + if (errCode != E_OK) { + return errCode; + } + tableInfo.SetTrackerTable(GetTrackerTable(schema)); + bool isChanged = false; + std::tie(errCode, isChanged) = CheckSchemaValidAndChangeStatus(db, knowledgeSchema, schema, tableInfo); + if (errCode != E_OK) { + return errCode; + } + if (!isChanged) { + LOGI("Knowledge schema is no change, table %s len %zu", + DBCommon::StringMiddleMasking(schema.tableName).c_str(), schema.tableName.size()); + std::unique_ptr tableManager = std::make_unique(); + tableManager->CheckAndCreateTrigger(db, tableInfo, ""); + return E_OK; + } + errCode = InitLogTable(db, schema, tableInfo); + if (errCode != E_OK) { + return errCode; + } + knowledgeSchema.InsertTrackerSchema(GetTrackerSchema(schema)); + return SaveKnowledgeSourceSchema(db, knowledgeSchema); +} + +int KnowledgeSourceUtils::InitMeta(sqlite3 *db, const std::string &table) +{ + int errCode = SQLiteRelationalUtils::CreateRelationalMetaTable(db); + if (errCode != E_OK) { + LOGE("Create relational store meta table failed. err=%d", errCode); + return errCode; + } + return SQLiteRelationalUtils::InitCursorToMeta(db, false, table); +} + +std::pair KnowledgeSourceUtils::CheckSchemaValidAndChangeStatus(sqlite3 *db, + const RelationalSchemaObject &knowledgeSchema, + const KnowledgeSourceSchema &schema, const TableInfo &tableInfo) +{ + std::pair res = {E_OK, false}; + auto &[errCode, isChange] = res; + if (!IsSchemaValid(schema, tableInfo)) { + errCode = -E_INVALID_ARGS; + return res; + } + RelationalSchemaObject rdbSchema; + std::tie(errCode, rdbSchema) = GetRDBSchema(db, false); + if (errCode != E_OK) { + return res; + } + if (IsTableInRDBSchema(schema.tableName, rdbSchema, false)) { + errCode = -E_INVALID_ARGS; + return res; + } + std::tie(errCode, rdbSchema) = GetRDBSchema(db, true); + if (errCode != E_OK) { + return res; + } + if (IsTableInRDBSchema(schema.tableName, rdbSchema, true)) { + errCode = -E_INVALID_ARGS; + return res; + } + isChange = IsSchemaChange(knowledgeSchema, schema); + return res; +} + +bool KnowledgeSourceUtils::IsSchemaValid(const KnowledgeSourceSchema &schema, const TableInfo &tableInfo) +{ + auto fields = tableInfo.GetFields(); + for (const auto &col : schema.knowledgeColNames) { + if (fields.find(col) == fields.end()) { + LOGE("Not exist knowledge col %s %zu table %s %zu", + DBCommon::StringMiddleMasking(col).c_str(), col.size(), + DBCommon::StringMiddleMasking(schema.tableName).c_str(), schema.tableName.size()); + return false; + } + } + for (const auto &col : schema.extendColNames) { + if (fields.find(col) == fields.end()) { + LOGE("Not exist extend col %s %zu table %s %zu", + DBCommon::StringMiddleMasking(col).c_str(), col.size(), + DBCommon::StringMiddleMasking(schema.tableName).c_str(), schema.tableName.size()); + return false; + } + } + return true; +} + +bool KnowledgeSourceUtils::IsTableInRDBSchema(const std::string &table, const RelationalSchemaObject &rdbSchema, + bool isTracker) +{ + if (isTracker) { + auto trackerTable = rdbSchema.GetTrackerTable(table); + if (!trackerTable.GetTableName().empty()) { + LOGE("Table %s len %zu was tracker table", DBCommon::StringMiddleMasking(table).c_str(), table.size()); + return true; + } + return false; + } + auto tableInfo = rdbSchema.GetTable(table); + if (!tableInfo.GetTableName().empty()) { + LOGE("Table %s len %zu was distributed table, type %d", DBCommon::StringMiddleMasking(table).c_str(), + table.size(), static_cast(tableInfo.GetTableSyncType())); + return true; + } + return false; +} + +bool KnowledgeSourceUtils::IsSchemaChange(const RelationalSchemaObject &dbSchema, const KnowledgeSourceSchema &schema) +{ + auto trackerTable = dbSchema.GetTrackerTable(schema.tableName); + if (trackerTable.IsEmpty()) { + LOGI("Check knowledge schema was change by first set"); + return true; + } + const auto &dbExtendsNames = trackerTable.GetExtendNames(); + if (dbExtendsNames != schema.extendColNames) { + LOGI("Check knowledge schema was change by extend col old %zu new %zu", + dbExtendsNames.size(), schema.extendColNames.size()); + return true; + } + const auto &dbTrackerCols = trackerTable.GetTrackerColNames(); + if (dbTrackerCols != schema.knowledgeColNames) { + LOGI("Check knowledge schema was change by knowledge col old %zu new %zu", + dbTrackerCols.size(), schema.knowledgeColNames.size()); + return true; + } + return false; +} + +int KnowledgeSourceUtils::InitLogTable(sqlite3 *db, const KnowledgeSourceSchema &schema, const TableInfo &tableInfo) +{ + std::unique_ptr tableManager = std::make_unique(); + auto errCode = tableManager->CreateRelationalLogTable(db, tableInfo); + if (errCode != E_OK) { + return errCode; + } + errCode = GenLogForExistData(db, tableInfo, tableManager); + if (errCode != E_OK) { + LOGE("Gen log failed %d", errCode); + return errCode; + } + errCode = SQLiteRelationalUtils::SetLogTriggerStatus(db, true); + if (errCode != E_OK) { + return errCode; + } + return tableManager->AddRelationalLogTableTrigger(db, tableInfo, ""); +} + +std::pair KnowledgeSourceUtils::GetKnowledgeSourceSchema(sqlite3 *db) +{ + std::pair res; + auto &[errCode, rdbSchema] = res; + const std::string knowledgeKey(DBConstant::RDB_KNOWLEDGE_SCHEMA_KEY); + const Key schemaKey(knowledgeKey.begin(), knowledgeKey.end()); + Value schemaVal; + errCode = SQLiteRelationalUtils::GetKvData(db, false, schemaKey, schemaVal); // save schema to meta_data + if (errCode == -E_NOT_FOUND) { + LOGD("Not found knowledge schema in db"); + errCode = E_OK; + return res; + } + if (errCode != E_OK) { + LOGE("Get knowledge schema from meta table failed. %d", errCode); + return res; + } + std::string schemaJson(schemaVal.begin(), schemaVal.end()); + errCode = rdbSchema.ParseFromTrackerSchemaString(schemaJson); + return res; +} + +std::pair KnowledgeSourceUtils::GetRDBSchema(sqlite3 *db, bool isTracker) +{ + std::pair res; + auto &[errCode, rdbSchema] = res; + std::string schemaKey = isTracker ? DBConstant::RELATIONAL_TRACKER_SCHEMA_KEY : DBConstant::RELATIONAL_SCHEMA_KEY; + const Key schema(schemaKey.begin(), schemaKey.end()); + Value schemaVal; + errCode = SQLiteRelationalUtils::GetKvData(db, false, schema, schemaVal); // save schema to meta_data + if (errCode == -E_NOT_FOUND) { + LOGD("Not found rdb schema in db"); + errCode = E_OK; + return res; + } + if (errCode != E_OK) { + LOGE("Get rdb schema from meta table failed. %d", errCode); + return res; + } + std::string schemaJson(schemaVal.begin(), schemaVal.end()); + if (isTracker) { + errCode = rdbSchema.ParseFromTrackerSchemaString(schemaJson); + } else { + errCode = rdbSchema.ParseFromSchemaString(schemaJson); + } + return res; +} + +int KnowledgeSourceUtils::SaveKnowledgeSourceSchema(sqlite3 *db, const RelationalSchemaObject &schema) +{ + const std::string knowledgeKey(DBConstant::RDB_KNOWLEDGE_SCHEMA_KEY); + const Key schemaKey(knowledgeKey.begin(), knowledgeKey.end()); + Value schemaVal; + DBCommon::StringToVector(schema.ToSchemaString(), schemaVal); + int errCode = SQLiteRelationalUtils::PutKvData(db, false, schemaKey, schemaVal); // save schema to meta_data + if (errCode != E_OK) { + LOGE("Save schema to meta table failed. %d", errCode); + } + return errCode; +} +} \ No newline at end of file diff --git a/frameworks/libs/distributeddb/interfaces/src/relational/knowledge_source_utils.h b/frameworks/libs/distributeddb/interfaces/src/relational/knowledge_source_utils.h new file mode 100644 index 0000000000000000000000000000000000000000..06d8db629d8eefac13188acc13538ef01aec5845 --- /dev/null +++ b/frameworks/libs/distributeddb/interfaces/src/relational/knowledge_source_utils.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef KNOWLEDGE_SOURCE_UTILS_H +#define KNOWLEDGE_SOURCE_UTILS_H + +#include "relational_schema_object.h" +#include "relational_store_client.h" + +namespace DistributedDB { +class KnowledgeSourceUtils { +public: + static int SetKnowledgeSourceSchema(sqlite3 *db, const KnowledgeSourceSchema &schema); + + static int CleanDeletedData(sqlite3 *db, const std::string &tableName, uint64_t cursor); +protected: + static int SetKnowledgeSourceSchemaInner(sqlite3 *db, const KnowledgeSourceSchema &schema); + + static int InitMeta(sqlite3 *db, const std::string &table); + + static std::pair GetKnowledgeSourceSchema(sqlite3 *db); + + static std::pair GetRDBSchema(sqlite3 *db, bool isTracker); + + static int SaveKnowledgeSourceSchema(sqlite3 *db, const RelationalSchemaObject &schema); + + static std::pair CheckSchemaValidAndChangeStatus(sqlite3 *db, + const RelationalSchemaObject &knowledgeSchema, + const KnowledgeSourceSchema &schema, const TableInfo &tableInfo); + + static bool IsSchemaValid(const KnowledgeSourceSchema &schema, const TableInfo &tableInfo); + + static bool IsTableInRDBSchema(const std::string &table, const RelationalSchemaObject &rdbSchema, bool isTracker); + + static bool IsSchemaChange(const RelationalSchemaObject &dbSchema, const KnowledgeSourceSchema &schema); + + static int InitLogTable(sqlite3 *db, const KnowledgeSourceSchema &schema, const TableInfo &tableInfo); +}; +} + +#endif // KNOWLEDGE_SOURCE_UTILS_H diff --git a/frameworks/libs/distributeddb/interfaces/src/relational/relational_store_manager.cpp b/frameworks/libs/distributeddb/interfaces/src/relational/relational_store_manager.cpp index f72eeb1e547d53c1ecea6d8e378eccbaa795d938..6c1da9dc1565ca014c58e34571ee9d462713d17c 100644 --- a/frameworks/libs/distributeddb/interfaces/src/relational/relational_store_manager.cpp +++ b/frameworks/libs/distributeddb/interfaces/src/relational/relational_store_manager.cpp @@ -22,7 +22,6 @@ #include "db_dfx_adapter.h" #include "db_errno.h" #include "cloud/cloud_db_constant.h" -#include "cloud/cloud_storage_utils.h" #include "kv_store_errno.h" #include "log_print.h" #include "param_check_utils.h" @@ -158,78 +157,6 @@ std::string RelationalStoreManager::GetDistributedTableName(const std::string &d return DBCommon::GetDistributedTableName(device, tableName); } -DB_API std::string RelationalStoreManager::GetDistributedLogTableName(const std::string &tableName) -{ - return DBCommon::GetLogTableName(tableName); -} - -static int GetCollateTypeByName(const std::map &collateTypeMap, - const std::string &name, CollateType &collateType) -{ - auto it = collateTypeMap.find(name); - if (it == collateTypeMap.end()) { - LOGW("collate map doesn't contain primary key we need"); - collateType = CollateType::COLLATE_NONE; - return E_OK; - } - if (static_cast(it->second) >= static_cast(CollateType::COLLATE_BUTT)) { - LOGE("collate type is invalid"); - return -E_INVALID_ARGS; - } - collateType = it->second; - return E_OK; -} - -DB_API std::vector RelationalStoreManager::CalcPrimaryKeyHash(const std::map &primaryKey, - const std::map &collateTypeMap) -{ - std::vector result; - if (primaryKey.empty()) { - LOGW("primaryKey is empty"); - return result; - } - int errCode = E_OK; - CollateType collateType = CollateType::COLLATE_NONE; - if (primaryKey.size() == 1) { - auto iter = primaryKey.begin(); - Field field = {iter->first, static_cast(iter->second.index()), true, false}; - if (GetCollateTypeByName(collateTypeMap, iter->first, collateType) != E_OK) { - return result; - } - errCode = CloudStorageUtils::CalculateHashKeyForOneField(field, primaryKey, false, collateType, result); - if (errCode != E_OK) { - // never happen - LOGE("calc hash fail when there is one primary key errCode = %d", errCode); - } - } else { - std::vector tempRes; - std::map pkOrderByUpperName; - for (const auto &item : primaryKey) { // we sort by upper case name in log table when calculate hash - pkOrderByUpperName[DBCommon::ToUpperCase(item.first)] = item.second; - } - - for (const auto &item : pkOrderByUpperName) { - std::vector temp; - Field field = {DBCommon::ToLowerCase(item.first), static_cast(item.second.index()), true, false}; - if (GetCollateTypeByName(collateTypeMap, DBCommon::ToLowerCase(item.first), collateType) != E_OK) { - return result; - } - errCode = CloudStorageUtils::CalculateHashKeyForOneField(field, primaryKey, false, collateType, temp); - if (errCode != E_OK) { - // never happen - LOGE("calc hash fail when there is more than one primary key errCode = %d", errCode); - return result; - } - tempRes.insert(tempRes.end(), temp.begin(), temp.end()); - } - errCode = DBCommon::CalcValueHash(tempRes, result); - if (errCode != E_OK) { - LOGE("calc hash fail when calc the composite primary key errCode = %d", errCode); - } - } - return result; -} - void RelationalStoreManager::SetAutoLaunchRequestCallback(const AutoLaunchRequestCallback &callback) { RuntimeContext::GetInstance()->SetAutoLaunchRequestCallback(callback, DBTypeInner::DB_RELATION); diff --git a/frameworks/libs/distributeddb/interfaces/src/relational/relational_store_manager_client.cpp b/frameworks/libs/distributeddb/interfaces/src/relational/relational_store_manager_client.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c5da577263ccf87c39eb5169493a836ce12af845 --- /dev/null +++ b/frameworks/libs/distributeddb/interfaces/src/relational/relational_store_manager_client.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifdef RELATIONAL_STORE +#include "relational_store_manager.h" + +#include "db_common.h" +#include "cloud/cloud_storage_utils.h" + +namespace DistributedDB { +DB_API std::string RelationalStoreManager::GetDistributedLogTableName(const std::string &tableName) +{ + return DBCommon::GetLogTableName(tableName); +} + +static int GetCollateTypeByName(const std::map &collateTypeMap, + const std::string &name, CollateType &collateType) +{ + auto it = collateTypeMap.find(name); + if (it == collateTypeMap.end()) { + LOGW("collate map doesn't contain primary key we need"); + collateType = CollateType::COLLATE_NONE; + return E_OK; + } + if (static_cast(it->second) >= static_cast(CollateType::COLLATE_BUTT)) { + LOGE("collate type is invalid"); + return -E_INVALID_ARGS; + } + collateType = it->second; + return E_OK; +} + +DB_API std::vector RelationalStoreManager::CalcPrimaryKeyHash(const std::map &primaryKey, + const std::map &collateTypeMap) +{ + std::vector result; + if (primaryKey.empty()) { + LOGW("primaryKey is empty"); + return result; + } + int errCode = E_OK; + CollateType collateType = CollateType::COLLATE_NONE; + if (primaryKey.size() == 1) { + auto iter = primaryKey.begin(); + Field field = {iter->first, static_cast(iter->second.index()), true, false}; + if (GetCollateTypeByName(collateTypeMap, iter->first, collateType) != E_OK) { + return result; + } + errCode = CloudStorageUtils::CalculateHashKeyForOneField(field, primaryKey, false, collateType, result); + if (errCode != E_OK) { + // never happen + LOGE("calc hash fail when there is one primary key errCode = %d", errCode); + } + } else { + std::vector tempRes; + std::map pkOrderByUpperName; + for (const auto &item : primaryKey) { // we sort by upper case name in log table when calculate hash + pkOrderByUpperName[DBCommon::ToUpperCase(item.first)] = item.second; + } + + for (const auto &item : pkOrderByUpperName) { + std::vector temp; + Field field = {DBCommon::ToLowerCase(item.first), static_cast(item.second.index()), true, false}; + if (GetCollateTypeByName(collateTypeMap, DBCommon::ToLowerCase(item.first), collateType) != E_OK) { + return result; + } + errCode = CloudStorageUtils::CalculateHashKeyForOneField(field, primaryKey, false, collateType, temp); + if (errCode != E_OK) { + // never happen + LOGE("calc hash fail when there is more than one primary key errCode = %d", errCode); + return result; + } + tempRes.insert(tempRes.end(), temp.begin(), temp.end()); + } + errCode = DBCommon::CalcValueHash(tempRes, result); + if (errCode != E_OK) { + LOGE("calc hash fail when calc the composite primary key errCode = %d", errCode); + } + } + return result; +} +} // namespace DistributedDB +#endif diff --git a/frameworks/libs/distributeddb/interfaces/src/relational/relational_store_sqlite_ext.cpp b/frameworks/libs/distributeddb/interfaces/src/relational/relational_store_sqlite_ext.cpp index 0f092ef00f3af6a2be942299f0437335d63f898f..991769501c634b3c691c770d6f05af31b18a9750 100644 --- a/frameworks/libs/distributeddb/interfaces/src/relational/relational_store_sqlite_ext.cpp +++ b/frameworks/libs/distributeddb/interfaces/src/relational/relational_store_sqlite_ext.cpp @@ -14,6 +14,7 @@ */ #include #include +#include #include #include #include @@ -24,6 +25,7 @@ #include "concurrent_adapter.h" #include "db_common.h" #include "db_constant.h" +#include "knowledge_source_utils.h" #include "kv_store_errno.h" #include "param_check_utils.h" #include "platform_specific.h" @@ -64,6 +66,7 @@ using namespace DistributedDB::OS; using namespace DistributedDB; namespace { +const std::string DISTRIBUTED_TABLE_MODE = "distributed_table_mode"; constexpr int E_OK = 0; constexpr int E_ERROR = 1; constexpr int STR_TO_LL_BY_DEVALUE = 10; @@ -308,8 +311,8 @@ class TimeHelperManager { public: static TimeHelperManager *GetInstance() { - static auto instance = new TimeHelperManager(); - return instance; + static TimeHelperManager instance; + return &instance; } void AddStore(const std::string &storeId) @@ -559,7 +562,7 @@ int GetLocalTimeOffsetFromMeta(sqlite3 *db, TimeOffset &offset) int GetTableModeFromMeta(sqlite3 *db, DistributedTableMode &mode) { - std::string keyStr = RelationalDBProperties::DISTRIBUTED_TABLE_MODE; + std::string keyStr = DISTRIBUTED_TABLE_MODE; int64_t dbVal = 0; int errCode = GetNumValueFromMeta(db, keyStr, dbVal); if (errCode != E_OK) { @@ -694,6 +697,7 @@ void CloudDataChangedObserver(sqlite3_context *ctx, int argc, sqlite3_value **ar auto changeStatus = static_cast(sqlite3_value_int(argv[3])); // 3 is param index bool isTrackerChange = (changeStatus & CloudDbConstant::ON_CHANGE_TRACKER) != 0; bool isP2pChange = (changeStatus & CloudDbConstant::ON_CHANGE_P2P) != 0; + bool isKnowledgeDataChange = (changeStatus & CloudDbConstant::ON_CHANGE_KNOWLEDGE) != 0; bool isExistObserver = false; { std::lock_guard lock(g_clientObserverMutex); @@ -707,10 +711,12 @@ void CloudDataChangedObserver(sqlite3_context *ctx, int argc, sqlite3_value **ar if (itTable != g_clientChangedDataMap[hashFileName].tableData.end()) { itTable->second.isTrackedDataChange |= isTrackerChange; itTable->second.isP2pSyncDataChange |= isP2pChange; + itTable->second.isKnowledgeDataChange |= isKnowledgeDataChange; } else { DistributedDB::ChangeProperties properties = { .isTrackedDataChange = isTrackerChange, - .isP2pSyncDataChange = isP2pChange + .isP2pSyncDataChange = isP2pChange, + .isKnowledgeDataChange = isKnowledgeDataChange }; g_clientChangedDataMap[hashFileName].tableData.insert_or_assign(tableName, properties); } @@ -1839,6 +1845,53 @@ DB_API DistributedDB::DBStatus CreateDataChangeTempTrigger(sqlite3 *db) { return DistributedDB::TransferDBErrno(CreateTempTrigger(db)); } + +DistributedDB::DBStatus SetKnowledgeSourceSchema(sqlite3 *db, const DistributedDB::KnowledgeSourceSchema &schema) +{ + if (db == nullptr) { + LOGE("Can't set knowledge source schema with null db"); + return INVALID_ARGS; + } + int errCode = KnowledgeSourceUtils::SetKnowledgeSourceSchema(db, schema); + if (errCode == DistributedDB::E_OK) { + LOGI("Set knowledge source schema success, table %s len %zu", + DBCommon::StringMiddleMasking(schema.tableName).c_str(), schema.tableName.size()); + } else { + LOGE("Set knowledge source schema failed %d, table %s len %zu", + errCode, DBCommon::StringMiddleMasking(schema.tableName).c_str(), schema.tableName.size()); + } + return TransferDBErrno(errCode); +} + +DistributedDB::DBStatus CleanDeletedData(sqlite3 *db, const std::string &tableName, uint64_t cursor) +{ + if (db == nullptr) { + LOGE("Can't clean deleted data with null db"); + return INVALID_ARGS; + } + if (cursor > static_cast(INT64_MAX)) { + LOGW("Cursor is too large %" PRIu64, cursor); + cursor = 0; + } + int errCode = KnowledgeSourceUtils::CleanDeletedData(db, tableName, cursor); + if (errCode != DistributedDB::E_OK) { + LOGE("Clean delete data failed %d, table %s len %zu cursor " PRIu64, + errCode, DBCommon::StringMiddleMasking(tableName).c_str(), tableName.size(), cursor); + } + return TransferDBErrno(errCode); +} + +void Clean(bool isOpenSslClean) +{ +#ifdef USE_FFRT + ConcurrentAdapter::Stop(); +#endif + if (isOpenSslClean) { + OPENSSL_cleanup(); + } + Logger::DeleteInstance(); +} + // hw export the symbols #ifdef SQLITE_DISTRIBUTE_RELATIONAL #if defined(__GNUC__) diff --git a/frameworks/libs/distributeddb/storage/src/cloud/cloud_storage_utils.cpp b/frameworks/libs/distributeddb/storage/src/cloud/cloud_storage_utils.cpp index ee7540a243ea648a43cbdb4882420563a7e56c1c..05d175be5eda1a0adeafa04f6374aba14b410e6a 100644 --- a/frameworks/libs/distributeddb/storage/src/cloud/cloud_storage_utils.cpp +++ b/frameworks/libs/distributeddb/storage/src/cloud/cloud_storage_utils.cpp @@ -200,90 +200,6 @@ int CloudStorageUtils::BindAsset(int index, const VBucket &vBucket, const Field return errCode; } -int CloudStorageUtils::Int64ToVector(const VBucket &vBucket, const Field &field, CollateType collateType, - std::vector &value) -{ - (void)collateType; - int64_t val = 0; - if (CloudStorageUtils::GetValueFromVBucket(field.colName, vBucket, val) != E_OK) { - return -E_CLOUD_ERROR; - } - DBCommon::StringToVector(std::to_string(val), value); - return E_OK; -} - -int CloudStorageUtils::BoolToVector(const VBucket &vBucket, const Field &field, CollateType collateType, - std::vector &value) -{ - (void)collateType; - bool val = false; - if (CloudStorageUtils::GetValueFromVBucket(field.colName, vBucket, val) != E_OK) { // LCOV_EXCL_BR_LINE - return -E_CLOUD_ERROR; - } - DBCommon::StringToVector(std::to_string(val ? 1 : 0), value); - return E_OK; -} - -int CloudStorageUtils::DoubleToVector(const VBucket &vBucket, const Field &field, CollateType collateType, - std::vector &value) -{ - (void)collateType; - double val = 0.0; - if (CloudStorageUtils::GetValueFromVBucket(field.colName, vBucket, val) != E_OK) { // LCOV_EXCL_BR_LINE - return -E_CLOUD_ERROR; - } - std::ostringstream s; - s << val; - DBCommon::StringToVector(s.str(), value); - return E_OK; -} - -int CloudStorageUtils::TextToVector(const VBucket &vBucket, const Field &field, CollateType collateType, - std::vector &value) -{ - std::string val; - if (CloudStorageUtils::GetValueFromVBucket(field.colName, vBucket, val) != E_OK) { - return -E_CLOUD_ERROR; - } - if (collateType == CollateType::COLLATE_NOCASE) { - std::transform(val.begin(), val.end(), val.begin(), ::toupper); - } else if (collateType == CollateType::COLLATE_RTRIM) { - DBCommon::RTrim(val); - } - - DBCommon::StringToVector(val, value); - return E_OK; -} - -int CloudStorageUtils::BlobToVector(const VBucket &vBucket, const Field &field, CollateType collateType, - std::vector &value) -{ - (void)collateType; - if (field.type == TYPE_INDEX) { // LCOV_EXCL_BR_LINE - return CloudStorageUtils::GetValueFromVBucket(field.colName, vBucket, value); - } else if (field.type == TYPE_INDEX) { - Asset val; - if (CloudStorageUtils::GetValueFromVBucket(field.colName, vBucket, val) != E_OK) { // LCOV_EXCL_BR_LINE - return -E_CLOUD_ERROR; - } - int errCode = RuntimeContext::GetInstance()->AssetToBlob(val, value); - if (errCode != E_OK) { // LCOV_EXCL_BR_LINE - LOGE("asset to blob fail, %d", errCode); - } - return errCode; - } else { - Assets val; - if (CloudStorageUtils::GetValueFromVBucket(field.colName, vBucket, val) != E_OK) { // LCOV_EXCL_BR_LINE - return -E_CLOUD_ERROR; - } - int errCode = RuntimeContext::GetInstance()->AssetsToBlob(val, value); - if (errCode != E_OK) { // LCOV_EXCL_BR_LINE - LOGE("assets to blob fail, %d", errCode); - } - return errCode; - } -} - std::set CloudStorageUtils::GetCloudPrimaryKey(const TableSchema &tableSchema) { std::set pkSet; @@ -627,38 +543,6 @@ bool CloudStorageUtils::IsAssets(const Type &type) return type.index() == TYPE_INDEX; } -int CloudStorageUtils::CalculateHashKeyForOneField(const Field &field, const VBucket &vBucket, bool allowEmpty, - CollateType collateType, std::vector &hashValue) -{ - Type type; - bool isExisted = GetTypeCaseInsensitive(field.colName, vBucket, type); - if (allowEmpty && !isExisted) { - return E_OK; // if vBucket from cloud doesn't contain primary key and allowEmpty, no need to calculate hash - } - static std::map &)>> toVecFunc = { - { TYPE_INDEX, &CloudStorageUtils::Int64ToVector }, - { TYPE_INDEX, &CloudStorageUtils::BoolToVector }, - { TYPE_INDEX, &CloudStorageUtils::DoubleToVector }, - { TYPE_INDEX, &CloudStorageUtils::TextToVector }, - { TYPE_INDEX, &CloudStorageUtils::BlobToVector }, - { TYPE_INDEX, &CloudStorageUtils::BlobToVector }, - { TYPE_INDEX, &CloudStorageUtils::BlobToVector }, - }; - auto it = toVecFunc.find(field.type); - if (it == toVecFunc.end()) { - LOGE("unknown cloud type when convert field to vector."); - return -E_CLOUD_ERROR; - } - std::vector value; - int errCode = it->second(vBucket, field, collateType, value); - if (errCode != E_OK) { - LOGE("convert cloud field fail, %d", errCode); - return errCode; - } - return DBCommon::CalcValueHash(value, hashValue); -} - bool CloudStorageUtils::IsAssetsContainDuplicateAsset(Assets &assets) { std::set set; @@ -786,50 +670,6 @@ bool CloudStorageUtils::CheckAssetStatus(const Assets &assets) return true; } -std::string CloudStorageUtils::GetTableRefUpdateSql(const TableInfo &table, OpType opType) -{ - std::string sql; - std::string rowid = std::string(DBConstant::SQLITE_INNER_ROWID); - for (const auto &reference : table.GetTableReference()) { - if (reference.columns.empty()) { - return ""; - } - std::string sourceLogName = DBCommon::GetLogTableName(reference.sourceTableName); - sql += " UPDATE " + sourceLogName + " SET timestamp=get_raw_sys_time(), flag=flag|0x02 WHERE "; - int index = 0; - for (const auto &itCol : reference.columns) { - if (opType != OpType::UPDATE) { - continue; - } - if (index++ != 0) { - sql += " OR "; - } - sql += " (OLD." + itCol.second + " IS NOT " + " NEW." + itCol.second + ")"; - } - if (opType == OpType::UPDATE) { - sql += " AND "; - } - sql += " (flag&0x08=0x00) AND data_key IN (SELECT " + sourceLogName + ".data_key FROM " + sourceLogName + - " LEFT JOIN " + reference.sourceTableName + " ON " + sourceLogName + ".data_key = " + - reference.sourceTableName + "." + rowid + " WHERE "; - index = 0; - for (const auto &itCol : reference.columns) { - if (index++ != 0) { - sql += " OR "; - } - if (opType == OpType::UPDATE) { - sql += itCol.first + "=OLD." + itCol.second + " OR " + itCol.first + "=NEW." + itCol.second; - } else if (opType == OpType::INSERT) { - sql += itCol.first + "=NEW." + itCol.second; - } else if (opType == OpType::DELETE) { - sql += itCol.first + "=OLD." + itCol.second; - } - } - sql += ");"; - } - return sql; -} - std::string CloudStorageUtils::GetLeftJoinLogSql(const std::string &tableName, bool logAsTableA) { std::string sql; @@ -843,16 +683,6 @@ std::string CloudStorageUtils::GetLeftJoinLogSql(const std::string &tableName, b return sql; } -std::string CloudStorageUtils::GetUpdateLockChangedSql() -{ - return " status = CASE WHEN status == 2 THEN 3 ELSE status END"; -} - -std::string CloudStorageUtils::GetDeleteLockChangedSql() -{ - return " status = CASE WHEN status == 2 or status == 3 THEN 1 ELSE status END"; -} - bool CloudStorageUtils::ChkFillCloudAssetParam(const CloudSyncBatch &data, int errCode) { if (data.assets.empty()) { @@ -1028,34 +858,6 @@ bool CloudStorageUtils::CheckCloudSchemaFields(const TableSchema &tableSchema, c return true; } -void CloudStorageUtils::TransferFieldToLower(VBucket &vBucket) -{ - for (auto it = vBucket.begin(); it != vBucket.end();) { - std::string lowerField(it->first.length(), ' '); - std::transform(it->first.begin(), it->first.end(), lowerField.begin(), ::tolower); - if (lowerField != it->first) { - vBucket[lowerField] = std::move(vBucket[it->first]); - vBucket.erase(it++); - } else { - it++; - } - } -} - -bool CloudStorageUtils::GetTypeCaseInsensitive(const std::string &fieldName, const VBucket &vBucket, Type &data) -{ - auto tmpFieldName = fieldName; - auto tmpVBucket = vBucket; - std::transform(tmpFieldName.begin(), tmpFieldName.end(), tmpFieldName.begin(), ::tolower); - TransferFieldToLower(tmpVBucket); - auto it = tmpVBucket.find(tmpFieldName); - if (it == tmpVBucket.end()) { - return false; - } - data = it->second; - return true; -} - int CloudStorageUtils::BindUpdateLogStmtFromVBucket(const VBucket &vBucket, const TableSchema &tableSchema, const std::vector &colNames, sqlite3_stmt *updateLogStmt) { @@ -1502,26 +1304,6 @@ bool CloudStorageUtils::IsSystemRecord(const Key &key) return keyStr.find(prefixKey) == 0; } -std::string CloudStorageUtils::GetSelectIncCursorSql(const std::string &tableName) -{ - return "(SELECT value FROM " + DBCommon::GetMetaTableName() + " WHERE key=x'" + - DBCommon::TransferStringToHex(DBCommon::GetCursorKey(tableName)) + "')"; -} - -std::string CloudStorageUtils::GetCursorIncSql(const std::string &tableName) -{ - return "UPDATE " + DBCommon::GetMetaTableName() + " SET value=value+1 WHERE key=x'" + - DBCommon::TransferStringToHex(DBCommon::GetCursorKey(tableName)) + "';"; -} - -std::string CloudStorageUtils::GetCursorIncSqlWhenAllow(const std::string &tableName) -{ - std::string prefix = DBConstant::RELATIONAL_PREFIX; - return "UPDATE " + prefix + "metadata" + " SET value= case when (select 1 from " + - prefix + "metadata" + " where key='cursor_inc_flag' AND value = 'true') then value + 1" + - " else value end WHERE key=x'" + DBCommon::TransferStringToHex(DBCommon::GetCursorKey(tableName)) + "';"; -} - std::string CloudStorageUtils::GetCursorUpgradeSql(const std::string &tableName) { return "INSERT OR REPLACE INTO " + DBCommon::GetMetaTableName() + "(key,value) VALUES (x'" + diff --git a/frameworks/libs/distributeddb/storage/src/cloud/cloud_storage_utils_client.cpp b/frameworks/libs/distributeddb/storage/src/cloud/cloud_storage_utils_client.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e122dec45986cc61529b7223057f1785de17e338 --- /dev/null +++ b/frameworks/libs/distributeddb/storage/src/cloud/cloud_storage_utils_client.cpp @@ -0,0 +1,247 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "cloud/cloud_storage_utils.h" + +#include "db_common.h" +#include "runtime_context.h" + +namespace DistributedDB { +int CloudStorageUtils::Int64ToVector(const VBucket &vBucket, const Field &field, CollateType collateType, + std::vector &value) +{ + (void)collateType; + int64_t val = 0; + if (CloudStorageUtils::GetValueFromVBucket(field.colName, vBucket, val) != E_OK) { + return -E_CLOUD_ERROR; + } + DBCommon::StringToVector(std::to_string(val), value); + return E_OK; +} + +int CloudStorageUtils::BoolToVector(const VBucket &vBucket, const Field &field, CollateType collateType, + std::vector &value) +{ + (void)collateType; + bool val = false; + if (CloudStorageUtils::GetValueFromVBucket(field.colName, vBucket, val) != E_OK) { // LCOV_EXCL_BR_LINE + return -E_CLOUD_ERROR; + } + DBCommon::StringToVector(std::to_string(val ? 1 : 0), value); + return E_OK; +} + +int CloudStorageUtils::DoubleToVector(const VBucket &vBucket, const Field &field, CollateType collateType, + std::vector &value) +{ + (void)collateType; + double val = 0.0; + if (CloudStorageUtils::GetValueFromVBucket(field.colName, vBucket, val) != E_OK) { // LCOV_EXCL_BR_LINE + return -E_CLOUD_ERROR; + } + std::ostringstream s; + s << val; + DBCommon::StringToVector(s.str(), value); + return E_OK; +} + +int CloudStorageUtils::TextToVector(const VBucket &vBucket, const Field &field, CollateType collateType, + std::vector &value) +{ + std::string val; + if (CloudStorageUtils::GetValueFromVBucket(field.colName, vBucket, val) != E_OK) { + return -E_CLOUD_ERROR; + } + if (collateType == CollateType::COLLATE_NOCASE) { + std::transform(val.begin(), val.end(), val.begin(), ::toupper); + } else if (collateType == CollateType::COLLATE_RTRIM) { + DBCommon::RTrim(val); + } + + DBCommon::StringToVector(val, value); + return E_OK; +} + +int CloudStorageUtils::BlobToVector(const VBucket &vBucket, const Field &field, CollateType collateType, + std::vector &value) +{ + (void)collateType; + if (field.type == TYPE_INDEX) { // LCOV_EXCL_BR_LINE + return CloudStorageUtils::GetValueFromVBucket(field.colName, vBucket, value); + } else if (field.type == TYPE_INDEX) { + Asset val; + if (CloudStorageUtils::GetValueFromVBucket(field.colName, vBucket, val) != E_OK) { // LCOV_EXCL_BR_LINE + return -E_CLOUD_ERROR; + } +#ifdef RDB_CLIENT + return E_OK; +#else + int errCode = RuntimeContext::GetInstance()->AssetToBlob(val, value); + if (errCode != E_OK) { // LCOV_EXCL_BR_LINE + LOGE("asset to blob fail, %d", errCode); + } + return errCode; +#endif + } else { + Assets val; + if (CloudStorageUtils::GetValueFromVBucket(field.colName, vBucket, val) != E_OK) { // LCOV_EXCL_BR_LINE + return -E_CLOUD_ERROR; + } +#ifdef RDB_CLIENT + return E_OK; +#else + int errCode = RuntimeContext::GetInstance()->AssetsToBlob(val, value); + if (errCode != E_OK) { // LCOV_EXCL_BR_LINE + LOGE("assets to blob fail, %d", errCode); + } + return errCode; +#endif + } +} + +int CloudStorageUtils::CalculateHashKeyForOneField(const Field &field, const VBucket &vBucket, bool allowEmpty, + CollateType collateType, std::vector &hashValue) +{ + Type type; + bool isExisted = GetTypeCaseInsensitive(field.colName, vBucket, type); + if (allowEmpty && !isExisted) { + return E_OK; // if vBucket from cloud doesn't contain primary key and allowEmpty, no need to calculate hash + } + static std::map &)>> toVecFunc = { + { TYPE_INDEX, &CloudStorageUtils::Int64ToVector }, + { TYPE_INDEX, &CloudStorageUtils::BoolToVector }, + { TYPE_INDEX, &CloudStorageUtils::DoubleToVector }, + { TYPE_INDEX, &CloudStorageUtils::TextToVector }, + { TYPE_INDEX, &CloudStorageUtils::BlobToVector }, + { TYPE_INDEX, &CloudStorageUtils::BlobToVector }, + { TYPE_INDEX, &CloudStorageUtils::BlobToVector }, + }; + auto it = toVecFunc.find(field.type); + if (it == toVecFunc.end()) { + LOGE("unknown cloud type when convert field to vector."); + return -E_CLOUD_ERROR; + } + std::vector value; + int errCode = it->second(vBucket, field, collateType, value); + if (errCode != E_OK) { + LOGE("convert cloud field fail, %d", errCode); + return errCode; + } + return DBCommon::CalcValueHash(value, hashValue); +} + +void CloudStorageUtils::TransferFieldToLower(VBucket &vBucket) +{ + for (auto it = vBucket.begin(); it != vBucket.end();) { + std::string lowerField(it->first.length(), ' '); + std::transform(it->first.begin(), it->first.end(), lowerField.begin(), ::tolower); + if (lowerField != it->first) { + vBucket[lowerField] = std::move(vBucket[it->first]); + vBucket.erase(it++); + } else { + it++; + } + } +} + +bool CloudStorageUtils::GetTypeCaseInsensitive(const std::string &fieldName, const VBucket &vBucket, Type &data) +{ + auto tmpFieldName = fieldName; + auto tmpVBucket = vBucket; + std::transform(tmpFieldName.begin(), tmpFieldName.end(), tmpFieldName.begin(), ::tolower); + TransferFieldToLower(tmpVBucket); + auto it = tmpVBucket.find(tmpFieldName); + if (it == tmpVBucket.end()) { + return false; + } + data = it->second; + return true; +} + +std::string CloudStorageUtils::GetSelectIncCursorSql(const std::string &tableName) +{ + return "(SELECT value FROM " + DBCommon::GetMetaTableName() + " WHERE key=x'" + + DBCommon::TransferStringToHex(DBCommon::GetCursorKey(tableName)) + "')"; +} + +std::string CloudStorageUtils::GetCursorIncSql(const std::string &tableName) +{ + return "UPDATE " + DBCommon::GetMetaTableName() + " SET value=value+1 WHERE key=x'" + + DBCommon::TransferStringToHex(DBCommon::GetCursorKey(tableName)) + "';"; +} + +std::string CloudStorageUtils::GetCursorIncSqlWhenAllow(const std::string &tableName) +{ + std::string prefix = DBConstant::RELATIONAL_PREFIX; + return "UPDATE " + prefix + "metadata" + " SET value= case when (select 1 from " + + prefix + "metadata" + " where key='cursor_inc_flag' AND value = 'true') then value + 1" + + " else value end WHERE key=x'" + DBCommon::TransferStringToHex(DBCommon::GetCursorKey(tableName)) + "';"; +} + +std::string CloudStorageUtils::GetUpdateLockChangedSql() +{ + return " status = CASE WHEN status == 2 THEN 3 ELSE status END"; +} + +std::string CloudStorageUtils::GetDeleteLockChangedSql() +{ + return " status = CASE WHEN status == 2 or status == 3 THEN 1 ELSE status END"; +} + +std::string CloudStorageUtils::GetTableRefUpdateSql(const TableInfo &table, OpType opType) +{ + std::string sql; + std::string rowid = std::string(DBConstant::SQLITE_INNER_ROWID); + for (const auto &reference : table.GetTableReference()) { + if (reference.columns.empty()) { + return ""; + } + std::string sourceLogName = DBCommon::GetLogTableName(reference.sourceTableName); + sql += " UPDATE " + sourceLogName + " SET timestamp=get_raw_sys_time(), flag=flag|0x02 WHERE "; + int index = 0; + for (const auto &itCol : reference.columns) { + if (opType != OpType::UPDATE) { + continue; + } + if (index++ != 0) { + sql += " OR "; + } + sql += " (OLD." + itCol.second + " IS NOT " + " NEW." + itCol.second + ")"; + } + if (opType == OpType::UPDATE) { + sql += " AND "; + } + sql += " (flag&0x08=0x00) AND data_key IN (SELECT " + sourceLogName + ".data_key FROM " + sourceLogName + + " LEFT JOIN " + reference.sourceTableName + " ON " + sourceLogName + ".data_key = " + + reference.sourceTableName + "." + rowid + " WHERE "; + index = 0; + for (const auto &itCol : reference.columns) { + if (index++ != 0) { + sql += " OR "; + } + if (opType == OpType::UPDATE) { + sql += itCol.first + "=OLD." + itCol.second + " OR " + itCol.first + "=NEW." + itCol.second; + } else if (opType == OpType::INSERT) { + sql += itCol.first + "=NEW." + itCol.second; + } else if (opType == OpType::DELETE) { + sql += itCol.first + "=OLD." + itCol.second; + } + } + sql += ");"; + } + return sql; +} +} diff --git a/frameworks/libs/distributeddb/storage/src/sqlite/relational/simple_tracker_log_table_manager.cpp b/frameworks/libs/distributeddb/storage/src/sqlite/relational/simple_tracker_log_table_manager.cpp index 66c55e4b06ce1e41359220042f598d0fa73382c2..b53a3ef8cceb96261a3dc79818f328eac2895aff 100644 --- a/frameworks/libs/distributeddb/storage/src/sqlite/relational/simple_tracker_log_table_manager.cpp +++ b/frameworks/libs/distributeddb/storage/src/sqlite/relational/simple_tracker_log_table_manager.cpp @@ -76,7 +76,8 @@ std::string SimpleTrackerLogTableManager::GetInsertTrigger(const TableInfo &tabl insertTrigger += CalcPrimaryKeyHash("NEW.", table, identity) + ", '', "; insertTrigger += table.GetTrackerTable().GetAssignValSql(); insertTrigger += ", " + CloudStorageUtils::GetSelectIncCursorSql(tableName) + ", '', '', 0);\n"; - insertTrigger += "SELECT client_observer('" + tableName + "', NEW._rowid_, 0, 1"; + insertTrigger += "SELECT client_observer('" + tableName + "', NEW._rowid_, 0, "; + insertTrigger += table.GetTrackerTable().GetOnChangeType(); insertTrigger += ");\n"; insertTrigger += "END;"; return insertTrigger; @@ -103,7 +104,8 @@ std::string SimpleTrackerLogTableManager::GetUpdateTrigger(const TableInfo &tabl updateTrigger += table.GetTrackerTable().GetExtendAssignValSql(); updateTrigger += table.GetTrackerTable().GetDiffIncCursorSql(tableName); updateTrigger += " WHERE data_key = OLD." + std::string(DBConstant::SQLITE_INNER_ROWID) + ";\n"; - updateTrigger += "SELECT client_observer('" + tableName + "', OLD." + std::string(DBConstant::SQLITE_INNER_ROWID); + updateTrigger += "SELECT client_observer('" + tableName + "', OLD." + + std::string(DBConstant::SQLITE_INNER_ROWID); updateTrigger += ", 1, "; updateTrigger += table.GetTrackerTable().GetDiffTrackerValSql(); updateTrigger += ");"; @@ -131,7 +133,7 @@ std::string SimpleTrackerLogTableManager::GetDeleteTrigger(const TableInfo &tabl deleteTrigger += " WHERE data_key = OLD." + std::string(DBConstant::SQLITE_INNER_ROWID) + ";"; // -1 is rowid when data is deleted, 2 means change type is delete(ClientChangeType) deleteTrigger += "SELECT client_observer('" + tableName + "', -1, 2, "; - deleteTrigger += table.GetTrackerTable().IsEmpty() ? "0" : "1"; + deleteTrigger += table.GetTrackerTable().IsEmpty() ? "0" : table.GetTrackerTable().GetOnChangeType(); deleteTrigger += ");\n"; deleteTrigger += "END;"; return deleteTrigger; diff --git a/frameworks/libs/distributeddb/storage/src/sqlite/relational/simple_tracker_log_table_manager.h b/frameworks/libs/distributeddb/storage/src/sqlite/relational/simple_tracker_log_table_manager.h index 4460a866672b81388bac1d6f5f0053d44509faf0..58626799be0c09d8b348d5c3b8b254ad48faceb9 100644 --- a/frameworks/libs/distributeddb/storage/src/sqlite/relational/simple_tracker_log_table_manager.h +++ b/frameworks/libs/distributeddb/storage/src/sqlite/relational/simple_tracker_log_table_manager.h @@ -27,7 +27,6 @@ public: // The parameter "references" is "", "NEW." or "OLD.". "identity" is a hash string that identifies a device. std::string CalcPrimaryKeyHash(const std::string &references, const TableInfo &table, const std::string &identity) override; - private: void GetIndexSql(const TableInfo &table, std::vector &schema) override; std::string GetPrimaryKeySql(const TableInfo &table) override; diff --git a/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_relational_utils.cpp b/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_relational_utils.cpp index 5847be36cc6edfca672c60dda5734024ff2e65d4..22f7f18dc6841053922a0a77127c86ced54d00b9 100644 --- a/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_relational_utils.cpp +++ b/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_relational_utils.cpp @@ -395,21 +395,6 @@ void SQLiteRelationalUtils::AddUpgradeSqlToList(const TableInfo &tableInfo, } } -int SQLiteRelationalUtils::AnalysisTrackerTable(sqlite3 *db, const TrackerTable &trackerTable, TableInfo &tableInfo) -{ - int errCode = SQLiteUtils::AnalysisSchema(db, trackerTable.GetTableName(), tableInfo, true); - if (errCode != E_OK) { - LOGE("analysis table schema failed %d.", errCode); - return errCode; - } - tableInfo.SetTrackerTable(trackerTable); - errCode = tableInfo.CheckTrackerTable(); - if (errCode != E_OK) { - LOGE("check tracker table schema failed %d.", errCode); - } - return errCode; -} - int SQLiteRelationalUtils::QueryCount(sqlite3 *db, const std::string &tableName, int64_t &count) { std::string sql = "SELECT COUNT(1) FROM " + tableName ; diff --git a/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_relational_utils.h b/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_relational_utils.h index dcd5232748f251ecfea6e328875e6c95b703b971..b2bf0f957a4293f613c98f3e69b115b35625f443 100644 --- a/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_relational_utils.h +++ b/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_relational_utils.h @@ -68,6 +68,28 @@ public: static int GetMetaLocalTimeOffset(sqlite3 *db, int64_t &timeOffset); static std::pair GetCurrentVirtualTime(sqlite3 *db); + + static int CreateRelationalMetaTable(sqlite3 *db); + + static int GetKvData(sqlite3 *db, bool isMemory, const Key &key, Value &value); + static int PutKvData(sqlite3 *db, bool isMemory, const Key &key, const Value &value); + + static int InitCursorToMeta(sqlite3 *db, bool isMemory, const std::string &tableName); + static int SetLogTriggerStatus(sqlite3 *db, bool status); + + struct GenLogParam { + sqlite3 *db = nullptr; + bool isMemory = false; + bool isTrackerTable = false; + }; + static int GeneLogInfoForExistedData(const std::string &identity, const TableInfo &tableInfo, + std::unique_ptr &logMgrPtr, GenLogParam ¶m); + + static int GetExistedDataTimeOffset(sqlite3 *db, const std::string &tableName, bool isMem, int64_t &timeOffset); + + static std::string GetExtendValue(const TrackerTable &trackerTable); + + static int CleanTrackerData(sqlite3 *db, const std::string &tableName, int64_t cursor, bool isOnlyTrackTable); private: static int BindExtendStatementByType(sqlite3_stmt *statement, int cid, Type &typeVal); diff --git a/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_relational_utils_client.cpp b/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_relational_utils_client.cpp new file mode 100644 index 0000000000000000000000000000000000000000..34fd0bb40a13a3d6eb69edb481fcb692a4d2828a --- /dev/null +++ b/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_relational_utils_client.cpp @@ -0,0 +1,248 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "sqlite_relational_utils.h" +#include "db_common.h" + +namespace DistributedDB { +int SQLiteRelationalUtils::CreateRelationalMetaTable(sqlite3 *db) +{ + std::string sql = + "CREATE TABLE IF NOT EXISTS " + std::string(DBConstant::RELATIONAL_PREFIX) + "metadata(" \ + "key BLOB PRIMARY KEY NOT NULL," \ + "value BLOB);"; + int errCode = SQLiteUtils::ExecuteRawSQL(db, sql); + if (errCode != E_OK) { + LOGE("[SQLite] execute create table sql failed, err=%d", errCode); + } + return errCode; +} + +int SQLiteRelationalUtils::GetKvData(sqlite3 *db, bool isMemory, const Key &key, Value &value) +{ + static const std::string SELECT_META_VALUE_SQL = "SELECT value FROM " + std::string(DBConstant::RELATIONAL_PREFIX) + + "metadata WHERE key=?;"; + sqlite3_stmt *statement = nullptr; + int errCode = SQLiteUtils::GetStatement(db, SELECT_META_VALUE_SQL, statement); + if (errCode != E_OK) { + return SQLiteUtils::ProcessStatementErrCode(statement, true, errCode); + } + + errCode = SQLiteUtils::BindBlobToStatement(statement, 1, key, false); // first arg. + if (errCode != E_OK) { + return SQLiteUtils::ProcessStatementErrCode(statement, true, errCode); + } + + errCode = SQLiteUtils::StepWithRetry(statement, isMemory); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + errCode = -E_NOT_FOUND; + return SQLiteUtils::ProcessStatementErrCode(statement, true, errCode); + } else if (errCode != SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + return SQLiteUtils::ProcessStatementErrCode(statement, true, errCode); + } + + errCode = SQLiteUtils::GetColumnBlobValue(statement, 0, value); // only one result. + return SQLiteUtils::ProcessStatementErrCode(statement, true, errCode); +} + +int SQLiteRelationalUtils::PutKvData(sqlite3 *db, bool isMemory, const Key &key, const Value &value) +{ + static const std::string INSERT_META_SQL = "INSERT OR REPLACE INTO " + std::string(DBConstant::RELATIONAL_PREFIX) + + "metadata VALUES(?,?);"; + sqlite3_stmt *statement = nullptr; + int errCode = SQLiteUtils::GetStatement(db, INSERT_META_SQL, statement); + if (errCode != E_OK) { + return errCode; + } + + errCode = SQLiteUtils::BindBlobToStatement(statement, 1, key, false); // 1 means key index + if (errCode != E_OK) { + LOGE("[SingleVerExe][BindPutKv]Bind key error:%d", errCode); + return SQLiteUtils::ProcessStatementErrCode(statement, true, errCode); + } + + errCode = SQLiteUtils::BindBlobToStatement(statement, 2, value, true); // 2 means value index + if (errCode != E_OK) { + LOGE("[SingleVerExe][BindPutKv]Bind value error:%d", errCode); + return SQLiteUtils::ProcessStatementErrCode(statement, true, errCode); + } + errCode = SQLiteUtils::StepWithRetry(statement, isMemory); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + errCode = E_OK; + } + return SQLiteUtils::ProcessStatementErrCode(statement, true, errCode); +} + +int SQLiteRelationalUtils::InitCursorToMeta(sqlite3 *db, bool isMemory, const std::string &tableName) +{ + Value key; + Value cursor; + DBCommon::StringToVector(DBCommon::GetCursorKey(tableName), key); + int errCode = GetKvData(db, isMemory, key, cursor); + if (errCode == -E_NOT_FOUND) { + DBCommon::StringToVector(std::string("0"), cursor); + errCode = PutKvData(db, isMemory, key, cursor); + if (errCode != E_OK) { + LOGE("Init cursor to meta table failed. %d", errCode); + } + return errCode; + } + if (errCode != E_OK) { + LOGE("Get cursor from meta table failed. %d", errCode); + } + return errCode; +} + +int SQLiteRelationalUtils::SetLogTriggerStatus(sqlite3 *db, bool status) +{ + const std::string key = "log_trigger_switch"; + std::string val = status ? "true" : "false"; + std::string sql = "INSERT OR REPLACE INTO " + std::string(DBConstant::RELATIONAL_PREFIX) + "metadata" + + " VALUES ('" + key + "', '" + val + "')"; + int errCode = SQLiteUtils::ExecuteRawSQL(db, sql); + if (errCode != E_OK) { + LOGE("Set log trigger to %s failed. errCode=%d", val.c_str(), errCode); + } + return errCode; +} + +int SQLiteRelationalUtils::GeneLogInfoForExistedData(const std::string &identity, const TableInfo &tableInfo, + std::unique_ptr &logMgrPtr, GenLogParam ¶m) +{ + std::string tableName = tableInfo.GetTableName(); + int64_t timeOffset = 0; + int errCode = GetExistedDataTimeOffset(param.db, tableName, param.isMemory, timeOffset); + if (errCode != E_OK) { + return errCode; + } + errCode = SetLogTriggerStatus(param.db, false); + if (errCode != E_OK) { + return errCode; + } + std::string timeOffsetStr = std::to_string(timeOffset); + std::string logTable = DBConstant::RELATIONAL_PREFIX + tableName + "_log"; + std::string rowid = std::string(DBConstant::SQLITE_INNER_ROWID); + std::string flag = std::to_string(static_cast(LogInfoFlag::FLAG_LOCAL) | + static_cast(LogInfoFlag::FLAG_DEVICE_CLOUD_INCONSISTENCY)); + TrackerTable trackerTable = tableInfo.GetTrackerTable(); + trackerTable.SetTableName(tableName); + const std::string prefix = "a."; + std::string calPrimaryKeyHash = logMgrPtr->CalcPrimaryKeyHash(prefix, tableInfo, identity); + std::string sql = "INSERT OR REPLACE INTO " + logTable + " SELECT " + rowid + + ", '', '', " + timeOffsetStr + " + " + rowid + ", " + + timeOffsetStr + " + " + rowid + ", " + flag + ", " + calPrimaryKeyHash + ", '', "; + sql += GetExtendValue(tableInfo.GetTrackerTable()); + sql += ", 0, '', '', 0 FROM '" + tableName + "' AS a "; + if (param.isTrackerTable) { + sql += " WHERE 1 = 1;"; + } else { + sql += "WHERE NOT EXISTS (SELECT 1 FROM " + logTable + " WHERE data_key = a._rowid_);"; + } + errCode = trackerTable.ReBuildTempTrigger(param.db, TriggerMode::TriggerModeEnum::INSERT, [db = param.db, &sql]() { + int ret = SQLiteUtils::ExecuteRawSQL(db, sql); + if (ret != E_OK) { + LOGE("Failed to initialize cloud type log data.%d", ret); + } + return ret; + }); + return errCode; +} + +int SQLiteRelationalUtils::GetExistedDataTimeOffset(sqlite3 *db, const std::string &tableName, bool isMem, + int64_t &timeOffset) +{ + std::string sql = "SELECT get_sys_time(0) - max(" + std::string(DBConstant::SQLITE_INNER_ROWID) + ") - 1 FROM '" + + tableName + "';"; + sqlite3_stmt *stmt = nullptr; + int errCode = SQLiteUtils::GetStatement(db, sql, stmt); + if (errCode != E_OK) { + return errCode; + } + errCode = SQLiteUtils::StepWithRetry(stmt, isMem); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + timeOffset = static_cast(sqlite3_column_int64(stmt, 0)); + errCode = E_OK; + } + int ret = E_OK; + SQLiteUtils::ResetStatement(stmt, true, ret); + return errCode != E_OK ? errCode : ret; +} + +std::string SQLiteRelationalUtils::GetExtendValue(const TrackerTable &trackerTable) +{ + std::string extendValue; + const std::set &extendNames = trackerTable.GetExtendNames(); + if (!extendNames.empty()) { + extendValue += "json_object("; + for (const auto &extendName : extendNames) { + extendValue += "'" + extendName + "'," + extendName + ","; + } + extendValue.pop_back(); + extendValue += ")"; + } else { + extendValue = "''"; + } + return extendValue; +} + +int SQLiteRelationalUtils::CleanTrackerData(sqlite3 *db, const std::string &tableName, int64_t cursor, + bool isOnlyTrackTable) +{ + std::string sql; + if (isOnlyTrackTable) { + sql = "DELETE FROM " + std::string(DBConstant::RELATIONAL_PREFIX) + tableName + "_log"; + } else { + sql = "UPDATE " + std::string(DBConstant::RELATIONAL_PREFIX) + tableName + "_log SET extend_field = NULL"; + } + sql += " where data_key = -1 and cursor <= ?;"; + sqlite3_stmt *statement = nullptr; + int errCode = SQLiteUtils::GetStatement(db, sql, statement); + if (errCode != E_OK) { // LCOV_EXCL_BR_LINE + LOGE("get clean tracker data stmt failed %d.", errCode); + return errCode; + } + errCode = SQLiteUtils::BindInt64ToStatement(statement, 1, cursor); + int ret = E_OK; + if (errCode != E_OK) { // LCOV_EXCL_BR_LINE + LOGE("bind clean tracker data stmt failed %d.", errCode); + SQLiteUtils::ResetStatement(statement, true, ret); + return errCode; + } + errCode = SQLiteUtils::StepWithRetry(statement); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { // LCOV_EXCL_BR_LINE + errCode = E_OK; + } else { + LOGE("clean tracker step failed: %d.", errCode); + } + SQLiteUtils::ResetStatement(statement, true, ret); + return errCode == E_OK ? ret : errCode; +} + + +int SQLiteRelationalUtils::AnalysisTrackerTable(sqlite3 *db, const TrackerTable &trackerTable, TableInfo &tableInfo) +{ + int errCode = SQLiteUtils::AnalysisSchema(db, trackerTable.GetTableName(), tableInfo, true); + if (errCode != E_OK) { + LOGE("analysis table schema failed %d.", errCode); + return errCode; + } + tableInfo.SetTrackerTable(trackerTable); + errCode = tableInfo.CheckTrackerTable(); + if (errCode != E_OK) { + LOGE("check tracker table schema failed %d.", errCode); + } + return errCode; +} +} // namespace DistributedDB \ No newline at end of file diff --git a/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_single_relational_storage_engine.cpp b/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_single_relational_storage_engine.cpp index 39410e7fbd5e991289ff27687dcf9c1f291af303..cd8953976dc45a198dba038bc961285d360e0acb 100644 --- a/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_single_relational_storage_engine.cpp +++ b/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_single_relational_storage_engine.cpp @@ -40,7 +40,7 @@ StorageExecutor *SQLiteSingleRelationalStorageEngine::NewSQLiteStorageExecutor(s int SQLiteSingleRelationalStorageEngine::Upgrade(sqlite3 *db) { - int errCode = CreateRelationalMetaTable(db); + int errCode = SQLiteRelationalUtils::CreateRelationalMetaTable(db); if (errCode != E_OK) { LOGE("Create relational store meta table failed. err=%d", errCode); return errCode; @@ -441,19 +441,6 @@ void SQLiteSingleRelationalStorageEngine::SetProperties(const RelationalDBProper properties_ = properties; } -int SQLiteSingleRelationalStorageEngine::CreateRelationalMetaTable(sqlite3 *db) -{ - std::string sql = - "CREATE TABLE IF NOT EXISTS " + std::string(DBConstant::RELATIONAL_PREFIX) + "metadata(" \ - "key BLOB PRIMARY KEY NOT NULL," \ - "value BLOB);"; - int errCode = SQLiteUtils::ExecuteRawSQL(db, sql); - if (errCode != E_OK) { - LOGE("[SQLite] execute create table sql failed, err=%d", errCode); - } - return errCode; -} - int SQLiteSingleRelationalStorageEngine::SetTrackerTable(const TrackerSchema &schema, const TableInfo &tableInfo, bool isFirstCreate) { diff --git a/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_single_relational_storage_engine.h b/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_single_relational_storage_engine.h index 04527eba557a8da06d328130ef4e67abb51b3e52..7e77c11ee213fa6a6ea57cf295f74ffc6069a911 100644 --- a/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_single_relational_storage_engine.h +++ b/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_single_relational_storage_engine.h @@ -86,8 +86,6 @@ private: int CreateDistributedSharedTable(SQLiteSingleVerRelationalStorageExecutor *&handle, const std::string &tableName, const std::string &sharedTableName, TableSyncType syncType, RelationalSchemaObject &schema); - int CreateRelationalMetaTable(sqlite3 *db); - int CleanTrackerDeviceTable(const std::vector &tableNames, RelationalSchemaObject &trackerSchemaObj, SQLiteSingleVerRelationalStorageExecutor *&handle); diff --git a/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_single_ver_relational_storage_executor.cpp b/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_single_ver_relational_storage_executor.cpp index 0998c5f6593d8f32b052ccd0a41ec337cfd2451f..dad5c9ceb557e79d468663fdc507e2bd1da49a14 100644 --- a/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_single_ver_relational_storage_executor.cpp +++ b/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_single_ver_relational_storage_executor.cpp @@ -142,93 +142,14 @@ int CheckTableConstraint(const TableInfo &table, DistributedTableMode mode, Tabl return E_OK; } -namespace { -int GetExistedDataTimeOffset(sqlite3 *db, const std::string &tableName, bool isMem, int64_t &timeOffset) -{ - std::string sql = "SELECT get_sys_time(0) - max(" + std::string(DBConstant::SQLITE_INNER_ROWID) + ") - 1 FROM '" + - tableName + "';"; - sqlite3_stmt *stmt = nullptr; - int errCode = SQLiteUtils::GetStatement(db, sql, stmt); - if (errCode != E_OK) { - return errCode; - } - errCode = SQLiteUtils::StepWithRetry(stmt, isMem); - if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { - timeOffset = static_cast(sqlite3_column_int64(stmt, 0)); - errCode = E_OK; - } - int ret = E_OK; - SQLiteUtils::ResetStatement(stmt, true, ret); - return errCode != E_OK ? errCode : ret; -} -} - -std::string GetExtendValue(const TrackerTable &trackerTable) -{ - std::string extendValue; - const std::set &extendNames = trackerTable.GetExtendNames(); - if (!extendNames.empty()) { - extendValue += "json_object("; - for (const auto &extendName : extendNames) { - extendValue += "'" + extendName + "'," + extendName + ","; - } - extendValue.pop_back(); - extendValue += ")"; - } else { - extendValue = "''"; - } - return extendValue; -} - -int SQLiteSingleVerRelationalStorageExecutor::GeneLogInfoForExistedDataInner(sqlite3 *db, const std::string &identity, - const TableInfo &tableInfo, std::unique_ptr &logMgrPtr, bool isTrackerTable) -{ - std::string tableName = tableInfo.GetTableName(); - int64_t timeOffset = 0; - int errCode = GetExistedDataTimeOffset(db, tableName, isMemDb_, timeOffset); - if (errCode != E_OK) { - return errCode; - } - errCode = SetLogTriggerStatus(false); - if (errCode != E_OK) { - return errCode; - } - std::string timeOffsetStr = std::to_string(timeOffset); - std::string logTable = DBConstant::RELATIONAL_PREFIX + tableName + "_log"; - std::string rowid = std::string(DBConstant::SQLITE_INNER_ROWID); - std::string flag = std::to_string(static_cast(LogInfoFlag::FLAG_LOCAL) | - static_cast(LogInfoFlag::FLAG_DEVICE_CLOUD_INCONSISTENCY)); - TrackerTable trackerTable = tableInfo.GetTrackerTable(); - trackerTable.SetTableName(tableName); - const std::string prefix = "a."; - std::string calPrimaryKeyHash = logMgrPtr->CalcPrimaryKeyHash(prefix, tableInfo, identity); - std::string sql = "INSERT OR REPLACE INTO " + logTable + " SELECT " + rowid + - ", '', '', " + timeOffsetStr + " + " + rowid + ", " + - timeOffsetStr + " + " + rowid + ", " + flag + ", " + calPrimaryKeyHash + ", '', "; - sql += GetExtendValue(tableInfo.GetTrackerTable()); - sql += ", 0, '', '', 0 FROM '" + tableName + "' AS a "; - if (isTrackerTable) { - sql += " WHERE 1 = 1;"; - } else { - sql += "WHERE NOT EXISTS (SELECT 1 FROM " + logTable + " WHERE data_key = a._rowid_);"; - } - errCode = trackerTable.ReBuildTempTrigger(db, TriggerMode::TriggerModeEnum::INSERT, [db, &sql]() { - int ret = SQLiteUtils::ExecuteRawSQL(db, sql); - if (ret != E_OK) { - LOGE("Failed to initialize cloud type log data.%d", ret); - } - return ret; - }); - return errCode; -} - int SQLiteSingleVerRelationalStorageExecutor::GeneLogInfoForExistedData(sqlite3 *db, const std::string &identity, const TableInfo &tableInfo, std::unique_ptr &logMgrPtr, bool isTrackerTable) { if (tableInfo.GetTableSyncType() == TableSyncType::DEVICE_COOPERATION) { return UpdateTrackerTable(db, identity, tableInfo, logMgrPtr, false); } - return GeneLogInfoForExistedDataInner(db, identity, tableInfo, logMgrPtr, isTrackerTable); + SQLiteRelationalUtils::GenLogParam param = {db, isMemDb_, isTrackerTable}; + return SQLiteRelationalUtils::GeneLogInfoForExistedData(identity, tableInfo, logMgrPtr, param); } int SQLiteSingleVerRelationalStorageExecutor::ResetLogStatus(std::string &tableName) @@ -328,7 +249,8 @@ int SQLiteSingleVerRelationalStorageExecutor::CreateRelationalLogTable(Distribut bool isOnceDropped = false; (void)IsTableOnceDropped(tableName, isOnceDropped); if (isOnceDropped && table.GetTrackerTable().GetTableName().empty()) { - errCode = GeneLogInfoForExistedDataInner(dbHandle_, identity, table, tableManager, false); + SQLiteRelationalUtils::GenLogParam param = {dbHandle_, isMemDb_, false}; + errCode = SQLiteRelationalUtils::GeneLogInfoForExistedData(identity, table, tableManager, param); } } if (errCode != E_OK) { @@ -778,63 +700,12 @@ static size_t GetDataItemSerialSize(DataItem &item, size_t appendLen) int SQLiteSingleVerRelationalStorageExecutor::GetKvData(const Key &key, Value &value) const { - static const std::string SELECT_META_VALUE_SQL = "SELECT value FROM " + std::string(DBConstant::RELATIONAL_PREFIX) + - "metadata WHERE key=?;"; - sqlite3_stmt *statement = nullptr; - int errCode = SQLiteUtils::GetStatement(dbHandle_, SELECT_META_VALUE_SQL, statement); - if (errCode != E_OK) { - goto END; - } - - errCode = SQLiteUtils::BindBlobToStatement(statement, 1, key, false); // first arg. - if (errCode != E_OK) { - goto END; - } - - errCode = SQLiteUtils::StepWithRetry(statement, isMemDb_); - if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { - errCode = -E_NOT_FOUND; - goto END; - } else if (errCode != SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { - goto END; - } - - errCode = SQLiteUtils::GetColumnBlobValue(statement, 0, value); // only one result. -END: - int ret = E_OK; - SQLiteUtils::ResetStatement(statement, true, ret); - return errCode != E_OK ? errCode : ret; + return SQLiteRelationalUtils::GetKvData(dbHandle_, isMemDb_, key, value); } int SQLiteSingleVerRelationalStorageExecutor::PutKvData(const Key &key, const Value &value) const { - static const std::string INSERT_META_SQL = "INSERT OR REPLACE INTO " + std::string(DBConstant::RELATIONAL_PREFIX) + - "metadata VALUES(?,?);"; - sqlite3_stmt *statement = nullptr; - int errCode = SQLiteUtils::GetStatement(dbHandle_, INSERT_META_SQL, statement); - if (errCode != E_OK) { - return errCode; - } - - errCode = SQLiteUtils::BindBlobToStatement(statement, 1, key, false); // 1 means key index - if (errCode != E_OK) { - LOGE("[SingleVerExe][BindPutKv]Bind key error:%d", errCode); - goto ERROR; - } - - errCode = SQLiteUtils::BindBlobToStatement(statement, 2, value, true); // 2 means value index - if (errCode != E_OK) { - LOGE("[SingleVerExe][BindPutKv]Bind value error:%d", errCode); - goto ERROR; - } - errCode = SQLiteUtils::StepWithRetry(statement, isMemDb_); - if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { - errCode = E_OK; - } -ERROR: - int ret = E_OK; - SQLiteUtils::ResetStatement(statement, true, ret); - return errCode != E_OK ? errCode : ret; + return SQLiteRelationalUtils::PutKvData(dbHandle_, isMemDb_, key, value); } int SQLiteSingleVerRelationalStorageExecutor::DeleteMetaData(const std::vector &keys) const @@ -1558,15 +1429,7 @@ int SQLiteSingleVerRelationalStorageExecutor::GetMaxTimestamp(const std::vector< int SQLiteSingleVerRelationalStorageExecutor::SetLogTriggerStatus(bool status) { - const std::string key = "log_trigger_switch"; - std::string val = status ? "true" : "false"; - std::string sql = "INSERT OR REPLACE INTO " + std::string(DBConstant::RELATIONAL_PREFIX) + "metadata" + - " VALUES ('" + key + "', '" + val + "')"; - int errCode = SQLiteUtils::ExecuteRawSQL(dbHandle_, sql); - if (errCode != E_OK) { - LOGE("Set log trigger to %s failed. errCode=%d", val.c_str(), errCode); - } - return errCode; + return SQLiteRelationalUtils::SetLogTriggerStatus(dbHandle_, status); } namespace { diff --git a/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_single_ver_relational_storage_executor.h b/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_single_ver_relational_storage_executor.h index 8acac8e99787cd73959897cdbbc8e805946a649d..150c6e03283834f819b5ce4180358f41716c6707 100644 --- a/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_single_ver_relational_storage_executor.h +++ b/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_single_ver_relational_storage_executor.h @@ -225,7 +225,7 @@ public: void SetUploadConfig(int32_t maxUploadCount, int32_t maxUploadSize); - int InitCursorToMeta(const std::string &tableName); + int InitCursorToMeta(const std::string &tableName) const; void SetTableSchema(const TableSchema &tableSchema); @@ -329,9 +329,6 @@ private: int GeneLogInfoForExistedData(sqlite3 *db, const std::string &identity, const TableInfo &tableInfo, std::unique_ptr &logMgrPtr, bool isTrackerTable); - int GeneLogInfoForExistedDataInner(sqlite3 *db, const std::string &identity, const TableInfo &tableInfo, - std::unique_ptr &logMgrPtr, bool isTrackerTable); - int CleanExtendAndCursorForDeleteData(const std::string &tableName); int GetCloudDataForSync(const CloudUploadRecorder &uploadRecorder, sqlite3_stmt *statement, diff --git a/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_single_ver_relational_storage_executor_extend.cpp b/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_single_ver_relational_storage_executor_extend.cpp index 007fe7e5abddb3f577b08eae027741a179d2a3fe..2e33253e0baa9aa243eb562e76e4b353e9591e43 100644 --- a/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_single_ver_relational_storage_executor_extend.cpp +++ b/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_single_ver_relational_storage_executor_extend.cpp @@ -1309,24 +1309,9 @@ int SQLiteSingleVerRelationalStorageExecutor::LogicDeleteCloudData(const std::st return E_OK; } -int SQLiteSingleVerRelationalStorageExecutor::InitCursorToMeta(const std::string &tableName) +int SQLiteSingleVerRelationalStorageExecutor::InitCursorToMeta(const std::string &tableName) const { - Value key; - Value cursor; - DBCommon::StringToVector(DBCommon::GetCursorKey(tableName), key); - int errCode = GetKvData(key, cursor); - if (errCode == -E_NOT_FOUND) { - DBCommon::StringToVector(std::string("0"), cursor); - errCode = PutKvData(key, cursor); - if (errCode != E_OK) { - LOGE("Init cursor to meta table failed. %d", errCode); - } - return errCode; - } - if (errCode != E_OK) { - LOGE("Get cursor from meta table failed. %d", errCode); - } - return errCode; + return SQLiteRelationalUtils::InitCursorToMeta(dbHandle_, isMemDb_, tableName); } void SQLiteSingleVerRelationalStorageExecutor::SetTableSchema(const TableSchema &tableSchema) diff --git a/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_single_ver_relational_storage_extend_executor.cpp b/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_single_ver_relational_storage_extend_executor.cpp index 73cb0655cf55835e317a431bf7a865293c1e6f6f..4d0abe7f448552b47cb15aa30e284677fc937253 100644 --- a/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_single_ver_relational_storage_extend_executor.cpp +++ b/frameworks/libs/distributeddb/storage/src/sqlite/relational/sqlite_single_ver_relational_storage_extend_executor.cpp @@ -614,34 +614,7 @@ int SQLiteSingleVerRelationalStorageExecutor::ClearAllTempSyncTrigger() int SQLiteSingleVerRelationalStorageExecutor::CleanTrackerData(const std::string &tableName, int64_t cursor, bool isOnlyTrackTable) { - std::string sql; - if (isOnlyTrackTable) { - sql = "DELETE FROM " + std::string(DBConstant::RELATIONAL_PREFIX) + tableName + "_log"; - } else { - sql = "UPDATE " + std::string(DBConstant::RELATIONAL_PREFIX) + tableName + "_log SET extend_field = NULL"; - } - sql += " where data_key = -1 and cursor <= ?;"; - sqlite3_stmt *statement = nullptr; - int errCode = SQLiteUtils::GetStatement(dbHandle_, sql, statement); - if (errCode != E_OK) { // LCOV_EXCL_BR_LINE - LOGE("get clean tracker data stmt failed %d.", errCode); - return errCode; - } - errCode = SQLiteUtils::BindInt64ToStatement(statement, 1, cursor); - int ret = E_OK; - if (errCode != E_OK) { // LCOV_EXCL_BR_LINE - LOGE("bind clean tracker data stmt failed %d.", errCode); - SQLiteUtils::ResetStatement(statement, true, ret); - return errCode; - } - errCode = SQLiteUtils::StepWithRetry(statement); - if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { // LCOV_EXCL_BR_LINE - errCode = E_OK; - } else { - LOGE("clean tracker step failed: %d.", errCode); - } - SQLiteUtils::ResetStatement(statement, true, ret); - return errCode == E_OK ? ret : errCode; + return SQLiteRelationalUtils::CleanTrackerData(dbHandle_, tableName, cursor, isOnlyTrackTable); } int SQLiteSingleVerRelationalStorageExecutor::CreateSharedTable(const TableSchema &tableSchema) diff --git a/frameworks/libs/distributeddb/storage/src/sqlite/sqlite_utils.cpp b/frameworks/libs/distributeddb/storage/src/sqlite/sqlite_utils.cpp index cf5292755c309afc4adc26eb6b3e1ecf2437797d..e94646adc5ec06d64c8614843230647ac0ec7a57 100644 --- a/frameworks/libs/distributeddb/storage/src/sqlite/sqlite_utils.cpp +++ b/frameworks/libs/distributeddb/storage/src/sqlite/sqlite_utils.cpp @@ -42,14 +42,9 @@ namespace DistributedDB { std::mutex SQLiteUtils::logMutex_; std::string SQLiteUtils::lastErrorMsg_; namespace { - const int BUSY_SLEEP_TIME = 50; // sleep for 50us - const int NO_SIZE_LIMIT = -1; - const int MAX_STEP_TIMES = 8000; const int BIND_KEY_INDEX = 1; const int BIND_VAL_INDEX = 2; const int USING_STR_LEN = -1; - const int MAX_BLOB_READ_SIZE = 64 * 1024 * 1024; // 64M limit - const int MAX_TEXT_READ_SIZE = 5 * 1024 * 1024; // 5M limit const int HEAD_SIZE = 3; const int END_SIZE = 3; constexpr int MIN_SIZE = HEAD_SIZE + END_SIZE + 3; @@ -58,10 +53,6 @@ namespace { const std::string WAL_MODE_SQL = "PRAGMA journal_mode=WAL;"; const std::string SYNC_MODE_FULL_SQL = "PRAGMA synchronous=FULL;"; const std::string USER_VERSION_SQL = "PRAGMA user_version;"; - const std::string BEGIN_SQL = "BEGIN TRANSACTION"; - const std::string BEGIN_IMMEDIATE_SQL = "BEGIN IMMEDIATE TRANSACTION"; - const std::string COMMIT_SQL = "COMMIT TRANSACTION"; - const std::string ROLLBACK_SQL = "ROLLBACK TRANSACTION"; const std::string DEFAULT_ATTACH_CIPHER = "PRAGMA cipher_default_attach_cipher="; const std::string DEFAULT_ATTACH_KDF_ITER = "PRAGMA cipher_default_attach_kdf_iter=5000"; const std::string SHA256_ALGO_SQL = "PRAGMA codec_hmac_algo=SHA256;"; @@ -82,21 +73,6 @@ namespace { std::map> g_serverChangedDataMap; } -namespace TriggerMode { -const std::map TRIGGER_MODE_MAP = { - {TriggerModeEnum::NONE, ""}, - {TriggerModeEnum::INSERT, "INSERT"}, - {TriggerModeEnum::UPDATE, "UPDATE"}, - {TriggerModeEnum::DELETE, "DELETE"}, -}; - -std::string GetTriggerModeString(TriggerModeEnum mode) -{ - auto it = TRIGGER_MODE_MAP.find(mode); - return (it == TRIGGER_MODE_MAP.end()) ? "" : it->second; -} -} - std::string SQLiteUtils::Anonymous(const std::string &name) { if (name.length() <= HEAD_SIZE) { @@ -231,150 +207,6 @@ END: return errCode; } -int SQLiteUtils::GetStatement(sqlite3 *db, const std::string &sql, sqlite3_stmt *&statement) -{ - if (db == nullptr) { - LOGE("Invalid db for statement"); - return -E_INVALID_DB; - } - // Prepare the new statement only when the input parameter is not null - if (statement != nullptr) { - return E_OK; - } - int errCode = sqlite3_prepare_v2(db, sql.c_str(), NO_SIZE_LIMIT, &statement, nullptr); - if (errCode != SQLITE_OK) { - LOGE("Prepare SQLite statement failed:%d, sys:%d", errCode, errno); - errCode = SQLiteUtils::MapSQLiteErrno(errCode); - SQLiteUtils::ResetStatement(statement, true, errCode); - return errCode; - } - - if (statement == nullptr) { - return -E_INVALID_DB; - } - - return E_OK; -} - -int SQLiteUtils::BindTextToStatement(sqlite3_stmt *statement, int index, const std::string &str) -{ - if (statement == nullptr) { - return -E_INVALID_ARGS; - } - - int errCode = sqlite3_bind_text(statement, index, str.c_str(), str.length(), SQLITE_TRANSIENT); - if (errCode != SQLITE_OK) { - LOGE("[SQLiteUtil][Bind text]Failed to bind the value:%d", errCode); - return SQLiteUtils::MapSQLiteErrno(errCode); - } - - return E_OK; -} - -int SQLiteUtils::BindInt64ToStatement(sqlite3_stmt *statement, int index, int64_t value) -{ - // statement check outSide - int errCode = sqlite3_bind_int64(statement, index, value); - if (errCode != SQLITE_OK) { - LOGE("[SQLiteUtil][Bind int64]Failed to bind the value:%d", errCode); - return SQLiteUtils::MapSQLiteErrno(errCode); - } - - return E_OK; -} - -int SQLiteUtils::BindBlobToStatement(sqlite3_stmt *statement, int index, const std::vector &value, - bool permEmpty) -{ - if (statement == nullptr) { - return -E_INVALID_ARGS; - } - - // Check empty value. - if (value.empty() && !permEmpty) { - LOGI("[SQLiteUtil][Bind blob]Invalid value"); - return -E_INVALID_ARGS; - } - - int errCode; - if (value.empty()) { - errCode = sqlite3_bind_zeroblob(statement, index, -1); // -1 for zero-length blob. - } else { - errCode = sqlite3_bind_blob(statement, index, static_cast(value.data()), - value.size(), SQLITE_TRANSIENT); - } - - if (errCode != SQLITE_OK) { - LOGE("[SQLiteUtil][Bind blob]Failed to bind the value:%d", errCode); - return SQLiteUtils::MapSQLiteErrno(errCode); - } - - return E_OK; -} - -void SQLiteUtils::ResetStatement(sqlite3_stmt *&statement, bool isNeedFinalize, int &errCode) -{ - ResetStatement(statement, isNeedFinalize, false, errCode); -} - -void SQLiteUtils::ResetStatement(sqlite3_stmt *&statement, bool isNeedFinalize, bool isIgnoreResetRet, int &errCode) -{ - if (statement == nullptr) { - return; - } - - int innerCode = SQLITE_OK; - // if need finalize the statement, just goto finalize. - if (!isNeedFinalize) { - // reset the statement firstly. - innerCode = sqlite3_reset(statement); - if (innerCode != SQLITE_OK && !isIgnoreResetRet) { - LOGE("[SQLiteUtils] reset statement error:%d, sys:%d", innerCode, errno); - isNeedFinalize = true; - } else { - sqlite3_clear_bindings(statement); - } - } - - if (isNeedFinalize) { - int finalizeResult = sqlite3_finalize(statement); - if (finalizeResult != SQLITE_OK) { - LOGE("[SQLiteUtils] finalize statement error:%d, sys:%d", finalizeResult, errno); - innerCode = finalizeResult; - } - statement = nullptr; - } - - if (innerCode != SQLITE_OK) { // the sqlite error code has higher priority. - errCode = SQLiteUtils::MapSQLiteErrno(innerCode); - } -} - -int SQLiteUtils::StepWithRetry(sqlite3_stmt *statement, bool isMemDb) -{ - if (statement == nullptr) { - return -E_INVALID_ARGS; - } - - int errCode = E_OK; - int retryCount = 0; - do { - errCode = sqlite3_step(statement); - if ((errCode == SQLITE_LOCKED) && isMemDb) { - std::this_thread::sleep_for(std::chrono::microseconds(BUSY_SLEEP_TIME)); - retryCount++; - } else { - break; - } - } while (retryCount <= MAX_STEP_TIMES); - - if (errCode != SQLITE_DONE && errCode != SQLITE_ROW) { - LOGE("[SQLiteUtils] Step error:%d, sys:%d", errCode, errno); - } - - return SQLiteUtils::MapSQLiteErrno(errCode); -} - int SQLiteUtils::BindPrefixKey(sqlite3_stmt *statement, int index, const Key &keyPrefix) { if (statement == nullptr) { @@ -414,57 +246,6 @@ int SQLiteUtils::BindPrefixKey(sqlite3_stmt *statement, int index, const Key &ke return E_OK; } -int SQLiteUtils::BeginTransaction(sqlite3 *db, TransactType type) -{ - if (type == TransactType::IMMEDIATE) { - return ExecuteRawSQL(db, BEGIN_IMMEDIATE_SQL, true); - } - - return ExecuteRawSQL(db, BEGIN_SQL, true); -} - -int SQLiteUtils::CommitTransaction(sqlite3 *db) -{ - return ExecuteRawSQL(db, COMMIT_SQL, true); -} - -int SQLiteUtils::RollbackTransaction(sqlite3 *db) -{ - return ExecuteRawSQL(db, ROLLBACK_SQL, true); -} - -int SQLiteUtils::ExecuteRawSQL(sqlite3 *db, const std::string &sql, bool ignoreResetFail) -{ - if (db == nullptr) { - return -E_INVALID_DB; - } - - sqlite3_stmt *stmt = nullptr; - int errCode = SQLiteUtils::GetStatement(db, sql, stmt); - if (errCode != SQLiteUtils::MapSQLiteErrno(SQLITE_OK)) { - LOGE("[SQLiteUtils][ExecuteSQL] prepare statement failed(%d), sys(%d)", errCode, errno); - return errCode; - } - - do { - errCode = SQLiteUtils::StepWithRetry(stmt); - if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { - errCode = E_OK; - break; - } else if (errCode != SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { - LOGE("[SQLiteUtils][ExecuteSQL] execute statement failed(%d), sys(%d)", errCode, errno); - break; - } - } while (true); - - int ret = E_OK; - SQLiteUtils::ResetStatement(stmt, true, ret); - if (!ignoreResetFail && ret != E_OK) { - return errCode != E_OK ? errCode : ret; - } - return errCode; -} - int SQLiteUtils::SetKey(sqlite3 *db, CipherType type, const CipherPassword &passwd, bool setWal, uint32_t iterTimes) { if (db == nullptr) { @@ -515,57 +296,6 @@ int SQLiteUtils::SetKey(sqlite3 *db, CipherType type, const CipherPassword &pass return errCode; } -int SQLiteUtils::GetColumnBlobValue(sqlite3_stmt *statement, int index, std::vector &value) -{ - if (statement == nullptr) { - return -E_INVALID_ARGS; - } - - int keySize = sqlite3_column_bytes(statement, index); - if (keySize < 0) { - LOGW("[SQLiteUtils][Column blob] size less than zero:%d", keySize); - value.resize(0); - return E_OK; - } - auto keyRead = static_cast(sqlite3_column_blob(statement, index)); - if (keySize == 0 || keyRead == nullptr) { - value.resize(0); - } else { - if (keySize > MAX_BLOB_READ_SIZE) { - LOGW("[SQLiteUtils][Column blob] size over limit:%d", keySize); - keySize = MAX_BLOB_READ_SIZE + 1; - } - value.resize(keySize); - value.assign(keyRead, keyRead + keySize); - } - return E_OK; -} - -int SQLiteUtils::GetColumnTextValue(sqlite3_stmt *statement, int index, std::string &value) -{ - if (statement == nullptr) { - return -E_INVALID_ARGS; - } - - int valSize = sqlite3_column_bytes(statement, index); - if (valSize < 0) { - LOGW("[SQLiteUtils][Column Text] size less than zero:%d", valSize); - value = {}; - return E_OK; - } - const unsigned char *val = sqlite3_column_text(statement, index); - if (valSize == 0 || val == nullptr) { - value = {}; - return E_OK; - } - value = std::string(reinterpret_cast(val)); - if (valSize > MAX_TEXT_READ_SIZE) { - LOGW("[SQLiteUtils][Column text] size over limit:%d", valSize); - value.resize(MAX_TEXT_READ_SIZE + 1); // Reset value size to invalid - } - return E_OK; -} - int SQLiteUtils::AttachNewDatabase(sqlite3 *db, CipherType type, const CipherPassword &password, const std::string &attachDbAbsPath, const std::string &attachAsName) { @@ -712,265 +442,7 @@ int SQLiteUtils::CheckIntegrity(sqlite3 *db, const std::string &sql) SQLiteUtils::ResetStatement(statement, true, ret); return errCode != E_OK ? errCode : ret; } -#ifdef RELATIONAL_STORE - -namespace { // anonymous namespace for schema analysis -int AnalysisSchemaSqlAndTrigger(sqlite3 *db, const std::string &tableName, TableInfo &table, bool caseSensitive) -{ - std::string sql = "SELECT type, sql FROM sqlite_master WHERE tbl_name = ? "; - if (!caseSensitive) { - sql += "COLLATE NOCASE"; - } - sqlite3_stmt *statement = nullptr; - int errCode = SQLiteUtils::GetStatement(db, sql, statement); - if (errCode != E_OK) { - LOGE("[AnalysisSchema] Prepare the analysis schema sql and trigger statement error:%d", errCode); - return errCode; - } - errCode = SQLiteUtils::BindTextToStatement(statement, 1, tableName); - int ret = E_OK; - if (errCode != E_OK) { - LOGE("[AnalysisSchema] Bind table name failed:%d", errCode); - SQLiteUtils::ResetStatement(statement, true, ret); - return errCode; - } - - errCode = -E_NOT_FOUND; - do { - int err = SQLiteUtils::StepWithRetry(statement); - if (err == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { - break; - } else if (err == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { - errCode = E_OK; - std::string type; - (void) SQLiteUtils::GetColumnTextValue(statement, 0, type); - if (type == "table") { - std::string createTableSql; - (void) SQLiteUtils::GetColumnTextValue(statement, 1, createTableSql); // 1 means create table sql - table.SetCreateTableSql(createTableSql); - } - } else { - LOGE("[AnalysisSchema] Step for the analysis create table sql and trigger failed:%d", err); - errCode = SQLiteUtils::MapSQLiteErrno(err); - break; - } - } while (true); - SQLiteUtils::ResetStatement(statement, true, ret); - return errCode != E_OK ? errCode : ret; -} - -int GetSchemaIndexList(sqlite3 *db, const std::string &tableName, std::vector &indexList, - std::vector &uniqueList) -{ - std::string sql = "pragma index_list('" + tableName + "')"; - sqlite3_stmt *statement = nullptr; - int errCode = SQLiteUtils::GetStatement(db, sql, statement); - if (errCode != E_OK) { - LOGE("[AnalysisSchema] Prepare the get schema index list statement error:%d", errCode); - return errCode; - } - - do { - errCode = SQLiteUtils::StepWithRetry(statement); - if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { - errCode = E_OK; - break; - } else if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { - std::string indexName; - (void) SQLiteUtils::GetColumnTextValue(statement, 1, indexName); // 1 means index name - int unique = sqlite3_column_int64(statement, 2); // 2 means index type, whether unique - if (unique == 0) { // 0 means index created by user declare - indexList.push_back(indexName); - } else if (unique == 1) { // 1 means an unique define - uniqueList.push_back(indexName); - } - } else { - LOGW("[AnalysisSchema] Step for the get schema index list failed:%d", errCode); - break; - } - } while (true); - int ret = E_OK; - SQLiteUtils::ResetStatement(statement, true, ret); - return errCode != E_OK ? errCode : ret; -} - -int AnalysisSchemaIndexDefine(sqlite3 *db, const std::string &indexName, CompositeFields &indexDefine) -{ - auto sql = "pragma index_info('" + indexName + "')"; - sqlite3_stmt *statement = nullptr; - int errCode = SQLiteUtils::GetStatement(db, sql, statement); - if (errCode != E_OK) { - LOGE("[AnalysisSchema] Prepare the analysis schema index statement error:%d", errCode); - return errCode; - } - - do { - errCode = SQLiteUtils::StepWithRetry(statement); - if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { - errCode = E_OK; - break; - } else if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { - std::string indexField; - (void) SQLiteUtils::GetColumnTextValue(statement, 2, indexField); // 2 means index's column name. - indexDefine.push_back(indexField); - } else { - LOGW("[AnalysisSchema] Step for the analysis schema index failed:%d", errCode); - break; - } - } while (true); - - int ret = E_OK; - SQLiteUtils::ResetStatement(statement, true, ret); - return errCode != E_OK ? errCode : ret; -} - -int AnalysisSchemaIndex(sqlite3 *db, const std::string &tableName, TableInfo &table) -{ - std::vector indexList; - std::vector uniqueList; - int errCode = GetSchemaIndexList(db, tableName, indexList, uniqueList); - if (errCode != E_OK) { - LOGE("[AnalysisSchema] get schema index list failed."); - return errCode; - } - - for (const auto &indexName : indexList) { - CompositeFields indexDefine; - errCode = AnalysisSchemaIndexDefine(db, indexName, indexDefine); - if (errCode != E_OK) { - LOGE("[AnalysisSchema] analysis schema index columns failed."); - return errCode; - } - table.AddIndexDefine(indexName, indexDefine); - } - std::vector uniques; - for (const auto &uniqueName : uniqueList) { - CompositeFields uniqueDefine; - errCode = AnalysisSchemaIndexDefine(db, uniqueName, uniqueDefine); - if (errCode != E_OK) { - LOGE("[AnalysisSchema] analysis schema unique columns failed."); - return errCode; - } - uniques.push_back(uniqueDefine); - } - table.SetUniqueDefine(uniques); - return E_OK; -} - -void SetPrimaryKeyCollateType(const std::string &sql, FieldInfo &field) -{ - std::string upperFieldName = DBCommon::ToUpperCase(field.GetFieldName()); - if (DBCommon::HasConstraint(sql, "PRIMARY KEY COLLATE NOCASE", " ", " ,)") || - DBCommon::HasConstraint(sql, upperFieldName + " TEXT COLLATE NOCASE", " (,", " ,")) { - field.SetCollateType(CollateType::COLLATE_NOCASE); - } else if (DBCommon::HasConstraint(sql, "PRIMARY KEY COLLATE RTRIM", " ", " ,)") || - DBCommon::HasConstraint(sql, upperFieldName + " TEXT COLLATE RTRIM", " (,", " ,")) { - field.SetCollateType(CollateType::COLLATE_RTRIM); - } -} - -int SetFieldInfo(sqlite3_stmt *statement, TableInfo &table) -{ - FieldInfo field; - field.SetColumnId(sqlite3_column_int(statement, 0)); // 0 means column id index - - std::string tmpString; - (void) SQLiteUtils::GetColumnTextValue(statement, 1, tmpString); // 1 means column name index - if (!DBCommon::CheckIsAlnumOrUnderscore(tmpString)) { - LOGE("[AnalysisSchema] unsupported field name."); - return -E_NOT_SUPPORT; - } - field.SetFieldName(tmpString); - - (void) SQLiteUtils::GetColumnTextValue(statement, 2, tmpString); // 2 means datatype index - field.SetDataType(tmpString); - - field.SetNotNull(static_cast(sqlite3_column_int64(statement, 3))); // 3 means whether null index - - (void) SQLiteUtils::GetColumnTextValue(statement, 4, tmpString); // 4 means default value index - if (!tmpString.empty()) { - field.SetDefaultValue(tmpString); - } - - int keyIndex = sqlite3_column_int(statement, 5); // 5 means primary key index - if (keyIndex != 0) { // not 0 means is a primary key - table.SetPrimaryKey(field.GetFieldName(), keyIndex); - SetPrimaryKeyCollateType(table.GetCreateTableSql(), field); - } - table.AddField(field); - return E_OK; -} -} // end of anonymous namespace for schema analysis - -int SQLiteUtils::AnalysisSchemaFieldDefine(sqlite3 *db, const std::string &tableName, TableInfo &table) -{ - std::string sql = "pragma table_info('" + tableName + "')"; - sqlite3_stmt *statement = nullptr; - int errCode = SQLiteUtils::GetStatement(db, sql, statement); - if (errCode != E_OK) { - LOGE("[AnalysisSchema] Prepare the analysis schema field statement error:%d", errCode); - return errCode; - } - - do { - errCode = SQLiteUtils::StepWithRetry(statement); - if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { - errCode = E_OK; - break; - } else if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { - errCode = SetFieldInfo(statement, table); - if (errCode != E_OK) { - break; - } - } else { - LOGW("[AnalysisSchema] Step for the analysis schema field failed:%d", errCode); - break; - } - } while (true); - - if (table.GetPrimaryKey().empty()) { - table.SetPrimaryKey("rowid", 1); - } - - int ret = E_OK; - SQLiteUtils::ResetStatement(statement, true, ret); - return errCode != E_OK ? errCode : ret; -} - -int SQLiteUtils::AnalysisSchema(sqlite3 *db, const std::string &tableName, TableInfo &table, bool caseSensitive) -{ - if (db == nullptr) { - return -E_INVALID_DB; - } - - if (!DBCommon::CheckIsAlnumOrUnderscore(tableName)) { - LOGE("[AnalysisSchema] unsupported table name."); - return -E_NOT_SUPPORT; - } - - int errCode = AnalysisSchemaSqlAndTrigger(db, tableName, table, caseSensitive); - if (errCode != E_OK) { - LOGE("[AnalysisSchema] Analysis sql and trigger failed. errCode = [%d]", errCode); - return errCode; - } - - errCode = AnalysisSchemaIndex(db, tableName, table); - if (errCode != E_OK) { - LOGE("[AnalysisSchema] Analysis index failed."); - return errCode; - } - - errCode = AnalysisSchemaFieldDefine(db, tableName, table); - if (errCode != E_OK) { - LOGE("[AnalysisSchema] Analysis field failed."); - return errCode; - } - - table.SetTableName(tableName); - return E_OK; -} -#endif #ifndef OMIT_ENCRYPT int SQLiteUtils::ExportDatabase(sqlite3 *db, CipherType type, const CipherPassword &passwd, const std::string &newDbName) @@ -1158,39 +630,6 @@ int SQLiteUtils::SetUserVer(sqlite3 *db, int version) return SQLiteUtils::ExecuteRawSQL(db, userVersionSql); } -int SQLiteUtils::MapSQLiteErrno(int errCode) -{ - switch (errCode) { - case SQLITE_OK: - return E_OK; - case SQLITE_IOERR: - if (errno == EKEYREVOKED) { - return -E_EKEYREVOKED; - } - break; - case SQLITE_CORRUPT: - case SQLITE_NOTADB: - return -E_INVALID_PASSWD_OR_CORRUPTED_DB; - case SQLITE_LOCKED: - case SQLITE_BUSY: - return -E_BUSY; - case SQLITE_ERROR: - if (errno == EKEYREVOKED) { - return -E_EKEYREVOKED; - } - break; - case SQLITE_AUTH: - return -E_DENIED_SQL; - case SQLITE_CONSTRAINT: - return -E_CONSTRAINT; - case SQLITE_CANTOPEN: - return -E_SQLITE_CANT_OPEN; - default: - break; - } - return -errCode; -} - int SQLiteUtils::SetBusyTimeout(sqlite3 *db, int timeout) { if (db == nullptr) { diff --git a/frameworks/libs/distributeddb/storage/src/sqlite/sqlite_utils.h b/frameworks/libs/distributeddb/storage/src/sqlite/sqlite_utils.h index 1953db51e23826a3d88761ee7f8ad9a1e1d94592..69b1a18c75891541ab13fb27faa260cbb2f52db7 100644 --- a/frameworks/libs/distributeddb/storage/src/sqlite/sqlite_utils.h +++ b/frameworks/libs/distributeddb/storage/src/sqlite/sqlite_utils.h @@ -213,6 +213,8 @@ public: static int UpdateLocalDataModifyTime(sqlite3 *db, const std::string &virtualTime, const std::string &modifyTime); static int UpdateLocalDataCloudFlag(sqlite3 *db); + + static int ProcessStatementErrCode(sqlite3_stmt *&statement, bool isNeedFinalize, int errCode); private: static int CreateDataBase(const OpenDbProperties &properties, sqlite3 *&dbTemp, bool setWal); diff --git a/frameworks/libs/distributeddb/storage/src/sqlite/sqlite_utils_client.cpp b/frameworks/libs/distributeddb/storage/src/sqlite/sqlite_utils_client.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4825ffda9691f898a7041e1860908105a17182c3 --- /dev/null +++ b/frameworks/libs/distributeddb/storage/src/sqlite/sqlite_utils_client.cpp @@ -0,0 +1,651 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "sqlite_utils.h" + +#include "db_common.h" +#include "platform_specific.h" + +namespace DistributedDB { +namespace { + const int BUSY_SLEEP_TIME = 50; // sleep for 50us + const int NO_SIZE_LIMIT = -1; + const int MAX_STEP_TIMES = 8000; + const std::string BEGIN_SQL = "BEGIN TRANSACTION"; + const std::string BEGIN_IMMEDIATE_SQL = "BEGIN IMMEDIATE TRANSACTION"; + const std::string COMMIT_SQL = "COMMIT TRANSACTION"; + const std::string ROLLBACK_SQL = "ROLLBACK TRANSACTION"; + const int MAX_BLOB_READ_SIZE = 64 * 1024 * 1024; // 64M limit + const int MAX_TEXT_READ_SIZE = 5 * 1024 * 1024; // 5M limit + + const constexpr char *CHECK_TABLE_CREATED = "SELECT EXISTS(SELECT 1 FROM sqlite_master WHERE " \ + "type='table' AND (tbl_name=? COLLATE NOCASE));"; + const constexpr char *CHECK_META_DB_TABLE_CREATED = "SELECT EXISTS(SELECT 1 FROM meta.sqlite_master WHERE " \ + "type='table' AND (tbl_name=? COLLATE NOCASE));"; +} + +namespace TriggerMode { +const std::map TRIGGER_MODE_MAP = { + {TriggerModeEnum::NONE, ""}, + {TriggerModeEnum::INSERT, "INSERT"}, + {TriggerModeEnum::UPDATE, "UPDATE"}, + {TriggerModeEnum::DELETE, "DELETE"}, +}; + +std::string GetTriggerModeString(TriggerModeEnum mode) +{ + auto it = TRIGGER_MODE_MAP.find(mode); + return (it == TRIGGER_MODE_MAP.end()) ? "" : it->second; +} +} + +int SQLiteUtils::StepWithRetry(sqlite3_stmt *statement, bool isMemDb) +{ + if (statement == nullptr) { + return -E_INVALID_ARGS; + } + + int errCode = E_OK; + int retryCount = 0; + do { + errCode = sqlite3_step(statement); + if ((errCode == SQLITE_LOCKED) && isMemDb) { + std::this_thread::sleep_for(std::chrono::microseconds(BUSY_SLEEP_TIME)); + retryCount++; + } else { + break; + } + } while (retryCount <= MAX_STEP_TIMES); + + if (errCode != SQLITE_DONE && errCode != SQLITE_ROW) { + LOGE("[SQLiteUtils] Step error:%d, sys:%d", errCode, errno); + } + + return SQLiteUtils::MapSQLiteErrno(errCode); +} + +int SQLiteUtils::BeginTransaction(sqlite3 *db, TransactType type) +{ + if (type == TransactType::IMMEDIATE) { + return ExecuteRawSQL(db, BEGIN_IMMEDIATE_SQL, true); + } + + return ExecuteRawSQL(db, BEGIN_SQL, true); +} + +int SQLiteUtils::CommitTransaction(sqlite3 *db) +{ + return ExecuteRawSQL(db, COMMIT_SQL, true); +} + +int SQLiteUtils::RollbackTransaction(sqlite3 *db) +{ + return ExecuteRawSQL(db, ROLLBACK_SQL, true); +} + +int SQLiteUtils::ExecuteRawSQL(sqlite3 *db, const std::string &sql, bool ignoreResetFail) +{ + if (db == nullptr) { + return -E_INVALID_DB; + } + + sqlite3_stmt *stmt = nullptr; + int errCode = SQLiteUtils::GetStatement(db, sql, stmt); + if (errCode != SQLiteUtils::MapSQLiteErrno(SQLITE_OK)) { + LOGE("[SQLiteUtils][ExecuteSQL] prepare statement failed(%d), sys(%d)", errCode, errno); + return errCode; + } + + do { + errCode = SQLiteUtils::StepWithRetry(stmt); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + errCode = E_OK; + break; + } else if (errCode != SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + LOGE("[SQLiteUtils][ExecuteSQL] execute statement failed(%d), sys(%d)", errCode, errno); + break; + } + } while (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)); + + int ret = E_OK; + SQLiteUtils::ResetStatement(stmt, true, ret); + if (!ignoreResetFail && ret != E_OK) { + return errCode != E_OK ? errCode : ret; + } + return errCode; +} + +int SQLiteUtils::MapSQLiteErrno(int errCode) +{ + switch (errCode) { + case SQLITE_OK: + return E_OK; + case SQLITE_IOERR: + if (errno == EKEYREVOKED) { + return -E_EKEYREVOKED; + } + break; + case SQLITE_CORRUPT: + case SQLITE_NOTADB: + return -E_INVALID_PASSWD_OR_CORRUPTED_DB; + case SQLITE_LOCKED: + case SQLITE_BUSY: + return -E_BUSY; + case SQLITE_ERROR: + if (errno == EKEYREVOKED) { + return -E_EKEYREVOKED; + } + break; + case SQLITE_AUTH: + return -E_DENIED_SQL; + case SQLITE_CONSTRAINT: + return -E_CONSTRAINT; + case SQLITE_CANTOPEN: + return -E_SQLITE_CANT_OPEN; + default: + break; + } + return -errCode; +} + +int SQLiteUtils::GetStatement(sqlite3 *db, const std::string &sql, sqlite3_stmt *&statement) +{ + if (db == nullptr) { + LOGE("Invalid db for statement"); + return -E_INVALID_DB; + } + // Prepare the new statement only when the input parameter is not null + if (statement != nullptr) { + return E_OK; + } + int errCode = sqlite3_prepare_v2(db, sql.c_str(), NO_SIZE_LIMIT, &statement, nullptr); + if (errCode != SQLITE_OK) { + LOGE("Prepare SQLite statement failed:%d, sys:%d", errCode, errno); + errCode = SQLiteUtils::MapSQLiteErrno(errCode); + SQLiteUtils::ResetStatement(statement, true, errCode); + return errCode; + } + + if (statement == nullptr) { + return -E_INVALID_DB; + } + + return E_OK; +} + +void SQLiteUtils::ResetStatement(sqlite3_stmt *&statement, bool isNeedFinalize, int &errCode) +{ + ResetStatement(statement, isNeedFinalize, false, errCode); +} + +void SQLiteUtils::ResetStatement(sqlite3_stmt *&statement, bool isNeedFinalize, bool isIgnoreResetRet, int &errCode) +{ + if (statement == nullptr) { + return; + } + + int innerCode = SQLITE_OK; + // if need finalize the statement, just goto finalize. + if (!isNeedFinalize) { + // reset the statement firstly. + innerCode = sqlite3_reset(statement); + if (innerCode != SQLITE_OK && !isIgnoreResetRet) { + LOGE("[SQLiteUtils] reset statement error:%d, sys:%d", innerCode, errno); + isNeedFinalize = true; + } else { + sqlite3_clear_bindings(statement); + } + } + + if (isNeedFinalize) { + int finalizeResult = sqlite3_finalize(statement); + if (finalizeResult != SQLITE_OK) { + LOGE("[SQLiteUtils] finalize statement error:%d, sys:%d", finalizeResult, errno); + innerCode = finalizeResult; + } + statement = nullptr; + } + + if (innerCode != SQLITE_OK) { // the sqlite error code has higher priority. + errCode = SQLiteUtils::MapSQLiteErrno(innerCode); + } +} + +#ifdef RELATIONAL_STORE +namespace { // anonymous namespace for schema analysis +int AnalysisSchemaSqlAndTrigger(sqlite3 *db, const std::string &tableName, TableInfo &table, bool caseSensitive) +{ + std::string sql = "SELECT type, sql FROM sqlite_master WHERE tbl_name = ? "; + if (!caseSensitive) { + sql += "COLLATE NOCASE"; + } + sqlite3_stmt *statement = nullptr; + int errCode = SQLiteUtils::GetStatement(db, sql, statement); + if (errCode != E_OK) { + LOGE("[AnalysisSchema] Prepare the analysis schema sql and trigger statement error:%d", errCode); + return errCode; + } + errCode = SQLiteUtils::BindTextToStatement(statement, 1, tableName); + int ret = E_OK; + if (errCode != E_OK) { + LOGE("[AnalysisSchema] Bind table name failed:%d", errCode); + SQLiteUtils::ResetStatement(statement, true, ret); + return errCode; + } + + errCode = -E_NOT_FOUND; + int err = SQLiteUtils::MapSQLiteErrno(SQLITE_ROW); + do { + err = SQLiteUtils::StepWithRetry(statement); + if (err == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + break; + } else if (err == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + errCode = E_OK; + std::string type; + (void) SQLiteUtils::GetColumnTextValue(statement, 0, type); + if (type == "table") { + std::string createTableSql; + (void) SQLiteUtils::GetColumnTextValue(statement, 1, createTableSql); // 1 means create table sql + table.SetCreateTableSql(createTableSql); + } + } else { + LOGE("[AnalysisSchema] Step for the analysis create table sql and trigger failed:%d", err); + errCode = err; + break; + } + } while (err == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)); + SQLiteUtils::ResetStatement(statement, true, ret); + return errCode != E_OK ? errCode : ret; +} + +int AnalysisSchemaIndexDefine(sqlite3 *db, const std::string &indexName, CompositeFields &indexDefine) +{ + auto sql = "pragma index_info('" + indexName + "')"; + sqlite3_stmt *statement = nullptr; + int errCode = SQLiteUtils::GetStatement(db, sql, statement); + if (errCode != E_OK) { + LOGE("[AnalysisSchema] Prepare the analysis schema index statement error:%d", errCode); + return errCode; + } + + do { + errCode = SQLiteUtils::StepWithRetry(statement); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + errCode = E_OK; + break; + } else if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + std::string indexField; + (void) SQLiteUtils::GetColumnTextValue(statement, 2, indexField); // 2 means index's column name. + indexDefine.push_back(indexField); + } else { + LOGW("[AnalysisSchema] Step for the analysis schema index failed:%d", errCode); + break; + } + } while (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)); + + int ret = E_OK; + SQLiteUtils::ResetStatement(statement, true, ret); + return errCode != E_OK ? errCode : ret; +} + +int GetSchemaIndexList(sqlite3 *db, const std::string &tableName, std::vector &indexList, + std::vector &uniqueList) +{ + std::string sql = "pragma index_list('" + tableName + "')"; + sqlite3_stmt *statement = nullptr; + int errCode = SQLiteUtils::GetStatement(db, sql, statement); + if (errCode != E_OK) { + LOGE("[AnalysisSchema] Prepare the get schema index list statement error:%d", errCode); + return errCode; + } + + do { + errCode = SQLiteUtils::StepWithRetry(statement); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + errCode = E_OK; + break; + } else if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + std::string indexName; + (void) SQLiteUtils::GetColumnTextValue(statement, 1, indexName); // 1 means index name + int unique = sqlite3_column_int64(statement, 2); // 2 means index type, whether unique + if (unique == 0) { // 0 means index created by user declare + indexList.push_back(indexName); + } else if (unique == 1) { // 1 means an unique define + uniqueList.push_back(indexName); + } + } else { + LOGW("[AnalysisSchema] Step for the get schema index list failed:%d", errCode); + break; + } + } while (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)); + int ret = E_OK; + SQLiteUtils::ResetStatement(statement, true, ret); + return errCode != E_OK ? errCode : ret; +} + +int AnalysisSchemaIndex(sqlite3 *db, const std::string &tableName, TableInfo &table) +{ + std::vector indexList; + std::vector uniqueList; + int errCode = GetSchemaIndexList(db, tableName, indexList, uniqueList); + if (errCode != E_OK) { + LOGE("[AnalysisSchema] get schema index list failed."); + return errCode; + } + + for (const auto &indexName : indexList) { + CompositeFields indexDefine; + errCode = AnalysisSchemaIndexDefine(db, indexName, indexDefine); + if (errCode != E_OK) { + LOGE("[AnalysisSchema] analysis schema index columns failed."); + return errCode; + } + table.AddIndexDefine(indexName, indexDefine); + } + + std::vector uniques; + for (const auto &uniqueName : uniqueList) { + CompositeFields uniqueDefine; + errCode = AnalysisSchemaIndexDefine(db, uniqueName, uniqueDefine); + if (errCode != E_OK) { + LOGE("[AnalysisSchema] analysis schema unique columns failed."); + return errCode; + } + uniques.push_back(uniqueDefine); + } + table.SetUniqueDefine(uniques); + return E_OK; +} + +void SetPrimaryKeyCollateType(const std::string &sql, FieldInfo &field) +{ + std::string upperFieldName = DBCommon::ToUpperCase(field.GetFieldName()); + if (DBCommon::HasConstraint(sql, "PRIMARY KEY COLLATE NOCASE", " ", " ,)") || + DBCommon::HasConstraint(sql, upperFieldName + " TEXT COLLATE NOCASE", " (,", " ,")) { + field.SetCollateType(CollateType::COLLATE_NOCASE); + } else if (DBCommon::HasConstraint(sql, "PRIMARY KEY COLLATE RTRIM", " ", " ,)") || + DBCommon::HasConstraint(sql, upperFieldName + " TEXT COLLATE RTRIM", " (,", " ,")) { + field.SetCollateType(CollateType::COLLATE_RTRIM); + } +} + +int SetFieldInfo(sqlite3_stmt *statement, TableInfo &table) +{ + FieldInfo field; + field.SetColumnId(sqlite3_column_int(statement, 0)); // 0 means column id index + + std::string tmpString; + (void) SQLiteUtils::GetColumnTextValue(statement, 1, tmpString); // 1 means column name index + if (!DBCommon::CheckIsAlnumOrUnderscore(tmpString)) { + LOGE("[AnalysisSchema] unsupported field name."); + return -E_NOT_SUPPORT; + } + field.SetFieldName(tmpString); + + (void) SQLiteUtils::GetColumnTextValue(statement, 2, tmpString); // 2 means datatype index + field.SetDataType(tmpString); + + field.SetNotNull(static_cast(sqlite3_column_int64(statement, 3))); // 3 means whether null index + + (void) SQLiteUtils::GetColumnTextValue(statement, 4, tmpString); // 4 means default value index + if (!tmpString.empty()) { + field.SetDefaultValue(tmpString); + } + + int keyIndex = sqlite3_column_int(statement, 5); // 5 means primary key index + if (keyIndex != 0) { // not 0 means is a primary key + table.SetPrimaryKey(field.GetFieldName(), keyIndex); + SetPrimaryKeyCollateType(table.GetCreateTableSql(), field); + } + table.AddField(field); + return E_OK; +} +} // end of anonymous namespace for schema analysis + +int SQLiteUtils::AnalysisSchemaFieldDefine(sqlite3 *db, const std::string &tableName, TableInfo &table) +{ + std::string sql = "pragma table_info('" + tableName + "')"; + sqlite3_stmt *statement = nullptr; + int errCode = SQLiteUtils::GetStatement(db, sql, statement); + if (errCode != E_OK) { + LOGE("[AnalysisSchema] Prepare the analysis schema field statement error:%d", errCode); + return errCode; + } + + do { + errCode = SQLiteUtils::StepWithRetry(statement); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + errCode = E_OK; + break; + } else if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + errCode = SetFieldInfo(statement, table); + if (errCode != E_OK) { + break; + } + } else { + LOGW("[AnalysisSchema] Step for the analysis schema field failed:%d", errCode); + break; + } + } while (errCode == E_OK); + + if (table.GetPrimaryKey().empty()) { + table.SetPrimaryKey("rowid", 1); + } + + int ret = E_OK; + SQLiteUtils::ResetStatement(statement, true, ret); + return errCode != E_OK ? errCode : ret; +} + +int SQLiteUtils::AnalysisSchema(sqlite3 *db, const std::string &tableName, TableInfo &table, bool caseSensitive) +{ + if (db == nullptr) { + return -E_INVALID_DB; + } + + if (!DBCommon::CheckIsAlnumOrUnderscore(tableName)) { + LOGE("[AnalysisSchema] unsupported table name."); + return -E_NOT_SUPPORT; + } + + int errCode = AnalysisSchemaSqlAndTrigger(db, tableName, table, caseSensitive); + if (errCode != E_OK) { + LOGE("[AnalysisSchema] Analysis sql and trigger failed. errCode = [%d]", errCode); + return errCode; + } + + errCode = AnalysisSchemaIndex(db, tableName, table); + if (errCode != E_OK) { + LOGE("[AnalysisSchema] Analysis index failed."); + return errCode; + } + + errCode = AnalysisSchemaFieldDefine(db, tableName, table); + if (errCode != E_OK) { + LOGE("[AnalysisSchema] Analysis field failed."); + return errCode; + } + + table.SetTableName(tableName); + return E_OK; +} +#endif + +int SQLiteUtils::BindTextToStatement(sqlite3_stmt *statement, int index, const std::string &str) +{ + if (statement == nullptr) { + return -E_INVALID_ARGS; + } + + int errCode = sqlite3_bind_text(statement, index, str.c_str(), str.length(), SQLITE_TRANSIENT); + if (errCode != SQLITE_OK) { + LOGE("[SQLiteUtil][Bind text]Failed to bind the value:%d", errCode); + return SQLiteUtils::MapSQLiteErrno(errCode); + } + + return E_OK; +} + +int SQLiteUtils::ProcessStatementErrCode(sqlite3_stmt *&statement, bool isNeedFinalize, int errCode) +{ + int ret = E_OK; + SQLiteUtils::ResetStatement(statement, isNeedFinalize, ret); + return errCode != E_OK ? errCode : ret; +} + +int SQLiteUtils::CheckTableExists(sqlite3 *db, const std::string &tableName, bool &isCreated, bool isCheckMeta) +{ + if (db == nullptr) { + return -1; + } + + sqlite3_stmt *stmt = nullptr; + int errCode = SQLiteUtils::GetStatement(db, isCheckMeta ? CHECK_META_DB_TABLE_CREATED : CHECK_TABLE_CREATED, stmt); + if (errCode != SQLiteUtils::MapSQLiteErrno(SQLITE_OK)) { + LOGE("Get check table statement failed. err=%d", errCode); + return errCode; + } + + errCode = SQLiteUtils::BindTextToStatement(stmt, 1, tableName); + if (errCode != SQLiteUtils::MapSQLiteErrno(SQLITE_OK)) { + LOGE("Bind table name to statement failed. err=%d", errCode); + return ProcessStatementErrCode(stmt, true, errCode); + } + + errCode = SQLiteUtils::StepWithRetry(stmt); + if (errCode != SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + LOGE("Check table exists failed. err=%d", errCode); // should always return a row data + return ProcessStatementErrCode(stmt, true, errCode); + } + errCode = E_OK; + isCreated = (sqlite3_column_int(stmt, 0) == 1); + return ProcessStatementErrCode(stmt, true, errCode); +} + +int SQLiteUtils::BindBlobToStatement(sqlite3_stmt *statement, int index, const std::vector &value, + bool permEmpty) +{ + if (statement == nullptr) { + return -E_INVALID_ARGS; + } + + // Check empty value. + if (value.empty() && !permEmpty) { + LOGI("[SQLiteUtil][Bind blob]Invalid value"); + return -E_INVALID_ARGS; + } + + int errCode; + if (value.empty()) { + errCode = sqlite3_bind_zeroblob(statement, index, -1); // -1 for zero-length blob. + } else { + errCode = sqlite3_bind_blob(statement, index, static_cast(value.data()), + value.size(), SQLITE_TRANSIENT); + } + + if (errCode != SQLITE_OK) { + LOGE("[SQLiteUtil][Bind blob]Failed to bind the value:%d", errCode); + return SQLiteUtils::MapSQLiteErrno(errCode); + } + + return E_OK; +} + +int SQLiteUtils::GetColumnTextValue(sqlite3_stmt *statement, int index, std::string &value) +{ + if (statement == nullptr) { + return -E_INVALID_ARGS; + } + + int valSize = sqlite3_column_bytes(statement, index); + if (valSize < 0) { + LOGW("[SQLiteUtils][Column Text] size less than zero:%d", valSize); + value = {}; + return E_OK; + } + const unsigned char *val = sqlite3_column_text(statement, index); + if (valSize == 0 || val == nullptr) { + value = {}; + return E_OK; + } + value = std::string(reinterpret_cast(val)); + if (valSize > MAX_TEXT_READ_SIZE) { + LOGW("[SQLiteUtils][Column text] size over limit:%d", valSize); + value.resize(MAX_TEXT_READ_SIZE + 1); // Reset value size to invalid + } + return E_OK; +} + +int SQLiteUtils::GetColumnBlobValue(sqlite3_stmt *statement, int index, std::vector &value) +{ + if (statement == nullptr) { + return -E_INVALID_ARGS; + } + + int keySize = sqlite3_column_bytes(statement, index); + if (keySize < 0) { + LOGW("[SQLiteUtils][Column blob] size less than zero:%d", keySize); + value.resize(0); + return E_OK; + } + auto keyRead = static_cast(sqlite3_column_blob(statement, index)); + if (keySize == 0 || keyRead == nullptr) { + value.resize(0); + } else { + if (keySize > MAX_BLOB_READ_SIZE) { + LOGW("[SQLiteUtils][Column blob] size over limit:%d", keySize); + keySize = MAX_BLOB_READ_SIZE + 1; + } + value.resize(keySize); + value.assign(keyRead, keyRead + keySize); + } + return E_OK; +} + +int SQLiteUtils::GetCountBySql(sqlite3 *db, const std::string &sql, int &count) +{ + sqlite3_stmt *stmt = nullptr; + int errCode = SQLiteUtils::GetStatement(db, sql, stmt); + if (errCode != E_OK) { + LOGE("[SQLiteUtils][GetCountBySql] Get stmt failed when get local data count: %d", errCode); + return errCode; + } + errCode = SQLiteUtils::StepWithRetry(stmt, false); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + count = static_cast(sqlite3_column_int(stmt, 0)); + errCode = E_OK; + } else { + LOGE("[SQLiteUtils][GetCountBySql] Query local data count failed: %d", errCode); + } + int ret = E_OK; + SQLiteUtils::ResetStatement(stmt, true, ret); + if (ret != E_OK) { + LOGE("[SQLiteUtils][GetCountBySql] Reset stmt failed when get local data count: %d", ret); + } + return errCode != E_OK ? errCode : ret; +} + +int SQLiteUtils::BindInt64ToStatement(sqlite3_stmt *statement, int index, int64_t value) +{ + // statement check outSide + int errCode = sqlite3_bind_int64(statement, index, value); + if (errCode != SQLITE_OK) { + LOGE("[SQLiteUtil][Bind int64]Failed to bind the value:%d", errCode); + return SQLiteUtils::MapSQLiteErrno(errCode); + } + + return E_OK; +} +} // namespace DistributedDB diff --git a/frameworks/libs/distributeddb/storage/src/sqlite/sqlite_utils_extend.cpp b/frameworks/libs/distributeddb/storage/src/sqlite/sqlite_utils_extend.cpp index 2164d1871ba20e8640a7275cabff0b366f76ca56..7100ec7890170e218e6b8cf7a06877ed8eeec44f 100644 --- a/frameworks/libs/distributeddb/storage/src/sqlite/sqlite_utils_extend.cpp +++ b/frameworks/libs/distributeddb/storage/src/sqlite/sqlite_utils_extend.cpp @@ -46,11 +46,6 @@ namespace { const std::string WAL_MODE_SQL = "PRAGMA journal_mode=WAL;"; const std::string SHA1_ALGO_SQL = "PRAGMA codec_hmac_algo=SHA1;"; const std::string SHA256_ALGO_REKEY_SQL = "PRAGMA codec_rekey_hmac_algo=SHA256;"; - - const constexpr char *CHECK_TABLE_CREATED = "SELECT EXISTS(SELECT 1 FROM sqlite_master WHERE " \ - "type='table' AND (tbl_name=? COLLATE NOCASE));"; - const constexpr char *CHECK_META_DB_TABLE_CREATED = "SELECT EXISTS(SELECT 1 FROM meta.sqlite_master WHERE " \ - "type='table' AND (tbl_name=? COLLATE NOCASE));"; } struct ValueParseCache { @@ -648,38 +643,6 @@ int SQLiteUtils::UpdateCipherShaAlgo(sqlite3 *db, bool setWal, CipherType type, return Rekey(db, passwd); } -int SQLiteUtils::CheckTableExists(sqlite3 *db, const std::string &tableName, bool &isCreated, bool isCheckMeta) -{ - if (db == nullptr) { - return -1; - } - - sqlite3_stmt *stmt = nullptr; - int errCode = SQLiteUtils::GetStatement(db, isCheckMeta ? CHECK_META_DB_TABLE_CREATED : CHECK_TABLE_CREATED, stmt); - if (errCode != SQLiteUtils::MapSQLiteErrno(SQLITE_OK)) { - LOGE("Get check table statement failed. err=%d", errCode); - return errCode; - } - - errCode = SQLiteUtils::BindTextToStatement(stmt, 1, tableName); - if (errCode != SQLiteUtils::MapSQLiteErrno(SQLITE_OK)) { - LOGE("Bind table name to statement failed. err=%d", errCode); - goto END; - } - - errCode = SQLiteUtils::StepWithRetry(stmt); - if (errCode != SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { - LOGE("Check table exists failed. err=%d", errCode); // should always return a row data - goto END; - } - errCode = E_OK; - isCreated = (sqlite3_column_int(stmt, 0) == 1); -END: - int ret = E_OK; - SQLiteUtils::ResetStatement(stmt, true, ret); - return errCode != E_OK ? errCode : ret; -} - int SQLiteUtils::StepNext(sqlite3_stmt *stmt, bool isMemDb) { if (stmt == nullptr) { @@ -694,29 +657,6 @@ int SQLiteUtils::StepNext(sqlite3_stmt *stmt, bool isMemDb) return errCode; } -int SQLiteUtils::GetCountBySql(sqlite3 *db, const std::string &sql, int &count) -{ - sqlite3_stmt *stmt = nullptr; - int errCode = SQLiteUtils::GetStatement(db, sql, stmt); - if (errCode != E_OK) { - LOGE("[SQLiteUtils][GetCountBySql] Get stmt failed when get local data count: %d", errCode); - return errCode; - } - errCode = SQLiteUtils::StepWithRetry(stmt, false); - if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { - count = static_cast(sqlite3_column_int(stmt, 0)); - errCode = E_OK; - } else { - LOGE("[SQLiteUtils][GetCountBySql] Query local data count failed: %d", errCode); - } - int ret = E_OK; - SQLiteUtils::ResetStatement(stmt, true, ret); - if (ret != E_OK) { - LOGE("[SQLiteUtils][GetCountBySql] Reset stmt failed when get local data count: %d", ret); - } - return errCode != E_OK ? errCode : ret; -} - bool SQLiteUtils::IsStmtReadOnly(sqlite3_stmt *statement) { if (statement == nullptr) { diff --git a/frameworks/libs/distributeddb/test/BUILD.gn b/frameworks/libs/distributeddb/test/BUILD.gn index 8ac0ef3f127cb3200c97bf2bb3644c0ea1dd56fd..860328166ff35d2d82968fe1c02788fed3fba27c 100644 --- a/frameworks/libs/distributeddb/test/BUILD.gn +++ b/frameworks/libs/distributeddb/test/BUILD.gn @@ -129,15 +129,18 @@ ohos_source_set("src_file") { sources += [ "unittest/common/common/basic_unit_test.cpp", "unittest/common/common/distributeddb_data_generate_unit_test.cpp", + "unittest/common/common/distributeddb_tools_unit_client_test.cpp", "unittest/common/common/distributeddb_tools_unit_test.cpp", "unittest/common/common/kv_general_ut.cpp", "unittest/common/common/native_sqlite.cpp", "unittest/common/common/rdb_data_generator.cpp", + "unittest/common/common/rdb_data_generator_client.cpp", "unittest/common/common/rdb_general_ut.cpp", "unittest/common/common/system_time.cpp", "unittest/common/common/thread_pool_test_stub.cpp", "unittest/common/interfaces/process_system_api_adapter_impl.cpp", "unittest/common/syncer/cloud/cloud_db_data_utils.cpp", + "unittest/common/syncer/cloud/cloud_db_sync_utils_client_test.cpp", "unittest/common/syncer/cloud/cloud_db_sync_utils_test.cpp", "unittest/common/syncer/cloud/virtual_asset_loader.cpp", "unittest/common/syncer/cloud/virtual_cloud_data_translate.cpp", @@ -876,13 +879,13 @@ distributeddb_unittest("DistributedDBBasicKVTest") { } distributeddb_unittest("DistributedDBKVDataStatusTest") { - sources = [ - "unittest/common/store_test/kv/distributeddb_kv_data_status_test.cpp", - ] + sources = + [ "unittest/common/store_test/kv/distributeddb_kv_data_status_test.cpp" ] } distributeddb_unittest("DistributedDBBasicRDBTest") { - sources = [ "unittest/common/store_test/rdb/distributeddb_basic_rdb_test.cpp" ] + sources = + [ "unittest/common/store_test/rdb/distributeddb_basic_rdb_test.cpp" ] } distributeddb_unittest("DistributedDBRDBDataStatusTest") { @@ -939,6 +942,156 @@ ohos_unittest("tokenizer_test") { ] } +distributeddb_unittest("DistributedDBRDBKnowledgeTest") { + sources = + [ "unittest/common/store_test/rdb/distributeddb_rdb_knowledge_test.cpp" ] +} + +############################################################################### +config("distributeddb_client_module_private_config") { + visibility = [ ":*" ] + + include_dirs = [ + "./unittest/common/common", + "./unittest/common/syncer", + "./unittest/common/syncer/cloud", + "./unittest/common/interfaces", + "../include", + "../interfaces/include", + "../interfaces/include/cloud", + "../interfaces/include/relational", + "../interfaces/src", + "../interfaces/src/relational", + "../common/include", + "../common/include/cloud", + "../common/include/relational", + "../communicator/include", + "../communicator/src", + "../storage/include", + "../storage/src", + "../storage/src/kv", + "../storage/src/relational", + "../storage/src/sqlite", + "../storage/src/sqlite/relational", + "../syncer/include", + "../syncer/src", + "../syncer/src/cloud", + "../syncer/src/device", + "../gaussdb_rd/include" + ] + + defines = [ + "_LARGEFILE64_SOURCE", + "_FILE_OFFSET_BITS=64", + "SQLITE_HAS_CODEC", + "SQLITE_ENABLE_JSON1", + "USING_HILOG_LOGGER", + "USE_SQLITE_SYMBOLS", + "USING_DB_JSON_EXTRACT_AUTOMATICALLY", + "JSONCPP_USE_BUILDER", + "OMIT_FLATBUFFER", + "OMIT_MULTI_VER", + "RELATIONAL_STORE", + "SQLITE_DISTRIBUTE_RELATIONAL", + "USE_DFX_ABILITY", + "SQLITE_ENABLE_DROPTABLE_CALLBACK", + "OPENSSL_SUPPRESS_DEPRECATED", + "USE_EXCEPTIONS", + ] + if (is_ohos) { + defines += [ "USE_FFRT" ] + defines += [ "RDB_CLIENT" ] + } +} + +ohos_source_set("distributeddb_client_src_file") { + testonly = true + + sources = distributeddb_client_src + sources += [ + "unittest/common/common/distributeddb_tools_unit_client_test.cpp", + "unittest/common/common/rdb_data_generator_client.cpp", + "unittest/common/common/system_time.cpp", + "unittest/common/syncer/cloud/cloud_db_sync_utils_client_test.cpp", + ] + + configs = [ ":distributeddb_client_module_private_config" ] + + branch_protector_ret = "pac_ret" + sanitize = { + ubsan = true + boundary_sanitize = true + cfi = true + cfi_cross_dso = true + debug = false + } + + ldflags = [ "-Wl,--exclude-libs,ALL" ] + deps = [ "../gaussdb_rd:gaussdb_rd" ] + configs += [ ":gaussdb_rd_config" ] + public_configs = [ ":gaussdb_rd_public_config" ] + + external_deps = [ + "cJSON:cjson", + "c_utils:utils", + "ffrt:libffrt", + "googletest:gmock_main", + "googletest:gtest_main", + "hilog:libhilog", + "hisysevent:libhisysevent", + "hitrace:hitrace_meter", + "jsoncpp:jsoncpp", + "openssl:libcrypto_shared", + "sqlite:sqlite", + "zlib:libz", + ] + part_name = "kv_store" +} + +template("distributeddb_client_unittest") { + ohos_unittest(target_name) { + forward_variables_from(invoker, "*") + module_out_path = module_output_path + if (!defined(deps)) { + deps = [] + } + if (!defined(external_deps)) { + external_deps = [] + } + configs = [ ":distributeddb_client_module_private_config" ] + deps += [ + ":distributeddb_client_src_file", + "../gaussdb_rd:gaussdb_rd", + ] + + ldflags = [ "-Wl,--exclude-libs,ALL" ] + + external_deps = [ + "c_utils:utils", + "ffrt:libffrt", + "googletest:gmock_main", + "googletest:gtest_main", + "hilog:libhilog", + "hisysevent:libhisysevent", + "hitrace:hitrace_meter", + "jsoncpp:jsoncpp", + "openssl:libcrypto_shared", + "sqlite:sqlite", + "zlib:libz", + ] + } +} + +distributeddb_client_unittest("DistributedDBRDBKnowledgeClientTest") { + sources = + [ "unittest/common/store_test/rdb/distributeddb_rdb_knowledge_client_test.cpp" ] +} + +distributeddb_client_unittest("DistributedDBCloudInterfacesRelationalExtClientTest") { + sources = + [ "unittest/common/interfaces/distributeddb_cloud_interfaces_relational_ext_client_test.cpp" ] +} + ############################################################################### group("unittest") { testonly = true @@ -1024,6 +1177,7 @@ group("unittest") { ":DistributedDBParcelTest", ":DistributedDBRDBCollaborationTest", ":DistributedDBRDBDataStatusTest", + ":DistributedDBRDBKnowledgeTest", ":DistributedDBRelationalCloudSyncableStorageTest", ":DistributedDBRelationalEncryptedDbTest", ":DistributedDBRelationalGetDataTest", diff --git a/frameworks/libs/distributeddb/test/unittest/common/common/distributeddb_tools_unit_client_test.cpp b/frameworks/libs/distributeddb/test/unittest/common/common/distributeddb_tools_unit_client_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1cf3169564b8ea14a9c9296006d02d27474eeff0 --- /dev/null +++ b/frameworks/libs/distributeddb/test/unittest/common/common/distributeddb_tools_unit_client_test.cpp @@ -0,0 +1,182 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "distributeddb_tools_unit_test.h" +#include "platform_specific.h" + +using namespace DistributedDB; + +namespace DistributedDBUnitTest { +int DistributedDBToolsUnitTest::GetCurrentDir(std::string &dir) +{ + static const int maxFileLength = 1024; + dir = ""; + char buffer[maxFileLength] = {0}; + int length = readlink("/proc/self/exe", buffer, maxFileLength); + if (length < 0 || length >= maxFileLength) { + LOGE("read directory err length:%d", length); + return -E_LENGTH_ERROR; + } + LOGD("DIR = %s", buffer); + dir = buffer; + if (dir.rfind("/") == std::string::npos && dir.rfind("\\") == std::string::npos) { + LOGE("current patch format err"); + return -E_INVALID_PATH; + } + + if (dir.rfind("/") != std::string::npos) { + dir.erase(dir.rfind("/") + 1); + } + return E_OK; +} + +void DistributedDBToolsUnitTest::TestDirInit(std::string &dir) +{ + if (GetCurrentDir(dir) != E_OK) { + dir = "/"; + } + + dir.append("testDbDir"); + DIR *dirTmp = opendir(dir.c_str()); + if (dirTmp == nullptr) { + if (OS::MakeDBDirectory(dir) != 0) { + LOGI("MakeDirectory err!"); + dir = "/"; + return; + } + } else { + closedir(dirTmp); + } +} + +int DistributedDBToolsUnitTest::RemoveTestDbFiles(const std::string &dir) +{ + bool isExisted = OS::CheckPathExistence(dir); + if (!isExisted) { + return E_OK; + } + + int nFile = 0; + std::string dirName; + struct dirent *direntPtr = nullptr; + DIR *dirPtr = opendir(dir.c_str()); + if (dirPtr == nullptr) { + LOGE("opendir error!"); + return -E_INVALID_PATH; + } + while (true) { + direntPtr = readdir(dirPtr); + // condition to exit the loop + if (direntPtr == nullptr) { + break; + } + // only remove all *.db files + std::string str(direntPtr->d_name); + if (str == "." || str == "..") { + continue; + } + dirName.clear(); + dirName.append(dir).append("/").append(str); + if (direntPtr->d_type == DT_DIR) { + RemoveTestDbFiles(dirName); + rmdir(dirName.c_str()); + } else if (remove(dirName.c_str()) != 0) { + LOGI("remove file: %s failed!", dirName.c_str()); + continue; + } + nFile++; + } + closedir(dirPtr); + LOGI("Total %d test db files are removed!", nFile); + return 0; +} + +sqlite3 *RelationalTestUtils::CreateDataBase(const std::string &dbUri) +{ + LOGD("Create database: %s", dbUri.c_str()); + sqlite3 *db = nullptr; + if (int r = sqlite3_open_v2(dbUri.c_str(), &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, nullptr) != SQLITE_OK) { + LOGE("Open database [%s] failed. %d", dbUri.c_str(), r); + if (db != nullptr) { + (void)sqlite3_close_v2(db); + db = nullptr; + } + } + return db; +} + +int RelationalTestUtils::ExecSql(sqlite3 *db, const std::string &sql) +{ + if (db == nullptr || sql.empty()) { + return -E_INVALID_ARGS; + } + char *errMsg = nullptr; + int errCode = sqlite3_exec(db, sql.c_str(), nullptr, nullptr, &errMsg); + if (errCode != SQLITE_OK && errMsg != nullptr) { + LOGE("Execute sql failed. %d err: %s", errCode, errMsg); + } + sqlite3_free(errMsg); + return errCode; +} + +int RelationalTestUtils::ExecSql(sqlite3 *db, const std::string &sql, + const std::function &bindCallback, const std::function &resultCallback) +{ + if (db == nullptr || sql.empty()) { + return -E_INVALID_ARGS; + } + + bool bindFinish = true; + sqlite3_stmt *stmt = nullptr; + int errCode = SQLiteUtils::GetStatement(db, sql, stmt); + if (errCode != E_OK) { + goto END; + } + + do { + if (bindCallback) { + errCode = bindCallback(stmt); + if (errCode != E_OK && errCode != -E_UNFINISHED) { + goto END; + } + bindFinish = (errCode != -E_UNFINISHED); // continue bind if unfinished + } + + bool isStepFinished = false; + while (!isStepFinished) { + errCode = SQLiteUtils::StepWithRetry(stmt); + if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { + errCode = E_OK; // Step finished + isStepFinished = true; + break; + } else if (errCode != SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + goto END; // Step return error + } + if (resultCallback == nullptr) { + continue; + } + errCode = resultCallback(stmt); + if (errCode != E_OK) { + goto END; + } + } + SQLiteUtils::ResetStatement(stmt, false, errCode); + } while (!bindFinish); + +END: + SQLiteUtils::ResetStatement(stmt, true, errCode); + return errCode; +} +} // namespace DistributedDBUnitTest diff --git a/frameworks/libs/distributeddb/test/unittest/common/common/distributeddb_tools_unit_test.cpp b/frameworks/libs/distributeddb/test/unittest/common/common/distributeddb_tools_unit_test.cpp index 9469dd063fcd86ad41a2e57e489ba343b524b3a1..c38f23e3b2dbd455374e83a12aed7322c700dbf8 100644 --- a/frameworks/libs/distributeddb/test/unittest/common/common/distributeddb_tools_unit_test.cpp +++ b/frameworks/libs/distributeddb/test/unittest/common/common/distributeddb_tools_unit_test.cpp @@ -224,90 +224,6 @@ int DistributedDBToolsUnitTest::GetResourceDir(std::string& dir) return E_OK; } -int DistributedDBToolsUnitTest::GetCurrentDir(std::string &dir) -{ - static const int maxFileLength = 1024; - dir = ""; - char buffer[maxFileLength] = {0}; - int length = readlink("/proc/self/exe", buffer, maxFileLength); - if (length < 0 || length >= maxFileLength) { - LOGE("read directory err length:%d", length); - return -E_LENGTH_ERROR; - } - LOGD("DIR = %s", buffer); - dir = buffer; - if (dir.rfind("/") == std::string::npos && dir.rfind("\\") == std::string::npos) { - LOGE("current patch format err"); - return -E_INVALID_PATH; - } - - if (dir.rfind("/") != std::string::npos) { - dir.erase(dir.rfind("/") + 1); - } - return E_OK; -} - -void DistributedDBToolsUnitTest::TestDirInit(std::string &dir) -{ - if (GetCurrentDir(dir) != E_OK) { - dir = "/"; - } - - dir.append("testDbDir"); - DIR *dirTmp = opendir(dir.c_str()); - if (dirTmp == nullptr) { - if (OS::MakeDBDirectory(dir) != 0) { - LOGI("MakeDirectory err!"); - dir = "/"; - return; - } - } else { - closedir(dirTmp); - } -} - -int DistributedDBToolsUnitTest::RemoveTestDbFiles(const std::string &dir) -{ - bool isExisted = OS::CheckPathExistence(dir); - if (!isExisted) { - return E_OK; - } - - int nFile = 0; - std::string dirName; - struct dirent *direntPtr = nullptr; - DIR *dirPtr = opendir(dir.c_str()); - if (dirPtr == nullptr) { - LOGE("opendir error!"); - return -E_INVALID_PATH; - } - while (true) { - direntPtr = readdir(dirPtr); - // condition to exit the loop - if (direntPtr == nullptr) { - break; - } - // only remove all *.db files - std::string str(direntPtr->d_name); - if (str == "." || str == "..") { - continue; - } - dirName.clear(); - dirName.append(dir).append("/").append(str); - if (direntPtr->d_type == DT_DIR) { - RemoveTestDbFiles(dirName); - rmdir(dirName.c_str()); - } else if (remove(dirName.c_str()) != 0) { - LOGI("remove file: %s failed!", dirName.c_str()); - continue; - } - nFile++; - } - closedir(dirPtr); - LOGI("Total %d test db files are removed!", nFile); - return 0; -} - #ifndef OMIT_MULTI_VER void DistributedDBToolsUnitTest::KvStoreDelegateCallback( DBStatus statusSrc, KvStoreDelegate *kvStoreSrc, DBStatus &statusDst, KvStoreDelegate *&kvStoreDst) @@ -1154,81 +1070,6 @@ int DistributedDBToolsUnitTest::BuildMessage(const DataSyncMessageInfo &messageI return E_OK; } -sqlite3 *RelationalTestUtils::CreateDataBase(const std::string &dbUri) -{ - LOGD("Create database: %s", dbUri.c_str()); - sqlite3 *db = nullptr; - if (int r = sqlite3_open_v2(dbUri.c_str(), &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, nullptr) != SQLITE_OK) { - LOGE("Open database [%s] failed. %d", dbUri.c_str(), r); - if (db != nullptr) { - (void)sqlite3_close_v2(db); - db = nullptr; - } - } - return db; -} - -int RelationalTestUtils::ExecSql(sqlite3 *db, const std::string &sql) -{ - if (db == nullptr || sql.empty()) { - return -E_INVALID_ARGS; - } - char *errMsg = nullptr; - int errCode = sqlite3_exec(db, sql.c_str(), nullptr, nullptr, &errMsg); - if (errCode != SQLITE_OK && errMsg != nullptr) { - LOGE("Execute sql failed. %d err: %s", errCode, errMsg); - } - sqlite3_free(errMsg); - return errCode; -} - -int RelationalTestUtils::ExecSql(sqlite3 *db, const std::string &sql, - const std::function &bindCallback, const std::function &resultCallback) -{ - if (db == nullptr || sql.empty()) { - return -E_INVALID_ARGS; - } - - bool bindFinish = true; - sqlite3_stmt *stmt = nullptr; - int errCode = SQLiteUtils::GetStatement(db, sql, stmt); - if (errCode != E_OK) { - goto END; - } - - do { - if (bindCallback) { - errCode = bindCallback(stmt); - if (errCode != E_OK && errCode != -E_UNFINISHED) { - goto END; - } - bindFinish = (errCode != -E_UNFINISHED); // continue bind if unfinished - } - - while (true) { - errCode = SQLiteUtils::StepWithRetry(stmt); - if (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) { - errCode = E_OK; // Step finished - break; - } else if (errCode != SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { - goto END; // Step return error - } - if (resultCallback == nullptr) { - continue; - } - errCode = resultCallback(stmt); - if (errCode != E_OK) { - goto END; - } - } - SQLiteUtils::ResetStatement(stmt, false, errCode); - } while (!bindFinish); - -END: - SQLiteUtils::ResetStatement(stmt, true, errCode); - return errCode; -} - void RelationalTestUtils::CreateDeviceTable(sqlite3 *db, const std::string &table, const std::string &device) { ASSERT_NE(db, nullptr); diff --git a/frameworks/libs/distributeddb/test/unittest/common/common/rdb_data_generator.cpp b/frameworks/libs/distributeddb/test/unittest/common/common/rdb_data_generator.cpp index 428f93351a95d0dddf8a8a8dd16bef0902d5a243..ec8cf0405eff2ae5cb292daa16c4b81d09d12688 100644 --- a/frameworks/libs/distributeddb/test/unittest/common/common/rdb_data_generator.cpp +++ b/frameworks/libs/distributeddb/test/unittest/common/common/rdb_data_generator.cpp @@ -65,26 +65,6 @@ int RDBDataGenerator::InitTable(const TableSchema &table, bool notNullWithStr, b return errCode; } -std::string RDBDataGenerator::GetTypeText(int type) -{ - switch (type) { - case DistributedDB::TYPE_INDEX: - return "INTEGER"; - case DistributedDB::TYPE_INDEX: - return "TEXT"; - case DistributedDB::TYPE_INDEX: - return "ASSETS"; - case DistributedDB::TYPE_INDEX: - return "ASSET"; - case DistributedDB::TYPE_INDEX: - return "DOUBLE"; - case DistributedDB::TYPE_INDEX: - return "BLOB"; - default: - return ""; - } -} - DistributedDB::DBStatus RDBDataGenerator::InsertCloudDBData(int64_t begin, int64_t count, int64_t gidStart, const DistributedDB::DataBaseSchema &schema, const std::shared_ptr &virtualCloudDb) @@ -493,48 +473,6 @@ DistributedDB::TableSchema RDBDataGenerator::FlipTableSchema(const DistributedDB return res; } -int RDBDataGenerator::InitDatabaseWithSchemaInfo(const UtDateBaseSchemaInfo &schemaInfo, sqlite3 &db) -{ - int errCode = RelationalTestUtils::ExecSql(&db, "PRAGMA journal_mode=WAL;"); - if (errCode != SQLITE_OK) { - LOGE("[RDBDataGenerator] Execute sql failed %d", errCode); - return errCode; - } - for (const auto &tableInfo : schemaInfo.tablesInfo) { - errCode = InitTableWithSchemaInfo(tableInfo, db); - if (errCode != SQLITE_OK) { - LOGE("[RDBDataGenerator] Init table failed %d, %s", errCode, tableInfo.name.c_str()); - break; - } - } - return errCode; -} - -int RDBDataGenerator::InitTableWithSchemaInfo(const UtTableSchemaInfo &tableInfo, sqlite3 &db) -{ - std::string sql = "CREATE TABLE IF NOT EXISTS " + tableInfo.name + "("; - for (const auto &fieldInfo : tableInfo.fieldInfo) { - sql += "'" + fieldInfo.field.colName + "' " + GetTypeText(fieldInfo.field.type); - if (fieldInfo.field.primary) { - sql += " PRIMARY KEY"; - if (fieldInfo.isAutoIncrement) { - sql += " AUTOINCREMENT"; - } - } - if (!fieldInfo.field.nullable) { - sql += " NOT NULL ON CONFLICT IGNORE"; - } - sql += ","; - } - sql.pop_back(); - sql += ");"; - int errCode = RelationalTestUtils::ExecSql(&db, sql); - if (errCode != SQLITE_OK) { - LOGE("[RDBDataGenerator] Execute sql failed %d, sql is %s", errCode, sql.c_str()); - } - return errCode; -} - Bytes RDBDataGenerator::GenerateBytes(int64_t index) { std::string str = std::to_string(index); diff --git a/frameworks/libs/distributeddb/test/unittest/common/common/rdb_data_generator_client.cpp b/frameworks/libs/distributeddb/test/unittest/common/common/rdb_data_generator_client.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0ea931fdbabdb0664b269d16435e51501f587ff2 --- /dev/null +++ b/frameworks/libs/distributeddb/test/unittest/common/common/rdb_data_generator_client.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "rdb_data_generator.h" + +#include "distributeddb_tools_unit_test.h" +#include "log_print.h" + +namespace DistributedDBUnitTest { +using namespace DistributedDB; +std::string RDBDataGenerator::GetTypeText(int type) +{ + switch (type) { + case DistributedDB::TYPE_INDEX: + return "INTEGER"; + case DistributedDB::TYPE_INDEX: + return "TEXT"; + case DistributedDB::TYPE_INDEX: + return "ASSETS"; + case DistributedDB::TYPE_INDEX: + return "ASSET"; + case DistributedDB::TYPE_INDEX: + return "DOUBLE"; + case DistributedDB::TYPE_INDEX: + return "BLOB"; + default: + return ""; + } +} + +int RDBDataGenerator::InitDatabaseWithSchemaInfo(const UtDateBaseSchemaInfo &schemaInfo, sqlite3 &db) +{ + int errCode = RelationalTestUtils::ExecSql(&db, "PRAGMA journal_mode=WAL;"); + if (errCode != SQLITE_OK) { + LOGE("[RDBDataGenerator] Execute sql failed %d", errCode); + return errCode; + } + for (const auto &tableInfo : schemaInfo.tablesInfo) { + errCode = InitTableWithSchemaInfo(tableInfo, db); + if (errCode != SQLITE_OK) { + LOGE("[RDBDataGenerator] Init table failed %d, %s", errCode, tableInfo.name.c_str()); + break; + } + } + return errCode; +} + +int RDBDataGenerator::InitTableWithSchemaInfo(const UtTableSchemaInfo &tableInfo, sqlite3 &db) +{ + std::string sql = "CREATE TABLE IF NOT EXISTS " + tableInfo.name + "("; + for (const auto &fieldInfo : tableInfo.fieldInfo) { + sql += "'" + fieldInfo.field.colName + "' " + GetTypeText(fieldInfo.field.type); + if (fieldInfo.field.primary) { + sql += " PRIMARY KEY"; + if (fieldInfo.isAutoIncrement) { + sql += " AUTOINCREMENT"; + } + } + if (!fieldInfo.field.nullable) { + sql += " NOT NULL ON CONFLICT IGNORE"; + } + sql += ","; + } + sql.pop_back(); + sql += ");"; + int errCode = RelationalTestUtils::ExecSql(&db, sql); + if (errCode != SQLITE_OK) { + LOGE("[RDBDataGenerator] Execute sql failed %d, sql is %s", errCode, sql.c_str()); + } + return errCode; +} +} \ No newline at end of file diff --git a/frameworks/libs/distributeddb/test/unittest/common/common/rdb_general_ut.cpp b/frameworks/libs/distributeddb/test/unittest/common/common/rdb_general_ut.cpp index 5aedbec19a6c51772071aaa05b269cc30c5b8c27..00fce118fab500edb6c688cb6d9564f3364c687c 100644 --- a/frameworks/libs/distributeddb/test/unittest/common/common/rdb_general_ut.cpp +++ b/frameworks/libs/distributeddb/test/unittest/common/common/rdb_general_ut.cpp @@ -20,6 +20,10 @@ using namespace DistributedDBUnitTest; namespace DistributedDB { +const std::string CIPHER_CONFIG_SQL = "PRAGMA codec_cipher='aes-256-gcm';"; +const std::string KDF_ITER_CONFIG_SQL = "PRAGMA codec_kdf_iter=5000;"; +const std::string SHA256_ALGO_SQL = "PRAGMA codec_hmac_algo=SHA256;"; + const Field intField = {"id", TYPE_INDEX, true, false}; const Field stringField = {"name", TYPE_INDEX, false, false}; const Field boolField = {"gender", TYPE_INDEX, false, true}; @@ -42,22 +46,18 @@ UtDateBaseSchemaInfo g_defaultSchemaInfo = { int RDBGeneralUt::InitDelegate(const StoreInfo &info) { - std::string storePath = GetTestDir() + "/" + info.storeId + ".db"; - sqlite3 *db = RelationalTestUtils::CreateDataBase(storePath); - if (db == nullptr) { - LOGE("[RDBGeneralUt] Create database failed %s", storePath.c_str()); - return -E_INVALID_DB; - } - UtDateBaseSchemaInfo schemaInfo = GetTableSchemaInfo(info); - int errCode = RDBDataGenerator::InitDatabaseWithSchemaInfo(schemaInfo, *db); - if (errCode != SQLITE_OK) { - LOGE("[RDBGeneralUt] Init database failed %d", errCode); - return errCode; + InitDatabase(info); + { + std::lock_guard autoLock(storeMutex_); + if (stores_.find(info) != stores_.end()) { + return E_OK; + } } + std::string storePath = GetTestDir() + "/" + info.storeId + ".db"; RelationalStoreManager mgr(info.appId, info.userId); RelationalStoreDelegate *delegate = nullptr; RelationalStoreDelegate::Option option = GetOption(); - errCode = mgr.OpenStore(storePath, info.storeId, option, delegate); + int errCode = mgr.OpenStore(storePath, info.storeId, option, delegate); if ((errCode != E_OK )|| (delegate == nullptr)) { LOGE("[RDBGeneralUt] Open store failed %d", errCode); return errCode; @@ -65,7 +65,6 @@ int RDBGeneralUt::InitDelegate(const StoreInfo &info) { std::lock_guard autoLock(storeMutex_); stores_[info] = delegate; - sqliteDb_[info] = db; } LOGI("[RDBGeneralUt] Init delegate app %s store %s user %s success", info.appId.c_str(), info.storeId.c_str(), info.userId.c_str()); @@ -196,6 +195,7 @@ void RDBGeneralUt::CloseAllDelegate() } stores_.clear(); schemaInfoMap_.clear(); + isDbEncrypted_ = false; LOGI("[RDBGeneralUt] Close all delegate success"); } @@ -223,18 +223,34 @@ void RDBGeneralUt::TearDown() int RDBGeneralUt::InitDatabase(const StoreInfo &info) { - auto schema = GetSchema(info); - auto db = GetSqliteHandle(info); + std::string storePath = GetTestDir() + "/" + info.storeId + ".db"; + sqlite3 *db = RelationalTestUtils::CreateDataBase(storePath); if (db == nullptr) { - LOGE("[RDBGeneralUt] Get null sqlite when init database"); + LOGE("[RDBGeneralUt] Create database failed %s", storePath.c_str()); return -E_INVALID_DB; } - auto errCode = RDBDataGenerator::InitDatabase(schema, *db); - if (errCode != E_OK) { - LOGE("[RDBGeneralUt] Init db failed %d app %s store %s user %s", errCode, info.appId.c_str(), - info.storeId.c_str(), info.userId.c_str()); + int errCode = E_OK; + if (GetIsDbEncrypted()) { + errCode = EncryptedDb(db); + if (errCode != E_OK) { + return errCode; + } } - return errCode; + UtDateBaseSchemaInfo schemaInfo = GetTableSchemaInfo(info); + errCode = RDBDataGenerator::InitDatabaseWithSchemaInfo(schemaInfo, *db); + if (errCode != SQLITE_OK) { + LOGE("[RDBGeneralUt] Init database failed %d", errCode); + return errCode; + } + { + std::lock_guard autoLock(storeMutex_); + if (sqliteDb_.find(info) != sqliteDb_.end()) { + sqlite3_close_v2(db); + return E_OK; + } + sqliteDb_[info] = db; + } + return E_OK; } sqlite3 *RDBGeneralUt::GetSqliteHandle(const StoreInfo &info) const @@ -466,4 +482,49 @@ int RDBGeneralUt::GetCloudDataCount(const std::string &tableName) const LOGI("[RDBGeneralUt] Count cloud table %s success, count %d", tableName.c_str(), realCount); return realCount; } + +void RDBGeneralUt::SetIsDbEncrypted(bool isdbEncrypted) +{ + isDbEncrypted_ = isdbEncrypted; +} + +bool RDBGeneralUt::GetIsDbEncrypted() const +{ + return isDbEncrypted_; +} + +int RDBGeneralUt::EncryptedDb(sqlite3 *db) +{ + std::string passwd(PASSWD_VECTOR.begin(), PASSWD_VECTOR.end()); + int rc = sqlite3_key(db, passwd.c_str(), passwd.size()); + if (rc != SQLITE_OK) { + sqlite3_close(db); + LOGE("sqlite3_key failed, %d, passwd is %s.", rc, passwd.c_str()); + return -E_INVALID_DB; + } + char *errMsg = nullptr; + int errCode = sqlite3_exec(db, CIPHER_CONFIG_SQL.c_str(), nullptr, nullptr, &errMsg); + if (errCode != SQLITE_OK || errMsg != nullptr) { + LOGE("set cipher failed: %d, cipher is %s.", errCode, CIPHER_CONFIG_SQL.c_str()); + sqlite3_close(db); + sqlite3_free(errMsg); + return -E_INVALID_DB; + } + errMsg = nullptr; + errCode = sqlite3_exec(db, KDF_ITER_CONFIG_SQL.c_str(), nullptr, nullptr, &errMsg); + if (errCode != SQLITE_OK || errMsg != nullptr) { + LOGE("set iterTimes failed: %d, iterTimes is %s.", errCode, KDF_ITER_CONFIG_SQL.c_str()); + sqlite3_close(db); + sqlite3_free(errMsg); + return -E_INVALID_DB; + } + errCode = sqlite3_exec(db, SHA256_ALGO_SQL.c_str(), nullptr, nullptr, &errMsg); + if (errCode != SQLITE_OK || errMsg != nullptr) { + LOGE("set codec_hmac_algo failed: %d, codec_hmac_algo is %s.", errCode, SHA256_ALGO_SQL.c_str()); + sqlite3_close(db); + sqlite3_free(errMsg); + return -E_INVALID_DB; + } + return E_OK; +} } \ No newline at end of file diff --git a/frameworks/libs/distributeddb/test/unittest/common/common/rdb_general_ut.h b/frameworks/libs/distributeddb/test/unittest/common/common/rdb_general_ut.h index 8bd647a1bf11f7d60aa015a1318b33e04eea9ffa..d5597a4b7dbd30c4d49906477e2bd81ca8d0fd45 100644 --- a/frameworks/libs/distributeddb/test/unittest/common/common/rdb_general_ut.h +++ b/frameworks/libs/distributeddb/test/unittest/common/common/rdb_general_ut.h @@ -24,6 +24,7 @@ namespace DistributedDB { const std::string g_defaultTable1 = "defaultTable1"; const std::string g_defaultTable2 = "defaultTable2"; +const std::vector PASSWD_VECTOR = {'P', 'a', 's', 's', 'w', 'o', 'r', 'd', '@', '1'}; class RDBGeneralUt : public BasicUnitTest { public: @@ -79,6 +80,10 @@ protected: int GetCloudDataCount(const std::string &tableName) const; + void SetIsDbEncrypted(bool isdbEncrypted); + bool GetIsDbEncrypted() const; + int EncryptedDb(sqlite3 *db); + mutable std::mutex storeMutex_; std::map stores_; std::map sqliteDb_; @@ -86,6 +91,7 @@ protected: std::shared_ptr virtualCloudDb_ = nullptr; std::shared_ptr virtualAssetLoader_ = nullptr; RelationalStoreDelegate::Option option_; + bool isDbEncrypted_ = false; }; } #endif // RDB_GENERAL_UT_H diff --git a/frameworks/libs/distributeddb/test/unittest/common/interfaces/distributeddb_cloud_interfaces_relational_ext_client_test.cpp b/frameworks/libs/distributeddb/test/unittest/common/interfaces/distributeddb_cloud_interfaces_relational_ext_client_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..33f40de54bb93d13c95d120972909f003c60963f --- /dev/null +++ b/frameworks/libs/distributeddb/test/unittest/common/interfaces/distributeddb_cloud_interfaces_relational_ext_client_test.cpp @@ -0,0 +1,1475 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "cloud_db_sync_utils_test.h" +#include "cloud_sync_log_table_manager.h" +#include "distributeddb_tools_unit_test.h" +#include "relational_store_client.h" +#include "simple_tracker_log_table_manager.h" +#include "sqlite_relational_utils.h" +#include "table_info.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; +using namespace std; + +namespace { +constexpr const char *DB_SUFFIX = ".db"; +constexpr const char *STORE_ID = "Relational_Store_ID"; +std::string g_dbDir; +std::string g_testDir; + +constexpr int E_ERROR = 1; +const int WAIT_TIME = 1000; // 1000ms +constexpr static uint64_t TO_100_NS = 10; // 1us to 100ns +const uint64_t MULTIPLES_BETWEEN_SECONDS_AND_MICROSECONDS = 1000000; +std::mutex g_mutex; +std::condition_variable g_cv; +bool g_alreadyNotify = false; + +class DistributedDBCloudInterfacesRelationalExtClientTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp() override; + void TearDown() override; + void CheckTriggerObserverTest002(const std::string &tableName, std::atomic &count); + + void ClientObserverFunc(ClientChangedData &clientChangedData) + { + for (const auto &tableEntry : clientChangedData.tableData) { + LOGD("client observer fired, table: %s", tableEntry.first.c_str()); + triggerTableData_.insert_or_assign(tableEntry.first, tableEntry.second); + } + triggeredCount_++; + { + std::unique_lock lock(g_mutex); + g_alreadyNotify = true; + } + g_cv.notify_one(); + } + + void ClientObserverFunc2(ClientChangedData &clientChangedData) + { + triggeredCount2_++; + { + std::unique_lock lock(g_mutex); + g_alreadyNotify = true; + } + g_cv.notify_one(); + } + + void CheckTriggerTableData(size_t dataSize, const std::string &tableName, ChangeProperties &properties, + int triggerCount) + { + ASSERT_EQ(triggerTableData_.size(), dataSize); + EXPECT_EQ(triggerTableData_.begin()->first, tableName); + EXPECT_EQ(triggerTableData_.begin()->second.isTrackedDataChange, properties.isTrackedDataChange); + EXPECT_EQ(triggeredCount_, triggerCount); + } + + void WaitAndResetNotify() + { + std::unique_lock lock(g_mutex); + WaitAndResetNotifyWithLock(lock); + } + + void WaitAndResetNotifyWithLock(std::unique_lock &lock) + { + g_cv.wait(lock, []() { + return g_alreadyNotify; + }); + g_alreadyNotify = false; + } + + std::map triggerTableData_; + int triggeredCount_ = 0; + int triggeredCount2_ = 0; +}; + +void DistributedDBCloudInterfacesRelationalExtClientTest::SetUpTestCase(void) +{ + DistributedDBToolsUnitTest::TestDirInit(g_testDir); + LOGD("Test dir is %s", g_testDir.c_str()); + g_dbDir = g_testDir + "/"; + DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir); +} + +void DistributedDBCloudInterfacesRelationalExtClientTest::TearDownTestCase(void) +{ +} + +void DistributedDBCloudInterfacesRelationalExtClientTest::SetUp() +{ +} + +void DistributedDBCloudInterfacesRelationalExtClientTest::TearDown() +{ + g_alreadyNotify = false; + DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir); +} + +void DistributedDBCloudInterfacesRelationalExtClientTest::CheckTriggerObserverTest002(const std::string &tableName, + std::atomic &count) +{ + count++; + ASSERT_EQ(triggerTableData_.size(), 1u); + EXPECT_EQ(triggerTableData_.begin()->first, tableName); + EXPECT_EQ(triggerTableData_.begin()->second.isTrackedDataChange, false); + EXPECT_EQ(triggeredCount_, count); +} + +static int GetCurrentSysTimeIn100Ns(uint64_t &outTime) +{ + struct timeval rawTime; + int errCode = gettimeofday(&rawTime, nullptr); + if (errCode < 0) { + return -E_ERROR; + } + outTime = static_cast(rawTime.tv_sec) * MULTIPLES_BETWEEN_SECONDS_AND_MICROSECONDS + + static_cast(rawTime.tv_usec); + outTime *= TO_100_NS; + return E_OK; +} + +static void ExecSqlAndWaitForObserver(sqlite3 *db, const std::string &sql, std::unique_lock &lock) +{ + EXPECT_EQ(RelationalTestUtils::ExecSql(db, sql), E_OK); + g_cv.wait(lock, []() { + return g_alreadyNotify; + }); + g_alreadyNotify = false; +} + +/** + * @tc.name: GetRawSysTimeTest001 + * @tc.desc: Test get_raw_sys_time has been registered in sqlite + * @tc.type: FUNC + * @tc.require: + * @tc.author: zhangshijie + */ +HWTEST_F(DistributedDBCloudInterfacesRelationalExtClientTest, GetRawSysTimeTest001, TestSize.Level0) +{ + const std::string sql = "select get_raw_sys_time();"; + sqlite3 *db = RelationalTestUtils::CreateDataBase(g_dbDir + STORE_ID + DB_SUFFIX); + EXPECT_NE(db, nullptr); + uint64_t curTime = 0; + int errCode = GetCurrentSysTimeIn100Ns(curTime); + EXPECT_EQ(errCode, E_OK); + errCode = RelationalTestUtils::ExecSql(db, sql, nullptr, [curTime] (sqlite3_stmt *stmt) { + EXPECT_GT(static_cast(sqlite3_column_int64(stmt, 0)), curTime); + return E_OK; + }); + EXPECT_EQ(errCode, SQLITE_OK); + EXPECT_EQ(sqlite3_close_v2(db), E_OK); +} + +static void PrepareData(const std::vector &tableNames, bool primaryKeyIsRowId, + DistributedDB::TableSyncType tableSyncType, bool userDefineRowid = true, bool createDistributeTable = true) +{ + /** + * @tc.steps:step1. create db, create table. + * @tc.expected: step1. return ok. + */ + sqlite3 *db = RelationalTestUtils::CreateDataBase(g_dbDir + STORE_ID + DB_SUFFIX); + EXPECT_NE(db, nullptr); + EXPECT_EQ(RelationalTestUtils::ExecSql(db, "PRAGMA journal_mode=WAL;"), SQLITE_OK); + std::string sql; + for (const auto &tableName : tableNames) { + if (primaryKeyIsRowId) { + sql = "create table " + tableName + "(rowid INTEGER primary key, id int, name TEXT);"; + } else { + if (userDefineRowid) { + sql = "create table " + tableName + "(rowid int, id int, name TEXT, PRIMARY KEY(id));"; + } else { + sql = "create table " + tableName + "(id int, name TEXT, PRIMARY KEY(id));"; + } + } + EXPECT_EQ(RelationalTestUtils::ExecSql(db, sql), E_OK); + } + + /** + * @tc.steps:step2. create distributed table. + * @tc.expected: step2. return ok. + */ + if (createDistributeTable) { + for (const auto &tableName : tableNames) { + TableInfo table; + table.SetTableName(tableName); + table.SetTableSyncType(tableSyncType); + std::unique_ptr tableManager = std::make_unique(); + EXPECT_NE(tableManager, nullptr); + EXPECT_EQ(SQLiteUtils::AnalysisSchema(db, tableName, table), E_OK); + std::vector fieldInfos = table.GetFieldInfos(); + EXPECT_EQ(SQLiteRelationalUtils::CreateRelationalMetaTable(db), E_OK); + EXPECT_EQ(SQLiteRelationalUtils::InitCursorToMeta(db, false, tableName), E_OK); + EXPECT_EQ(tableManager->CreateRelationalLogTable(db, table), E_OK); + EXPECT_EQ(tableManager->AddRelationalLogTableTrigger(db, table, ""), E_OK); + EXPECT_EQ(SQLiteRelationalUtils::SetLogTriggerStatus(db, true), E_OK); + } + } + EXPECT_EQ(sqlite3_close_v2(db), E_OK); +} + +static void SaveTrackerSchemaToMetaTable(sqlite3 *db, const std::string tableName, const TableInfo &tableInfo) +{ + RelationalSchemaObject tracker; + tracker.SetTableMode(DistributedDB::DistributedTableMode::SPLIT_BY_DEVICE); + tracker.RemoveRelationalTable(tableName); + tracker.AddRelationalTable(tableInfo); + const Key schemaKey(DBConstant::RELATIONAL_SCHEMA_KEY.begin(), DBConstant::RELATIONAL_SCHEMA_KEY.end()); + Value schemaVal; + auto schemaStr = tracker.ToSchemaString(); + EXPECT_FALSE(schemaStr.size() > SchemaConstant::SCHEMA_STRING_SIZE_LIMIT); + DBCommon::StringToVector(schemaStr, schemaVal); + EXPECT_EQ(SQLiteRelationalUtils::PutKvData(db, false, schemaKey, schemaVal), E_OK); +} + +static void SetTracerSchema(const std::vector &tableNames, DistributedDB::TableSyncType tableSyncType) +{ + sqlite3 *db = RelationalTestUtils::CreateDataBase(g_dbDir + STORE_ID + DB_SUFFIX); + EXPECT_NE(db, nullptr); + for (const auto &tableName : tableNames) { + TableInfo tableInfo; + tableInfo.SetTableName(tableName); + tableInfo.SetTableSyncType(tableSyncType); + std::unique_ptr tableManager = std::make_unique(); + EXPECT_NE(tableManager, nullptr); + TrackerSchema schema; + schema.tableName = tableName; + schema.extendColNames = {"id"}; + schema.trackerColNames = {"name"}; + TrackerTable trackerTable; + trackerTable.Init(schema); + EXPECT_EQ(SQLiteRelationalUtils::AnalysisTrackerTable(db, trackerTable, tableInfo), E_OK); + EXPECT_EQ(tableManager->CreateRelationalLogTable(db, tableInfo), E_OK); + EXPECT_EQ(SQLiteRelationalUtils::InitCursorToMeta(db, false, tableName), E_OK); + EXPECT_EQ(SQLiteRelationalUtils::SetLogTriggerStatus(db, true), E_OK); + EXPECT_EQ(tableManager->AddRelationalLogTableTrigger(db, tableInfo, ""), E_OK); + SaveTrackerSchemaToMetaTable(db, tableName, tableInfo); + } + EXPECT_EQ(sqlite3_close_v2(db), E_OK); +} + +/** + * @tc.name: InsertTriggerTest003 + * @tc.desc: Test insert trigger in sqlite when use "insert or replace" + * @tc.type: FUNC + * @tc.require: + * @tc.author: zhangshijie + */ +HWTEST_F(DistributedDBCloudInterfacesRelationalExtClientTest, InsertTriggerTest003, TestSize.Level1) +{ + /** + * @tc.steps:step1. prepare data. + * @tc.expected: step1. return ok. + */ + const std::string tableName = "sync_data"; + PrepareData({tableName}, false, DistributedDB::CLOUD_COOPERATION); + + /** + * @tc.steps:step2. insert data into sync_data. + * @tc.expected: step2. return ok. + */ + sqlite3 *db = RelationalTestUtils::CreateDataBase(g_dbDir + STORE_ID + DB_SUFFIX); + EXPECT_NE(db, nullptr); + std::string sql = "insert into " + tableName + " VALUES(2, 1, 'zhangsan1');"; + EXPECT_EQ(RelationalTestUtils::ExecSql(db, sql), E_OK); + + // update cloud_gid in log table + std::string gid = "test_gid"; + sql = "update " + DBCommon::GetLogTableName(tableName) + " set cloud_gid = '" + gid + "'"; + EXPECT_EQ(RelationalTestUtils::ExecSql(db, sql), E_OK); + // use insert or replace to update data + sql = "insert or replace into " + tableName + " VALUES(3, 1, 'zhangsan1');"; + EXPECT_EQ(RelationalTestUtils::ExecSql(db, sql), E_OK); + + /** + * @tc.steps:step3. select data from log table. + * @tc.expected: step3. return ok. + */ + sql = "select data_key, device, ori_device, flag, cloud_gid from " + DBCommon::GetLogTableName(tableName); + int resultCount = 0; + int errCode = RelationalTestUtils::ExecSql(db, sql, nullptr, [&resultCount, gid] (sqlite3_stmt *stmt) { + EXPECT_EQ(sqlite3_column_int64(stmt, 0), 2); // 2 is row id + std::string device = ""; + EXPECT_EQ(SQLiteUtils::GetColumnTextValue(stmt, 1, device), E_OK); + EXPECT_EQ(device, ""); + std::string oriDevice = ""; + EXPECT_EQ(SQLiteUtils::GetColumnTextValue(stmt, 2, oriDevice), E_OK); // 2 is column index + EXPECT_EQ(oriDevice, ""); + + EXPECT_EQ(sqlite3_column_int(stmt, 3), 0x02|0x20); // 3 is column index flag == 0x02|0x20 + std::string gidStr; + EXPECT_EQ(SQLiteUtils::GetColumnTextValue(stmt, 4, gidStr), E_OK); // 4 is column index + EXPECT_EQ(gid, gidStr); + resultCount++; + return E_OK; + }); + EXPECT_EQ(errCode, SQLITE_OK); + EXPECT_EQ(resultCount, 1); + EXPECT_EQ(sqlite3_close_v2(db), E_OK); +} + +static void UpdateTriggerTest(bool primaryKeyIsRowId) +{ + /** + * @tc.steps:step1. prepare data. + * @tc.expected: step1. return ok. + */ + const std::string tableName = "sync_data"; + PrepareData({tableName}, primaryKeyIsRowId, DistributedDB::CLOUD_COOPERATION); + + /** + * @tc.steps:step2. insert data into sync_data_tmp. + * @tc.expected: step2. return ok. + */ + sqlite3 *db = RelationalTestUtils::CreateDataBase(g_dbDir + STORE_ID + DB_SUFFIX); + EXPECT_NE(db, nullptr); + std::string sql = "insert into " + tableName + " VALUES(2, 1, 'zhangsan');"; + EXPECT_EQ(RelationalTestUtils::ExecSql(db, sql), E_OK); + + /** + * @tc.steps:step3. update data. + * @tc.expected: step3. return ok. + */ + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME)); + sql = "update " + tableName + " set name = 'lisi';"; + EXPECT_EQ(RelationalTestUtils::ExecSql(db, sql), E_OK); + + /** + * @tc.steps:step4. select data from log table. + * @tc.expected: step4. return ok. + */ + sql = "select * from " + std::string(DBConstant::RELATIONAL_PREFIX) + tableName + "_log;"; + uint64_t curTime = 0; + int errCode = GetCurrentSysTimeIn100Ns(curTime); + EXPECT_EQ(errCode, E_OK); + + int resultCount = 0; + errCode = RelationalTestUtils::ExecSql(db, sql, nullptr, [curTime, &resultCount, primaryKeyIsRowId] ( + sqlite3_stmt *stmt) { + if (primaryKeyIsRowId) { + EXPECT_EQ(sqlite3_column_int64(stmt, 0), 2); // 2 is row id + } else { + EXPECT_EQ(sqlite3_column_int64(stmt, 0), 1); // 1 is row id + } + + EXPECT_EQ(sqlite3_column_int(stmt, 5), 0x02|0x20); // 5 is column index, flag == 0x02|0x20 + + std::string device = ""; + EXPECT_EQ(SQLiteUtils::GetColumnTextValue(stmt, 1, device), E_OK); + EXPECT_EQ(device, ""); + std::string oriDevice = ""; + EXPECT_EQ(SQLiteUtils::GetColumnTextValue(stmt, 2, oriDevice), E_OK); // 2 is column index + EXPECT_EQ(oriDevice, ""); + + int64_t timestamp = sqlite3_column_int64(stmt, 3); // 3 is column index + int64_t wtimestamp = sqlite3_column_int64(stmt, 4); // 4 is column index + int64_t diff = MULTIPLES_BETWEEN_SECONDS_AND_MICROSECONDS * TO_100_NS; + EXPECT_TRUE(timestamp - wtimestamp > diff); + EXPECT_TRUE(static_cast(curTime - timestamp) < diff); + + resultCount++; + return E_OK; + }); + EXPECT_EQ(errCode, SQLITE_OK); + EXPECT_EQ(resultCount, 1); + EXPECT_EQ(sqlite3_close_v2(db), E_OK); +} + +/** + * @tc.name: UpdateTriggerTest001 + * @tc.desc: Test update trigger in sqlite for primary key is not row id + * @tc.type: FUNC + * @tc.require: + * @tc.author: zhangshijie + */ +HWTEST_F(DistributedDBCloudInterfacesRelationalExtClientTest, UpdateTriggerTest001, TestSize.Level1) +{ + UpdateTriggerTest(false); +} + +/** + * @tc.name: UpdateTriggerTest002 + * @tc.desc: Test update trigger in sqlite for primary key is row id + * @tc.type: FUNC + * @tc.require: + * @tc.author: zhangshijie + */ +HWTEST_F(DistributedDBCloudInterfacesRelationalExtClientTest, UpdateTriggerTest002, TestSize.Level1) +{ + UpdateTriggerTest(true); +} + +/** + * @tc.name: DeleteTriggerTest001 + * @tc.desc: Test delete trigger in sqlite + * @tc.type: FUNC + * @tc.require: + * @tc.author: zhangshijie + */ +HWTEST_F(DistributedDBCloudInterfacesRelationalExtClientTest, DeleteTriggerTest001, TestSize.Level1) +{ + /** + * @tc.steps:step1. prepare data. + * @tc.expected: step1. return ok. + */ + const std::string tableName = "sync_data"; + PrepareData({tableName}, true, DistributedDB::CLOUD_COOPERATION); + + /** + * @tc.steps:step2. insert data into sync_data. + * @tc.expected: step2. return ok. + */ + sqlite3 *db = RelationalTestUtils::CreateDataBase(g_dbDir + STORE_ID + DB_SUFFIX); + EXPECT_NE(db, nullptr); + std::string sql = "insert into " + tableName + " VALUES(2, 1, 'zhangsan');"; + EXPECT_EQ(RelationalTestUtils::ExecSql(db, sql), E_OK); + + /** + * @tc.steps:step3. delete data. + * @tc.expected: step3. return ok. + */ + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME)); + sql = "delete from " + tableName + " where name = 'zhangsan';"; + EXPECT_EQ(RelationalTestUtils::ExecSql(db, sql), E_OK); + + /** + * @tc.steps:step4. select data from log table. + * @tc.expected: step4. return ok. + */ + sql = "select * from " + std::string(DBConstant::RELATIONAL_PREFIX) + tableName + "_log;"; + uint64_t curTime = 0; + int errCode = GetCurrentSysTimeIn100Ns(curTime); + EXPECT_EQ(errCode, E_OK); + + int resultCount = 0; + errCode = RelationalTestUtils::ExecSql(db, sql, nullptr, [curTime, &resultCount] (sqlite3_stmt *stmt) { + EXPECT_EQ(sqlite3_column_int64(stmt, 0), -1); + EXPECT_EQ(sqlite3_column_int(stmt, 5), 3); // 5 is column index, flag == 3 + + std::string device = "de"; + EXPECT_EQ(SQLiteUtils::GetColumnTextValue(stmt, 1, device), E_OK); + EXPECT_EQ(device, ""); + std::string oriDevice = "de"; + EXPECT_EQ(SQLiteUtils::GetColumnTextValue(stmt, 2, oriDevice), E_OK); // 2 is column index + EXPECT_EQ(oriDevice, ""); + + int64_t timestamp = sqlite3_column_int64(stmt, 3); // 3 is column index + int64_t wtimestamp = sqlite3_column_int64(stmt, 4); // 4 is column index + int64_t diff = MULTIPLES_BETWEEN_SECONDS_AND_MICROSECONDS * TO_100_NS; + EXPECT_TRUE(timestamp - wtimestamp > diff); + EXPECT_TRUE(static_cast(curTime - timestamp) < diff); + + resultCount++; + return E_OK; + }); + EXPECT_EQ(errCode, SQLITE_OK); + EXPECT_EQ(resultCount, 1); + EXPECT_EQ(sqlite3_close_v2(db), E_OK); +} + +/** + * @tc.name: TriggerObserverTest001 + * @tc.desc: Test invalid args for RegisterClientObserver and UnRegisterClientObserver + * @tc.type: FUNC + * @tc.require: + * @tc.author: zhangshijie + */ +HWTEST_F(DistributedDBCloudInterfacesRelationalExtClientTest, TriggerObserverTest001, TestSize.Level0) +{ + /** + * @tc.steps:step1. call RegisterClientObserver and UnRegisterClientObserver with db = nullptr. + * @tc.expected: step1. return INVALID_ARGS. + */ + ClientObserver clientObserver = std::bind(&DistributedDBCloudInterfacesRelationalExtClientTest::ClientObserverFunc, + this, std::placeholders::_1); + EXPECT_EQ(RegisterClientObserver(nullptr, clientObserver), INVALID_ARGS); + EXPECT_EQ(UnRegisterClientObserver(nullptr), INVALID_ARGS); + + /** + * @tc.steps:step2. call RegisterClientObserver with nullptr clientObserver. + * @tc.expected: step2. return INVALID_ARGS. + */ + sqlite3 *db = RelationalTestUtils::CreateDataBase(g_dbDir + STORE_ID + DB_SUFFIX); + EXPECT_NE(db, nullptr); + EXPECT_EQ(RegisterClientObserver(db, nullptr), INVALID_ARGS); + + EXPECT_EQ(sqlite3_close_v2(db), E_OK); +} + +/** + * @tc.name: TriggerObserverTest002 + * @tc.desc: Test trigger client observer in sqlite + * @tc.type: FUNC + * @tc.require: + * @tc.author: zhangshijie + */ +HWTEST_F(DistributedDBCloudInterfacesRelationalExtClientTest, TriggerObserverTest002, TestSize.Level0) +{ + /** + * @tc.steps:step1. prepare data. + * @tc.expected: step1. return ok. + */ + const std::string tableName = "sync_data"; + PrepareData({tableName}, false, DistributedDB::CLOUD_COOPERATION, false); + + /** + * @tc.steps:step2. register client observer. + * @tc.expected: step2. return ok. + */ + sqlite3 *db = RelationalTestUtils::CreateDataBase(g_dbDir + STORE_ID + DB_SUFFIX); + EXPECT_NE(db, nullptr); + ClientObserver clientObserver = std::bind(&DistributedDBCloudInterfacesRelationalExtClientTest::ClientObserverFunc, + this, std::placeholders::_1); + EXPECT_EQ(RegisterClientObserver(db, clientObserver), OK); + RegisterDbHook(db); + + /** + * @tc.steps:step3. insert data into sync_data, check observer. + * @tc.expected: step3. check observer ok. + */ + std::string sql = "insert into " + tableName + " VALUES(1, 'zhangsan'), (2, 'lisi'), (3, 'wangwu');"; + EXPECT_EQ(RelationalTestUtils::ExecSql(db, sql), E_OK); + WaitAndResetNotify(); + std::atomic count = 0; // 0 is observer triggered counts + CheckTriggerObserverTest002(tableName, count); + + /** + * @tc.steps:step4. update data, check observer. + * @tc.expected: step4. check observer ok. + */ + sql = "update " + tableName + " set name = 'lisi1' where id = 2;"; + EXPECT_EQ(RelationalTestUtils::ExecSql(db, sql), E_OK); + WaitAndResetNotify(); + CheckTriggerObserverTest002(tableName, count); + + /** + * @tc.steps:step4. delete data, check observer. + * @tc.expected: step4. check observer ok. + */ + sql = "delete from " + tableName + " where id = 3;"; + EXPECT_EQ(RelationalTestUtils::ExecSql(db, sql), E_OK); + WaitAndResetNotify(); + CheckTriggerObserverTest002(tableName, count); + + /** + * @tc.steps:step5. register another observer, update data, check observer. + * @tc.expected: step5. check observer ok. + */ + triggeredCount_ = 0; + ClientObserver clientObserver2 = std::bind(&DistributedDBCloudInterfacesRelationalExtClientTest::ClientObserverFunc2, + this, std::placeholders::_1); + EXPECT_EQ(RegisterClientObserver(db, clientObserver2), OK); + RegisterDbHook(db); + sql = "update " + tableName + " set name = 'lisi2' where id = 2;"; + EXPECT_EQ(RelationalTestUtils::ExecSql(db, sql), E_OK); + WaitAndResetNotify(); + EXPECT_EQ(triggeredCount_, 0); + EXPECT_EQ(triggeredCount2_, 1); + + /** + * @tc.steps:step6. UnRegisterClientObserver, update data, check observer. + * @tc.expected: step6. check observer ok. + */ + triggeredCount2_ = 0; + EXPECT_EQ(UnRegisterClientObserver(db), OK); + sql = "update " + tableName + " set name = 'lisi3' where id = 2;"; + EXPECT_EQ(RelationalTestUtils::ExecSql(db, sql), E_OK); + EXPECT_EQ(triggeredCount2_, 0); // observer2 will not be triggered + EXPECT_EQ(sqlite3_close_v2(db), E_OK); +} + +/** + * @tc.name: TriggerObserverTest003 + * @tc.desc: Test RegisterClientObserver and UnRegisterClientObserver concurrently + * @tc.type: FUNC + * @tc.require: + * @tc.author: zhangshijie + */ +HWTEST_F(DistributedDBCloudInterfacesRelationalExtClientTest, TriggerObserverTest003, TestSize.Level1) +{ + for (int i = 0; i < 1000; i++) { // 1000 is loop times + std::thread t1 ([this]() { + sqlite3 *db = RelationalTestUtils::CreateDataBase(g_dbDir + STORE_ID + DB_SUFFIX); + EXPECT_NE(db, nullptr); + ClientObserver clientObserver = std::bind( + &DistributedDBCloudInterfacesRelationalExtClientTest::ClientObserverFunc, this, std::placeholders::_1); + EXPECT_EQ(RegisterClientObserver(db, clientObserver), OK); + EXPECT_EQ(UnRegisterClientObserver(db), OK); + EXPECT_EQ(sqlite3_close_v2(db), E_OK); + }); + + std::thread t2 ([this]() { + sqlite3 *db = RelationalTestUtils::CreateDataBase(g_dbDir + STORE_ID + DB_SUFFIX); + EXPECT_NE(db, nullptr); + ClientObserver clientObserver = std::bind( + &DistributedDBCloudInterfacesRelationalExtClientTest::ClientObserverFunc2, this, std::placeholders::_1); + EXPECT_EQ(RegisterClientObserver(db, clientObserver), OK); + EXPECT_EQ(UnRegisterClientObserver(db), OK); + EXPECT_EQ(sqlite3_close_v2(db), E_OK); + }); + + t1.join(); + t2.join(); + } +} + +/** + * @tc.name: TriggerObserverTest004 + * @tc.desc: Test batch insert/update/delete data then trigger client observer + * @tc.type: FUNC + * @tc.require: + * @tc.author: zhangshijie + */ +HWTEST_F(DistributedDBCloudInterfacesRelationalExtClientTest, TriggerObserverTest004, TestSize.Level1) +{ + /** + * @tc.steps:step1. prepare data. + * @tc.expected: step1. return ok. + */ + const std::string tableName = "sync_data"; + PrepareData({tableName}, false, DistributedDB::CLOUD_COOPERATION, false); + + /** + * @tc.steps:step2. register client observer. + * @tc.expected: step2. return ok. + */ + sqlite3 *db = RelationalTestUtils::CreateDataBase(g_dbDir + STORE_ID + DB_SUFFIX); + EXPECT_NE(db, nullptr); + ClientObserver clientObserver = std::bind(&DistributedDBCloudInterfacesRelationalExtClientTest::ClientObserverFunc, + this, std::placeholders::_1); + EXPECT_EQ(RegisterClientObserver(db, clientObserver), OK); + RegisterDbHook(db); + + /** + * @tc.steps:step3. insert data into sync_data, check observer. + * @tc.expected: step3. check observer ok. + */ + std::string sql; + int dataCounts = 1000; // 1000 is count of insert options. + for (int i = 1; i <= dataCounts; i++) { + sql = "insert into " + tableName + " VALUES(" + std::to_string(i) + ", 'zhangsan" + std::to_string(i) + "');"; + EXPECT_EQ(RelationalTestUtils::ExecSql(db, sql), E_OK); + } + std::unique_lock lock(g_mutex); + bool isEqual = g_cv.wait_for(lock, std::chrono::seconds(1), [this, dataCounts]() { // 1 is wait time + return triggeredCount_ == dataCounts; + }); + EXPECT_EQ(isEqual, true); + WaitAndResetNotifyWithLock(lock); + ASSERT_EQ(triggerTableData_.size(), 1u); + EXPECT_EQ(triggerTableData_.begin()->first, tableName); + EXPECT_EQ(triggeredCount_, dataCounts); + + /** + * @tc.steps:step4. insert or replace, check observer. + * @tc.expected: step5. check observer ok. + */ + triggeredCount_ = 0; + sql = "insert or replace into " + tableName + " VALUES(1000, 'lisi');"; + EXPECT_EQ(RelationalTestUtils::ExecSql(db, sql), E_OK); + isEqual = g_cv.wait_for(lock, std::chrono::seconds(1), [this]() { // 1 is wait time + return triggeredCount_ == 1; + }); + EXPECT_EQ(isEqual, true); + WaitAndResetNotifyWithLock(lock); + EXPECT_EQ(triggeredCount_, 1); // 1 is trigger times, first delete then insert + EXPECT_EQ(UnRegisterClientObserver(db), OK); + EXPECT_EQ(sqlite3_close_v2(db), E_OK); +} + +/** + * @tc.name: TriggerObserverTest005 + * @tc.desc: Test commit and rollback for one table then trigger client observer + * @tc.type: FUNC + * @tc.require: + * @tc.author: chenchaohao + */ +HWTEST_F(DistributedDBCloudInterfacesRelationalExtClientTest, TriggerObserverTest005, TestSize.Level1) +{ + /** + * @tc.steps:step1. prepare data. + * @tc.expected: step1. return ok. + */ + const std::string tableName = "sync_data"; + PrepareData({tableName}, false, DistributedDB::CLOUD_COOPERATION, false); + + /** + * @tc.steps:step2. register client observer. + * @tc.expected: step2. return ok. + */ + sqlite3 *db = RelationalTestUtils::CreateDataBase(g_dbDir + STORE_ID + DB_SUFFIX); + EXPECT_NE(db, nullptr); + ClientObserver clientObserver = std::bind(&DistributedDBCloudInterfacesRelationalExtClientTest::ClientObserverFunc, + this, std::placeholders::_1); + EXPECT_EQ(RegisterClientObserver(db, clientObserver), OK); + RegisterDbHook(db); + + /** + * @tc.steps:step3. begin transaction and commit. + * @tc.expected: step3. check observer ok. + */ + std::string sql = "begin;"; + EXPECT_EQ(RelationalTestUtils::ExecSql(db, sql), E_OK); + int dataCounts = 1000; // 1000 is count of insert options. + for (int i = 1; i <= dataCounts; i++) { + sql = "insert into " + tableName + " VALUES(" + std::to_string(i) + ", 'zhangsan" + std::to_string(i) + "');"; + EXPECT_EQ(RelationalTestUtils::ExecSql(db, sql), E_OK); + } + sql = "commit;"; + EXPECT_EQ(RelationalTestUtils::ExecSql(db, sql), E_OK); + WaitAndResetNotify(); + ASSERT_EQ(triggerTableData_.size(), 1u); + EXPECT_EQ(triggerTableData_.begin()->first, tableName); + EXPECT_EQ(triggeredCount_, 1); + + /** + * @tc.steps:step4. begin transaction and rollback. + * @tc.expected: step3. check observer ok. + */ + triggerTableData_.clear(); + triggeredCount_ = 0; + sql = "begin;"; + EXPECT_EQ(RelationalTestUtils::ExecSql(db, sql), E_OK); + for (int i = dataCounts + 1; i <= 2 * dataCounts; i++) { // 2 is double dataCounts + sql = "insert into " + tableName + " VALUES(" + std::to_string(i) + ", 'zhangsan" + std::to_string(i) + "');"; + EXPECT_EQ(RelationalTestUtils::ExecSql(db, sql), E_OK); + } + sql = "rollback;"; + EXPECT_EQ(RelationalTestUtils::ExecSql(db, sql), E_OK); + EXPECT_TRUE(triggerTableData_.empty()); + EXPECT_EQ(triggeredCount_, 0); + + /** + * @tc.steps:step5. insert or replace, check observer. + * @tc.expected: step5. check observer ok. + */ + triggeredCount_ = 0; + sql = "insert or replace into " + tableName + " VALUES(1000, 'lisi');"; + EXPECT_EQ(RelationalTestUtils::ExecSql(db, sql), E_OK); + WaitAndResetNotify(); + EXPECT_EQ(triggeredCount_, 1); // 1 is trigger times, first delete then insert + EXPECT_EQ(UnRegisterClientObserver(db), OK); + EXPECT_EQ(sqlite3_close_v2(db), E_OK); +} + +/** + * @tc.name: TriggerObserverTest006 + * @tc.desc: Test commit and rollback for multi-table then trigger client observer + * @tc.type: FUNC + * @tc.require: + * @tc.author: chenchaohao + */ +HWTEST_F(DistributedDBCloudInterfacesRelationalExtClientTest, TriggerObserverTest006, TestSize.Level1) +{ + /** + * @tc.steps:step1. prepare data. + * @tc.expected: step1. return ok. + */ + const std::string tableName1 = "sync_data1"; + const std::string tableName2 = "sync_data2"; + PrepareData({tableName1, tableName2}, false, DistributedDB::CLOUD_COOPERATION, false); + + /** + * @tc.steps:step2. register client observer. + * @tc.expected: step2. return ok. + */ + sqlite3 *db = RelationalTestUtils::CreateDataBase(g_dbDir + STORE_ID + DB_SUFFIX); + EXPECT_NE(db, nullptr); + ClientObserver clientObserver = std::bind(&DistributedDBCloudInterfacesRelationalExtClientTest::ClientObserverFunc, + this, std::placeholders::_1); + EXPECT_EQ(RegisterClientObserver(db, clientObserver), OK); + RegisterDbHook(db); + + /** + * @tc.steps:step3. begin transaction and commit. + * @tc.expected: step3. check observer ok. + */ + std::string sql = "insert into " + tableName1 + " VALUES(1, 'zhangsan'), (2, 'lisi'), (3, 'wangwu');"; + EXPECT_EQ(RelationalTestUtils::ExecSql(db, sql), E_OK); + WaitAndResetNotify(); + ASSERT_EQ(triggerTableData_.size(), 1u); // 1 is table size + EXPECT_EQ(triggerTableData_.begin()->first, tableName1); + EXPECT_EQ(triggeredCount_, 1); // 1 is trigger count + + /** + * @tc.steps:step4. UnRegisterClientObserver and insert table2. + * @tc.expected: step3. check observer ok. + */ + triggerTableData_.clear(); + triggeredCount_ = 0; + EXPECT_EQ(UnRegisterClientObserver(db), OK); + sql = "insert into " + tableName2 + " VALUES(1, 'zhangsan'), (2, 'lisi'), (3, 'wangwu');"; + EXPECT_EQ(RelationalTestUtils::ExecSql(db, sql), E_OK); + EXPECT_TRUE(triggerTableData_.empty()); + EXPECT_EQ(triggeredCount_, 0); + + /** + * @tc.steps:step5. RegisterClientObserver again and insert table1, check observer. + * @tc.expected: step5. check observer ok. + */ + EXPECT_EQ(RegisterClientObserver(db, clientObserver), OK); + RegisterDbHook(db); + sql = "insert into " + tableName1 + " VALUES(7, 'zhangjiu');"; + EXPECT_EQ(RelationalTestUtils::ExecSql(db, sql), E_OK); + WaitAndResetNotify(); + ASSERT_EQ(triggerTableData_.size(), 1u); // 1 is table size + EXPECT_EQ(triggerTableData_.begin()->first, tableName1); + EXPECT_EQ(triggeredCount_, 1); // 1 is trigger count + EXPECT_EQ(UnRegisterClientObserver(db), OK); + EXPECT_EQ(sqlite3_close_v2(db), E_OK); +} + +/** + * @tc.name: TriggerObserverTest007 + * @tc.desc: Test trigger client observer in tracker table + * @tc.type: FUNC + * @tc.require: + * @tc.author: zhangshijie + */ +HWTEST_F(DistributedDBCloudInterfacesRelationalExtClientTest, TriggerObserverTest007, TestSize.Level0) +{ + /** + * @tc.steps:step1. prepare data and set trackerTable + * @tc.expected: step1. return ok. + */ + const std::string tableName = "sync_data"; + PrepareData({tableName}, false, DistributedDB::CLOUD_COOPERATION, false); + SetTracerSchema({tableName}, DistributedDB::CLOUD_COOPERATION); + + /** + * @tc.steps:step2. register client observer. + * @tc.expected: step2. return ok. + */ + sqlite3 *db = RelationalTestUtils::CreateDataBase(g_dbDir + STORE_ID + DB_SUFFIX); + EXPECT_NE(db, nullptr); + + ClientObserver clientObserver = std::bind(&DistributedDBCloudInterfacesRelationalExtClientTest::ClientObserverFunc, + this, std::placeholders::_1); + EXPECT_EQ(RegisterClientObserver(db, clientObserver), OK); + RegisterDbHook(db); + + /** + * @tc.steps:step3. insert data into sync_data, check observer. + * @tc.expected: step3. check observer ok. + */ + std::string sql = "insert into " + tableName + " VALUES(1, 'zhangsan'), (2, 'lisi'), (3, 'wangwu');"; + std::unique_lock lock(g_mutex); + ExecSqlAndWaitForObserver(db, sql, lock); + ChangeProperties properties; + properties.isTrackedDataChange = true; + int triggerCount = 1; + CheckTriggerTableData(1u, tableName, properties, triggerCount); + + /** + * @tc.steps:step4. update data, check observer. + * @tc.expected: step4. check observer ok. + */ + sql = "update " + tableName + " set name = 'lisi1' where id = 2;"; + ExecSqlAndWaitForObserver(db, sql, lock); + CheckTriggerTableData(1u, tableName, properties, ++triggerCount); + + /** + * @tc.steps:step5. update to the same data again, check observer. + * @tc.expected: step5. check observer ok. + */ + sql = "update " + tableName + " set name = 'lisi1' where id = 2;"; + ExecSqlAndWaitForObserver(db, sql, lock); + properties.isTrackedDataChange = false; + CheckTriggerTableData(1u, tableName, properties, ++triggerCount); + + /** + * @tc.steps:step6. update to the same data again, set name is NULL, check observer. + * @tc.expected: step6. check observer ok. + */ + sql = "update " + tableName + " set name = NULL where id = 2;"; + ExecSqlAndWaitForObserver(db, sql, lock); + properties.isTrackedDataChange = true; + CheckTriggerTableData(1u, tableName, properties, ++triggerCount); + + /** + * @tc.steps:step7. update to the same data again, set name is empty, check observer. + * @tc.expected: step7. check observer ok. + */ + sql = "update " + tableName + " set name = '' where id = 2;"; + ExecSqlAndWaitForObserver(db, sql, lock); + CheckTriggerTableData(1u, tableName, properties, ++triggerCount); + + /** + * @tc.steps:step8. delete data, check observer. + * @tc.expected: step8. check observer ok. + */ + sql = "delete from " + tableName + " where id = 2;"; + ExecSqlAndWaitForObserver(db, sql, lock); + CheckTriggerTableData(1u, tableName, properties, ++triggerCount); + EXPECT_EQ(UnRegisterClientObserver(db), OK); + EXPECT_EQ(sqlite3_close_v2(db), E_OK); +} + +/** + * @tc.name: TriggerObserverTest008 + * @tc.desc: Test trigger client observer + * @tc.type: FUNC + * @tc.require: + * @tc.author: liaoyonghuang + */ +HWTEST_F(DistributedDBCloudInterfacesRelationalExtClientTest, TriggerObserverTest008, TestSize.Level0) +{ + /** + * @tc.steps:step1. prepare data + * @tc.expected: step1. return ok. + */ + const std::string tableName1 = "table1"; + PrepareData({tableName1}, false, DistributedDB::CLOUD_COOPERATION, false, true); + const std::string tableName2 = "table2"; + PrepareData({tableName2}, false, DistributedDB::CLOUD_COOPERATION, false, true); + + /** + * @tc.steps:step2. register client observer. + * @tc.expected: step2. return ok. + */ + sqlite3 *db = RelationalTestUtils::CreateDataBase(g_dbDir + STORE_ID + DB_SUFFIX); + EXPECT_NE(db, nullptr); + ClientObserver clientObserver = std::bind(&DistributedDBCloudInterfacesRelationalExtClientTest::ClientObserverFunc, + this, std::placeholders::_1); + EXPECT_EQ(RegisterClientObserver(db, clientObserver), OK); + + /** + * @tc.steps:step3. insert data into sync_data, check observer. + * @tc.expected: step3. check observer ok. + */ + std::string sql = "insert into " + tableName1 + " VALUES(1, 'zhangsan'), (2, 'lisi'), (3, 'wangwu');"; + EXPECT_EQ(RelationalTestUtils::ExecSql(db, sql), E_OK); + EXPECT_EQ(triggerTableData_.size(), 0u); + + /** + * @tc.steps:step4. re-register client observer and register hook + * @tc.expected: step4. return ok. + */ + EXPECT_EQ(UnRegisterClientObserver(db), OK); + EXPECT_EQ(RegisterClientObserver(db, clientObserver), OK); + RegisterDbHook(db); + + /** + * @tc.steps:step5. insert data into sync_data, check observer. + * @tc.expected: step5. check observer ok. + */ + sql = "insert into " + tableName2 + " VALUES(4, 'aaa');"; + std::unique_lock lock(g_mutex); + ExecSqlAndWaitForObserver(db, sql, lock); + EXPECT_EQ(triggerTableData_.size(), 1u); + EXPECT_EQ(UnRegisterClientObserver(db), OK); + EXPECT_EQ(sqlite3_close_v2(db), E_OK); +} + +void InitLogicDeleteData(sqlite3 *&db, const std::string &tableName, uint64_t num) +{ + for (size_t i = 0; i < num; ++i) { + std::string sql = "insert or replace into " + tableName + " VALUES('" + std::to_string(i) + "', 'zhangsan');"; + EXPECT_EQ(RelationalTestUtils::ExecSql(db, sql), E_OK); + } + std::string sql = "update " + std::string(DBConstant::RELATIONAL_PREFIX) + tableName + "_log" + + " SET flag = flag | 0x08"; + EXPECT_EQ(RelationalTestUtils::ExecSql(db, sql), E_OK); +} + +void CheckLogicDeleteData(sqlite3 *&db, const std::string &tableName, uint64_t expectNum) +{ + std::string sql = "select count(*) from " + std::string(DBConstant::RELATIONAL_PREFIX) + tableName + "_log" + " where flag&0x08=0x08 and flag&0x01=0"; + sqlite3_stmt *stmt = nullptr; + EXPECT_EQ(SQLiteUtils::GetStatement(db, sql, stmt), E_OK); + while (SQLiteUtils::StepWithRetry(stmt) == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + uint64_t count = static_cast(sqlite3_column_int64(stmt, 0)); + EXPECT_EQ(count, expectNum); + } + int errCode; + SQLiteUtils::ResetStatement(stmt, true, errCode); + stmt = nullptr; + sql = "select count(*) from " + tableName; + while (SQLiteUtils::StepWithRetry(stmt) == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + uint64_t count = static_cast(sqlite3_column_int64(stmt, 0)); + EXPECT_EQ(count, expectNum); + } + SQLiteUtils::ResetStatement(stmt, true, errCode); +} + +/** + * @tc.name: DropDeleteData001 + * @tc.desc: Test trigger client observer in tracker table + * @tc.type: FUNC + * @tc.require: + * @tc.author: + */ +HWTEST_F(DistributedDBCloudInterfacesRelationalExtClientTest, DropDeleteData001, TestSize.Level0) +{ + /** + * @tc.steps:step1. prepare data. + * @tc.expected: step1. return ok. + */ + const std::string tableName = "sync_data"; + PrepareData({tableName}, false, DistributedDB::CLOUD_COOPERATION, false); + sqlite3 *db = RelationalTestUtils::CreateDataBase(g_dbDir + STORE_ID + DB_SUFFIX); + EXPECT_NE(db, nullptr); + uint64_t num = 10; + InitLogicDeleteData(db, tableName, num); + + /** + * @tc.steps:step2. db handle is nullptr + * @tc.expected: step2. return INVALID_ARGS. + */ + EXPECT_EQ(DropLogicDeletedData(nullptr, tableName, 0u), INVALID_ARGS); + + /** + * @tc.steps:step3. tableName is empty + * @tc.expected: step3. return INVALID_ARGS. + */ + EXPECT_EQ(DropLogicDeletedData(db, "", 0u), INVALID_ARGS); + + /** + * @tc.steps:step4. tableName is no exist + * @tc.expected: step4. return INVALID_ARGS. + */ + EXPECT_EQ(DropLogicDeletedData(db, tableName + "_", 0u), DB_ERROR); + + /** + * @tc.steps:step5. cursor is 0 + * @tc.expected: step5. return OK. + */ + EXPECT_EQ(DropLogicDeletedData(db, tableName, 0u), OK); + CheckLogicDeleteData(db, tableName, 0u); + + /** + * @tc.steps:step6. init data again, and cursor is 15 + * @tc.expected: step6. return OK. + */ + uint64_t cursor = 15; + InitLogicDeleteData(db, tableName, num); + EXPECT_EQ(DropLogicDeletedData(db, tableName, cursor), OK); + CheckLogicDeleteData(db, tableName, cursor - num); + EXPECT_EQ(sqlite3_close_v2(db), E_OK); +} + +void InitDataStatus(const std::string &tableName, int count, sqlite3 *db) +{ + int type = 4; // the num of different status + for (int i = 1; i <= type * count; i++) { + std::string sql = "INSERT INTO " + tableName + " VALUES(" + std::to_string(i) + ", 'zhangsan" + + std::to_string(i) + "');"; + EXPECT_EQ(RelationalTestUtils::ExecSql(db, sql), E_OK); + } + std::string countStr = std::to_string(count); + std::string sql = "UPDATE " + DBCommon::GetLogTableName(tableName) + " SET status=(CASE WHEN data_key<=" + + countStr + " THEN 0 WHEN data_key>" + countStr + " AND data_key<=2*" + countStr + " THEN 1 WHEN data_key>2*" + + countStr + " AND data_key<=3*" + countStr + " THEN 2 ELSE 3 END)"; + EXPECT_EQ(RelationalTestUtils::ExecSql(db, sql), E_OK); +} + +void CheckDataStatus(const std::string &tableName, const std::string &condition, sqlite3 *db, int64_t expect) +{ + std::string sql = "SELECT count(1) FROM " + DBCommon::GetLogTableName(tableName) + " WHERE " + condition; + sqlite3_stmt *stmt = nullptr; + EXPECT_EQ(SQLiteUtils::GetStatement(db, sql, stmt), E_OK); + while (SQLiteUtils::StepWithRetry(stmt) == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + int64_t count = static_cast(sqlite3_column_int64(stmt, 0)); + EXPECT_EQ(count, expect); + } + int errCode; + SQLiteUtils::ResetStatement(stmt, true, errCode); +} + +/** + * @tc.name: LockDataTest001 + * @tc.desc: Test status after lock + * @tc.type: FUNC + * @tc.require: + * @tc.author: bty + */ +HWTEST_F(DistributedDBCloudInterfacesRelationalExtClientTest, LockDataTest001, TestSize.Level1) +{ + /** + * @tc.steps:step1. init data and lock, hashKey has no matching data + * @tc.expected: step1. return NOT_FOUND. + */ + const std::string tableName = "sync_data"; + PrepareData({tableName}, false, DistributedDB::CLOUD_COOPERATION, false); + sqlite3 *db = RelationalTestUtils::CreateDataBase(g_dbDir + STORE_ID + DB_SUFFIX); + EXPECT_NE(db, nullptr); + int count = 10; + InitDataStatus(tableName, count, db); + std::vector> hashKey; + hashKey.push_back({'1'}); + EXPECT_EQ(Lock(tableName, hashKey, db), NOT_FOUND); + + /** + * @tc.steps:step2. init data and lock, hashKey has matching data + * @tc.expected: step2. return OK. + */ + hashKey.clear(); + CloudDBSyncUtilsTest::GetHashKey(tableName, " 1=1 ", db, hashKey); + EXPECT_EQ(Lock(tableName, hashKey, db), OK); + + /** + * @tc.steps:step3. check status + * @tc.expected: step3. return OK. + */ + CheckDataStatus(tableName, " status = 2 and data_key <= 10 ", db, count); + CheckDataStatus(tableName, " status = 3 and data_key <= 20 ", db, count); + CheckDataStatus(tableName, " status = 2 ", db, count + count); + CheckDataStatus(tableName, " status = 3 ", db, count + count); + EXPECT_EQ(sqlite3_close_v2(db), SQLITE_OK); +} + +/** + * @tc.name: LockDataTest002 + * @tc.desc: Test status after unLock + * @tc.type: FUNC + * @tc.require: + * @tc.author: bty + */ +HWTEST_F(DistributedDBCloudInterfacesRelationalExtClientTest, LockDataTest002, TestSize.Level1) +{ + /** + * @tc.steps:step1. init data and unLock, there is data to be compensated for + * @tc.expected: step1. return WAIT_COMPENSATED_SYNC. + */ + const std::string tableName = "sync_data"; + PrepareData({tableName}, false, DistributedDB::CLOUD_COOPERATION, false); + sqlite3 *db = RelationalTestUtils::CreateDataBase(g_dbDir + STORE_ID + DB_SUFFIX); + EXPECT_NE(db, nullptr); + int count = 10; + InitDataStatus(tableName, count, db); + std::vector> hashKey; + CloudDBSyncUtilsTest::GetHashKey(tableName, " 1=1 ", db, hashKey); + EXPECT_EQ(UnLock(tableName, hashKey, db), WAIT_COMPENSATED_SYNC); + + /** + * @tc.steps:step2. check status + * @tc.expected: step2. return OK. + */ + CheckDataStatus(tableName, " status = 0 and data_key <= 10 ", db, count); + CheckDataStatus(tableName, " status = 1 and data_key <= 20 ", db, count); + CheckDataStatus(tableName, " status = 0 ", db, count + count); + CheckDataStatus(tableName, " status = 1 ", db, count + count); + + /** + * @tc.steps:step3. unLock again, there is data to be compensated for + * @tc.expected: step3. return WAIT_COMPENSATED_SYNC. + */ + EXPECT_EQ(UnLock(tableName, hashKey, db), WAIT_COMPENSATED_SYNC); + + /** + * @tc.steps:step4. unLock again, there is no data to be compensated for + * @tc.expected: step4. return OK. + */ + std::string sql = "update " + DBCommon::GetLogTableName(tableName) + " SET status=0"; + EXPECT_EQ(RelationalTestUtils::ExecSql(db, sql), E_OK); + EXPECT_EQ(UnLock(tableName, hashKey, db), OK); + + /** + * @tc.steps:step5. unLock again, hashKey has matching data + * @tc.expected: step5. return NOT_FOUND. + */ + hashKey.clear(); + hashKey.push_back({'1'}); + EXPECT_EQ(UnLock(tableName, hashKey, db), NOT_FOUND); + EXPECT_EQ(sqlite3_close_v2(db), SQLITE_OK); +} + +DistributedDB::StoreObserver::StoreChangedInfo g_changedData; + +class MockStoreObserver : public StoreObserver { +public: + virtual ~MockStoreObserver() {}; + void OnChange(StoreChangedInfo &&data) override + { + g_changedData = data; + std::unique_lock lock(g_mutex); + g_cv.notify_one(); + g_alreadyNotify = true; + }; +}; + +void CreateTableForStoreObserver(sqlite3 *db, const std::string tableName) +{ + std::string sql = "create table " + tableName + "(id INTEGER primary key, name TEXT);"; + EXPECT_EQ(RelationalTestUtils::ExecSql(db, sql), E_OK); + sql = "create table no_" + tableName + "(id INTEGER, name TEXT);"; + EXPECT_EQ(RelationalTestUtils::ExecSql(db, sql), E_OK); + sql = "create table mult_" + tableName + "(id INTEGER, name TEXT, age int, "; + sql += "PRIMARY KEY (id, name));"; + EXPECT_EQ(RelationalTestUtils::ExecSql(db, sql), E_OK); +} + +void PrepareDataForStoreObserver(sqlite3 *db, const std::string &tableName, int begin, int dataCounts) +{ + std::string sql = "begin;"; + EXPECT_EQ(RelationalTestUtils::ExecSql(db, sql), E_OK); + for (int i = begin; i < begin + dataCounts; i++) { + sql = "insert into " + tableName + " VALUES(" + std::to_string(i + 1) + ", 'zhangsan" + + std::to_string(i + 1) + "');"; + EXPECT_EQ(RelationalTestUtils::ExecSql(db, sql), E_OK); + sql = "insert into no_" + tableName +" VALUES(" + std::to_string(i + 1) + ", 'zhangsan" + + std::to_string(i + 1) + "');"; + EXPECT_EQ(RelationalTestUtils::ExecSql(db, sql), E_OK); + sql = "insert into mult_" + tableName + " VALUES(" + std::to_string(i + 1) + ", 'zhangsan"; + sql += std::to_string(i + 1) + "', 18);"; + EXPECT_EQ(RelationalTestUtils::ExecSql(db, sql), E_OK); + } + for (int i = begin; i < dataCounts / 2 + begin; i++) { // 2 is half + sql = "update " + tableName + " set name = 'lisi' where id = " + std::to_string(i + 1) + ";"; + EXPECT_EQ(RelationalTestUtils::ExecSql(db, sql), E_OK); + sql = "update no_" + tableName + " set name = 'lisi' where _rowid_ = " + std::to_string(i + 1) + ";"; + EXPECT_EQ(RelationalTestUtils::ExecSql(db, sql), E_OK); + sql = "update mult_" + tableName + " set age = 20 where id = " + std::to_string(i + 1); + sql += " and name = 'zhangsan" + std::to_string(i + 1) + "';"; + EXPECT_EQ(RelationalTestUtils::ExecSql(db, sql), E_OK); + } + for (int i = dataCounts / 2 + begin; i < dataCounts + begin; i++) { // 2 is half + sql = "delete from " + tableName + " where id = " + std::to_string(i + 1) + ";"; + EXPECT_EQ(RelationalTestUtils::ExecSql(db, sql), E_OK); + sql = "delete from no_" + tableName + " where _rowid_ = " + std::to_string(i + 1) + ";"; + EXPECT_EQ(RelationalTestUtils::ExecSql(db, sql), E_OK); + sql = "delete from mult_" + tableName + " where id = " + std::to_string(i + 1); + sql += " and name = 'zhangsan" + std::to_string(i + 1) + "';"; + EXPECT_EQ(RelationalTestUtils::ExecSql(db, sql), E_OK); + } + sql = "commit;"; + EXPECT_EQ(RelationalTestUtils::ExecSql(db, sql), E_OK); +} + +void CheckChangedData(int num, int times = 0, int offset = 0) +{ + if (num == 1) { + for (size_t i = 1; i <= g_changedData[num].primaryData[ChangeType::OP_INSERT].size(); i++) { + EXPECT_EQ(std::get(g_changedData[num].primaryData[ChangeType::OP_INSERT][i - 1][0]), + static_cast(i + offset - times * 5)); // 5 is rowid times + } + for (size_t i = 1; i <= g_changedData[num].primaryData[ChangeType::OP_DELETE].size(); i++) { + EXPECT_EQ(std::get(g_changedData[num].primaryData[ChangeType::OP_DELETE][i - 1][0]), + static_cast(i + offset + 5)); // 5 is offset + } + return; + } + for (size_t i = 1; i <= g_changedData[num].primaryData[ChangeType::OP_INSERT].size(); i++) { + EXPECT_EQ(std::get(g_changedData[num].primaryData[ChangeType::OP_INSERT][i - 1][0]), + static_cast(i + offset)); + } + for (size_t i = 1; i <= g_changedData[num].primaryData[ChangeType::OP_UPDATE].size(); i++) { + EXPECT_EQ(std::get(g_changedData[num].primaryData[ChangeType::OP_UPDATE][i - 1][0]), + static_cast(i + offset)); + } + for (size_t i = 1; i <= g_changedData[num].primaryData[ChangeType::OP_DELETE].size(); i++) { + EXPECT_EQ(std::get(g_changedData[num].primaryData[ChangeType::OP_DELETE][i - 1][0]), + static_cast(i + offset + 5)); // 5 is offset + } +} + +/** + * @tc.name: RegisterStoreObserverTest001 + * @tc.desc: Test commit for three table then trigger store observer + * @tc.type: FUNC + * @tc.require: + * @tc.author: chenchaohao + */ +HWTEST_F(DistributedDBCloudInterfacesRelationalExtClientTest, RegisterStoreObserverTest001, TestSize.Level1) +{ + /** + * @tc.steps:step1. prepare db and create table. + * @tc.expected: step1. return ok. + */ + sqlite3 *db = RelationalTestUtils::CreateDataBase(g_dbDir + STORE_ID + DB_SUFFIX); + EXPECT_NE(db, nullptr); + EXPECT_EQ(RelationalTestUtils::ExecSql(db, "PRAGMA journal_mode=WAL;"), SQLITE_OK); + std::string tableName = "primary_test"; + CreateTableForStoreObserver(db, tableName); + + /** + * @tc.steps:step2. register store observer and check onchange. + * @tc.expected: step2. return ok. + */ + auto storeObserver = std::make_shared(); + EXPECT_EQ(RegisterStoreObserver(db, storeObserver), OK); + RegisterDbHook(db); + EXPECT_TRUE(g_changedData.empty()); + int dataCounts = 10; // 10 is count of insert options. + int begin = 0; + PrepareDataForStoreObserver(db, tableName, begin, dataCounts); + { + std::unique_lock lock(g_mutex); + g_cv.wait(lock, []() { + return g_alreadyNotify; + }); + g_alreadyNotify = false; + } + EXPECT_EQ(g_changedData[0].tableName, "primary_test"); + CheckChangedData(0); + EXPECT_EQ(g_changedData[1].tableName, "no_primary_test"); + CheckChangedData(1); + EXPECT_EQ(g_changedData[2].tableName, "mult_primary_test"); // 2 is mult primary table + CheckChangedData(2); // 2 is mult primary table + g_changedData.clear(); + + /** + * @tc.steps:step3. unregister store observer and update data check onchange. + * @tc.expected: step3. return ok. + */ + EXPECT_EQ(UnregisterStoreObserver(db), OK); + begin = 10; // 10 is begin id + PrepareDataForStoreObserver(db, tableName, begin, dataCounts); + EXPECT_TRUE(g_changedData.empty()); + EXPECT_EQ(sqlite3_close_v2(db), E_OK); +} + +/** + * @tc.name: RegisterStoreObserverTest002 + * @tc.desc: Test commit for three table then trigger client observer when register then create table + * @tc.type: FUNC + * @tc.require: + * @tc.author: chenchaohao + */ +HWTEST_F(DistributedDBCloudInterfacesRelationalExtClientTest, RegisterStoreObserverTest002, TestSize.Level1) +{ + /** + * @tc.steps:step1. prepare db and register store observer then create table. + * @tc.expected: step1. return ok. + */ + sqlite3 *db = RelationalTestUtils::CreateDataBase(g_dbDir + STORE_ID + DB_SUFFIX); + EXPECT_NE(db, nullptr); + auto storeObserver = std::make_shared(); + EXPECT_EQ(RegisterStoreObserver(db, storeObserver), OK); + RegisterDbHook(db); + EXPECT_EQ(RelationalTestUtils::ExecSql(db, "PRAGMA journal_mode=WAL;"), SQLITE_OK); + std::string tableName = "primary_test"; + CreateTableForStoreObserver(db, tableName); + + /** + * @tc.steps:step2. update data and check onchange. + * @tc.expected: step2. return ok. + */ + EXPECT_TRUE(g_changedData.empty()); + int dataCounts = 10; // 10 is count of insert options. + int begin = 0; + PrepareDataForStoreObserver(db, tableName, begin, dataCounts); + WaitAndResetNotify(); + EXPECT_EQ(g_changedData[0].tableName, "primary_test"); + CheckChangedData(0); + EXPECT_EQ(g_changedData[1].tableName, "no_primary_test"); + CheckChangedData(1); + EXPECT_EQ(g_changedData[2].tableName, "mult_primary_test"); // 2 is mult primary table + CheckChangedData(2); // 2 is mult primary table + g_changedData.clear(); + + /** + * @tc.steps:step3. unregister store observer and update data check onchange. + * @tc.expected: step3. return ok. + */ + EXPECT_EQ(UnregisterStoreObserver(db), OK); + begin = 10; // 11 is begin id + PrepareDataForStoreObserver(db, tableName, begin, dataCounts); + EXPECT_TRUE(g_changedData.empty()); + EXPECT_EQ(sqlite3_close_v2(db), E_OK); +} + +/** + * @tc.name: RegisterStoreObserverTest003 + * @tc.desc: Test commit for three table then trigger client observer when register two observer + * @tc.type: FUNC + * @tc.require: + * @tc.author: chenchaohao + */ +HWTEST_F(DistributedDBCloudInterfacesRelationalExtClientTest, RegisterStoreObserverTest003, TestSize.Level1) +{ + /** + * @tc.steps:step1. prepare db and register store observer then create table. + * @tc.expected: step1. return ok. + */ + sqlite3 *db = RelationalTestUtils::CreateDataBase(g_dbDir + STORE_ID + DB_SUFFIX); + EXPECT_NE(db, nullptr); + auto storeObserver1 = std::make_shared(); + auto storeObserver2 = std::make_shared(); + EXPECT_EQ(RegisterStoreObserver(db, storeObserver1), OK); + EXPECT_EQ(RegisterStoreObserver(db, storeObserver2), OK); + RegisterDbHook(db); + EXPECT_EQ(RelationalTestUtils::ExecSql(db, "PRAGMA journal_mode=WAL;"), SQLITE_OK); + std::string tableName = "primary_test"; + CreateTableForStoreObserver(db, tableName); + + /** + * @tc.steps:step2. update data and check onchange. + * @tc.expected: step2. return ok. + */ + EXPECT_TRUE(g_changedData.empty()); + int dataCounts = 10; // 10 is count of insert options. + int begin = 0; + PrepareDataForStoreObserver(db, tableName, begin, dataCounts); + WaitAndResetNotify(); + EXPECT_EQ(g_changedData[0].tableName, "primary_test"); + CheckChangedData(0); + EXPECT_EQ(g_changedData[1].tableName, "no_primary_test"); + CheckChangedData(1); + EXPECT_EQ(g_changedData[2].tableName, "mult_primary_test"); // 2 is mult primary table + CheckChangedData(2); // 2 is mult primary table + g_changedData.clear(); + + /** + * @tc.steps:step3. unregister store observer and update data check onchange. + * @tc.expected: step3. return ok. + */ + EXPECT_EQ(UnregisterStoreObserver(db, storeObserver1), OK); + begin = 10; // 11 is begin id + PrepareDataForStoreObserver(db, tableName, begin, dataCounts); + EXPECT_EQ(g_changedData[0].tableName, "primary_test"); + CheckChangedData(0, 1, dataCounts); + EXPECT_EQ(g_changedData[1].tableName, "no_primary_test"); + CheckChangedData(1, 1, dataCounts); + EXPECT_EQ(g_changedData[2].tableName, "mult_primary_test"); // 2 is mult primary table + CheckChangedData(2, 1, dataCounts); // 2 is mult primary table + g_changedData.clear(); + + EXPECT_EQ(UnregisterStoreObserver(db, storeObserver2), OK); + begin = 20; // 21 is begin id + PrepareDataForStoreObserver(db, tableName, begin, dataCounts); + EXPECT_TRUE(g_changedData.empty()); + EXPECT_EQ(sqlite3_close_v2(db), E_OK); +} + +/** + * @tc.name: RegisterStoreObserverTest004 + * @tc.desc: Test register two same observer + * @tc.type: FUNC + * @tc.require: + * @tc.author: chenchaohao + */ +HWTEST_F(DistributedDBCloudInterfacesRelationalExtClientTest, RegisterStoreObserverTest004, TestSize.Level1) +{ + /** + * @tc.steps:step1. prepare db and register store observer then create table. + * @tc.expected: step1. return ok. + */ + sqlite3 *db = RelationalTestUtils::CreateDataBase(g_dbDir + STORE_ID + DB_SUFFIX); + EXPECT_NE(db, nullptr); + auto storeObserver = std::make_shared(); + EXPECT_EQ(RegisterStoreObserver(db, storeObserver), OK); + EXPECT_EQ(RegisterStoreObserver(db, storeObserver), OK); + EXPECT_EQ(sqlite3_close_v2(db), SQLITE_OK); +} +} diff --git a/frameworks/libs/distributeddb/test/unittest/common/interfaces/distributeddb_cloud_interfaces_relational_ext_test.cpp b/frameworks/libs/distributeddb/test/unittest/common/interfaces/distributeddb_cloud_interfaces_relational_ext_test.cpp index 2faacb653ebead10022baf96386105167da69db1..f53ca9661eb6b1bb6397c091424c14a4c3ebfb2b 100644 --- a/frameworks/libs/distributeddb/test/unittest/common/interfaces/distributeddb_cloud_interfaces_relational_ext_test.cpp +++ b/frameworks/libs/distributeddb/test/unittest/common/interfaces/distributeddb_cloud_interfaces_relational_ext_test.cpp @@ -566,13 +566,7 @@ HWTEST_F(DistributedDBCloudInterfacesRelationalExtTest, TriggerObserverTest001, EXPECT_NE(db, nullptr); EXPECT_EQ(RegisterClientObserver(db, nullptr), INVALID_ARGS); - /** - * @tc.steps:step3. call RegisterClientObserver and UnRegisterClientObserver with closed db handle. - * @tc.expected: step3. return INVALID_ARGS. - */ EXPECT_EQ(sqlite3_close_v2(db), E_OK); - EXPECT_EQ(RegisterClientObserver(db, clientObserver), INVALID_ARGS); - EXPECT_EQ(UnRegisterClientObserver(db), INVALID_ARGS); } /** @@ -1912,4 +1906,22 @@ HWTEST_F(DistributedDBCloudInterfacesRelationalExtTest, AbnormalDelegateImplTest EXPECT_EQ(g_mgr.CloseStore(delegate), OK); delegate = nullptr; } + +/** + * @tc.name: CleanTest001 + * @tc.desc: Test calling the Clean interface twice + * @tc.type: FUNC + * @tc.require: + * @tc.author: suyue + */ +HWTEST_F(DistributedDBCloudInterfacesRelationalExtTest, CleanTest001, TestSize.Level0) +{ + Logger *loggerInstance = Logger::GetInstance(); + ASSERT_NE(loggerInstance, nullptr); + Clean(true); + Clean(false); + loggerInstance = nullptr; + Logger *newLoggerInstance = Logger::GetInstance(); + ASSERT_NE(newLoggerInstance, nullptr); +} } diff --git a/frameworks/libs/distributeddb/test/unittest/common/store_test/rdb/distributeddb_rdb_knowledge_client_test.cpp b/frameworks/libs/distributeddb/test/unittest/common/store_test/rdb/distributeddb_rdb_knowledge_client_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..aeaa0babcc260093c2dc93ba9f4206f7b4b11f30 --- /dev/null +++ b/frameworks/libs/distributeddb/test/unittest/common/store_test/rdb/distributeddb_rdb_knowledge_client_test.cpp @@ -0,0 +1,254 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "cloud_sync_log_table_manager.h" +#include "distributeddb_tools_unit_test.h" +#include "rdb_data_generator.h" +#include "relational_store_client.h" +#include "sqlite_relational_utils.h" +#include "table_info.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; + +namespace { + constexpr const char *DB_SUFFIX = ".db"; + constexpr const char *STORE_ID = "Relational_Store_ID"; + std::string g_dbDir; + std::string g_testDir; +} + +namespace { +class DistributedDBRDBKnowledgeClientTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp() override; + void TearDown() override; +protected: + static UtTableSchemaInfo GetTableSchema(const std::string &table); + static void SaveSchemaToMetaTable(sqlite3 *db, const std::string tableName, const TableInfo &tableInfo); + static void CreateDistributedTable(const std::vector &tableNames, + DistributedDB::TableSyncType tableSyncType); + static constexpr const char *KNOWLEDGE_TABLE = "KNOWLEDGE_TABLE"; + static constexpr const char *SYNC_TABLE = "SYNC_TABLE"; +}; + + +void DistributedDBRDBKnowledgeClientTest::SetUpTestCase(void) +{ + DistributedDBToolsUnitTest::TestDirInit(g_testDir); + LOGD("Test dir is %s", g_testDir.c_str()); + g_dbDir = g_testDir + "/"; + DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir); +} + +void DistributedDBRDBKnowledgeClientTest::TearDownTestCase(void) +{ +} + +void DistributedDBRDBKnowledgeClientTest::SetUp() +{ +} + +void DistributedDBRDBKnowledgeClientTest::TearDown() +{ + DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir); +} + +UtTableSchemaInfo DistributedDBRDBKnowledgeClientTest::GetTableSchema(const std::string &table) +{ + UtTableSchemaInfo tableSchema; + tableSchema.name = table; + UtFieldInfo field; + field.field.colName = "id"; + field.field.type = TYPE_INDEX; + field.field.primary = true; + tableSchema.fieldInfo.push_back(field); + field.field.primary = false; + field.field.colName = "int_field1"; + tableSchema.fieldInfo.push_back(field); + field.field.colName = "int_field2"; + tableSchema.fieldInfo.push_back(field); + field.field.colName = "int_field3"; + tableSchema.fieldInfo.push_back(field); + return tableSchema; +} + +void DistributedDBRDBKnowledgeClientTest::SaveSchemaToMetaTable(sqlite3 *db, const std::string tableName, + const TableInfo &tableInfo) +{ + RelationalSchemaObject schema; + schema.SetTableMode(DistributedDB::DistributedTableMode::SPLIT_BY_DEVICE); + schema.RemoveRelationalTable(tableName); + schema.AddRelationalTable(tableInfo); + + const Key schemaKey(DBConstant::RELATIONAL_SCHEMA_KEY.begin(), DBConstant::RELATIONAL_SCHEMA_KEY.end()); + Value schemaVal; + auto schemaStr = schema.ToSchemaString(); + EXPECT_FALSE(schemaStr.size() > SchemaConstant::SCHEMA_STRING_SIZE_LIMIT); + DBCommon::StringToVector(schemaStr, schemaVal); + EXPECT_EQ(SQLiteRelationalUtils::PutKvData(db, false, schemaKey, schemaVal), E_OK); +} + +void DistributedDBRDBKnowledgeClientTest::CreateDistributedTable(const std::vector &tableNames, + DistributedDB::TableSyncType tableSyncType) +{ + sqlite3 *db = RelationalTestUtils::CreateDataBase(g_dbDir + STORE_ID + DB_SUFFIX); + EXPECT_NE(db, nullptr); + for (const auto &tableName : tableNames) { + TableInfo tableInfo; + tableInfo.SetTableName(tableName); + tableInfo.SetTableSyncType(tableSyncType); + TrackerTable table111; + tableInfo.SetTrackerTable(table111); + DistributedTable distributedTable; + distributedTable.tableName = tableName; + tableInfo.SetDistributedTable(distributedTable); + std::unique_ptr tableManager = std::make_unique(); + EXPECT_NE(tableManager, nullptr); + EXPECT_EQ(SQLiteUtils::AnalysisSchema(db, tableName, tableInfo), E_OK); + std::vector fieldInfos = tableInfo.GetFieldInfos(); + EXPECT_EQ(SQLiteRelationalUtils::CreateRelationalMetaTable(db), E_OK); + EXPECT_EQ(SQLiteRelationalUtils::InitCursorToMeta(db, false, tableName), E_OK); + EXPECT_EQ(tableManager->CreateRelationalLogTable(db, tableInfo), E_OK); + EXPECT_EQ(tableManager->AddRelationalLogTableTrigger(db, tableInfo, ""), E_OK); + SQLiteRelationalUtils::SetLogTriggerStatus(db, true); + SaveSchemaToMetaTable(db, tableName, tableInfo); + } + EXPECT_EQ(sqlite3_close_v2(db), E_OK); +} + +void InsertDBData(const std::string &tableName, int count, sqlite3 *db) +{ + for (int i = 1; i <= count; i++) { + std::string sql = "INSERT INTO " + tableName + " VALUES(" + std::to_string(i) + ',' + std::to_string(i) + ',' + + std::to_string(i) + ',' + std::to_string(i) + ");"; + EXPECT_EQ(RelationalTestUtils::ExecSql(db, sql), E_OK); + } +} + +/** + * @tc.name: SetKnowledge001 + * @tc.desc: Test set knowledge schema. + * @tc.type: FUNC + * @tc.require: + * @tc.author: zqq + */ +HWTEST_F(DistributedDBRDBKnowledgeClientTest, SetKnowledge001, TestSize.Level0) +{ + /** + * @tc.steps: step1. Set knowledge source schema and clean deleted data. + * @tc.expected: step1. Ok + */ + UtTableSchemaInfo tableInfo = GetTableSchema(KNOWLEDGE_TABLE); + sqlite3 *db = RelationalTestUtils::CreateDataBase(g_dbDir + STORE_ID + DB_SUFFIX); + EXPECT_NE(db, nullptr); + RDBDataGenerator::InitTableWithSchemaInfo(tableInfo, *db); + KnowledgeSourceSchema schema; + schema.tableName = KNOWLEDGE_TABLE; + schema.extendColNames.insert("id"); + schema.knowledgeColNames.insert("int_field1"); + schema.knowledgeColNames.insert("int_field2"); + EXPECT_EQ(SetKnowledgeSourceSchema(db, schema), OK); + EXPECT_EQ(CleanDeletedData(db, schema.tableName, 0u), OK); + /** + * @tc.steps: step2. Clean deleted data after insert one data. + * @tc.expected: step2. Ok + */ + InsertDBData(schema.tableName, 1, db); + EXPECT_EQ(CleanDeletedData(db, schema.tableName, 0u), OK); + /** + * @tc.steps: step3. Clean deleted data after delete one data. + * @tc.expected: step3. Ok + */ + std::string sql = std::string("DELETE FROM ").append(KNOWLEDGE_TABLE).append(" WHERE 1=1"); + EXPECT_EQ(SQLiteUtils::ExecuteRawSQL(db, sql), E_OK); + EXPECT_EQ(CleanDeletedData(db, schema.tableName, 10u), OK); // delete which cursor less than 10 + + EXPECT_EQ(sqlite3_close_v2(db), SQLITE_OK); +} + +/** + * @tc.name: SetKnowledge002 + * @tc.desc: Test set knowledge schema with invalid args. + * @tc.type: FUNC + * @tc.require: + * @tc.author: zqq + */ +HWTEST_F(DistributedDBRDBKnowledgeClientTest, SetKnowledge002, TestSize.Level0) +{ + /** + * @tc.steps: step1. Set knowledge source schema and clean deleted data with null db. + * @tc.expected: step1. INVALID_ARGS + */ + KnowledgeSourceSchema schema; + schema.tableName = KNOWLEDGE_TABLE; + schema.extendColNames.insert("id"); + schema.knowledgeColNames.insert("int_field1"); + schema.knowledgeColNames.insert("int_field2"); + EXPECT_EQ(SetKnowledgeSourceSchema(nullptr, schema), INVALID_ARGS); + EXPECT_EQ(CleanDeletedData(nullptr, schema.tableName, 0u), INVALID_ARGS); + /** + * @tc.steps: step2. Set knowledge source schema and clean deleted data with null db. + * @tc.expected: step2. INVALID_ARGS + */ + sqlite3 *db = RelationalTestUtils::CreateDataBase(g_dbDir + STORE_ID + DB_SUFFIX); + ASSERT_NE(db, nullptr); + schema.tableName = "UNKNOWN_TABLE"; + EXPECT_EQ(SetKnowledgeSourceSchema(db, schema), INVALID_ARGS); + EXPECT_EQ(CleanDeletedData(db, schema.tableName, 0u), OK); + + EXPECT_EQ(sqlite3_close_v2(db), SQLITE_OK); +} + +/** + * @tc.name: SetKnowledge003 + * @tc.desc: Test set knowledge schema after create distributed table. + * @tc.type: FUNC + * @tc.require: + * @tc.author: zqq + */ +HWTEST_F(DistributedDBRDBKnowledgeClientTest, SetKnowledge003, TestSize.Level0) +{ + /** + * @tc.steps: step1. Create distributed table. + * @tc.expected: step1. Ok + */ + UtTableSchemaInfo tableInfo1 = GetTableSchema(KNOWLEDGE_TABLE); + sqlite3 *db = RelationalTestUtils::CreateDataBase(g_dbDir + STORE_ID + DB_SUFFIX); + EXPECT_NE(db, nullptr); + RDBDataGenerator::InitTableWithSchemaInfo(tableInfo1, *db); + UtTableSchemaInfo tableInfo2 = GetTableSchema(SYNC_TABLE); + RDBDataGenerator::InitTableWithSchemaInfo(tableInfo2, *db); + CreateDistributedTable({SYNC_TABLE}, DistributedDB::CLOUD_COOPERATION); + + /** + * @tc.steps: step2. Set knowledge source schema. + * @tc.expected: step2. INVALID_ARGS + */ + KnowledgeSourceSchema schema; + schema.tableName = KNOWLEDGE_TABLE; + schema.extendColNames.insert("id"); + schema.knowledgeColNames.insert("int_field1"); + schema.knowledgeColNames.insert("int_field2"); + schema.tableName = SYNC_TABLE; + EXPECT_EQ(SetKnowledgeSourceSchema(db, schema), INVALID_ARGS); + + EXPECT_EQ(sqlite3_close_v2(db), SQLITE_OK); +} +} diff --git a/frameworks/libs/distributeddb/test/unittest/common/store_test/rdb/distributeddb_rdb_knowledge_test.cpp b/frameworks/libs/distributeddb/test/unittest/common/store_test/rdb/distributeddb_rdb_knowledge_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..df719c26446c36471968e6ce0f18f54245068325 --- /dev/null +++ b/frameworks/libs/distributeddb/test/unittest/common/store_test/rdb/distributeddb_rdb_knowledge_test.cpp @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "rdb_general_ut.h" +#include "relational_store_client.h" + +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; + +namespace { +class DistributedDBRDBKnowledgeTest : public RDBGeneralUt { +public: + void SetUp() override; +protected: + static UtDateBaseSchemaInfo GetDefaultSchema(); + static UtTableSchemaInfo GetTableSchema(const std::string &table); + static KnowledgeSourceSchema GetKnowledgeSchema(); + static constexpr const char *KNOWLEDGE_TABLE = "KNOWLEDGE_TABLE"; + static constexpr const char *SYNC_TABLE = "SYNC_TABLE"; + StoreInfo info1_ = {USER_ID, APP_ID, STORE_ID_1}; +}; + +void DistributedDBRDBKnowledgeTest::SetUp() +{ + RDBGeneralUt::SetUp(); + SetSchemaInfo(info1_, GetDefaultSchema()); + InitDatabase(info1_); +} + +UtDateBaseSchemaInfo DistributedDBRDBKnowledgeTest::GetDefaultSchema() +{ + UtDateBaseSchemaInfo info; + info.tablesInfo.push_back(GetTableSchema(KNOWLEDGE_TABLE)); + return info; +} + +UtTableSchemaInfo DistributedDBRDBKnowledgeTest::GetTableSchema(const std::string &table) +{ + UtTableSchemaInfo tableSchema; + tableSchema.name = table; + UtFieldInfo field; + field.field.colName = "id"; + field.field.type = TYPE_INDEX; + field.field.primary = true; + tableSchema.fieldInfo.push_back(field); + field.field.primary = false; + field.field.colName = "int_field1"; + tableSchema.fieldInfo.push_back(field); + field.field.colName = "int_field2"; + tableSchema.fieldInfo.push_back(field); + field.field.colName = "int_field3"; + tableSchema.fieldInfo.push_back(field); + return tableSchema; +} + +KnowledgeSourceSchema DistributedDBRDBKnowledgeTest::GetKnowledgeSchema() +{ + KnowledgeSourceSchema schema; + schema.extendColNames.insert("id"); + schema.knowledgeColNames.insert("int_field1"); + schema.knowledgeColNames.insert("int_field2"); + schema.tableName = SYNC_TABLE; + return schema; +} + +/** + * @tc.name: SetKnowledge001 + * @tc.desc: Test set knowledge schema. + * @tc.type: FUNC + * @tc.require: + * @tc.author: zqq + */ +HWTEST_F(DistributedDBRDBKnowledgeTest, SetKnowledge001, TestSize.Level0) +{ + /** + * @tc.steps: step1. Set knowledge source schema and clean deleted data. + * @tc.expected: step1. Ok + */ + auto db = GetSqliteHandle(info1_); + ASSERT_NE(db, nullptr); + KnowledgeSourceSchema schema; + schema.tableName = KNOWLEDGE_TABLE; + schema.extendColNames.insert("id"); + schema.knowledgeColNames.insert("int_field1"); + schema.knowledgeColNames.insert("int_field2"); + EXPECT_EQ(SetKnowledgeSourceSchema(db, schema), OK); + EXPECT_EQ(CleanDeletedData(db, schema.tableName, 0u), OK); + /** + * @tc.steps: step2. Clean deleted data after insert one data. + * @tc.expected: step2. Ok + */ + EXPECT_EQ(InsertLocalDBData(0, 1, info1_), E_OK); + EXPECT_EQ(CleanDeletedData(db, schema.tableName, 0u), OK); + /** + * @tc.steps: step3. Clean deleted data after delete one data. + * @tc.expected: step3. Ok + */ + std::string sql = std::string("DELETE FROM ").append(KNOWLEDGE_TABLE).append(" WHERE 1=1"); + EXPECT_EQ(SQLiteUtils::ExecuteRawSQL(db, sql), E_OK); + EXPECT_EQ(CleanDeletedData(db, schema.tableName, 10u), OK); // delete which cursor less than 10 +} + +/** + * @tc.name: SetKnowledge002 + * @tc.desc: Test set knowledge schema with invalid args. + * @tc.type: FUNC + * @tc.require: + * @tc.author: zqq + */ +HWTEST_F(DistributedDBRDBKnowledgeTest, SetKnowledge002, TestSize.Level0) +{ + /** + * @tc.steps: step1. Set knowledge source schema and clean deleted data with null db. + * @tc.expected: step1. INVALID_ARGS + */ + KnowledgeSourceSchema schema; + schema.tableName = KNOWLEDGE_TABLE; + schema.extendColNames.insert("id"); + schema.knowledgeColNames.insert("int_field1"); + schema.knowledgeColNames.insert("int_field2"); + EXPECT_EQ(SetKnowledgeSourceSchema(nullptr, schema), INVALID_ARGS); + EXPECT_EQ(CleanDeletedData(nullptr, schema.tableName, 0u), INVALID_ARGS); + /** + * @tc.steps: step2. Set knowledge source schema and clean deleted data with null db. + * @tc.expected: step2. INVALID_ARGS + */ + auto db = GetSqliteHandle(info1_); + ASSERT_NE(db, nullptr); + schema.tableName = "UNKNOWN_TABLE"; + EXPECT_EQ(SetKnowledgeSourceSchema(db, schema), INVALID_ARGS); + EXPECT_EQ(CleanDeletedData(db, schema.tableName, 0u), OK); +} + +/** + * @tc.name: SetKnowledge003 + * @tc.desc: Test set knowledge schema after create distributed table. + * @tc.type: FUNC + * @tc.require: + * @tc.author: zqq + */ +HWTEST_F(DistributedDBRDBKnowledgeTest, SetKnowledge003, TestSize.Level0) +{ + /** + * @tc.steps: step1. Create distributed table. + * @tc.expected: step1. Ok + */ + UtDateBaseSchemaInfo info; + info.tablesInfo.push_back(GetTableSchema(KNOWLEDGE_TABLE)); + info.tablesInfo.push_back(GetTableSchema(SYNC_TABLE)); + SetSchemaInfo(info1_, info); + ASSERT_EQ(DistributedDB::RDBGeneralUt::InitDelegate(info1_), E_OK); + ASSERT_EQ(CreateDistributedTable(info1_, SYNC_TABLE), E_OK); + /** + * @tc.steps: step2. Set knowledge source schema. + * @tc.expected: step2. INVALID_ARGS + */ + auto db = GetSqliteHandle(info1_); + ASSERT_NE(db, nullptr); + EXPECT_EQ(SetKnowledgeSourceSchema(db, GetKnowledgeSchema()), INVALID_ARGS); +} + +/** + * @tc.name: SetKnowledge004 + * @tc.desc: Test set knowledge schema after create tracker table. + * @tc.type: FUNC + * @tc.require: + * @tc.author: zqq + */ +HWTEST_F(DistributedDBRDBKnowledgeTest, SetKnowledge004, TestSize.Level0) +{ + /** + * @tc.steps: step1. Create tracker table. + * @tc.expected: step1. Ok + */ + UtDateBaseSchemaInfo info; + info.tablesInfo.push_back(GetTableSchema(SYNC_TABLE)); + info.tablesInfo.push_back(GetTableSchema(KNOWLEDGE_TABLE)); + SetSchemaInfo(info1_, info); + ASSERT_EQ(DistributedDB::RDBGeneralUt::InitDelegate(info1_), E_OK); + ASSERT_EQ(DistributedDB::RDBGeneralUt::SetTrackerTables(info1_, {SYNC_TABLE}), E_OK); + /** + * @tc.steps: step2. Set knowledge source schema. + * @tc.expected: step2. INVALID_ARGS + */ + auto db = GetSqliteHandle(info1_); + ASSERT_NE(db, nullptr); + EXPECT_EQ(SetKnowledgeSourceSchema(db, GetKnowledgeSchema()), INVALID_ARGS); +} +} \ No newline at end of file diff --git a/frameworks/libs/distributeddb/test/unittest/common/syncer/cloud/cloud_db_sync_utils_client_test.cpp b/frameworks/libs/distributeddb/test/unittest/common/syncer/cloud/cloud_db_sync_utils_client_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1ec750f19edaf349ab16851f035e2316bfc10c5e --- /dev/null +++ b/frameworks/libs/distributeddb/test/unittest/common/syncer/cloud/cloud_db_sync_utils_client_test.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "cloud_db_sync_utils_test.h" + +namespace DistributedDB { + void CloudDBSyncUtilsTest::GetHashKey(const std::string &tableName, const std::string &condition, sqlite3 *db, + std::vector> &hashKey) + { + sqlite3_stmt *stmt = nullptr; + std::string sql = "select hash_key from " + DBCommon::GetLogTableName(tableName) + " where " + condition; + EXPECT_EQ(SQLiteUtils::GetStatement(db, sql, stmt), E_OK); + while (SQLiteUtils::StepWithRetry(stmt) == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { + std::vector blob; + EXPECT_EQ(SQLiteUtils::GetColumnBlobValue(stmt, 0, blob), E_OK); + hashKey.push_back(blob); + } + int errCode; + SQLiteUtils::ResetStatement(stmt, true, errCode); + } +} diff --git a/frameworks/libs/distributeddb/test/unittest/common/syncer/cloud/cloud_db_sync_utils_test.cpp b/frameworks/libs/distributeddb/test/unittest/common/syncer/cloud/cloud_db_sync_utils_test.cpp index 479520ad67d5ecd2f3b2a272ffaea5625e1c642b..b4c904c6eeb55f4baefd56c7e148039a59c1099c 100644 --- a/frameworks/libs/distributeddb/test/unittest/common/syncer/cloud/cloud_db_sync_utils_test.cpp +++ b/frameworks/libs/distributeddb/test/unittest/common/syncer/cloud/cloud_db_sync_utils_test.cpp @@ -328,19 +328,4 @@ namespace DistributedDB { EXPECT_EQ(sqlite3_exec(db, sql.c_str(), QueryCountCallback, reinterpret_cast(count), nullptr), SQLITE_OK); } - - void CloudDBSyncUtilsTest::GetHashKey(const std::string &tableName, const std::string &condition, sqlite3 *db, - std::vector> &hashKey) - { - sqlite3_stmt *stmt = nullptr; - std::string sql = "select hash_key from " + DBCommon::GetLogTableName(tableName) + " where " + condition; - EXPECT_EQ(SQLiteUtils::GetStatement(db, sql, stmt), E_OK); - while (SQLiteUtils::StepWithRetry(stmt) == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) { - std::vector blob; - EXPECT_EQ(SQLiteUtils::GetColumnBlobValue(stmt, 0, blob), E_OK); - hashKey.push_back(blob); - } - int errCode; - SQLiteUtils::ResetStatement(stmt, true, errCode); - } }