diff --git a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/CMakeLists.txt b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/CMakeLists.txt index b1ab9a9856081d9156aed1a2d6f4fe3c002eb1b5..cb213c24cf765bf138f42845a5e21ee87724535b 100644 --- a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/CMakeLists.txt +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/CMakeLists.txt @@ -162,6 +162,10 @@ include_directories( ${PROJECT_SOURCE_DIR}/src/interface/src ) +include_directories( + ${PROJECT_SOURCE_DIR}/test/unittest/common +) + set(DISTRIBUTEDDB_PATH ${PROJECT_SOURCE_DIR}/third_party/distributeddatamgr_kv_store/frameworks/libs/distributeddb/) include_directories( ${DISTRIBUTEDDB_PATH}/include diff --git a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/include/grd_base/grd_error.h b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/include/grd_base/grd_error.h index 16b5eb4b94ffded7618f1aa4cc47e060c5cd8338..ecc7c705309b063cddb0232aadefe8459e4365b8 100644 --- a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/include/grd_base/grd_error.h +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/include/grd_base/grd_error.h @@ -30,7 +30,7 @@ extern "C" { #define GRD_OVER_LIMIT (-2000) #define GRD_INVALID_ARGS (-3000) #define GRD_SYSTEM_ERR (-4000) -#define GRD_FAILED_FILE_FORMAT (-5000) +#define GRD_FAILED_FILE_OPERATION (-5000) #define GRD_INVALID_FILE_FORMAT (-6000) #define GRD_INNER_ERR (-8000) #define GRD_RESOURCE_BUSY (-9000) diff --git a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/include/grd_base/grd_type_export.h b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/include/grd_base/grd_type_export.h index a9c06e1886c3f405bfc07a931b6b17b12f5d9fe8..ca6163a2ca74a24fe5f6cdb14c5fb8d097cf9a0f 100644 --- a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/include/grd_base/grd_type_export.h +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/include/grd_base/grd_type_export.h @@ -38,7 +38,7 @@ typedef struct GRD_DB GRD_DB; /** * @brief flush database config -*/ + */ #define GRD_DB_FLUSH_ASYNC 0x00 #define GRD_DB_FLUSH_SYNC 0x01 @@ -51,11 +51,12 @@ typedef struct Query { /** * @brief Flags for create and drop collection */ -#define IGNORE_EXIST_TABLE 1 -#define IGNORE_NOT_EXIST_TABLE 1 +#define CHK_EXIST_COLLECTION 1 +#define CHK_NON_EXIST_COLLECTION 1 + +#define GRD_DOC_APPEND 0 +#define GRD_DOC_REPLACE 1 -#define IGNORE_DOC_APPEND 0 -#define IGNORE_DOC_REPLACE 1 #ifdef __cplusplus } #endif // __cplusplus diff --git a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/common/include/collection_option.h b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/common/include/collection_option.h new file mode 100644 index 0000000000000000000000000000000000000000..727592400325ca7e56f8980a1befde71dbad0b04 --- /dev/null +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/common/include/collection_option.h @@ -0,0 +1,37 @@ +/* +* Copyright (c) 2023 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 COLLECTION_OPTION_H +#define COLLECTION_OPTION_H + +#include +#include + +namespace DocumentDB { +class CollectionOption final { +public: + static CollectionOption ReadOption(const std::string &optStr, int &errCode); + + uint32_t GetMaxDoc() const; + std::string ToString() const; + + bool operator==(const CollectionOption &targetOption) const; + bool operator!=(const CollectionOption &targetOption) const; +private: + std::string option_ = "{}"; + uint32_t maxDoc_ = UINT32_MAX; +}; +} // namespace DocumentDB +#endif // COLLECTION_OPTION_H \ No newline at end of file diff --git a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/common/include/db_config.h b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/common/include/db_config.h new file mode 100644 index 0000000000000000000000000000000000000000..c04116f2a8931a7b6ee4aa2722d5c042f500503d --- /dev/null +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/common/include/db_config.h @@ -0,0 +1,46 @@ +/* +* Copyright (c) 2023 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 DB_CONFIG_H +#define DB_CONFIG_H + +#include + +namespace DocumentDB { +class DBConfig final { +public: + static DBConfig ReadConfig(const std::string &confStr, int &errCode); + + ~DBConfig() = default; + std::string ToString() const; + + int32_t GetPageSize() const; + + bool operator==(const DBConfig &targetConfig) const; + bool operator!=(const DBConfig &targetConfig) const; + +private: + DBConfig() = default; + + std::string configStr_ = {}; + int32_t pageSize_ = 4; // 4: default page size k + uint32_t redoFlushByTrx_ = 0; + uint32_t redoPubBufSize_ = 1024; // 1024: default 1024k buff size + int32_t maxConnNum_ = 100; // 100: default max conn + uint32_t bufferPoolSize_ = 1024; // 100: default 1024k pool size + uint32_t crcCheckEnable_ = 1; +}; +} // namespace DocumentDB +#endif // DB_CONFIG_H \ No newline at end of file diff --git a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/common/include/doc_common.h b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/common/include/doc_common.h index 24065eda2a3897f1cd314b0cfd012499a1749403..51ecd6f47ccc9c7d71da9f50cb796693a717014f 100644 --- a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/common/include/doc_common.h +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/common/include/doc_common.h @@ -20,15 +20,15 @@ #include #include "json_common.h" -class JsonCommon; namespace DocumentDB { +class JsonCommon; class CheckCommon { public: CheckCommon() = default; - ~CheckCommon(); - - static bool CheckCollectionName(const std::string &collectionName); + ~CheckCommon() = default; + + static bool CheckCollectionName(const std::string &collectionName, std::string &lowerCaseName); static bool CheckFilter(const std::string &filter); static bool CheckIdFormat(const std::string &data); static bool CheckDocument(const std::string &document); @@ -36,5 +36,6 @@ public: using Key = std::vector; using Value = std::vector; +constexpr const char *COLL_PREFIX = "GRD_COLL_"; } // DocumentDB #endif // DOC_COMMON_H \ No newline at end of file diff --git a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/common/include/doc_limit.h b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/common/include/doc_limit.h new file mode 100644 index 0000000000000000000000000000000000000000..38ceb943f64f845d8f87a30950712a586895d4b1 --- /dev/null +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/common/include/doc_limit.h @@ -0,0 +1,22 @@ +/* +* Copyright (c) 2023 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 DOC_LIMIT_H +#define DOC_LIMIT_H + +namespace DocumentDB { +constexpr const int MAX_DB_CONFIG_LEN = 512 * 1024; // 512 * 1024: 512k length +} // namespace DocumentDB +#endif // DOC_LIMIT_H \ No newline at end of file diff --git a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/common/include/json_common.h b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/common/include/json_common.h index 745718c1d6092a0158207cded3210f588212e29e..7c0f0456d8f747dbaf9e74e51f0677ed35bc50c8 100644 --- a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/common/include/json_common.h +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/common/include/json_common.h @@ -27,13 +27,16 @@ class JsonCommon public: JsonCommon() = default; ~JsonCommon(); - + static ResultValue GetValueByFiled(JsonObject *node, const std::string& filed); static bool CheckJsonField(const std::string &data); static int ParseNode(JsonObject *Node, std::vector singlePath, std::vector> &resultPath, bool isFirstFloor); - static std::vector> ParsePath(JsonObject* node); + static std::vector> ParsePath(const JsonObject* const node); static std::vector GetLeafValue(JsonObject *node); -private: + + static int Append(const JsonObject &src, const JsonObject &add); + +private: static bool CheckNode(JsonObject *Node, std::set filedSet, bool &errFlag); static int CheckLeafNode(JsonObject *Node, std::vector &leafValue); }; diff --git a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/common/include/os_api.h b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/common/include/os_api.h index e331ab336644d7cb745f46d884be9fa6ed8bf9ec..6bfbf3bb5742f41f9652fde989bbc63955a948b1 100644 --- a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/common/include/os_api.h +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/common/include/os_api.h @@ -18,6 +18,8 @@ #define OS_API_H namespace DocumentDB { namespace OSAPI { +bool CheckPermission(const std::string &filePath); + bool CheckPathExistence(const std::string &filePath); int GetRealPath(const std::string &inOriPath, std::string &outRealPath); diff --git a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/common/src/collection_option.cpp b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/common/src/collection_option.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2540653e4a60be6f557c206dc8c3fca1d9453721 --- /dev/null +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/common/src/collection_option.cpp @@ -0,0 +1,118 @@ +/* +* Copyright (c) 2023 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 "collection_option.h" + +#include +#include + +#include "doc_errno.h" +#include "json_object.h" +#include "log_print.h" + +namespace DocumentDB { +namespace { +const std::string OPT_MAX_DOC = "maxdoc"; +const std::vector DB_CONFIG = { + OPT_MAX_DOC, +}; + +bool CheckConfigSupport(const JsonObject &config, int &errCode) +{ + + JsonObject child = config.GetChild(); + while (!child.IsNull()) { + std::string fieldName = child.GetItemFiled(); + if (std::find(DB_CONFIG.begin(), DB_CONFIG.end(), fieldName) == DB_CONFIG.end()) { + GLOGE("Invalid collection config."); + errCode = -E_INVALID_CONFIG_VALUE; + return false; + } + child = child.GetNext(); + } + return true; +} +} +CollectionOption CollectionOption::ReadOption(const std::string &optStr, int &errCode) +{ + if (optStr.empty()) { + return {}; + } + + std::string lowerCaseOptStr = optStr; + std::transform(lowerCaseOptStr.begin(), lowerCaseOptStr.end(), lowerCaseOptStr.begin(), [](unsigned char c) { + return std::tolower(c); + }); + + JsonObject collOpt = JsonObject::Parse(lowerCaseOptStr, errCode); + if (errCode != E_OK) { + GLOGE("Read collection option failed from str. %d", errCode); + return {}; + } + + if (!CheckConfigSupport(collOpt, errCode)) { + GLOGE("Check collection option, not support config item. %d", errCode); + return {}; + } + + static const JsonFieldPath maxDocField = {OPT_MAX_DOC}; + if (!collOpt.IsFieldExists(maxDocField)) { + return {}; + } + + ValueObject maxDocValue = collOpt.GetObjectByPath(maxDocField, errCode); + if (errCode != E_OK) { + GLOGE("Read collection option failed. %d", errCode); + return {}; + } + + if (maxDocValue.GetValueType() != ValueObject::ValueType::VALUE_NUMBER) { + GLOGE("Check collection option failed, the field type of maxDoc is not NUMBER."); + errCode = -E_INVALID_CONFIG_VALUE; + return {}; + } + + if (maxDocValue.GetIntValue() <= 0 || maxDocValue.GetIntValue() > UINT32_MAX) { + GLOGE("Check collection option failed, invalid maxDoc value."); + errCode = -E_INVALID_CONFIG_VALUE; + return {}; + } + + CollectionOption option; + option.maxDoc_ = static_cast(maxDocValue.GetIntValue()); + option.option_ = optStr; + return option; +} + +uint32_t CollectionOption::GetMaxDoc() const +{ + return maxDoc_; +} + +std::string CollectionOption::ToString() const +{ + return option_; +} + +bool CollectionOption::operator==(const CollectionOption &targetOption) const +{ + return maxDoc_ == targetOption.maxDoc_; +} + +bool CollectionOption::operator!=(const CollectionOption &targetOption) const +{ + return !(*this == targetOption); +} +} \ No newline at end of file diff --git a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/common/src/db_config.cpp b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/common/src/db_config.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f824ddcf9cd1caddbcc07beac9e387d0cb507275 --- /dev/null +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/common/src/db_config.cpp @@ -0,0 +1,300 @@ +/* +* Copyright (c) 2023 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 "db_config.h" + +#include +#include + +#include "doc_errno.h" +#include "doc_limit.h" +#include "log_print.h" +#include "json_object.h" + +namespace DocumentDB { +namespace { +const int MIN_REDO_BUFFER_SIZE = 256; +const int MAX_REDO_BUFFER_SIZE = 16384; +const int MIN_CONNECTION_NUM = 16; +const int MAX_CONNECTION_NUM = 1024; +const int MIN_BUFFER_POOL_SIZE = 1024; +const int MAX_BUFFER_POOL_SIZE = 4 * 1024 * 1024; + +const std::string DB_CONFIG_PAGESIZE = "pagesize"; +const std::string DB_CONFIG_REDO_FLUSH_BY_TRX = "redoflushbytrx"; +const std::string DB_CONFIG_REDO_PUB_BUFF_SIZE = "redopubbufsize"; +const std::string DB_CONFIG_MAX_CONN_NUM = "maxconnnum"; +const std::string DB_CONFIG_BUFFER_POOL_SIZE = "bufferpoolsize"; +const std::string DB_CONFIG_CRC_CHECK_ENABLE = "crccheckenable"; + +const std::vector DB_CONFIG = { + DB_CONFIG_PAGESIZE, + DB_CONFIG_REDO_FLUSH_BY_TRX, + DB_CONFIG_REDO_PUB_BUFF_SIZE, + DB_CONFIG_MAX_CONN_NUM, + DB_CONFIG_BUFFER_POOL_SIZE, + DB_CONFIG_CRC_CHECK_ENABLE +}; + +bool CheckPageSizeConfig(const JsonObject &config, int32_t &pageSize, int &errCode) +{ + static const JsonFieldPath pageSizeField = {DB_CONFIG_PAGESIZE}; + if (!config.IsFieldExists(pageSizeField)) { + return true; + } + + ValueObject configValue = config.GetObjectByPath(pageSizeField, errCode); + if (configValue.GetValueType() != ValueObject::ValueType::VALUE_NUMBER) { + GLOGE("Check DB config failed, the field type of pageSize is not NUMBER."); + errCode = -E_INVALID_CONFIG_VALUE; + return false; + } + + static const std::vector pageSizeValid = {4, 8, 16, 32, 64}; + if (std::find(pageSizeValid.begin(), pageSizeValid.end(), configValue.GetIntValue()) == pageSizeValid.end()) { + GLOGE("Check DB config failed, invalid pageSize value."); + errCode = -E_INVALID_CONFIG_VALUE; + return false; + } + + pageSize = static_cast(configValue.GetIntValue()); + return true; +} + +bool CheckRedoFlushConfig(const JsonObject &config, uint32_t &redoFlush, int &errCode) +{ + static const JsonFieldPath redoFlushField = {DB_CONFIG_REDO_FLUSH_BY_TRX}; + if (!config.IsFieldExists(redoFlushField)) { + return true; + } + + ValueObject configValue = config.GetObjectByPath(redoFlushField, errCode); + if (configValue.GetValueType() != ValueObject::ValueType::VALUE_NUMBER) { + GLOGE("Check DB config failed, the field type of redoFlushByTrx is not NUMBER."); + errCode = -E_INVALID_CONFIG_VALUE; + return false; + } + + if (configValue.GetIntValue() != 0 && configValue.GetIntValue() != 1) { + GLOGE("Check DB config failed, invalid redoFlushByTrx value."); + errCode = -E_INVALID_CONFIG_VALUE; + return false; + } + + redoFlush = static_cast(configValue.GetIntValue()); + return true; +} + +bool CheckRedoBufSizeConfig(const JsonObject &config, uint32_t &redoBufSize, int &errCode) +{ + static const JsonFieldPath redoBufSizeField = {DB_CONFIG_REDO_PUB_BUFF_SIZE}; + if (!config.IsFieldExists(redoBufSizeField)) { + return true; + } + + ValueObject configValue = config.GetObjectByPath(redoBufSizeField, errCode); + if (configValue.GetValueType() != ValueObject::ValueType::VALUE_NUMBER) { + GLOGE("Check DB config failed, the field type of redoPubBufSize is not NUMBER."); + errCode = -E_INVALID_CONFIG_VALUE; + return false; + } + + if (configValue.GetIntValue() < MIN_REDO_BUFFER_SIZE && configValue.GetIntValue() > MAX_REDO_BUFFER_SIZE) { + GLOGE("Check DB config failed, invalid redoPubBufSize value."); + errCode = -E_INVALID_CONFIG_VALUE; + return false; + } + + redoBufSize = static_cast(configValue.GetIntValue()); + return true; +} + +bool CheckMaxConnNumConfig(const JsonObject &config, int32_t &maxConnNum, int &errCode) +{ + static const JsonFieldPath maxConnNumField = {DB_CONFIG_MAX_CONN_NUM}; + if (!config.IsFieldExists(maxConnNumField)) { + return true; + } + + ValueObject configValue = config.GetObjectByPath(maxConnNumField, errCode); + if (configValue.GetValueType() != ValueObject::ValueType::VALUE_NUMBER) { + GLOGE("Check DB config failed, the field type of maxConnNum is not NUMBER."); + errCode = -E_INVALID_CONFIG_VALUE; + return false; + } + + if (configValue.GetIntValue() < MIN_CONNECTION_NUM || configValue.GetIntValue() > MAX_CONNECTION_NUM) { + GLOGE("Check DB config failed, invalid maxConnNum value."); + errCode = -E_INVALID_CONFIG_VALUE; + return false; + } + + maxConnNum = static_cast(configValue.GetIntValue()); + return true; +} + +bool CheckBufferPoolSizeConfig(const JsonObject &config, int32_t pageSize, uint32_t &redoBufSize, + int &errCode) +{ + static const JsonFieldPath bufferPoolSizeField = {DB_CONFIG_BUFFER_POOL_SIZE}; + if (!config.IsFieldExists(bufferPoolSizeField)) { + return true; + } + + ValueObject configValue = config.GetObjectByPath(bufferPoolSizeField, errCode); + if (configValue.GetValueType() != ValueObject::ValueType::VALUE_NUMBER) { + GLOGE("Check DB config failed, the field type of bufferPoolSize is not NUMBER."); + errCode = -E_INVALID_CONFIG_VALUE; + return false; + } + + if (configValue.GetIntValue() < MIN_BUFFER_POOL_SIZE && configValue.GetIntValue() > MAX_BUFFER_POOL_SIZE || + configValue.GetIntValue() < pageSize * 33) { + GLOGE("Check DB config failed, invalid bufferPoolSize value."); + errCode = -E_INVALID_CONFIG_VALUE; + return false; + } + + redoBufSize = static_cast(configValue.GetIntValue()); + return true; +} + +bool CheckCrcCheckEnableConfig(const JsonObject &config, uint32_t &crcCheckEnable, int &errCode) +{ + static const JsonFieldPath crcCheckEnableField = {DB_CONFIG_CRC_CHECK_ENABLE}; + if (!config.IsFieldExists(crcCheckEnableField)) { + return true; + } + + ValueObject configValue = config.GetObjectByPath(crcCheckEnableField, errCode); + if (configValue.GetValueType() != ValueObject::ValueType::VALUE_NUMBER) { + GLOGE("Check DB config failed, the field type of crcCheckEnable is not NUMBER."); + errCode = -E_INVALID_CONFIG_VALUE; + return false; + } + + if (configValue.GetIntValue() != 0 && configValue.GetIntValue() != 1) { + GLOGE("Check DB config failed, invalid crcCheckEnable value."); + errCode = -E_INVALID_CONFIG_VALUE; + return false; + } + + crcCheckEnable = static_cast(configValue.GetIntValue()); + return true; +} + +bool CheckConfigSupport(const JsonObject &config, int &errCode) +{ + JsonObject child = config.GetChild(); + while (!child.IsNull()) { + std::string fieldName = child.GetItemFiled(); + if (std::find(DB_CONFIG.begin(), DB_CONFIG.end(), fieldName) == DB_CONFIG.end()) { + GLOGE("Invalid db config."); + errCode = -E_INVALID_CONFIG_VALUE; + return false; + } + child = child.GetNext(); + } + return true; +} +} + +DBConfig DBConfig::ReadConfig(const std::string &confStr, int &errCode) +{ + if (confStr.empty()) { + return {}; + } + + if (confStr.length() + 1 > MAX_DB_CONFIG_LEN) { + GLOGE("Config json string is too long."); + errCode = -E_OVER_LIMIT; + return {}; + } + + std::string lowerCaseConfStr = confStr; + std::transform(lowerCaseConfStr.begin(), lowerCaseConfStr.end(), lowerCaseConfStr.begin(), [](unsigned char c) { + return std::tolower(c); + }); + + JsonObject dbConfig = JsonObject::Parse(lowerCaseConfStr, errCode); + if (errCode != E_OK) { + GLOGE("Read DB config failed from str. %d", errCode); + return {}; + } + + if (!CheckConfigSupport(dbConfig, errCode)) { + GLOGE("Check DB config, not support config item. %d", errCode); + return {}; + } + + DBConfig conf; + if (!CheckPageSizeConfig(dbConfig, conf.pageSize_, errCode)) { + GLOGE("Check DB config 'pageSize' failed. %d", errCode); + return {}; + } + + if (!CheckRedoFlushConfig(dbConfig, conf.redoFlushByTrx_, errCode)) { + GLOGE("Check DB config 'redoFlushByTrx' failed. %d", errCode); + return {}; + } + + if (!CheckRedoBufSizeConfig(dbConfig, conf.redoPubBufSize_, errCode)) { + GLOGE("Check DB config 'redoPubBufSize' failed. %d", errCode); + return {}; + } + + if (!CheckMaxConnNumConfig(dbConfig, conf.maxConnNum_, errCode)) { + GLOGE("Check DB config 'maxConnNum' failed. %d", errCode); + return {}; + } + + if (!CheckBufferPoolSizeConfig(dbConfig, conf.pageSize_, conf.bufferPoolSize_, errCode)) { + GLOGE("Check DB config 'bufferPoolSize' failed. %d", errCode); + return {}; + } + + if (!CheckCrcCheckEnableConfig(dbConfig, conf.crcCheckEnable_, errCode)) { + GLOGE("Check DB config 'crcCheckEnable' failed. %d", errCode); + return {}; + } + + conf.configStr_ = confStr; + errCode = E_OK; + return conf; +} + +std::string DBConfig::ToString() const +{ + return configStr_; +} + +int32_t DBConfig::GetPageSize() const +{ + return pageSize_; +} + +bool DBConfig::operator==(const DBConfig &targetConfig) const +{ + return configStr_ == targetConfig.configStr_ && pageSize_ == targetConfig.pageSize_ && + redoFlushByTrx_ == targetConfig.redoFlushByTrx_ && redoPubBufSize_ == targetConfig.redoPubBufSize_ && + maxConnNum_ == targetConfig.maxConnNum_ && bufferPoolSize_ == targetConfig.bufferPoolSize_ && + crcCheckEnable_ == targetConfig.crcCheckEnable_; +} + +bool DBConfig::operator!=(const DBConfig &targetConfig) const +{ + return !(*this == targetConfig); +} +} // namespace DocumentDB \ No newline at end of file diff --git a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/common/src/doc_common.cpp b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/common/src/doc_common.cpp index ccee5654071fdf5d025f93752c6f8b062787beb6..ad8382adb69d9216065657242435ef0659d714c6 100644 --- a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/common/src/doc_common.cpp +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/common/src/doc_common.cpp @@ -12,6 +12,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include #include #include "doc_common.h" @@ -20,17 +21,47 @@ #include "securec.h" namespace DocumentDB { -bool CheckCommon::CheckCollectionName(const std::string &collectionName) +namespace { +constexpr const char *COLLECTION_PREFIX_GRD = "GRD_"; +constexpr const char *COLLECTION_PREFIX_GM_SYS = "GM_SYS_"; +const int MAX_COLLECTION_NAME = 512; + +bool CheckCollectionNamePrefix(const std::string &name, const std::string &prefix) +{ + if (name.length() < prefix.length()) { + return false; + } + + auto itPrefix = prefix.begin(); + auto itName = name.begin(); + while (itPrefix != prefix.end()) { + if (std::tolower(*itPrefix) != std::tolower(*itName)) { + return false; + } + itPrefix++; + itName++; + } + return true; +} +} + +bool CheckCommon::CheckCollectionName(const std::string &collectionName, std::string &lowerCaseName) { if (collectionName.empty()) { return false; } - if (collectionName.length() > 512) { + if (collectionName.length() + 1 > MAX_COLLECTION_NAME) { return false; } - if (collectionName.compare(0, 4, "GRD", 0, 4) == 0 || collectionName.compare(0, 7, "GM_SYS_", 0, 7) == 0) { + if (CheckCollectionNamePrefix(collectionName, COLLECTION_PREFIX_GRD) || + CheckCollectionNamePrefix(collectionName, COLLECTION_PREFIX_GM_SYS)) { return false; } + + lowerCaseName = collectionName; + std::transform(lowerCaseName.begin(), lowerCaseName.end(), lowerCaseName.begin(), [](unsigned char c){ + return std::tolower(c); + }); return true; } @@ -63,5 +94,4 @@ bool CheckCommon::CheckDocument(const std::string &document) { return true; } - } // namespace DocumentDB \ No newline at end of file diff --git a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/common/src/json_common.cpp b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/common/src/json_common.cpp index 18672eec62862520aa8363b867c021efde3bc349..46370a61ce659220fb023798de5b7996c129113a 100644 --- a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/common/src/json_common.cpp +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/common/src/json_common.cpp @@ -13,6 +13,7 @@ * limitations under the License. */ #include +#include #include "json_common.h" #include "doc_errno.h" @@ -145,7 +146,7 @@ int JsonCommon::ParseNode(JsonObject* node, std::vector singlePath, return 0; } -std::vector> JsonCommon::ParsePath(JsonObject* root) +std::vector> JsonCommon::ParsePath(const JsonObject* const root) { std::vector> resultPath; auto projectionJson = root->GetChild(); @@ -156,4 +157,97 @@ std::vector> JsonCommon::ParsePath(JsonObject* root) ParseNode(&projectionJson, singlePath, resultPath, true); return resultPath; } + +namespace { +JsonFieldPath ExpendPath(const JsonFieldPath &path, bool &isCollapse) +{ + if (path.size() > 1) { // only first lever has collapse field + return path; + } + JsonFieldPath splitPath; + const std::string &str = path[0]; + size_t start = 0; + size_t end = 0; + while ((end = str.find('.', start)) != std::string::npos) { + splitPath.push_back(str.substr(start, end - start)); + start = end + 1; + } + if (start < str.length()) { + splitPath.push_back(str.substr(start)); + } + isCollapse = (splitPath.size() > 1); + return splitPath; +} + +void JsonObjectIterator(const JsonObject &obj, JsonFieldPath path, + std::function foo) +{ + JsonObject child = obj.GetChild(); + while(!child.IsNull()) { + JsonFieldPath childPath = path; + childPath.push_back(child.GetItemFiled()); + if (foo != nullptr && foo(childPath, obj, child)) { + JsonObjectIterator(child, childPath, foo); + } + child = child.GetNext(); + } + return; +} +} + +int JsonCommon::Append(const JsonObject &src, const JsonObject &add) +{ + int externErrCode = E_OK; + JsonObjectIterator(add, {}, + [&src, &externErrCode](const JsonFieldPath &path, const JsonObject &father, const JsonObject &item) { + bool isCollapse = false; + JsonFieldPath itemPath = ExpendPath(path, isCollapse); + JsonFieldPath fatherPath = itemPath; + fatherPath.pop_back(); + int errCode = E_OK; + if (src.IsFieldExists(itemPath)) { + JsonObject srcItem = src.FindItem(itemPath, errCode); + if (errCode != E_OK) { + externErrCode = (externErrCode == E_OK ? errCode : externErrCode); + GLOGE("Find item in source json object failed. %d", errCode); + return false; + } + if (srcItem.GetType() == JsonObject::Type::JSON_LEAF && item.GetType() == JsonObject::Type::JSON_LEAF) { + srcItem.SetItemValue(item.GetItemValue()); + return false; // Both leaf node, no need iterate + } else if (srcItem.GetType() != item.GetType()) { + JsonObject srcFatherItem = src.FindItem(fatherPath, errCode); + if (errCode != E_OK) { + externErrCode = (externErrCode == E_OK ? errCode : externErrCode); + GLOGE("Find father item in source json object failed. %d", errCode); + return false; + } + srcFatherItem.DeleteItemFromObject(itemPath.back()); + srcFatherItem.AddItemToObject(itemPath.back(), item); + return false; // Different node types, overwrite directly, skip child node + } + return true; // Both array or object + } else { + if (isCollapse == true) { + GLOGE("Add collapse item to object failed, path not exist."); + externErrCode = -E_DATA_CONFLICT; + return false; + } + JsonObject srcFatherItem = src.FindItem(fatherPath, errCode); + if (errCode == E_OK) { + errCode = srcFatherItem.AddItemToObject(itemPath.back(), item); + if (errCode != E_OK) { + externErrCode = (externErrCode == E_OK ? errCode : externErrCode); + GLOGE("Add item to object failed. %d", errCode); + return false; + } + } else { + externErrCode = -E_DATA_CONFLICT; + GLOGE("Find father item in source json object failed. %d", errCode); + } + return false; // Source path not exist, overwrite directly, skip child node + } + }); + return externErrCode; +} } // namespace DocumentDB \ No newline at end of file diff --git a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/common/src/os_api.cpp b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/common/src/os_api.cpp index 13528043dcaebce25d9e9bc56d54ce0a81ce866e..31786a74dc86d85b77434dd791b98619996af373 100644 --- a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/common/src/os_api.cpp +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/common/src/os_api.cpp @@ -26,6 +26,11 @@ namespace { const int ACCESS_MODE_EXISTENCE = 0; } namespace OSAPI { +bool CheckPermission(const std::string &filePath) +{ + return (access(filePath.c_str(), R_OK) == 0) && (access(filePath.c_str(), W_OK) == 0); +} + bool CheckPathExistence(const std::string &filePath) { return (access(filePath.c_str(), ACCESS_MODE_EXISTENCE) == 0); diff --git a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/executor/base/grd_db_api.cpp b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/executor/base/grd_db_api.cpp index f4e3d65f3d7addedc13d0a9b4ae23586169668d2..e6c954e339ba961dc628a31631de76c587d56ab8 100644 --- a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/executor/base/grd_db_api.cpp +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/executor/base/grd_db_api.cpp @@ -20,29 +20,30 @@ #include "document_store.h" #include "grd_base/grd_error.h" #include "grd_type_inner.h" +#include "log_print.h" using namespace DocumentDB; -int TrasnferDocErr(int err) -{ - switch (err) { - case E_OK: - return GRD_OK; - case -E_ERROR: - return GRD_INNER_ERR; - case -E_INVALID_ARGS: - return GRD_INVALID_ARGS; - default: - return GRD_INNER_ERR; - } -} - int GRD_DBOpen(const char *dbPath, const char *configStr, unsigned int flags, GRD_DB **db) { + if (db == nullptr || (*db) != nullptr) { + return GRD_INVALID_ARGS; + } std::string path = (dbPath == nullptr ? "" : dbPath); + std::string config = (configStr == nullptr ? "" : configStr); DocumentStore *store = nullptr; - int ret = DocumentStoreManager::GetDocumentStore(path, store); + int ret = DocumentStoreManager::GetDocumentStore(path, config, flags, store); + if (ret != E_OK || store == nullptr) { + return TrasnferDocErr(ret); + } + *db = new (std::nothrow) GRD_DB(); + if (*db == nullptr) { + (void)DocumentStoreManager::CloseDocumentStore(store, GRD_DB_CLOSE_IGNORE_ERROR); + store = nullptr; + ret = -E_OUT_OF_MEMORY; + } + (*db)->store_ = store; return TrasnferDocErr(ret); } @@ -53,13 +54,12 @@ int GRD_DBClose(GRD_DB *db, unsigned int flags) return GRD_INVALID_ARGS; } - DocumentStoreManager::CloseType closeType = (flags == GRD_DB_CLOSE) ? DocumentStoreManager::CloseType::NORMAL : - DocumentStoreManager::CloseType::IGNORE_ERROR; - int status = DocumentStoreManager::CloseDocumentStore(db->store_, closeType); - if (status != E_OK) { - return GRD_RESOURCE_BUSY; + int ret = DocumentStoreManager::CloseDocumentStore(db->store_, flags); + if (ret != E_OK) { + return TrasnferDocErr(ret); } + db->store_ = nullptr; delete db; return GRD_OK; } diff --git a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/executor/document/grd_document_api.cpp b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/executor/document/grd_document_api.cpp index dc5ccf4653a0664803874c5a5e283e0d33cbd2ea..61f50633eb9f8452b0624bb6a4641330316f9288 100644 --- a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/executor/document/grd_document_api.cpp +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/executor/document/grd_document_api.cpp @@ -16,13 +16,19 @@ #include "grd_document/grd_document_api.h" #include "grd_base/grd_error.h" #include "grd_type_inner.h" +#include "log_print.h" +using namespace DocumentDB; int GRD_CreateCollection(GRD_DB *db, const char *collectionName, const char *optionStr, unsigned int flags) { if (db == nullptr || db->store_ == nullptr) { return GRD_INVALID_ARGS; } - return db->store_->CreateCollection(collectionName, optionStr, flags); + + std::string name = (collectionName == nullptr ? "" : collectionName); + std::string option = (optionStr == nullptr ? "" : optionStr); + int ret = db->store_->CreateCollection(name, option, flags); + return TrasnferDocErr(ret); } int GRD_DropCollection(GRD_DB *db, const char *collectionName, unsigned int flags) @@ -30,7 +36,10 @@ int GRD_DropCollection(GRD_DB *db, const char *collectionName, unsigned int flag if (db == nullptr || db->store_ == nullptr) { return GRD_INVALID_ARGS; } - return db->store_->DropCollection(collectionName, flags); + + std::string name = (collectionName == nullptr ? "" : collectionName); + int ret = db->store_->DropCollection(name, flags); + return TrasnferDocErr(ret); } int GRD_UpdateDoc(GRD_DB *db, const char *collectionName, const char *filter, const char *update, unsigned int flags) @@ -38,7 +47,12 @@ int GRD_UpdateDoc(GRD_DB *db, const char *collectionName, const char *filter, co if (db == nullptr || db->store_ == nullptr) { return GRD_INVALID_ARGS; } - return db->store_->UpdateDocument(collectionName, filter, update, flags); + + std::string name = (collectionName == nullptr ? "" : collectionName); + std::string filterStr = (filter == nullptr ? "" : filter); + std::string updateStr = (update == nullptr ? "" : update); + int ret = db->store_->UpdateDocument(name, filterStr, updateStr, flags); + return TrasnferDocErr(ret); } int GRD_UpSertDoc(GRD_DB *db, const char *collectionName, const char *filter, const char *document, unsigned int flags) @@ -46,5 +60,10 @@ int GRD_UpSertDoc(GRD_DB *db, const char *collectionName, const char *filter, co if (db == nullptr || db->store_ == nullptr) { return GRD_INVALID_ARGS; } - return db->store_->UpsertDocument(collectionName, filter, document, flags); + + std::string name = (collectionName == nullptr ? "" : collectionName); + std::string filterStr = (filter == nullptr ? "" : filter); + std::string documentStr = (document == nullptr ? "" : document); + int ret = db->store_->UpsertDocument(name, filterStr, documentStr, flags); + return TrasnferDocErr(ret); } diff --git a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/executor/include/grd_type_inner.h b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/executor/include/grd_type_inner.h index afc300ecac3a25c701fd76ad33ea7679ec9b8a54..6366370136761013c6cbeaa4dd65214df36cfcf6 100644 --- a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/executor/include/grd_type_inner.h +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/executor/include/grd_type_inner.h @@ -17,6 +17,8 @@ #define GRD_TYPE_INNER_H #include "document_store.h" +#include "doc_errno.h" +#include "grd_base/grd_error.h" typedef struct GRD_DB { DocumentDB::DocumentStore *store_ = nullptr; diff --git a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/interface/include/collection.h b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/interface/include/collection.h index 6dba77c9ec8635003e37faddb8106336e91bdab0..b71feaae45a66196a305e3cc6e8e11800b8f0c2d 100644 --- a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/interface/include/collection.h +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/interface/include/collection.h @@ -23,14 +23,14 @@ namespace DocumentDB { class Collection { public: - Collection(std::string name, KvStoreExecutor *executor); + Collection(const std::string &name, KvStoreExecutor *executor); ~Collection(); int PutDocument(const Key &key, const Value &document); int GetDocument(const Key &key, Value &document) const; int DeleteDocument(const Key &key); - int UpsertDocument(const Key &key, Value &document); + int UpsertDocument(const std::string &id, const std::string &document, bool isReplace = true); int UpdateDocument(const Key &key, Value &update); private: std::string name_; diff --git a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/interface/include/doc_errno.h b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/interface/include/doc_errno.h index ae226d971fc0cb87dec6d4e920ca9d4c86bde0af..b7448fe5353f5a844a4ce810e64de4b00381feb4 100644 --- a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/interface/include/doc_errno.h +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/interface/include/doc_errno.h @@ -16,7 +16,6 @@ #ifndef DOC_ERRNO_H #define DOC_ERRNO_H - namespace DocumentDB { constexpr int E_OK = 0; constexpr int E_BASE = 1000; @@ -32,7 +31,11 @@ constexpr int E_INVALID_CONFIG_VALUE = E_BASE + 13; constexpr int E_NOT_FOUND = E_BASE + 14; constexpr int E_COLLECTION_CONFLICT = E_BASE + 15; constexpr int E_NO_DATA = E_BASE + 16; +constexpr int E_NOT_PERMIT = E_BASE + 17; +constexpr int E_DATA_CONFLICT = E_BASE + 18; constexpr int E_INVALID_JSON_FORMAT = E_BASE + 40; constexpr int E_JSON_PATH_NOT_EXISTS = E_BASE + 41; + +int TrasnferDocErr(int err); } // DocumentDB #endif // DOC_ERRNO_H \ No newline at end of file diff --git a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/interface/include/document_store.h b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/interface/include/document_store.h index 93925e5529d254073cda0ba6cb04943bda29316c..b1fa3c39ae693e125d3fb8c846ba299d82f953b8 100644 --- a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/interface/include/document_store.h +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/interface/include/document_store.h @@ -18,6 +18,7 @@ #include #include +#include #include "collection.h" #include "kv_store_executor.h" @@ -28,13 +29,15 @@ public: DocumentStore(KvStoreExecutor *); ~DocumentStore(); - int CreateCollection(const std::string &name, const std::string &option, int flag); - int DropCollection(const std::string &name, int flag); + int CreateCollection(const std::string &name, const std::string &option, int flags); + int DropCollection(const std::string &name, int flags); int UpdateDocument(const std::string &collection, const std::string &filter, const std::string &update, int flag); - int UpsertDocument(const std::string &collection, const std::string &filter, const std::string &document, int flag); + int UpsertDocument(const std::string &collection, const std::string &filter, const std::string &document, int flags); private: + std::mutex dbMutex_; + KvStoreExecutor *executor_ = nullptr; std::map collections_; }; diff --git a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/interface/include/document_store_manager.h b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/interface/include/document_store_manager.h index acfb64aab060161111e64791d9b47fcabff302e3..7004316297c97f6d592863672b892e29d6c9a8f8 100644 --- a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/interface/include/document_store_manager.h +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/interface/include/document_store_manager.h @@ -22,17 +22,14 @@ namespace DocumentDB { class DocumentStoreManager { public: - static int GetDocumentStore(const std::string &path, DocumentStore *&store); + static int GetDocumentStore(const std::string &path, const std::string &config, unsigned int flags, + DocumentStore *&store); - enum class CloseType { - NORMAL, - IGNORE_ERROR, - }; - - static int CloseDocumentStore(DocumentStore *store, CloseType type); + static int CloseDocumentStore(DocumentStore *store, unsigned int flags); private: - static bool CheckDBPath(const std::string &path, std::string &canonicalPath); + static bool CheckDBPath(const std::string &path, std::string &canonicalPath, std::string &dbName, int &errCode); + static bool CheckDBConfig(const std::string &config, int &errCode); }; } // DocumentDB #endif // DOCUMENT_STORE_MANAGER_H \ No newline at end of file diff --git a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/interface/src/collection.cpp b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/interface/src/collection.cpp index 3e2562b110a1a89278264b86ad456d5b73691b1b..d04eb8022ac7fb77f4fb400d1fe0eca2251340d4 100644 --- a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/interface/src/collection.cpp +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/interface/src/collection.cpp @@ -14,11 +14,21 @@ */ #include "collection.h" + +#include + +#include "doc_common.h" #include "doc_errno.h" +#include "log_print.h" namespace DocumentDB { -Collection::Collection(std::string name, KvStoreExecutor *executor) : name_(name), executor_(executor) +Collection::Collection(const std::string &name, KvStoreExecutor *executor) : executor_(executor) { + std::string lowerCaseName = name; + std::transform(lowerCaseName.begin(), lowerCaseName.end(), lowerCaseName.begin(), [](unsigned char c) { + return std::tolower(c); + }); + name_ = COLL_PREFIX + lowerCaseName; } Collection::~Collection() @@ -44,12 +54,57 @@ int Collection::DeleteDocument(const Key &key) return E_OK; } -int Collection::UpsertDocument(const Key &key, Value &document) +int Collection::UpsertDocument(const std::string &id, const std::string &document, bool isReplace) { if (executor_ == nullptr) { return -E_INVALID_ARGS; } - return executor_->PutData(name_, key, document); + + int errCode = E_OK; + bool isCollExist = executor_->IsCollectionExists(name_, errCode); + if (errCode != E_OK) { + GLOGE("Check collection failed. %d", errCode); + return -errCode; + } + if (!isCollExist) { + GLOGE("Collection not created."); + return -E_NO_DATA; + } + + JsonObject upsertValue = JsonObject::Parse(document, errCode); + if (errCode != E_OK) { + GLOGD("Parse upsert value failed. %d", errCode); + return errCode; + } + + Key keyId(id.begin(), id.end()); + Value valSet(document.begin(), document.end()); + + if (!isReplace) { + Value valueGot; + errCode = executor_->GetData(name_, keyId, valueGot); + std::string valueGotStr = std::string(valueGot.begin(), valueGot.end()); + + if (errCode != E_OK && errCode != -E_NOT_FOUND) { + GLOGE("Get original document failed. %d", errCode); + return errCode; + } else if (errCode == E_OK) { // document has been inserted + GLOGD("Document has been inserted, append value."); + JsonObject originValue = JsonObject::Parse(valueGotStr, errCode); + if (errCode != E_OK) { + GLOGD("Parse original value failed. %d %s", errCode, valueGotStr.c_str()); + return errCode; + } + + errCode = JsonCommon::Append(originValue, upsertValue); + if (errCode != E_OK) { + GLOGD("Append value failed. %d", errCode); + return errCode; + } + } + } + + return executor_->PutData(name_, keyId, valSet); } int Collection::UpdateDocument(const Key &key, Value &update) diff --git a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/interface/src/doc_errno.cpp b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/interface/src/doc_errno.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a6913454e1f04b73aeaf88d7e133b783dd9532d5 --- /dev/null +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/interface/src/doc_errno.cpp @@ -0,0 +1,65 @@ +/* +* Copyright (c) 2023 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 "doc_errno.h" +#include "grd_base/grd_error.h" + +namespace DocumentDB { +int GetErrorCategory(int errCode) +{ + int categoryCode = errCode % 1000000; + categoryCode /= 1000; + categoryCode *= 1000; + return categoryCode; +} + +int TrasnferDocErr(int err) +{ + int outErr = GRD_OK; + switch (err) { + case E_OK: + return GRD_OK; + case -E_ERROR: + outErr = GRD_INNER_ERR; + break; + case -E_INVALID_ARGS: + outErr = GRD_INVALID_ARGS; + break; + case -E_FILE_OPERATION: + outErr = GRD_FAILED_FILE_OPERATION; + break; + case -E_OVER_LIMIT: + outErr = GRD_OVER_LIMIT; + break; + case -E_INVALID_JSON_FORMAT: + outErr = GRD_INVALID_JSON_FORMAT; + break; + case -E_INVALID_CONFIG_VALUE: + outErr = GRD_INVALID_CONFIG_VALUE; + break; + case -E_COLLECTION_CONFLICT: + outErr = GRD_COLLECTION_CONFLICT; + break; + case -E_NO_DATA: + outErr = GRD_NO_DATA; + break; + default: + outErr = GRD_INNER_ERR; + break; + } + + return GetErrorCategory(outErr); +} +} \ No newline at end of file diff --git a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/interface/src/document_store.cpp b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/interface/src/document_store.cpp index 6fd1f679c1bd16c0664b34416f7390a83bf44b70..1e3558c510f2f55e60e6307a4addc2adbc17c1ab 100644 --- a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/interface/src/document_store.cpp +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/interface/src/document_store.cpp @@ -14,7 +14,12 @@ */ #include "document_store.h" + +#include "collection_option.h" +#include "doc_common.h" #include "doc_errno.h" +#include "grd_base/grd_type_export.h" +#include "log_print.h" namespace DocumentDB { DocumentStore::DocumentStore(KvStoreExecutor *executor) : executor_(executor) @@ -26,16 +31,76 @@ DocumentStore::~DocumentStore() delete executor_; } -int DocumentStore::CreateCollection(const std::string &name, const std::string &option, int flag) +int DocumentStore::CreateCollection(const std::string &name, const std::string &option, int flags) { - executor_->CreateCollection(name, flag); - return E_OK; + std::string lowerCaseName; + if (!CheckCommon::CheckCollectionName(name, lowerCaseName)) { + GLOGE("Check collection name invalid."); + return -E_INVALID_ARGS; + } + + int errCode = E_OK; + CollectionOption collOption = CollectionOption::ReadOption(option, errCode); + if (errCode != E_OK) { + GLOGE("Read collection option str failed. %d", errCode); + return errCode; + } + + if (flags != 0 && flags != CHK_EXIST_COLLECTION) { + GLOGE("Check flags invalid."); + return -E_INVALID_ARGS; + } + + std::lock_guard lock(dbMutex_); + bool ignoreExists = (flags == CHK_EXIST_COLLECTION); + errCode = executor_->CreateCollection(lowerCaseName, ignoreExists); + if (errCode != E_OK) { + GLOGE("Create collection failed. %d", errCode); + return errCode; + } + std::string oriOptStr; + errCode = executor_->GetCollectionOption(lowerCaseName, oriOptStr); + if (errCode == -E_NOT_FOUND) { + executor_->SetCollectionOption(lowerCaseName, collOption.ToString()); + errCode = E_OK; + } else { + CollectionOption oriOption = CollectionOption::ReadOption(oriOptStr, errCode); + if (collOption != oriOption) { + GLOGE("Create collection failed, option changed."); + return -E_INVALID_CONFIG_VALUE; + } + } + + return errCode; } -int DocumentStore::DropCollection(const std::string &name, int flag) +int DocumentStore::DropCollection(const std::string &name, int flags) { - executor_->DropCollection(name, flag); - return E_OK; + std::string lowerCaseName; + if (!CheckCommon::CheckCollectionName(name, lowerCaseName)) { + GLOGE("Check collection name invalid."); + return -E_INVALID_ARGS; + } + + if (flags != 0 && flags != CHK_NON_EXIST_COLLECTION) { + GLOGE("Check flags invalid."); + return -E_INVALID_ARGS; + } + + bool ignoreNonExists = (flags == CHK_NON_EXIST_COLLECTION); + int errCode = executor_->DropCollection(lowerCaseName, ignoreNonExists); + if (errCode != E_OK) { + GLOGE("Drop collection failed. %d", errCode); + return errCode; + } + + std::lock_guard lock(dbMutex_); + errCode = executor_->CleanCollectionOption(lowerCaseName); + if (errCode != E_OK) { + GLOGE("Clean collection option failed. %d", errCode); + } + + return errCode; } int DocumentStore::UpdateDocument(const std::string &collection, const std::string &filter, const std::string &update, @@ -45,13 +110,29 @@ int DocumentStore::UpdateDocument(const std::string &collection, const std::stri } int DocumentStore::UpsertDocument(const std::string &collection, const std::string &filter, const std::string &document, - int flag) + int flags) { - auto coll = Collection(collection, executor_); + std::string lowerCaseCollName; + if (!CheckCommon::CheckCollectionName(collection, lowerCaseCollName)) { + GLOGE("Check collection name invalid."); + return -E_INVALID_ARGS; + } + + // TODO:: check filter + + // TODO:: check document + + if (flags != GRD_DOC_APPEND && flags != GRD_DOC_REPLACE) { + GLOGE("Check flags invalid."); + return -E_INVALID_ARGS; + } + + auto coll = Collection(lowerCaseCollName, executor_); - Key key(filter.begin(), filter.end()); - Value value(document.begin(), document.end()); + std::string docId(filter.begin(), filter.end()); + bool isReplace = (flags & GRD_DOC_REPLACE == GRD_DOC_REPLACE); - return coll.PutDocument(key, value); + std::lock_guard lock(dbMutex_); + return coll.UpsertDocument(docId, document, isReplace); } } // namespace DocumentDB \ No newline at end of file diff --git a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/interface/src/document_store_manager.cpp b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/interface/src/document_store_manager.cpp index f811f8bec1b658a4ada028ea6c03920156d3b238..2be63060a095847a8725c6abefdff15f8c70c11e 100644 --- a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/interface/src/document_store_manager.cpp +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/interface/src/document_store_manager.cpp @@ -13,6 +13,7 @@ * limitations under the License. */ +#include "db_config.h" #include "document_store_manager.h" #include "doc_errno.h" #include "grd_base/grd_type_export.h" @@ -21,41 +22,104 @@ #include "os_api.h" namespace DocumentDB { -int DocumentStoreManager::GetDocumentStore(const std::string &path, DocumentStore *&store) +namespace { +bool CheckDBOpenFlag(unsigned int flag) { + unsigned int mask = ~(GRD_DB_OPEN_CREATE | GRD_DB_OPEN_CHECK_FOR_ABNORMAL | GRD_DB_OPEN_CHECK); + unsigned int invalidOpt = (GRD_DB_OPEN_CHECK_FOR_ABNORMAL | GRD_DB_OPEN_CHECK); + return ((flag & mask) == 0x00) && ((flag & invalidOpt) != invalidOpt); +} + +bool CheckDBCloseFlag(unsigned int flag) +{ + return (flag == GRD_DB_CLOSE) || (flag == GRD_DB_CLOSE_IGNORE_ERROR); +} +} + +int DocumentStoreManager::GetDocumentStore(const std::string &path, const std::string &config, unsigned int flags, + DocumentStore *&store) +{ + std::string canonicalPath; + std::string dbName; + int errCode = E_OK; + if (!CheckDBPath(path, canonicalPath, dbName, errCode)) { + GLOGE("Check document db file path failed."); + return errCode; + } + + DBConfig dbConfig = DBConfig::ReadConfig(config, errCode); + if (errCode != E_OK) { + GLOGE("Read db config str failed. %d", errCode); + return errCode; + } + + if (!CheckDBOpenFlag(flags)) { + GLOGE("Check document db open flags failed."); + return -E_INVALID_ARGS; + } + KvStoreExecutor *executor = nullptr; - KvStoreManager::GetKvStore(path, executor); + errCode = KvStoreManager::GetKvStore(canonicalPath + "/" + dbName, dbConfig, executor); + if (errCode != E_OK) { + GLOGE("Open document store failed. %d", errCode); + return errCode; + } + store = new (std::nothrow) DocumentStore(executor); - return E_OK; + if (store == nullptr) { + return -E_OUT_OF_MEMORY; + } + + return errCode; } -int DocumentStoreManager::CloseDocumentStore(DocumentStore *store, CloseType type) +int DocumentStoreManager::CloseDocumentStore(DocumentStore *store, unsigned int flags) { - if (type == CloseType::NORMAL) { - // TODO: check result set + if (!CheckDBCloseFlag(flags)) { + GLOGE("Check document db close flags failed."); + return -E_INVALID_ARGS; } delete store; return E_OK; } -bool DocumentStoreManager::CheckDBPath(const std::string &path, std::string &canonicalPath) +bool DocumentStoreManager::CheckDBPath(const std::string &path, std::string &canonicalPath, std::string &dbName, + int &errCode) { if (path.empty()) { - GLOGE("invalid path empty"); - return -E_INVALID_ARGS; + GLOGE("Invalid path empty"); + errCode = -E_INVALID_ARGS; + return false; } if (path.back() == '/') { - GLOGE("invalid path end with slash"); - return -E_INVALID_ARGS; + GLOGE("Invalid path end with slash"); + errCode = -E_INVALID_ARGS; + return false; } - std::string canonicalDir; - int errCode = OSAPI::GetRealPath(path, canonicalDir); - if (errCode == E_OK) { + std::string dirPath; + OSAPI::SplitFilePath(path, dirPath, dbName); + + int innerErrCode = OSAPI::GetRealPath(dirPath, canonicalPath); + if (innerErrCode != E_OK) { GLOGE("Get real path failed. %d", errCode); + errCode = -E_FILE_OPERATION; + return false; } - return errCode; + + if (!OSAPI::CheckPermission(canonicalPath)) { + GLOGE("Check path permission failed. %d", errCode); + errCode = -E_FILE_OPERATION; + return false; + } + + return true; +} + +bool DocumentStoreManager::CheckDBConfig(const std::string &config, int &errCode) +{ + return true; } } // DocumentDB \ No newline at end of file diff --git a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/oh_adapter/include/json_object.h b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/oh_adapter/include/json_object.h index 6165b79b97eeafb18183c54a20d371d51944dba3..0766ed3061aca38463b49dfbf881308b41c9f465 100644 --- a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/oh_adapter/include/json_object.h +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/oh_adapter/include/json_object.h @@ -16,6 +16,7 @@ #ifndef JSON_OBJECT_H #define JSON_OBJECT_H +#include #include #include #include @@ -26,7 +27,6 @@ namespace DocumentDB { class ValueObject { public: enum class ValueType { - // VALUE_INVALID = -1, VALUE_NULL = 0, VALUE_BOOL, VALUE_NUMBER, @@ -63,35 +63,44 @@ public: ~JsonObject (); - int Init(const std::string &str); - - std::string Print(); + std::string Print() const; JsonObject GetObjectItem(const std::string &field, int &errCode); JsonObject GetArrayItem(int index, int &errCode); - JsonObject GetNext(); - JsonObject GetChild(); + JsonObject GetNext() const; + JsonObject GetChild() const; int DeleteItemFromObject(const std::string &field); + int AddItemToObject(const JsonObject &item); + int AddItemToObject(const std::string &fieldName, const JsonObject &item); ValueObject GetItemValue() const; - std::string GetItemFiled() const; + void SetItemValue(const ValueObject &value) const; + std::string GetItemFiled() const; bool IsFieldExists(const JsonFieldPath &jsonPath) const; JsonObject FindItem(const JsonFieldPath &jsonPath, int &errCode) const; ValueObject GetObjectByPath(const JsonFieldPath &jsonPath, int &errCode) const; int DeleteItemOnTarget(const JsonFieldPath &path); - bool IsNull(); + bool IsNull() const; + + enum class Type { + JSON_LEAF, + JSON_OBJECT, + JSON_ARRAY + }; + Type GetType() const; private: JsonObject(); + int Init(const std::string &str); int GetDeep(cJSON *cjson); - cJSON *cjson_; + cJSON *cjson_ = nullptr; bool isOwner_ = false; bool caseSensitive_ = false; }; diff --git a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/oh_adapter/include/kv_store_executor.h b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/oh_adapter/include/kv_store_executor.h index f1d21444e73602b0430e281412147037424a13c1..8037fc6bcbf2644e43dccb298e7c774ec9cd1a0b 100644 --- a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/oh_adapter/include/kv_store_executor.h +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/oh_adapter/include/kv_store_executor.h @@ -25,11 +25,16 @@ public: virtual ~KvStoreExecutor() = default; virtual int PutData(const std::string &collName, const Key &key, const Value &value) = 0; - virtual int GetData(const std::string &collName, const Key &key, Value &value) const = 0; + virtual int DelData(const std::string &collName, const Key &key) = 0; + + virtual int CreateCollection(const std::string &name, bool ignoreExists) = 0; + virtual int DropCollection(const std::string &name, bool ignoreNonExists) = 0; + virtual bool IsCollectionExists(const std::string &name, int &errCode) = 0; - virtual int CreateCollection(const std::string &name, int flag) = 0; - virtual int DropCollection(const std::string &name, int flag) = 0; + virtual int GetCollectionOption(const std::string &name, std::string &option) = 0; + virtual int SetCollectionOption(const std::string &name, const std::string &option) = 0; + virtual int CleanCollectionOption(const std::string &name) = 0; }; } // DocumentDB #endif // KV_STORE_EXECUTOR_H \ No newline at end of file diff --git a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/oh_adapter/include/kv_store_manager.h b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/oh_adapter/include/kv_store_manager.h index 6c82e98367bd306349b2e4622d68e72d636475bb..97b79f649350a92b31bdc5b4e115e4a02d2f398c 100644 --- a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/oh_adapter/include/kv_store_manager.h +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/oh_adapter/include/kv_store_manager.h @@ -17,13 +17,14 @@ #define KV_STORE_MANAGER_H #include +#include "db_config.h" #include "doc_common.h" #include "kv_store_executor.h" namespace DocumentDB { class KvStoreManager { public: - static int GetKvStore(const std::string &path, KvStoreExecutor *&executor); + static int GetKvStore(const std::string &path, const DBConfig &config, KvStoreExecutor *&executor); }; } // DocumentDB #endif // KV_STORE_MANAGER_H \ No newline at end of file diff --git a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/oh_adapter/src/json_object.cpp b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/oh_adapter/src/json_object.cpp index f8b2aa44047fc85542ad97193a2c0e6fc6e903e6..c9def30239a05dae9a2b704dbc11180ab04637ba 100644 --- a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/oh_adapter/src/json_object.cpp +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/oh_adapter/src/json_object.cpp @@ -25,6 +25,13 @@ namespace { #define COLLECTION_LENS_MAX (512) #define JSON_LENS_MAX (512) #define JSON_DEEP_MAX (4) + +bool IsNumber(const std::string &str) +{ + return std::all_of(str.begin(), str.end(), [](char c) { + return std::isdigit(c); + }); +} } ValueObject::ValueObject(bool val) @@ -95,7 +102,7 @@ JsonObject::~JsonObject() } } -bool JsonObject::IsNull() +bool JsonObject::IsNull() const { if (cjson_ == nullptr) { return true; @@ -103,6 +110,16 @@ bool JsonObject::IsNull() return false; } +JsonObject::Type JsonObject::GetType() const +{ + if (cjson_->type == cJSON_Object) { + return JsonObject::Type::JSON_OBJECT; + } else if (cjson_->type == cJSON_Array) { + return JsonObject::Type::JSON_ARRAY; + } + return JsonObject::Type::JSON_LEAF; +} + int JsonObject::GetDeep(cJSON *cjson) { if (cjson->child == nullptr) { @@ -127,7 +144,7 @@ int JsonObject::Init(const std::string &str) isOwner_ = true; cjson_ = cJSON_ParseWithOpts(str.c_str(), &end, true); if (cjson_ == nullptr) { - return -E_INVALID_ARGS; + return -E_INVALID_JSON_FORMAT; } if (cjson_->type != cJSON_Object) { @@ -140,14 +157,14 @@ int JsonObject::Init(const std::string &str) return E_OK; } -std::string JsonObject::Print() +std::string JsonObject::Print() const { if (cjson_ == nullptr) { return ""; } char *ret = cJSON_PrintUnformatted(cjson_); std::string str = ret; - free(ret); + cJSON_free(ret); return str; } @@ -187,7 +204,7 @@ JsonObject JsonObject::GetArrayItem(int index, int &errCode) return item; } -JsonObject JsonObject::GetNext() +JsonObject JsonObject::GetNext() const { if (cjson_ == nullptr) { return JsonObject(); @@ -201,7 +218,7 @@ JsonObject JsonObject::GetNext() return next; } -JsonObject JsonObject::GetChild() +JsonObject JsonObject::GetChild() const { if (cjson_ == nullptr) { return JsonObject(); @@ -224,6 +241,42 @@ int JsonObject::DeleteItemFromObject(const std::string &field) return E_OK; } +int JsonObject::AddItemToObject(const JsonObject &item) +{ + if (item.IsNull()) { + GLOGD("Add null object."); + return E_OK; + } + + cJSON *cpoyItem = cJSON_Duplicate(item.cjson_, true); + cJSON_AddItemToObject(cjson_, item.GetItemFiled().c_str(), cpoyItem); + return E_OK; +} + +int JsonObject::AddItemToObject(const std::string &fieldName, const JsonObject &item) +{ + if (item.IsNull()) { + GLOGD("Add null object."); + return E_OK; + } + // TODO: check item exist + if (cjson_->type == cJSON_Array) { + int n = 0; + cJSON *child = cjson_->child; + while (child != nullptr) { + child = child->next; + n++; + } + if (IsNumber(fieldName) && n <= std::stoi(fieldName)) { + GLOGE("Add item object to array over size."); + return -E_DATA_CONFLICT; + } + } + cJSON *cpoyItem = cJSON_Duplicate(item.cjson_, true); + cJSON_AddItemToObject(cjson_, fieldName.c_str(), cpoyItem); + return E_OK; +} + ValueObject JsonObject::GetItemValue() const { if (cjson_ == nullptr) { @@ -251,22 +304,45 @@ ValueObject JsonObject::GetItemValue() const return value; } +void JsonObject::SetItemValue(const ValueObject &value) const +{ + if (cjson_ == nullptr) { + return; + } + switch(value.GetValueType()) { + case ValueObject::ValueType::VALUE_NUMBER: + cJSON_SetNumberValue(cjson_, value.GetDoubleValue()); + break; + case ValueObject::ValueType::VALUE_STRING: + cJSON_SetValuestring(cjson_, value.GetStringValue().c_str()); + break; + default: + break; + } +} + std::string JsonObject::GetItemFiled() const { if (cjson_ == nullptr) { return ""; } + if (cjson_->string == nullptr) { - return ""; - } - return cjson_->string; -} + cJSON *tail = cjson_; + while(tail->next != nullptr) { + tail = tail->next; + } -namespace { -bool IsNumber(const std::string &str) { - return std::all_of(str.begin(), str.end(), [](char c) { - return std::isdigit(c); - }); + int index = 0; + cJSON *head = cjson_; + while (head->prev != tail) { + head = head->prev; + index++; + } + return std::to_string(index); + } else { + return cjson_->string; + } } cJSON *GetChild(cJSON *cjson, const std::string &field, bool caseSens) @@ -294,29 +370,33 @@ cJSON *MoveToPath(cJSON *cjson, const JsonFieldPath &jsonPath, bool caseSens) for (const auto &field : jsonPath) { cjson = GetChild(cjson, field, caseSens); if (cjson == nullptr) { - GLOGW("Invalid json field path, no such field."); + break; } } return cjson; } -} - bool JsonObject::IsFieldExists(const JsonFieldPath &jsonPath) const { - return (MoveToPath(cjson_, jsonPath, caseSensitive_) == nullptr); + return (MoveToPath(cjson_, jsonPath, caseSensitive_) != nullptr); } JsonObject JsonObject::FindItem(const JsonFieldPath &jsonPath, int &errCode) const { if (jsonPath.empty()) { - return JsonObject(); + JsonObject curr = JsonObject(); + curr.cjson_ = cjson_; + curr.caseSensitive_ = caseSensitive_; + curr.isOwner_ = false; + GLOGW("Path empty, return current object"); + return curr; } cJSON *findItem = MoveToPath(cjson_, jsonPath, caseSensitive_); if (findItem == nullptr) { GLOGE("Find item failed. json field path not found."); errCode = -E_JSON_PATH_NOT_EXISTS; + return {}; } JsonObject item; @@ -368,4 +448,4 @@ int JsonObject::DeleteItemOnTarget(const JsonFieldPath &path) return E_OK; } -} \ No newline at end of file +} // namespace DocumentDB diff --git a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/oh_adapter/src/kv_store_manager.cpp b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/oh_adapter/src/kv_store_manager.cpp index ca9b278866df660c5ce77d2b5f522e8206a73e7b..fedc5197a8c0a0160442e89c72dacb429d937812 100644 --- a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/oh_adapter/src/kv_store_manager.cpp +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/oh_adapter/src/kv_store_manager.cpp @@ -15,6 +15,7 @@ #include "doc_errno.h" #include "kv_store_manager.h" +#include "log_print.h" #include "sqlite_store_executor_impl.h" #include "sqlite_utils.h" @@ -23,15 +24,50 @@ constexpr const char *APP_ID = "APP_ID"; constexpr const char *USER_ID = "USER_ID"; constexpr const char *STORE_ID = "STORE_ID"; -int KvStoreManager::GetKvStore(const std::string &path, KvStoreExecutor *&executor) +int KvStoreManager::GetKvStore(const std::string &path, const DBConfig &config, KvStoreExecutor *&executor) { + if (executor != nullptr) { + return -E_INVALID_ARGS; + } + sqlite3 *db = nullptr; - int errCode = SQLiteUtils::CreateDataBase(path, 0, db); - if (errCode != E_OK || db == nullptr) { - return -E_ERROR; + int errCode = SqliteStoreExecutor::CreateDatabase(path, config, db); + if (errCode != E_OK) { + GLOGE("Get kv store failed. %d", errCode); + return errCode; + } + + auto *sqliteExecutor = new (std::nothrow) SqliteStoreExecutor(db); + if (sqliteExecutor == nullptr) { + sqlite3_close_v2(db); + return -E_OUT_OF_MEMORY; } - executor = new (std::nothrow) SqliteStoreExecutor(db); + std::string oriConfigStr; + errCode = sqliteExecutor->GetDBConfig(oriConfigStr); + if (errCode == -E_NOT_FOUND) { + errCode = sqliteExecutor->SetDBConfig(config.ToString()); + } else if (errCode != E_OK) { + goto END; + } else { + DBConfig oriDbConfig = DBConfig::ReadConfig(oriConfigStr, errCode); + if (errCode != E_OK) { + GLOGE("Read db config failed. %d", errCode); + goto END; + } + if (config != oriDbConfig) { + errCode = -E_INVALID_CONFIG_VALUE; + GLOGE("Get kv store failed, db config changed. %d", errCode); + goto END; + } + } + + executor = sqliteExecutor; return E_OK; + +END: + delete sqliteExecutor; + sqliteExecutor = nullptr; + return errCode; } } \ No newline at end of file diff --git a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/oh_adapter/src/sqlite_store_executor_impl.cpp b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/oh_adapter/src/sqlite_store_executor_impl.cpp index 9f091ca1b010f452d3e0b1ac622b44943c8d41f0..98dfd182e740f21257426bf81345c1831efdebc2 100644 --- a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/oh_adapter/src/sqlite_store_executor_impl.cpp +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/oh_adapter/src/sqlite_store_executor_impl.cpp @@ -12,12 +12,47 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +#include "doc_common.h" #include "doc_errno.h" #include "log_print.h" #include "sqlite_utils.h" #include "sqlite_store_executor_impl.h" namespace DocumentDB { +int SqliteStoreExecutor::CreateDatabase(const std::string &path, const DBConfig &config, sqlite3 *&db) +{ + if (db != nullptr) { + return -E_INVALID_ARGS; + } + + int errCode = SQLiteUtils::CreateDataBase(path, 0, db); + if (errCode != E_OK || db == nullptr) { + GLOGE("Open or create database failed. %d", errCode); + return errCode; + } + + std::string pageSizeSql = "PRAGMA page_size=" + std::to_string(config.GetPageSize() * 1024); + errCode = SQLiteUtils::ExecSql(db, pageSizeSql); + if (errCode != E_OK) { + GLOGE("Set db page size failed. %d", errCode); + goto END; + } + + errCode = SQLiteUtils::ExecSql(db, "CREATE TABLE IF NOT EXISTS grd_meta (key BLOB PRIMARY KEY, value BLOB);"); + if (errCode != E_OK) { + GLOGE("Create meta table failed. %d", errCode); + goto END; + } + + return E_OK; + +END: + sqlite3_close_v2(db); + db = nullptr; + return errCode; +} + SqliteStoreExecutor::SqliteStoreExecutor(sqlite3 *handle) : dbHandle_(handle) { } @@ -28,6 +63,24 @@ SqliteStoreExecutor::~SqliteStoreExecutor() dbHandle_ = nullptr; } +int SqliteStoreExecutor::GetDBConfig(std::string &config) +{ + std::string dbConfigKeyStr = "DB_CONFIG"; + Key dbConfigKey = {dbConfigKeyStr.begin(), dbConfigKeyStr.end()}; + Value dbConfigVal; + int errCode = GetData("grd_meta", dbConfigKey, dbConfigVal); + config.assign(dbConfigVal.begin(), dbConfigVal.end()); + return errCode; +} + +int SqliteStoreExecutor::SetDBConfig(const std::string &config) +{ + std::string dbConfigKeyStr = "DB_CONFIG"; + Key dbConfigKey = {dbConfigKeyStr.begin(), dbConfigKeyStr.end()}; + Value dbConfigVal = {config.begin(), config.end()}; + return PutData("grd_meta", dbConfigKey, dbConfigVal); +} + int SqliteStoreExecutor::PutData(const std::string &collName, const Key &key, const Value &value) { if (dbHandle_ == nullptr) { @@ -41,7 +94,7 @@ int SqliteStoreExecutor::PutData(const std::string &collName, const Key &key, co return E_OK; }, nullptr); if (errCode != SQLITE_OK) { - GLOGE("[sqlite executor] create collectoin failed. err=%d", errCode); + GLOGE("[sqlite executor] Put data failed. err=%d", errCode); return errCode; } return E_OK; @@ -50,51 +103,142 @@ int SqliteStoreExecutor::PutData(const std::string &collName, const Key &key, co int SqliteStoreExecutor::GetData(const std::string &collName, const Key &key, Value &value) const { if (dbHandle_ == nullptr) { + GLOGE("Invalid db handle."); return -E_ERROR; } - + int innerErrorCode = -E_NOT_FOUND; std::string sql = "SELECT value FROM '" + collName + "' WHERE key=?;"; int errCode = SQLiteUtils::ExecSql(dbHandle_, sql, [key](sqlite3_stmt *stmt) { SQLiteUtils::BindBlobToStatement(stmt, 1, key); return E_OK; - }, [&value](sqlite3_stmt *stmt) { + }, [&value, &innerErrorCode](sqlite3_stmt *stmt) { SQLiteUtils::GetColumnBlobValue(stmt, 0, value); + innerErrorCode = E_OK; return E_OK; }); if (errCode != SQLITE_OK) { - GLOGE("[sqlite executor] create collectoin failed. err=%d", errCode); + GLOGE("[sqlite executor] Get data failed. err=%d", errCode); return errCode; } - return E_OK; + return innerErrorCode; } -int SqliteStoreExecutor::CreateCollection(const std::string &name, int flag) +int SqliteStoreExecutor::DelData(const std::string &collName, const Key &key) { if (dbHandle_ == nullptr) { + GLOGE("Invalid db handle."); return -E_ERROR; } - std::string sql = "CREATE TABLE IF NOT EXISTS '" + name + "' (key BLOB PRIMARY KEY, value BLOB);"; + std::string sql = "DELETE FROM '" + collName + "' WHERE key=?;"; + int errCode = SQLiteUtils::ExecSql(dbHandle_, sql, [key](sqlite3_stmt *stmt) { + SQLiteUtils::BindBlobToStatement(stmt, 1, key); + return E_OK; + }, nullptr); + + if (errCode != SQLITE_OK) { + GLOGE("[sqlite executor] Delete data failed. err=%d", errCode); + } + return errCode; +} + +int SqliteStoreExecutor::CreateCollection(const std::string &name, bool ignoreExists) +{ + if (dbHandle_ == nullptr) { + return -E_ERROR; + } + std::string collName = COLL_PREFIX + name; + if (!ignoreExists) { + int errCode = E_OK; + bool isExists = IsCollectionExists(collName, errCode); + if (errCode != E_OK) { + return errCode; + } + if (isExists) { + GLOGE("[sqlite executor] Create collectoin failed, collection already exists."); + return -E_COLLECTION_CONFLICT; + } + } + + std::string sql = "CREATE TABLE IF NOT EXISTS '" + collName + "' (key BLOB PRIMARY KEY, value BLOB);"; int errCode = SQLiteUtils::ExecSql(dbHandle_, sql); if (errCode != SQLITE_OK) { - GLOGE("[sqlite executor] create collectoin failed. err=%d", errCode); + GLOGE("[sqlite executor] Create collectoin failed. err=%d", errCode); return errCode; } return E_OK; } -int SqliteStoreExecutor::DropCollection(const std::string &name, int flag) +int SqliteStoreExecutor::DropCollection(const std::string &name, bool ignoreNonExists) { if (dbHandle_ == nullptr) { return -E_ERROR; } - std::string sql = "DROP TABLE IF EXISTS '" + name + "';"; + std::string collName = COLL_PREFIX + name; + if (!ignoreNonExists) { + int errCode = E_OK; + bool isExists = IsCollectionExists(collName, errCode); + if (errCode != E_OK) { + return errCode; + } + if (!isExists) { + GLOGE("[sqlite executor] Drop collectoin failed, collection not exists."); + return -E_NO_DATA; + } + } + + std::string sql = "DROP TABLE IF EXISTS '" + collName + "';"; int errCode = SQLiteUtils::ExecSql(dbHandle_, sql); if (errCode != SQLITE_OK) { - GLOGE("[sqlite executor] drop collectoin failed. err=%d", errCode); + GLOGE("[sqlite executor] Drop collectoin failed. err=%d", errCode); return errCode; } return E_OK; } + +bool SqliteStoreExecutor::IsCollectionExists(const std::string &name, int &errCode) +{ + bool isExists = false; + std::string sql = "SELECT tbl_name FROM sqlite_master WHERE tbl_name=?;"; + + errCode = SQLiteUtils::ExecSql(dbHandle_, sql, [name](sqlite3_stmt *stmt) { + SQLiteUtils::BindTextToStatement(stmt, 1, name); + return E_OK; + }, [&isExists](sqlite3_stmt *stmt) { + isExists = true; + return E_OK; + }); + + if (errCode != E_OK) { + GLOGE("Check collection exist failed. %d", errCode); + } + + return isExists; +} + +int SqliteStoreExecutor::GetCollectionOption(const std::string &name, std::string &option) +{ + std::string collOptKeyStr = "COLLECTION_OPTION_" + name; + Key collOptKey = {collOptKeyStr.begin(), collOptKeyStr.end()}; + Value collOptVal; + int errCode = GetData("grd_meta", collOptKey, collOptVal); + option.assign(collOptVal.begin(), collOptVal.end()); + return errCode; +} + +int SqliteStoreExecutor::SetCollectionOption(const std::string &name, const std::string &option) +{ + std::string collOptKeyStr = "COLLECTION_OPTION_" + name; + Key collOptKey = {collOptKeyStr.begin(), collOptKeyStr.end()}; + Value collOptVal = {option.begin(), option.end()}; + return PutData("grd_meta", collOptKey, collOptVal); +} + +int SqliteStoreExecutor::CleanCollectionOption(const std::string &name) +{ + std::string collOptKeyStr = "COLLECTION_OPTION_" + name; + Key collOptKey = {collOptKeyStr.begin(), collOptKeyStr.end()}; + return DelData("grd_meta", collOptKey); +} } // DocumentDB \ No newline at end of file diff --git a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/oh_adapter/src/sqlite_store_executor_impl.h b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/oh_adapter/src/sqlite_store_executor_impl.h index ab15f02d3a5afdc05bcb9147f8dedda27a5bb9ab..bc6857b83a5263998a700cffb28a55b6b111e672 100644 --- a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/oh_adapter/src/sqlite_store_executor_impl.h +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/oh_adapter/src/sqlite_store_executor_impl.h @@ -16,20 +16,32 @@ #ifndef SQLITE_STORE_EXECUTOR_IMPL_H #define SQLITE_STORE_EXECUTOR_IMPL_H +#include "db_config.h" #include "kv_store_executor.h" #include "sqlite3.h" namespace DocumentDB { class SqliteStoreExecutor : public KvStoreExecutor { public: + static int CreateDatabase(const std::string &path, const DBConfig &config, sqlite3 *&db); + SqliteStoreExecutor(sqlite3 *handle); ~SqliteStoreExecutor() override; + int GetDBConfig(std::string &config); + int SetDBConfig(const std::string &config); + int PutData(const std::string &collName, const Key &key, const Value &value) override; int GetData(const std::string &collName, const Key &key, Value &value) const override; + int DelData(const std::string &collName, const Key &key) override; + + int CreateCollection(const std::string &name, bool ignoreExists) override; + int DropCollection(const std::string &name, bool ignoreNonExists) override; + bool IsCollectionExists(const std::string &name, int &errCode) override; - int CreateCollection(const std::string &name, int flag) override; - int DropCollection(const std::string &name, int flag) override; + int GetCollectionOption(const std::string &name, std::string &option) override; + int SetCollectionOption(const std::string &name, const std::string &option) override; + int CleanCollectionOption(const std::string &name) override; private: sqlite3 *dbHandle_ = nullptr; diff --git a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/oh_adapter/src/sqlite_utils.cpp b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/oh_adapter/src/sqlite_utils.cpp index 6a1082c539b7ad969138ea111d4288eea96dc07b..30c0d095134e5bf0687fc5ac7ea20c4b6cd7a27e 100644 --- a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/oh_adapter/src/sqlite_utils.cpp +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/oh_adapter/src/sqlite_utils.cpp @@ -19,11 +19,28 @@ namespace DocumentDB { const int MAX_BLOB_READ_SIZE = 5 * 1024 * 1024; // 5M limit TODO:: check blob size +const int MAX_TEXT_READ_SIZE = 5 * 1024 * 1024; // 5M limit 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"; +namespace { +int MapSqliteError(int errCode) +{ + switch (errCode) { + case SQLITE_OK: + return E_OK; + case SQLITE_PERM: + case SQLITE_CANTOPEN: + case SQLITE_READONLY: + return -E_FILE_OPERATION; + default: + return -E_ERROR; + } +} +} + void SQLiteUtils::SqliteLogCallback(void *data, int err, const char *msg) { GLOGD("[SQLite] err=%d sys=%d %s msg=%s", err, errno, sqlite3_errstr(err), msg); @@ -39,7 +56,7 @@ int SQLiteUtils::CreateDataBase(const std::string &path, int flag, sqlite3 *&db) db = nullptr; } } - return errCode; + return MapSqliteError(errCode); } int SQLiteUtils::GetStatement(sqlite3 *db, const std::string &sql, sqlite3_stmt *&statement) @@ -152,6 +169,44 @@ int SQLiteUtils::GetColumnBlobValue(sqlite3_stmt *statement, int index, std::vec return E_OK; } +int SQLiteUtils::BindTextToStatement(sqlite3_stmt *statement, int index, const std::string &value) +{ + if (statement == nullptr) { + return -E_INVALID_ARGS; + } + + int errCode = sqlite3_bind_text(statement, index, value.c_str(), value.length(), SQLITE_TRANSIENT); + if (errCode != SQLITE_OK) { + GLOGE("[SQLiteUtil][Bind text]Failed to bind the value:%d", errCode); + return 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 || valSize > MAX_TEXT_READ_SIZE) { + GLOGW("[SQLiteUtils][Column text] size over limit:%d", valSize); + value.resize(MAX_TEXT_READ_SIZE + 1); // Reset value size to invalid + return E_OK; // Return OK for continue get data, but value is invalid + } + + const unsigned char *val = sqlite3_column_text(statement, index); + if (valSize == 0 || val == nullptr) { + value = {}; + } else { + value = std::string(reinterpret_cast(val)); + } + + return E_OK; +} + int SQLiteUtils::BeginTransaction(sqlite3 *db, TransactType type) { if (type == TransactType::IMMEDIATE) { @@ -184,7 +239,7 @@ int SQLiteUtils::ExecSql(sqlite3 *db, const std::string &sql) } sqlite3_free(errMsg); - return errCode; + return MapSqliteError(errCode); } int SQLiteUtils::ExecSql(sqlite3 *db, const std::string &sql, const std::function &bindCallback, @@ -193,7 +248,6 @@ int SQLiteUtils::ExecSql(sqlite3 *db, const std::string &sql, const std::functio if (db == nullptr || sql.empty()) { return -E_INVALID_ARGS; } - bool bindFinish = true; sqlite3_stmt *stmt = nullptr; int errCode = SQLiteUtils::GetStatement(db, sql, stmt); @@ -227,6 +281,6 @@ int SQLiteUtils::ExecSql(sqlite3 *db, const std::string &sql, const std::functio END: (void)SQLiteUtils::ResetStatement(stmt, true); - return errCode; + return MapSqliteError(errCode); } } // namespace DocumentDB diff --git a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/oh_adapter/src/sqlite_utils.h b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/oh_adapter/src/sqlite_utils.h index 9b8e02d3ab890979b0c34cbe31eed234d350363c..4dcf5b1b977020dd35d9ed63ab1b1775eaf3ac1a 100644 --- a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/oh_adapter/src/sqlite_utils.h +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/oh_adapter/src/sqlite_utils.h @@ -39,6 +39,9 @@ public: static int BindBlobToStatement(sqlite3_stmt *statement, int index, const std::vector &value); static int GetColumnBlobValue(sqlite3_stmt *statement, int index, std::vector &value); + static int BindTextToStatement(sqlite3_stmt *statement, int index, const std::string &value); + static int GetColumnTextValue(sqlite3_stmt *statement, int index, std::string &value); + static int BeginTransaction(sqlite3 *db, TransactType type = TransactType::DEFERRED); static int CommitTransaction(sqlite3 *db); static int RollbackTransaction(sqlite3 *db); diff --git a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/test/unittest/api/documentdb_api_test.cpp b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/test/unittest/api/documentdb_api_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..83ad456822ce91959001107a1faa2e1acc1590f6 --- /dev/null +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/test/unittest/api/documentdb_api_test.cpp @@ -0,0 +1,617 @@ +/* +* Copyright (c) 2023 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 + +#include "doc_errno.h" +#include "documentdb_test_utils.h" +#include "log_print.h" +#include "grd_base/grd_db_api.h" +#include "grd_base/grd_error.h" +#include "grd_document/grd_document_api.h" +#include "sqlite_utils.h" + +using namespace DocumentDB; +using namespace testing::ext; +using namespace DocumentDBUnitTest; + +class DocumentDBApiTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DocumentDBApiTest::SetUpTestCase(void) +{ +} + +void DocumentDBApiTest::TearDownTestCase(void) +{ +} + +void DocumentDBApiTest::SetUp(void) +{ +} + +void DocumentDBApiTest::TearDown(void) +{ + DocumentDBTestUtils::RemoveTestDbFiles("./document.db"); +} + +/** + * @tc.name: OpenDBTest001 + * @tc.desc: Test open document db + * @tc.type: FUNC + * @tc.require: + * @tc.author: lianhuix + */ +HWTEST_F(DocumentDBApiTest, OpenDBTest001, TestSize.Level0) +{ + std::string path = "./document.db"; + GRD_DB *db = nullptr; + int status = GRD_DBOpen(path.c_str(), nullptr, GRD_DB_OPEN_CREATE, &db); + EXPECT_EQ(status, GRD_OK); + EXPECT_NE(db, nullptr); + GLOGD("Open DB test 001: status: %d", status); + + EXPECT_EQ(GRD_CreateCollection(db, "student", "", 0), GRD_OK); + + EXPECT_EQ(GRD_UpSertDoc(db, "student", "10001", R""({"name":"Tom","age":23})"", 0), GRD_OK); + EXPECT_EQ(GRD_UpSertDoc(db, "student", "10001", R""({"name":"Tom","age":23})"", 0), GRD_OK); + + EXPECT_EQ(GRD_DropCollection(db, "student", 0), GRD_OK); + + status = GRD_DBClose(db, GRD_DB_CLOSE); + EXPECT_EQ(status, GRD_OK); + db = nullptr; + + DocumentDBTestUtils::RemoveTestDbFiles(path); +} + +/** + * @tc.name: OpenDBTest002 + * @tc.desc: Test open document db with invalid db + * @tc.type: FUNC + * @tc.require: + * @tc.author: lianhuix + */ +HWTEST_F(DocumentDBApiTest, OpenDBTest002, TestSize.Level0) +{ + std::string path = "./document.db"; + int status = GRD_DBOpen(path.c_str(), nullptr, GRD_DB_OPEN_CREATE, nullptr); + EXPECT_EQ(status, GRD_INVALID_ARGS); + + GRD_DB *db = nullptr; + status = GRD_DBOpen(path.c_str(), nullptr, GRD_DB_OPEN_CREATE, &db); + EXPECT_EQ(status, GRD_OK); + EXPECT_NE(db, nullptr); + + status = GRD_DBOpen(path.c_str(), nullptr, GRD_DB_OPEN_CREATE, &db); + EXPECT_EQ(status, GRD_INVALID_ARGS); + + status = GRD_DBClose(db, GRD_DB_CLOSE); + EXPECT_EQ(status, GRD_OK); +} + +/** + * @tc.name: OpenDBPathTest001 + * @tc.desc: Test open document db with NULL path + * @tc.type: FUNC + * @tc.require: + * @tc.author: lianhuix + */ +HWTEST_F(DocumentDBApiTest, OpenDBPathTest001, TestSize.Level0) +{ + GRD_DB *db = nullptr; + std::vector invalidPath = { + nullptr, + "", + "/a/b/c/" + }; + for (auto path : invalidPath) { + GLOGD("OpenDBPathTest001: open db with path: %s", path); + int status = GRD_DBOpen(path, nullptr, GRD_DB_OPEN_CREATE, &db); + EXPECT_EQ(status, GRD_INVALID_ARGS); + } +} + +/** + * @tc.name: OpenDBPathTest002 + * @tc.desc: Test open document db with file no permission + * @tc.type: FUNC + * @tc.require: + * @tc.author: lianhuix + */ +HWTEST_F(DocumentDBApiTest, OpenDBPathTest002, TestSize.Level0) +{ + GRD_DB *db = nullptr; + std::string pathNoPerm = "/root/document.db"; + int status = GRD_DBOpen(pathNoPerm.c_str(), nullptr, GRD_DB_OPEN_CREATE, &db); + EXPECT_EQ(status, GRD_FAILED_FILE_OPERATION); +} + +/** + * @tc.name: OpenDBConfigTest001 + * @tc.desc: Test open document db with invalid config option + * @tc.type: FUNC + * @tc.require: + * @tc.author: lianhuix + */ +HWTEST_F(DocumentDBApiTest, OpenDBConfigTest001, TestSize.Level0) +{ + GRD_DB *db = nullptr; + std::string path= "./document.db"; + const int MAX_JSON_LEN = 512 * 1024; + std::string configStr = std::string(MAX_JSON_LEN, 'a'); + int status = GRD_DBOpen(path.c_str(), configStr.c_str(), GRD_DB_OPEN_CREATE, &db); + EXPECT_EQ(status, GRD_OVER_LIMIT); +} + +/** + * @tc.name: OpenDBConfigTest002 + * @tc.desc: Test open document db with invalid config format + * @tc.type: FUNC + * @tc.require: + * @tc.author: lianhuix + */ +HWTEST_F(DocumentDBApiTest, OpenDBConfigTest002, TestSize.Level0) +{ + GRD_DB *db = nullptr; + std::string path= "./document.db"; + int status = GRD_DBOpen(path.c_str(), "{aa}", GRD_DB_OPEN_CREATE, &db); + EXPECT_EQ(status, GRD_INVALID_FORMAT); +} + +/** + * @tc.name: OpenDBConfigTest003 + * @tc.desc: Test open document db with config not support + * @tc.type: FUNC + * @tc.require: + * @tc.author: lianhuix + */ +HWTEST_F(DocumentDBApiTest, OpenDBConfigTest003, TestSize.Level0) +{ + GRD_DB *db = nullptr; + std::string path= "./document.db"; + int status = GRD_DBOpen(path.c_str(), R""({"notSupport":123})"", GRD_DB_OPEN_CREATE, &db); + EXPECT_EQ(status, GRD_INVALID_ARGS); +} + +/** + * @tc.name: OpenDBConfigMaxConnNumTest001 + * @tc.desc: Test open document db with invalid config item maxConnNum + * @tc.type: FUNC + * @tc.require: + * @tc.author: lianhuix + */ +HWTEST_F(DocumentDBApiTest, OpenDBConfigMaxConnNumTest001, TestSize.Level0) +{ + GRD_DB *db = nullptr; + std::string path= "./document.db"; + + std::vector configList = { + R""({"maxConnNum":0})"", + R""({"maxConnNum":15})"", + R""({"maxConnNum":1025})"", + R""({"maxConnNum":1000000007})"", + R""({"maxConnNum":"16"})"", + R""({"maxConnNum":{"value":17}})"", + R""({"maxConnNum":[16,17,18]})"", + }; + for (const auto &config : configList) { + GLOGD("OpenDBConfigMaxConnNumTest001: test with config:%s", config.c_str()); + int status = GRD_DBOpen(path.c_str(), config.c_str(), GRD_DB_OPEN_CREATE, &db); + ASSERT_EQ(status, GRD_INVALID_ARGS); + } +} + +/** + * @tc.name: OpenDBConfigMaxConnNumTest002 + * @tc.desc: Test open document db with valid item maxConnNum + * @tc.type: FUNC + * @tc.require: + * @tc.author: lianhuix + */ +HWTEST_F(DocumentDBApiTest, OpenDBConfigMaxConnNumTest002, TestSize.Level1) +{ + GRD_DB *db = nullptr; + std::string path= "./document.db"; + + for (int i = 16; i <= 1024; i++) { + std::string config = "{\"maxConnNum\":" + std::to_string(i) + "}"; + int status = GRD_DBOpen(path.c_str(), config.c_str(), GRD_DB_OPEN_CREATE, &db); + EXPECT_EQ(status, GRD_OK); + ASSERT_NE(db, nullptr); + + status = GRD_DBClose(db, GRD_DB_CLOSE); + EXPECT_EQ(status, GRD_OK); + db = nullptr; + + DocumentDBTestUtils::RemoveTestDbFiles(path); + } +} + +/** + * @tc.name: OpenDBConfigMaxConnNumTest003 + * @tc.desc: Test reopen document db with different maxConnNum + * @tc.type: FUNC + * @tc.require: + * @tc.author: lianhuix + */ +HWTEST_F(DocumentDBApiTest, OpenDBConfigMaxConnNumTest003, TestSize.Level1) +{ + GRD_DB *db = nullptr; + std::string path= "./document.db"; + + std::string config = R""({"maxConnNum":16})""; + int status = GRD_DBOpen(path.c_str(), config.c_str(), GRD_DB_OPEN_CREATE, &db); + EXPECT_EQ(status, GRD_OK); + + status = GRD_DBClose(db, GRD_DB_CLOSE); + EXPECT_EQ(status, GRD_OK); + db = nullptr; + + config = R""({"maxConnNum":17})""; + status = GRD_DBOpen(path.c_str(), config.c_str(), GRD_DB_OPEN_CREATE, &db); + EXPECT_EQ(status, GRD_INVALID_ARGS); + + DocumentDBTestUtils::RemoveTestDbFiles(path); +} + +/** + * @tc.name: OpenDBConfigMaxConnNumTest004 + * @tc.desc: Test open document db over maxConnNum + * @tc.type: FUNC + * @tc.require: + * @tc.author: lianhuix + */ +HWTEST_F(DocumentDBApiTest, OpenDBConfigMaxConnNumTest004, TestSize.Level1) +{ + std::string path= "./document.db"; + + int maxCnt = 16; + std::string config = "{\"maxConnNum\":" + std::to_string(maxCnt) + "}"; + + std::vector dbList; + while (maxCnt--) { + GRD_DB *db = nullptr; + int status = GRD_DBOpen(path.c_str(), config.c_str(), GRD_DB_OPEN_CREATE, &db); + EXPECT_EQ(status, GRD_OK); + dbList.push_back(db); + } + + GRD_DB *db = nullptr; + int status = GRD_DBOpen(path.c_str(), config.c_str(), GRD_DB_OPEN_CREATE, &db); + EXPECT_EQ(status, GRD_OK); + EXPECT_NE(db, nullptr); + + for (auto *it : dbList) { + status = GRD_DBClose(it, GRD_DB_CLOSE); + EXPECT_EQ(status, GRD_OK); + } + + DocumentDBTestUtils::RemoveTestDbFiles(path); +} + +/** + * @tc.name: OpenDBConfigPageSizeTest001 + * @tc.desc: Test open document db with invalid config item pageSize + * @tc.type: FUNC + * @tc.require: + * @tc.author: lianhuix + */ +HWTEST_F(DocumentDBApiTest, OpenDBConfigPageSizeTest001, TestSize.Level0) +{ + GRD_DB *db = nullptr; + std::string path= "./document.db"; + + std::vector configList = { + R""({"pageSize":0})"", + R""({"pageSize":5})"", + R""({"pageSize":48})"", + R""({"pageSize":1000000007})"", + R""({"pageSize":"4"})"", + R""({"pageSize":{"value":8}})"", + R""({"pageSize":[16,32,64]})"", + }; + for (const auto &config : configList) { + GLOGD("OpenDBConfigPageSizeTest001: test with config:%s", config.c_str()); + int status = GRD_DBOpen(path.c_str(), config.c_str(), 0, &db); + EXPECT_EQ(status, GRD_INVALID_ARGS); + } +} + +namespace { +int GetDBPageSize(const std::string &path) +{ + sqlite3 *db = nullptr; + int ret = SQLiteUtils::CreateDataBase(path, 0, db); + EXPECT_EQ(ret, E_OK); + if (db == nullptr) { + return 0; + } + + int pageSize = 0; + SQLiteUtils::ExecSql(db, "PRAGMA page_size;", nullptr, [&pageSize](sqlite3_stmt *stmt) { + pageSize = sqlite3_column_int(stmt, 0); + return E_OK; + }); + + sqlite3_close_v2(db); + return pageSize; +} +} + +/** + * @tc.name: OpenDBConfigPageSizeTest002 + * @tc.desc: Test open document db with valid config item pageSize + * @tc.type: FUNC + * @tc.require: + * @tc.author: lianhuix + */ +HWTEST_F(DocumentDBApiTest, OpenDBConfigPageSizeTest002, TestSize.Level0) +{ + GRD_DB *db = nullptr; + std::string path= "./document.db"; + + for (int size : {4, 8, 16, 32, 64}) { + std::string config = "{\"pageSize\":" + std::to_string(size) + "}"; + int status = GRD_DBOpen(path.c_str(), config.c_str(), GRD_DB_OPEN_CREATE, &db); + EXPECT_EQ(status, GRD_OK); + + status = GRD_DBClose(db, GRD_DB_CLOSE); + EXPECT_EQ(status, GRD_OK); + db = nullptr; + + EXPECT_EQ(GetDBPageSize(path), size * 1024); + DocumentDBTestUtils::RemoveTestDbFiles(path); + } +} + +/** + * @tc.name: OpenDBConfigPageSizeTest003 + * @tc.desc: Test reopen document db with different pageSize + * @tc.type: FUNC + * @tc.require: + * @tc.author: lianhuix + */ +HWTEST_F(DocumentDBApiTest, OpenDBConfigPageSizeTest003, TestSize.Level1) +{ + GRD_DB *db = nullptr; + std::string path= "./document.db"; + + std::string config = R""({"pageSize":4})""; + int status = GRD_DBOpen(path.c_str(), config.c_str(), GRD_DB_OPEN_CREATE, &db); + EXPECT_EQ(status, GRD_OK); + + status = GRD_DBClose(db, GRD_DB_CLOSE); + EXPECT_EQ(status, GRD_OK); + db = nullptr; + + config = R""({"pageSize":8})""; + status = GRD_DBOpen(path.c_str(), config.c_str(), GRD_DB_OPEN_CREATE, &db); + EXPECT_EQ(status, GRD_INVALID_ARGS); + + DocumentDBTestUtils::RemoveTestDbFiles(path); +} + +/** + * @tc.name: OpenDBConfigRedoFlushTest001 + * @tc.desc: Test open document db with valid config item redoFlushByTrx + * @tc.type: FUNC + * @tc.require: + * @tc.author: lianhuix + */ +HWTEST_F(DocumentDBApiTest, OpenDBConfigRedoFlushTest001, TestSize.Level0) +{ + GRD_DB *db = nullptr; + std::string path= "./document.db"; + + for (int flush : {0, 1}) { + std::string config = "{\"redoFlushByTrx\":" + std::to_string(flush) + "}"; + int status = GRD_DBOpen(path.c_str(), config.c_str(), GRD_DB_OPEN_CREATE, &db); + EXPECT_EQ(status, GRD_OK); + + status = GRD_DBClose(db, GRD_DB_CLOSE); + EXPECT_EQ(status, GRD_OK); + db = nullptr; + + DocumentDBTestUtils::RemoveTestDbFiles(path); + } +} + +/** + * @tc.name: OpenDBConfigXXXTest001 + * @tc.desc: Test open document db with invalid config item redoFlushByTrx + * @tc.type: FUNC + * @tc.require: + * @tc.author: lianhuix + */ +HWTEST_F(DocumentDBApiTest, OpenDBConfigRedoFlushTest002, TestSize.Level0) +{ + GRD_DB *db = nullptr; + std::string path= "./document.db"; + + std::string config = R""({"redoFlushByTrx":3})""; + int status = GRD_DBOpen(path.c_str(), config.c_str(), GRD_DB_OPEN_CREATE, &db); + EXPECT_EQ(status, GRD_INVALID_ARGS); +} + +/** + * @tc.name: OpenDBFlagTest001 + * @tc.desc: Test open document db with invalid flag + * @tc.type: FUNC + * @tc.require: + * @tc.author: lianhuix + */ +HWTEST_F(DocumentDBApiTest, OpenDBFlagTest001, TestSize.Level0) +{ + GRD_DB *db = nullptr; + std::string path= "./document.db"; + std::vector invaldFlag = { + GRD_DB_OPEN_CHECK_FOR_ABNORMAL | GRD_DB_OPEN_CHECK, + GRD_DB_OPEN_CREATE | GRD_DB_OPEN_CHECK_FOR_ABNORMAL | GRD_DB_OPEN_CHECK, + 0x08, + 0xffff, + UINT32_MAX + }; + for (unsigned int flag : invaldFlag) { + GLOGD("OpenDBFlagTest001: open doc db with flag %u", flag); + int status = GRD_DBOpen(path.c_str(), "", flag, &db); + EXPECT_EQ(status, GRD_INVALID_ARGS); + } +} + +/** + * @tc.name: OpenDBFlagTest002 + * @tc.desc: Test open document db with valid flag + * @tc.type: FUNC + * @tc.require: + * @tc.author: lianhuix + */ +HWTEST_F(DocumentDBApiTest, OpenDBFlagTest002, TestSize.Level0) +{ + GRD_DB *db = nullptr; + std::string path= "./document.db"; + int status = GRD_DBOpen(path.c_str(), "", GRD_DB_OPEN_CREATE, &db); + EXPECT_EQ(status, GRD_OK); + + status = GRD_DBClose(db, GRD_DB_CLOSE); + EXPECT_EQ(status, GRD_OK); + db = nullptr; + + status = GRD_DBOpen(path.c_str(), "", GRD_DB_OPEN_ONLY, &db); + EXPECT_EQ(status, GRD_OK); + + status = GRD_DBClose(db, GRD_DB_CLOSE); + EXPECT_EQ(status, GRD_OK); + db = nullptr; + + status = GRD_DBOpen(path.c_str(), "", GRD_DB_OPEN_CHECK_FOR_ABNORMAL, &db); + EXPECT_EQ(status, GRD_OK); + + status = GRD_DBClose(db, GRD_DB_CLOSE); + EXPECT_EQ(status, GRD_OK); + db = nullptr; + + status = GRD_DBOpen(path.c_str(), "", GRD_DB_OPEN_CHECK, &db); + EXPECT_EQ(status, GRD_OK); + + status = GRD_DBClose(db, GRD_DB_CLOSE); + EXPECT_EQ(status, GRD_OK); + db = nullptr; + + DocumentDBTestUtils::RemoveTestDbFiles(path); +} + +/** + * @tc.name: CloseDBTest001 + * @tc.desc: Test close document db with invalid db + * @tc.type: FUNC + * @tc.require: + * @tc.author: lianhuix + */ +HWTEST_F(DocumentDBApiTest, CloseDBTest001, TestSize.Level0) +{ + GRD_DB *db = nullptr; + int status = GRD_DBClose(db, GRD_DB_CLOSE); + EXPECT_EQ(status, GRD_INVALID_ARGS); + + status = GRD_DBClose(db, GRD_DB_CLOSE_IGNORE_ERROR); + EXPECT_EQ(status, GRD_INVALID_ARGS); + db = nullptr; +} + +/** + * @tc.name: CloseDBFlagTest001 + * @tc.desc: Test close document db with valid flag + * @tc.type: FUNC + * @tc.require: + * @tc.author: lianhuix + */ +HWTEST_F(DocumentDBApiTest, CloseDBFlagTest001, TestSize.Level0) +{ + GRD_DB *db = nullptr; + std::string path= "./document.db"; + int status = GRD_DBOpen(path.c_str(), "", GRD_DB_OPEN_CREATE, &db); + EXPECT_EQ(status, GRD_OK); + ASSERT_NE(db, nullptr); + + status = GRD_DBClose(db, GRD_DB_CLOSE); + EXPECT_EQ(status, GRD_OK); + db = nullptr; + + DocumentDBTestUtils::RemoveTestDbFiles(path); +} + +/** + * @tc.name: CloseDBFlagTest002 + * @tc.desc: Test close document db with valid flag + * @tc.type: FUNC + * @tc.require: + * @tc.author: lianhuix + */ +HWTEST_F(DocumentDBApiTest, CloseDBFlagTest002, TestSize.Level0) +{ + GRD_DB *db = nullptr; + std::string path= "./document.db"; + int status = GRD_DBOpen(path.c_str(), "", GRD_DB_OPEN_CREATE, &db); + EXPECT_EQ(status, GRD_OK); + ASSERT_NE(db, nullptr); + + // TODO: open result set + + status = GRD_DBClose(db, GRD_DB_CLOSE_IGNORE_ERROR); + EXPECT_EQ(status, GRD_OK); + db = nullptr; + + DocumentDBTestUtils::RemoveTestDbFiles(path); +} + +/** + * @tc.name: CloseDBFlagTest003 + * @tc.desc: Test close document db with invalid flag + * @tc.type: FUNC + * @tc.require: + * @tc.author: lianhuix + */ +HWTEST_F(DocumentDBApiTest, CloseDBFlagTest003, TestSize.Level0) +{ + GRD_DB *db = nullptr; + std::string path= "./document.db"; + int status = GRD_DBOpen(path.c_str(), "", GRD_DB_OPEN_CREATE, &db); + EXPECT_EQ(status, GRD_OK); + ASSERT_NE(db, nullptr); + + std::vector invaldFlag = { + 0x02, + 0x03, + 0xffff, + UINT32_MAX + }; + for (unsigned int flag : invaldFlag) { + GLOGD("CloseDBFlagTest003: close doc db with flag %u", flag); + status = GRD_DBClose(db, flag); + EXPECT_EQ(status, GRD_INVALID_ARGS); + } + + status = GRD_DBClose(db, GRD_DB_CLOSE); + EXPECT_EQ(status, GRD_OK); + db = nullptr; + + DocumentDBTestUtils::RemoveTestDbFiles(path); +} \ No newline at end of file diff --git a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/test/unittest/api/documentdb_collection_test.cpp b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/test/unittest/api/documentdb_collection_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9172399440e46690d50065d583bcfa51cfeab4db --- /dev/null +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/test/unittest/api/documentdb_collection_test.cpp @@ -0,0 +1,209 @@ +/* +* Copyright (c) 2023 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 + +#include "doc_errno.h" +#include "documentdb_test_utils.h" +#include "log_print.h" +#include "grd_base/grd_db_api.h" +#include "grd_base/grd_error.h" +#include "grd_document/grd_document_api.h" +#include "sqlite_utils.h" + +using namespace DocumentDB; +using namespace testing::ext; +using namespace DocumentDBUnitTest; + +namespace { +std::string g_path = "./document.db"; +GRD_DB *g_db = nullptr; +} + +class DocumentDBCollectionTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DocumentDBCollectionTest::SetUpTestCase(void) +{ +} + +void DocumentDBCollectionTest::TearDownTestCase(void) +{ +} + +void DocumentDBCollectionTest::SetUp(void) +{ + EXPECT_EQ(GRD_DBOpen(g_path.c_str(), nullptr, GRD_DB_OPEN_CREATE, &g_db), GRD_OK); + EXPECT_NE(g_db, nullptr); +} + +void DocumentDBCollectionTest::TearDown(void) +{ + if (g_db != nullptr) { + EXPECT_EQ(GRD_DBClose(g_db, GRD_DB_CLOSE), GRD_OK); + g_db = nullptr; + } + DocumentDBTestUtils::RemoveTestDbFiles(g_path); +} + +/** + * @tc.name: CollectionTest001 + * @tc.desc: Test create collection with null db + * @tc.type: FUNC + * @tc.require: + * @tc.author: lianhuix + */ +HWTEST_F(DocumentDBCollectionTest, CollectionTest001, TestSize.Level0) +{ + EXPECT_EQ(GRD_CreateCollection(nullptr, "student", "", 0), GRD_INVALID_ARGS); +} + +namespace { +const int MAX_COLLECTION_LEN = 512; +} + +/** + * @tc.name: CollectionTest002 + * @tc.desc: Test create/drop collection with invalid collection name + * @tc.type: FUNC + * @tc.require: + * @tc.author: lianhuix + */ +HWTEST_F(DocumentDBCollectionTest, CollectionTest002, TestSize.Level0) +{ + string overLenName(MAX_COLLECTION_LEN, 'a'); + std::vector invalidName = { + nullptr, + "", + "GRD_123", + "grd_123", + "GM_SYS_123", + "gm_sys_123", + overLenName.c_str() + }; + + for (auto *it : invalidName) { + GLOGD("CollectionTest002: create collection with name: %s", it); + EXPECT_EQ(GRD_CreateCollection(g_db, it, "", 0), GRD_INVALID_ARGS); + EXPECT_EQ(GRD_DropCollection(g_db, it, 0), GRD_INVALID_ARGS); + } +} + +/** + * @tc.name: CollectionTest003 + * @tc.desc: Test create/drop collection with valid collection name + * @tc.type: FUNC + * @tc.require: + * @tc.author: lianhuix + */ +HWTEST_F(DocumentDBCollectionTest, CollectionTest003, TestSize.Level0) +{ + string overLenName(MAX_COLLECTION_LEN - 1, 'a'); + std::vector validName = { + "123", + "&^%@", + "中文字符", + "sqlite_master", + "NULL", + "SELECT", + overLenName.c_str() + }; + + for (auto *it : validName) { + GLOGD("CollectionTest003: create collection with name: %s", it); + EXPECT_EQ(GRD_CreateCollection(g_db, it, "", 0), GRD_OK); + EXPECT_EQ(GRD_DropCollection(g_db, it, 0), GRD_OK); + } +} + +/** + * @tc.name: CollectionTest004 + * @tc.desc: Test create collection with ignore flag + * @tc.type: FUNC + * @tc.require: + * @tc.author: lianhuix + */ +HWTEST_F(DocumentDBCollectionTest, CollectionTest004, TestSize.Level0) +{ + EXPECT_EQ(GRD_CreateCollection(g_db, "student", "", 0), GRD_OK); + EXPECT_EQ(GRD_CreateCollection(g_db, "student", "", 0), GRD_DATA_CONFLICT); + EXPECT_EQ(GRD_CreateCollection(g_db, "student", "", CHK_EXIST_COLLECTION), GRD_OK); +} + +/** + * @tc.name: CollectionTest005 + * @tc.desc: Test create collection with invalid option + * @tc.type: FUNC + * @tc.require: + * @tc.author: lianhuix + */ +HWTEST_F(DocumentDBCollectionTest, CollectionTest005, TestSize.Level0) +{ + EXPECT_EQ(GRD_CreateCollection(g_db, "student", R""({aa})"", 0), GRD_INVALID_FORMAT); + + std::vector invalidOption = { + R""({"maxDoc":0})"", + R""({"maxDoc":"123"})"", + R""({"maxDoc":{"value":1024}})"", + R""({"maxDoc":[1,2,4,8]})"", + R""({"minDoc":1024})"", + }; + + for (auto opt : invalidOption) { + GLOGD("CollectionTest005: create collection with option: %s", opt); + EXPECT_EQ(GRD_CreateCollection(g_db, "student", opt, 0), GRD_INVALID_ARGS); + } +} + +/** + * @tc.name: CollectionTest006 + * @tc.desc: Test create/drop collection with valid flag + * @tc.type: FUNC + * @tc.require: + * @tc.author: lianhuix + */ +HWTEST_F(DocumentDBCollectionTest, CollectionTest006, TestSize.Level0) +{ + EXPECT_EQ(GRD_CreateCollection(g_db, "student", R""({"maxDoc":1024})"", 0), GRD_OK); + + EXPECT_EQ(GRD_CreateCollection(g_db, "student", R""({"maxDoc":2048})"", CHK_EXIST_COLLECTION), GRD_INVALID_ARGS); + + EXPECT_EQ(GRD_DropCollection(g_db, "student", 0), GRD_OK); + EXPECT_EQ(GRD_DropCollection(g_db, "student", 0), GRD_NO_DATA); + EXPECT_EQ(GRD_DropCollection(g_db, "student", CHK_NON_EXIST_COLLECTION), GRD_OK); + + // Create collection with different option returnh OK after drop collection + EXPECT_EQ(GRD_CreateCollection(g_db, "student", R""({"maxDoc":2048})"", 0), GRD_OK); +} + +/** + * @tc.name: CollectionTest007 + * @tc.desc: Test create/drop collection with invalid flag + * @tc.type: FUNC + * @tc.require: + * @tc.author: lianhuix + */ +HWTEST_F(DocumentDBCollectionTest, CollectionTest007, TestSize.Level0) +{ + for (int flag : std::vector {2, 4, 8, 1024, UINT32_MAX}) { + EXPECT_EQ(GRD_CreateCollection(g_db, "student", "", flag), GRD_INVALID_ARGS); + EXPECT_EQ(GRD_DropCollection(g_db, "student", flag), GRD_INVALID_ARGS); + } +} \ No newline at end of file diff --git a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/test/unittest/api/documentdb_data_test.cpp b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/test/unittest/api/documentdb_data_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6f61aac7140255144b53f867dd924d534b5a4168 --- /dev/null +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/test/unittest/api/documentdb_data_test.cpp @@ -0,0 +1,195 @@ +/* +* Copyright (c) 2023 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 + +#include "doc_errno.h" +#include "documentdb_test_utils.h" +#include "log_print.h" +#include "grd_base/grd_db_api.h" +#include "grd_base/grd_error.h" +#include "grd_document/grd_document_api.h" +#include "sqlite_utils.h" + +using namespace DocumentDB; +using namespace testing::ext; +using namespace DocumentDBUnitTest; + +namespace { +std::string g_path = "./document.db"; +GRD_DB *g_db = nullptr; +const char *g_coll = "student"; +} + +class DocumentDBDataTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DocumentDBDataTest::SetUpTestCase(void) +{ +} + +void DocumentDBDataTest::TearDownTestCase(void) +{ +} + +void DocumentDBDataTest::SetUp(void) +{ + EXPECT_EQ(GRD_DBOpen(g_path.c_str(), nullptr, GRD_DB_OPEN_CREATE, &g_db), GRD_OK); + EXPECT_NE(g_db, nullptr); + + EXPECT_EQ(GRD_CreateCollection(g_db, g_coll, "", 0), GRD_OK); +} + +void DocumentDBDataTest::TearDown(void) +{ + if (g_db != nullptr) { + EXPECT_EQ(GRD_DBClose(g_db, GRD_DB_CLOSE), GRD_OK); + g_db = nullptr; + } + DocumentDBTestUtils::RemoveTestDbFiles(g_path); +} + +/** + * @tc.name: UpsertDataTest001 + * @tc.desc: Test upsert data into collection + * @tc.type: FUNC + * @tc.require: + * @tc.author: lianhuix + */ +HWTEST_F(DocumentDBDataTest, UpsertDataTest001, TestSize.Level0) +{ + std::string document = R""({"name":"Tmono","age":18,"addr":{"city":"shanghai","postal":200001}})""; + EXPECT_EQ(GRD_UpSertDoc(g_db, g_coll, "1234", document.c_str(), GRD_DOC_REPLACE), GRD_OK); +} + +/** + * @tc.name: UpsertDataTest002 + * @tc.desc: Test upsert data with db is nullptr + * @tc.type: FUNC + * @tc.require: + * @tc.author: lianhuix + */ +HWTEST_F(DocumentDBDataTest, UpsertDataTest002, TestSize.Level0) +{ + std::string document = R""({"name":"Tmono","age":18,"addr":{"city":"shanghai","postal":200001}})""; + EXPECT_EQ(GRD_UpSertDoc(nullptr, g_coll, "1234", document.c_str(), GRD_DOC_REPLACE), GRD_INVALID_ARGS); +} + +/** + * @tc.name: UpsertDataTest003 + * @tc.desc: Test upsert data with invalid collection name + * @tc.type: FUNC + * @tc.require: + * @tc.author: lianhuix + */ +HWTEST_F(DocumentDBDataTest, UpsertDataTest003, TestSize.Level0) +{ + std::string document = R""({"name":"Tmono","age":18,"addr":{"city":"shanghai","postal":200001}})""; + std::vector invalidName = { + nullptr, + "", + "GRD_123", + "grd_123", + "GM_SYS_123", + "gm_sys_123", + }; + for (auto name : invalidName) { + GLOGD("UpsertDataTest003: upsert data with collectionname: %s", name); + EXPECT_EQ(GRD_UpSertDoc(g_db, name, "1234", document.c_str(), GRD_DOC_REPLACE), GRD_INVALID_ARGS); + } +} + +HWTEST_F(DocumentDBDataTest, UpsertDataTest004, TestSize.Level0) +{ + std::string document = R""({"name":"Tmono","age":18,"addr":{"city":"shanghai","postal":200001}})""; + std::vector invalidFilter = { + nullptr, + "", + R""({"name":"Tmono"})"", + R""({"value":{"_id":"1234"}})"", + R""({"_id":1234})"", + }; + for (auto filter : invalidFilter) { + EXPECT_EQ(GRD_UpSertDoc(g_db, g_coll, filter, document.c_str(), GRD_DOC_REPLACE), GRD_INVALID_ARGS); + } +} + +HWTEST_F(DocumentDBDataTest, UpsertDataTest005, TestSize.Level0) +{ + std::string filter = R""({"_id":1234})""; + std::vector invalidDocument = { + nullptr, + "", + "GRD_123", + "grd_123", + "GM_SYS_123", + "gm_sys_123", + }; + for (auto document : invalidDocument) { + EXPECT_EQ(GRD_UpSertDoc(g_db, g_coll, filter.c_str(), document, GRD_DOC_REPLACE), GRD_INVALID_FORMAT); + } +} + +/** + * @tc.name: UpsertDataTest006 + * @tc.desc: Test upsert data with invalid flags + * @tc.type: FUNC + * @tc.require: + * @tc.author: lianhuix + */ +HWTEST_F(DocumentDBDataTest, UpsertDataTest006, TestSize.Level0) +{ + std::string filter = R""({"_id":1234})""; + std::string document = R""({"name":"Tmono","age":18,"addr":{"city":"shanghai","postal":200001}})""; + + for (auto flags : std::vector {2, 4, 8, 64, 1024, UINT32_MAX}) { + EXPECT_EQ(GRD_UpSertDoc(g_db, g_coll, filter.c_str(), document.c_str(), flags), GRD_INVALID_ARGS); + } +} + +/** + * @tc.name: UpsertDataTest007 + * @tc.desc: Test upsert data with collection not create + * @tc.type: FUNC + * @tc.require: + * @tc.author: lianhuix + */ +HWTEST_F(DocumentDBDataTest, UpsertDataTest007, TestSize.Level0) +{ + std::string val = R""({"name":"Tmono","age":18,"addr":{"city":"shanghai","postal":200001}})""; + EXPECT_EQ(GRD_UpSertDoc(g_db, "collection_not_exists", "1234", val.c_str(), GRD_DOC_REPLACE), GRD_NO_DATA); +} + +/** + * @tc.name: UpsertDataTest008 + * @tc.desc: Test upsert data with different document in append + * @tc.type: FUNC + * @tc.require: + * @tc.author: lianhuix + */ +HWTEST_F(DocumentDBDataTest, UpsertDataTest008, TestSize.Level0) +{ + std::string filter = R""({"_id":1234})""; + std::string document = R""({"name":"Tmn","age":18,"addr":{"city":"shanghai","postal":200001}})""; + EXPECT_EQ(GRD_UpSertDoc(g_db, g_coll, filter.c_str(), document.c_str(), GRD_DOC_REPLACE), GRD_OK); + + std::string updateDoc = R""({"name":"Xue","case":2,"age":28,"addr":{"city":"shenzhen","postal":518000}})""; + EXPECT_EQ(GRD_UpSertDoc(g_db, g_coll, filter.c_str(), updateDoc.c_str(), GRD_DOC_APPEND), GRD_OK); +} \ No newline at end of file diff --git a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/test/unittest/common/documentdb_test_utils.cpp b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/test/unittest/common/documentdb_test_utils.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f26640ad80bac465982013d705abb7d391323499 --- /dev/null +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/test/unittest/common/documentdb_test_utils.cpp @@ -0,0 +1,24 @@ +/* +* Copyright (c) 2023 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 "documentdb_test_utils.h" + +namespace DocumentDBUnitTest { +int DocumentDBTestUtils::RemoveTestDbFiles(const std::string &dir) +{ + (void)remove(dir.c_str()); // TODO: remove dir or files + return 0; +} +} // namespace DocumentDBUnitTest \ No newline at end of file diff --git a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/test/unittest/common/documentdb_test_utils.h b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/test/unittest/common/documentdb_test_utils.h new file mode 100644 index 0000000000000000000000000000000000000000..a513875ef87a922f39132229a2e9e620f2bda17c --- /dev/null +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/test/unittest/common/documentdb_test_utils.h @@ -0,0 +1,27 @@ +/* +* Copyright (c) 2023 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 DOCUMENTDB_TEST_UTILS_H +#define DOCUMENTDB_TEST_UTILS_H +#include + + +namespace DocumentDBUnitTest { +class DocumentDBTestUtils { +public: + static int RemoveTestDbFiles(const std::string &dir); +}; +} // namespace DocumentDBUnitTest +#endif // DOCUMENTDB_TEST_UTILS_H \ No newline at end of file diff --git a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/test/unittest/oh_adapter/documentdb_json_common_test.cpp b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/test/unittest/oh_adapter/documentdb_json_common_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..11717fe75652d63513e1ad8b579825b993cdb35b --- /dev/null +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/test/unittest/oh_adapter/documentdb_json_common_test.cpp @@ -0,0 +1,330 @@ +/* +* Copyright (c) 2023 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 +#include + +#include "documentdb_test_utils.h" +#include "doc_errno.h" +#include "json_common.h" +#include "log_print.h" + +using namespace DocumentDB; +using namespace testing::ext; +using namespace DocumentDBUnitTest; + +class DocumentDBJsonCommonTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DocumentDBJsonCommonTest::SetUpTestCase(void) +{ +} + +void DocumentDBJsonCommonTest::TearDownTestCase(void) +{ +} + +void DocumentDBJsonCommonTest::SetUp(void) +{ +} + +void DocumentDBJsonCommonTest::TearDown(void) +{ +} + +/** + * @tc.name: OpenDBTest001 + * @tc.desc: Test open document db + * @tc.type: FUNC + * @tc.require: + * @tc.author: lianhuix + */ +HWTEST_F(DocumentDBJsonCommonTest, JsonObjectAppendTest001, TestSize.Level0) +{ + std::string document = R""({"name":"Tmn","age":18,"addr":{"city":"shanghai","postal":200001}})""; + std::string updateDoc = R""({"name":"Xue","case":{"field1":1,"field2":"string","field3":[1,2,3]},"age":28,"addr":{"city":"shenzhen","postal":518000}})""; + + int errCode = E_OK; + JsonObject src = JsonObject::Parse(document, errCode); + EXPECT_EQ(errCode, E_OK); + JsonObject add = JsonObject::Parse(updateDoc, errCode); + EXPECT_EQ(errCode, E_OK); + + EXPECT_EQ(JsonCommon::Append(src, add), E_OK); + GLOGD("result: %s", src.Print().c_str()); + + JsonObject itemCase = src.FindItem({"case", "field1"}, errCode); + EXPECT_EQ(errCode, E_OK); + EXPECT_EQ(itemCase.GetItemValue().GetIntValue(), 1); + + JsonObject itemName = src.FindItem({"name"}, errCode); + EXPECT_EQ(errCode, E_OK); + EXPECT_EQ(itemName.GetItemValue().GetStringValue(), "Xue"); +} + +HWTEST_F(DocumentDBJsonCommonTest, JsonObjectAppendTest002, TestSize.Level0) +{ + std::string document = R""({"name":"Tmn","case":2,"age":[1,2,3],"addr":{"city":"shanghai","postal":200001}})""; + std::string updateDoc = R""({"name":["Xue","Neco","Lip"],"grade":99,"age":18,"addr": + [{"city":"shanghai","postal":200001},{"city":"beijing","postal":100000}]})""; + + int errCode = E_OK; + JsonObject src = JsonObject::Parse(document, errCode); + EXPECT_EQ(errCode, E_OK); + JsonObject add = JsonObject::Parse(updateDoc, errCode); + EXPECT_EQ(errCode, E_OK); + EXPECT_EQ(JsonCommon::Append(src, add), E_OK); + GLOGD("result: %s", src.Print().c_str()); + + JsonObject itemCase = src.FindItem({"grade"}, errCode); + EXPECT_EQ(errCode, E_OK); + EXPECT_EQ(itemCase.GetItemValue().GetIntValue(), 99); // 99: grade + + JsonObject itemName = src.FindItem({"name", "1"}, errCode); + EXPECT_EQ(errCode, E_OK); + EXPECT_EQ(itemName.GetItemValue().GetStringValue(), "Neco"); +} + + +HWTEST_F(DocumentDBJsonCommonTest, JsonObjectAppendTest003, TestSize.Level0) +{ + std::string document = R""({"name":["Tmn","BB","Alice"],"age":[1,2,3],"addr":[{"city":"shanghai","postal":200001},{"city":"wuhan","postal":430000}]})""; + std::string updateDoc = R""({"name":["Xue","Neco","Lip"],"age":18,"addr":[{"city":"shanghai","postal":200001},{"city":"beijing","postal":100000}]})""; + + int errCode = E_OK; + JsonObject src = JsonObject::Parse(document, errCode); + EXPECT_EQ(errCode, E_OK); + JsonObject add = JsonObject::Parse(updateDoc, errCode); + EXPECT_EQ(errCode, E_OK); + EXPECT_EQ(JsonCommon::Append(src, add), E_OK); + + GLOGD("result: %s", src.Print().c_str()); + JsonObject itemCase = src.FindItem({"addr", "1", "city"}, errCode); + EXPECT_EQ(errCode, E_OK); + EXPECT_EQ(itemCase.GetItemValue().GetStringValue(), "beijing"); // 99: grade + + JsonObject itemName = src.FindItem({"name", "1"}, errCode); + EXPECT_EQ(errCode, E_OK); + EXPECT_EQ(itemName.GetItemValue().GetStringValue(), "Neco"); +} + +HWTEST_F(DocumentDBJsonCommonTest, JsonObjectAppendTest004, TestSize.Level0) +{ + std::string document = R""({"name":["Tmn","BB","Alice"]})""; + std::string updateDoc = R""({"name.5":"GG"})"";; + + int errCode = E_OK; + JsonObject src = JsonObject::Parse(document, errCode); + EXPECT_EQ(errCode, E_OK); + JsonObject add = JsonObject::Parse(updateDoc, errCode); + EXPECT_EQ(errCode, E_OK); + EXPECT_EQ(JsonCommon::Append(src, add), -E_DATA_CONFLICT); + GLOGD("result: %s", src.Print().c_str()); +} + +HWTEST_F(DocumentDBJsonCommonTest, JsonObjectAppendTest005, TestSize.Level0) +{ + std::string document = R""({"name":["Tmn","BB","Alice"]})""; + std::string updateDoc = R""({"name.2":"GG"})"";; + + int errCode = E_OK; + JsonObject src = JsonObject::Parse(document, errCode); + EXPECT_EQ(errCode, E_OK); + JsonObject add = JsonObject::Parse(updateDoc, errCode); + EXPECT_EQ(errCode, E_OK); + + EXPECT_EQ(JsonCommon::Append(src, add), E_OK); + GLOGD("result: %s", src.Print().c_str()); + + JsonObject itemCase = src.FindItem({"name", "2"}, errCode); + EXPECT_EQ(errCode, E_OK); + EXPECT_EQ(itemCase.GetItemValue().GetStringValue(), "GG"); +} + +HWTEST_F(DocumentDBJsonCommonTest, JsonObjectAppendTest006, TestSize.Level0) +{ + std::string document = R""({"name":{"first":"Tno","last":"moray"}})""; + std::string updateDoc = R""({"name":{"midle.AA":"GG"}})""; + + int errCode = E_OK; + JsonObject src = JsonObject::Parse(document, errCode); + EXPECT_EQ(errCode, E_OK); + JsonObject add = JsonObject::Parse(updateDoc, errCode); + EXPECT_EQ(errCode, E_OK); + + EXPECT_EQ(JsonCommon::Append(src, add), E_OK); + GLOGD("result: %s", src.Print().c_str()); + + JsonObject itemCase = src.FindItem({"name", "midle.AA"}, errCode); + EXPECT_EQ(errCode, E_OK); + EXPECT_EQ(itemCase.GetItemValue().GetStringValue(), "GG"); +} + +HWTEST_F(DocumentDBJsonCommonTest, JsonObjectAppendTest007, TestSize.Level0) +{ + std::string document = R""({"name":{"first":["XX","CC"],"last":"moray"}})""; + std::string updateDoc = R""({"name.first.0":"LL"})""; + + int errCode = E_OK; + JsonObject src = JsonObject::Parse(document, errCode); + EXPECT_EQ(errCode, E_OK); + JsonObject add = JsonObject::Parse(updateDoc, errCode); + EXPECT_EQ(errCode, E_OK); + + EXPECT_EQ(JsonCommon::Append(src, add), E_OK); + GLOGD("result: %s", src.Print().c_str()); + + JsonObject itemCase = src.FindItem({"name", "first", "0"}, errCode); + EXPECT_EQ(errCode, E_OK); + EXPECT_EQ(itemCase.GetItemValue().GetStringValue(), "LL"); +} + +HWTEST_F(DocumentDBJsonCommonTest, JsonObjectAppendTest008, TestSize.Level0) +{ + std::string document = R""({"name":{"first":"XX","last":"moray"}})""; + std::string updateDoc = R""({"name":{"first":["XXX","BBB","CCC"]}})""; + + int errCode = E_OK; + JsonObject src = JsonObject::Parse(document, errCode); + EXPECT_EQ(errCode, E_OK); + JsonObject add = JsonObject::Parse(updateDoc, errCode); + EXPECT_EQ(errCode, E_OK); + + EXPECT_EQ(JsonCommon::Append(src, add), E_OK); + GLOGD("result: %s", src.Print().c_str()); + + JsonObject itemCase = src.FindItem({"name", "first", "0"}, errCode); + EXPECT_EQ(errCode, E_OK); + EXPECT_EQ(itemCase.GetItemValue().GetStringValue(), "XXX"); +} + + +HWTEST_F(DocumentDBJsonCommonTest, JsonObjectAppendTest009, TestSize.Level0) +{ + std::string document = R""({"name":{"first":["XXX","BBB","CCC"],"last":"moray"}})""; + std::string updateDoc = R""({"name":{"first":"XX"}})""; + + int errCode = E_OK; + JsonObject src = JsonObject::Parse(document, errCode); + EXPECT_EQ(errCode, E_OK); + JsonObject add = JsonObject::Parse(updateDoc, errCode); + EXPECT_EQ(errCode, E_OK); + + EXPECT_EQ(JsonCommon::Append(src, add), E_OK); + GLOGD("result: %s", src.Print().c_str()); + + JsonObject itemCase = src.FindItem({"name", "first"}, errCode); + EXPECT_EQ(errCode, E_OK); + EXPECT_EQ(itemCase.GetItemValue().GetStringValue(), "XX"); +} + +HWTEST_F(DocumentDBJsonCommonTest, JsonObjectAppendTest010, TestSize.Level0) +{ + std::string document = R""({"name":{"first":["XXX","BBB","CCC"],"last":"moray"}})""; + std::string updateDoc = R""({"name":{"first":{"XX":"AA"}}})""; + + int errCode = E_OK; + JsonObject src = JsonObject::Parse(document, errCode); + EXPECT_EQ(errCode, E_OK); + JsonObject add = JsonObject::Parse(updateDoc, errCode); + EXPECT_EQ(errCode, E_OK); + + EXPECT_EQ(JsonCommon::Append(src, add), E_OK); + GLOGD("result: %s", src.Print().c_str()); + + JsonObject itemCase = src.FindItem({"name", "first", "XX"}, errCode); + EXPECT_EQ(errCode, E_OK); + EXPECT_EQ(itemCase.GetItemValue().GetStringValue(), "AA"); +} + +HWTEST_F(DocumentDBJsonCommonTest, JsonObjectAppendTest011, TestSize.Level0) +{ + std::string document = R""({"name":{"first":["XXX","BBB","CCC"],"last":"moray"}})""; + std::string updateDoc = R""({"name.last.AA.B":"Mnado"})""; + + int errCode = E_OK; + JsonObject src = JsonObject::Parse(document, errCode); + EXPECT_EQ(errCode, E_OK); + JsonObject add = JsonObject::Parse(updateDoc, errCode); + EXPECT_EQ(errCode, E_OK); + + EXPECT_EQ(JsonCommon::Append(src, add), -E_DATA_CONFLICT); + GLOGD("result: %s", src.Print().c_str()); +} + +HWTEST_F(DocumentDBJsonCommonTest, JsonObjectAppendTest012, TestSize.Level0) +{ + std::string document = R""({"name":["Tmn","BB","Alice"]})""; + std::string updateDoc = R""({"name.first":"GG"})"";; + + int errCode = E_OK; + JsonObject src = JsonObject::Parse(document, errCode); + EXPECT_EQ(errCode, E_OK); + JsonObject add = JsonObject::Parse(updateDoc, errCode); + EXPECT_EQ(errCode, E_OK); + EXPECT_EQ(JsonCommon::Append(src, add), -E_DATA_CONFLICT); + GLOGD("result: %s", src.Print().c_str()); +} + +HWTEST_F(DocumentDBJsonCommonTest, JsonObjectAppendTest013, TestSize.Level0) +{ + std::string document = R""({"name":["Tmn","BB","Alice"]})""; + std::string updateDoc = R""({"name":{"first":"GG"}})"";; + + int errCode = E_OK; + JsonObject src = JsonObject::Parse(document, errCode); + EXPECT_EQ(errCode, E_OK); + JsonObject add = JsonObject::Parse(updateDoc, errCode); + EXPECT_EQ(errCode, E_OK); + EXPECT_EQ(JsonCommon::Append(src, add), E_OK); + GLOGD("result: %s", src.Print().c_str()); +} + +HWTEST_F(DocumentDBJsonCommonTest, JsonObjectAppendTest014, TestSize.Level0) +{ + std::string document = R""({"name":{"first":"Xue","second":"Lang"}})""; + std::string updateDoc = R""({"name.0":"GG"})"";; + + int errCode = E_OK; + JsonObject src = JsonObject::Parse(document, errCode); + EXPECT_EQ(errCode, E_OK); + JsonObject add = JsonObject::Parse(updateDoc, errCode); + EXPECT_EQ(errCode, E_OK); + EXPECT_EQ(JsonCommon::Append(src, add), -E_DATA_CONFLICT); + GLOGD("result: %s", src.Print().c_str()); +} + +HWTEST_F(DocumentDBJsonCommonTest, JsonObjectAppendTest015, TestSize.Level0) +{ + std::string document = R""({"name":{"first":"Xue","second":"Lang"}})""; + std::string updateDoc = R""({"name.first":["GG","MM"]})"";; + + int errCode = E_OK; + JsonObject src = JsonObject::Parse(document, errCode); + EXPECT_EQ(errCode, E_OK); + JsonObject add = JsonObject::Parse(updateDoc, errCode); + EXPECT_EQ(errCode, E_OK); + EXPECT_EQ(JsonCommon::Append(src, add), E_OK); + GLOGD("result: %s", src.Print().c_str()); + + JsonObject itemCase = src.FindItem({"name", "first", "0"}, errCode); + EXPECT_EQ(errCode, E_OK); + EXPECT_EQ(itemCase.GetItemValue().GetStringValue(), "GG"); +} \ No newline at end of file diff --git a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/test/unittest/api/doucumentdb_api_test.cpp b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/test/unittest/oh_adapter/documentdb_jsonobject_test.cpp similarity index 40% rename from services/distributeddataservice/service/data_share/gaussdb_rd_Simple/test/unittest/api/doucumentdb_api_test.cpp rename to services/distributeddataservice/service/data_share/gaussdb_rd_Simple/test/unittest/oh_adapter/documentdb_jsonobject_test.cpp index 63ca9cab9ef11810fe59557d23b9a7b073eb8eef..fa16a27a019fc874f28bff8beef2a4bc71208ffa 100644 --- a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/test/unittest/api/doucumentdb_api_test.cpp +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/test/unittest/oh_adapter/documentdb_jsonobject_test.cpp @@ -14,16 +14,15 @@ */ #include - -#include "log_print.h" -#include "grd_base/grd_db_api.h" -#include "grd_base/grd_error.h" -#include "grd_document/grd_document_api.h" +#include "documentdb_test_utils.h" +#include "doc_errno.h" +#include "json_object.h" using namespace DocumentDB; using namespace testing::ext; +using namespace DocumentDBUnitTest; -class DocumentDBApiTest : public testing::Test { +class DocumentDBJsonObjectTest : public testing::Test { public: static void SetUpTestCase(void); static void TearDownTestCase(void); @@ -31,19 +30,19 @@ public: void TearDown(); }; -void DocumentDBApiTest::SetUpTestCase(void) +void DocumentDBJsonObjectTest::SetUpTestCase(void) { } -void DocumentDBApiTest::TearDownTestCase(void) +void DocumentDBJsonObjectTest::TearDownTestCase(void) { } -void DocumentDBApiTest::SetUp(void) +void DocumentDBJsonObjectTest::SetUp(void) { } -void DocumentDBApiTest::TearDown(void) +void DocumentDBJsonObjectTest::TearDown(void) { } @@ -54,38 +53,16 @@ void DocumentDBApiTest::TearDown(void) * @tc.require: * @tc.author: lianhuix */ -HWTEST_F(DocumentDBApiTest, OpenDBTest001, TestSize.Level1) +HWTEST_F(DocumentDBJsonObjectTest, JsonObjectTest001, TestSize.Level0) { - std::string path = "./document.db"; - GRD_DB *db = nullptr; - int status = GRD_DBOpen(path.c_str(), nullptr, 0, &db); - EXPECT_EQ(status, GRD_OK); - EXPECT_NE(db, nullptr); - GLOGD("Open DB test 001: status: %d", status); - - EXPECT_EQ(GRD_CreateCollection(db, "student", "", 0), GRD_OK); - - EXPECT_EQ(GRD_UpSertDoc(db, "student", "10001", "{name:\"Tom\",age:23}", 0), GRD_OK); - EXPECT_EQ(GRD_UpSertDoc(db, "student", "10001", "{name:\"Tom\",age:24}", 0), GRD_OK); + const std::string config = R""({"a":123,"b":{"c":234,"d":"12345"}})""; - EXPECT_EQ(GRD_DropCollection(db, "student", 0), GRD_OK); + int ret = E_OK; + JsonObject conf = JsonObject::Parse(config, ret); + EXPECT_EQ(ret, E_OK); - status = GRD_DBClose(db, 0); - EXPECT_EQ(status, GRD_OK); - db = nullptr; -} + ValueObject obj = conf.GetObjectByPath({"b", "c"}, ret); -/** - * @tc.name: OpenDBTest001 - * @tc.desc: Test open document db with NULL path - * @tc.type: FUNC - * @tc.require: - * @tc.author: lianhuix - */ -HWTEST_F(DocumentDBApiTest, OpenDBTest002, TestSize.Level1) -{ - GRD_DB *db = nullptr; - char *path = nullptr; - int status = GRD_DBOpen(path, nullptr, 0, &db); - EXPECT_EQ(status, GRD_INVALID_ARGS); -} + EXPECT_EQ(obj.GetValueType(), ValueObject::ValueType::VALUE_NUMBER); + EXPECT_EQ(obj.GetIntValue(), 234); +} \ No newline at end of file