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 007d13aae0180cabb81889dc380344b270185500..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 @@ -22,32 +22,64 @@ extern "C" { #endif // __cplusplus +// Error category #define GRD_OK 0 +// Error category #define GRD_NOT_SUPPORT (-1000) #define GRD_OVER_LIMIT (-2000) #define GRD_INVALID_ARGS (-3000) #define GRD_SYSTEM_ERR (-4000) +#define GRD_FAILED_FILE_OPERATION (-5000) +#define GRD_INVALID_FILE_FORMAT (-6000) #define GRD_INNER_ERR (-8000) #define GRD_RESOURCE_BUSY (-9000) #define GRD_NO_DATA (-11000) #define GRD_FAILED_MEMORY_ALLOCATE (-13000) #define GRD_FAILED_MEMORY_RELEASE (-14000) +#define GRD_DATA_CONFLICT (-16000) +#define GRD_NOT_AVAILABLE (-19000) #define GRD_INVALID_FORMAT (-37000) +#define GRD_REBUILD_DATABASE (-38000) -#define GRD_FIELD_NOT_FOUND (-5003002) -#define GRD_FIELD_TYPE_NOT_MATCH (-5003003) -#define GRD_LARGE_JSON_NEST (-5003004) +// not support +#define GRD_JSON_OPERATION_NOT_SUPPORT (-5001001) -#define GRD_UNVAILABLE_JSON_LIB (-5004001) +// Exceed limit +#define GRD_JSON_LEN_LIMIT (-5002001) +// Invalid parameter +#define GRD_FIELD_TYPE_NOT_MATCH (-5003001) +#define GRD_LARGE_JSON_NEST (-5003002) +#define GRD_INVALID_JSON_TYPE (-5003003) +#define GRD_INVALID_CONFIG_VALUE (-5003004) +#define GRD_INVALID_OPERATOR (-5003005) +#define GRD_INVALID_PROJECTION_FIELD (-5003006) +#define GRD_INVALID_PROJECTION_VALUE (-5003007) +#define GRD_ARRAY_INDEX_NOT_FOUND (-5003008) +// System err +#define GRD_JSON_LIB_HANDLE_FAILED (-5004001) + +// no data #define GRD_COLLECTION_NOT_FOUND (-5011001) #define GRD_RECORD_NOT_FOUND (-5011002) +#define GRD_DB_NOT_FOUND (-11003) +#define GRD_FIELD_NOT_FOUND (-5011004) -#define GRD_INVALID_JSON_FORMAT (-5037001) +// data conflicted +#define GRD_COLLECTION_CONFLICT (-5016001) +#define GRD_KEY_CONFLICT (-5016002) +#define GRD_FIELD_TYPE_CONFLICT (-5016003) +// Cursor or ResultSet not available +#define GRD_RESULT_SET_NOT_AVAILABLE (-5019001) + +// Invalid format +#define GRD_INVALID_JSON_FORMAT (-5037001) +#define GRD_INVALID_KEY_FORMAT (-5037002) +#define GRD_INVALID_COLLECTION_NAME (-5037003) #ifdef __cplusplus } #endif // __cplusplus 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 352d2162b9916b154b8a1d038ac0a03e7c735490..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 @@ -27,15 +27,22 @@ typedef struct GRD_DB GRD_DB; */ #define GRD_DB_OPEN_ONLY 0x00 #define GRD_DB_OPEN_CREATE 0x01 - +#define GRD_DB_OPEN_CHECK_FOR_ABNORMAL 0x02 //check data in database if close abnormally last time, + //if data is corrupted, rebuild the database +#define GRD_DB_OPEN_CHECK 0x04//check data in database when open database, if data is corrupted, rebuild the database. /** * @brief Close database config */ #define GRD_DB_CLOSE 0x00 #define GRD_DB_CLOSE_IGNORE_ERROR 0x01 -#define GRD_DB_ID_DISPLAY 0x01 +/** + * @brief flush database config + */ +#define GRD_DB_FLUSH_ASYNC 0x00 +#define GRD_DB_FLUSH_SYNC 0x01 +#define GRD_DOC_ID_DISPLAY 0x01 typedef struct Query { const char *filter; const char *projection; @@ -44,7 +51,11 @@ typedef struct Query { /** * @brief Flags for create and drop collection */ -#define FLAG_CHECK_UDEFINED_DUPLICAte_TABLE 1 +#define CHK_EXIST_COLLECTION 1 +#define CHK_NON_EXIST_COLLECTION 1 + +#define GRD_DOC_APPEND 0 +#define GRD_DOC_REPLACE 1 #ifdef __cplusplus } diff --git a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/include/grd_document/grd_document_api.h b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/include/grd_document/grd_document_api.h index fc062be4497d52feca315a8142c6a48c157b72ac..96ad01e3b0a7f4bac02fa77e3861bf317d6489eb 100644 --- a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/include/grd_document/grd_document_api.h +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/include/grd_document/grd_document_api.h @@ -37,6 +37,8 @@ int GRD_UpSertDoc(GRD_DB *db, const char *collectionName, const char *filter, co int GRD_DeleteDoc(GRD_DB *db, const char *collectionName, const char *filter, unsigned int flags); +int GRD_Flush(GRD_DB *db, unsigned int flags); + #ifdef __cplusplus } #endif 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_limit.h similarity index 76% rename from services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/common/include/doc_common.h rename to services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/common/include/doc_limit.h index a4919b67f803d95adf6ad4b521e9011ea0d2d532..38ceb943f64f845d8f87a30950712a586895d4b1 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_limit.h @@ -13,14 +13,10 @@ * limitations under the License. */ -#ifndef DOC_COMMON_H -#define DOC_COMMON_H - -#include -#include +#ifndef DOC_LIMIT_H +#define DOC_LIMIT_H namespace DocumentDB { -using Key = std::vector; -using Value = std::vector; -} // DocumentDB -#endif // DOC_COMMON_H \ No newline at end of file +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 new file mode 100644 index 0000000000000000000000000000000000000000..4494f01e9c1e5d8031e924a67ff0428a36303b34 --- /dev/null +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/common/include/json_common.h @@ -0,0 +1,47 @@ +/* +* 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 JSON_COMMON_H +#define JSON_COMMON_H + +#include +#include +#include +#include +#include "json_object.h" + +namespace DocumentDB { +class JsonCommon +{ +public: + JsonCommon() = default; + ~JsonCommon(); + + static ValueObject GetValueByFiled(JsonObject &node, const std::string& filed); + static bool CheckJsonField(JsonObject &node); + static bool CheckProjectionField(JsonObject &node); + static int ParseNode(JsonObject &Node, std::vector singlePath, std::vector> &resultPath, bool isFirstFloor); + static std::vector> ParsePath(const JsonObject &node); + static std::vector GetLeafValue(JsonObject &node); + + static int Append(const JsonObject &src, const JsonObject &add); + +private: + static bool CheckNode(JsonObject &Node, std::set filedSet, bool &errFlag); + static bool CheckProjectionNode(JsonObject &Node, std::set filedSet, bool &errFlag, bool isFirstFloor); + static int CheckLeafNode(JsonObject &Node, std::vector &leafValue); +}; +} // DocumentDB +#endif // JSON_COMMON_H \ No newline at end of file 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..fcb3da1e104a498199062579447580dea2c2ae56 --- /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 || static_cast(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..b60de5b19c912b46c8e0e1c58ef4737f8345cb8a --- /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/json_common.cpp b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/common/src/json_common.cpp new file mode 100644 index 0000000000000000000000000000000000000000..47a75e9c19df9005c94aa216b17228b2be4fd734 --- /dev/null +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/common/src/json_common.cpp @@ -0,0 +1,311 @@ +/* + * 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 "json_common.h" +#include "doc_errno.h" +#include "log_print.h" +#include "securec.h" + +namespace DocumentDB { +ValueObject JsonCommon::GetValueByFiled(JsonObject &node, const std::string& filed) +{ + while (!node.IsNull()) { + if (node.GetItemFiled() == filed) { + auto itemValue = node.GetItemValue(); + return itemValue; + } + if (node.GetNext().IsNull()) { + return ValueObject(); + } + auto nodeNew = node.GetNext(); + node = nodeNew; + } + return ValueObject(); +} + +int JsonCommon::CheckLeafNode(JsonObject &node, std::vector &leafValue) +{ + if (node.GetChild().IsNull()) { + auto itemValue = node.GetItemValue(); + leafValue.emplace_back(itemValue); + } + if (!node.GetChild().IsNull()) { + auto nodeNew = node.GetChild(); + CheckLeafNode(nodeNew, leafValue); + } + if (!node.GetNext().IsNull()) { + auto nodeNew = node.GetNext(); + CheckLeafNode(nodeNew, leafValue); + } + return E_OK; +} + +std::vector JsonCommon::GetLeafValue(JsonObject &node) +{ + std::vector leafValue; + CheckLeafNode(node, leafValue); + return leafValue; +} + +bool JsonCommon::CheckNode(JsonObject &node, std::set filedSet, bool &errFlag) +{ + if (!errFlag) { + return false; + } + std::string fieldName; + if (!node.IsNull()) { + int ret = 0; + fieldName = node.GetItemFiled(ret); + if (filedSet.find(fieldName) == filedSet.end()) { + if (ret == E_OK) { + filedSet.insert(fieldName); + } + if (ret == E_OK && fieldName.empty()) { + errFlag = false; + return false; + } + } + else { + errFlag = false; + return false; + } + for (int i = 0; i < fieldName.size(); i++) { + if (!((isalpha(fieldName[i])) || (isdigit(fieldName[i])) || '_' == fieldName[i])) { + errFlag = false; + return false; + } + if (i == 0 && (isdigit(fieldName[i]))) { + errFlag = false; + return false; + } + } + } + if (!node.GetChild().IsNull()) { + auto nodeNew = node.GetChild(); + std::set newFiledSet; + CheckNode(nodeNew, newFiledSet, errFlag); + } + if (!node.GetNext().IsNull()) { + auto nodeNew = node.GetNext(); + CheckNode(nodeNew, filedSet, errFlag); + } + return errFlag; +} + +bool JsonCommon::CheckJsonField(JsonObject &jsonObj) +{ + std::set filedSet; + bool errFlag = true; + return CheckNode(jsonObj, filedSet, errFlag); +} + +bool JsonCommon::CheckProjectionNode(JsonObject &node, std::set filedSet, bool &errFlag, bool isFirstFloor) +{ + if (!errFlag) { + return false; + } + std::string fieldName; + if (!node.IsNull()) { + int ret = 0; + fieldName = node.GetItemFiled(ret); + if (filedSet.find(fieldName) == filedSet.end()) { + if (ret == E_OK) { + filedSet.insert(fieldName); + } + if (ret == E_OK && fieldName.empty()) { + errFlag = false; + return false; + } + } + else { + errFlag = false; + return false; + } + for (int i = 0; i < fieldName.size(); i++) { + if (!((isalpha(fieldName[i])) || (isdigit(fieldName[i])) || ('_' == fieldName[i]) || (isFirstFloor && '.' == fieldName[i]))) { + errFlag = false; + return false; + } + if (i == 0 && (isdigit(fieldName[i]))) { + errFlag = false; + return false; + } + } + } + if (!node.GetChild().IsNull()) { + auto nodeNew = node.GetChild(); + std::set newFiledSet; + CheckProjectionNode(nodeNew, newFiledSet, errFlag, false); + } + if (!node.GetNext().IsNull()) { + auto nodeNew = node.GetNext(); + CheckProjectionNode(nodeNew, filedSet, errFlag, isFirstFloor); + } + return errFlag; +} + +bool JsonCommon::CheckProjectionField(JsonObject &jsonObj) +{ + std::set filedSet; + bool errFlag = true; + bool isFirstFloor = true; + return CheckProjectionNode(jsonObj, filedSet, errFlag, isFirstFloor); +} + +int JsonCommon::ParseNode(JsonObject &node, std::vector singlePath, std::vector> &resultPath, bool isFirstFloor) +{ + std::vector fatherPath; + if (isFirstFloor) { + std::string tempParseName; + std::vector allFiledsName; + std::string priFieldName = node.GetItemFiled(); + for (int j = 0; j < priFieldName.size(); j++) { + if (priFieldName[j] != '.') { + tempParseName = tempParseName + priFieldName[j]; + } + if (priFieldName[j] == '.' || j == priFieldName.size() - 1) { + allFiledsName.emplace_back(tempParseName); + tempParseName.clear(); + } + } + fatherPath = singlePath; + singlePath.insert(singlePath.end(), allFiledsName.begin(), allFiledsName.end()); + } else { + std::vector allFiledsName; + allFiledsName.emplace_back(node.GetItemFiled()); + fatherPath = singlePath; + singlePath.insert(singlePath.end(), allFiledsName.begin(), allFiledsName.end()); + } + if (!node.GetChild().IsNull() && node.GetChild().GetItemFiled() != "") { + auto nodeNew = node.GetChild(); + ParseNode(nodeNew, singlePath, resultPath, false); + } + else { + resultPath.emplace_back(singlePath); + } + if (!node.GetNext().IsNull()) { + auto nodeNew = node.GetNext(); + ParseNode(nodeNew, fatherPath, resultPath, isFirstFloor); + } + return 0; +} + +std::vector> JsonCommon::ParsePath(const JsonObject &root) +{ + std::vector> resultPath; + auto projectionJson = root.GetChild(); + if (projectionJson.IsNull()) { + GLOGE("projectionJson is null"); + } + std::vector singlePath; + 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) { + 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..a60e53f46fb3c4495fb77797c5adfa0650d0714a 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 @@ -17,32 +17,32 @@ #include "doc_errno.h" #include "document_store_manager.h" -#include "document_store.h" #include "grd_base/grd_error.h" +#include "log_print.h" #include "grd_type_inner.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 +53,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/document_check.cpp b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/executor/document/document_check.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e8370c8d4435ebfc89b26718fb247078783a09af --- /dev/null +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/executor/document/document_check.cpp @@ -0,0 +1,152 @@ +/* + * 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 "document_check.h" +#include "doc_errno.h" +#include "log_print.h" +#include "securec.h" + +namespace DocumentDB { +namespace { +constexpr const char *KEY_ID = "_id"; +constexpr const char *COLLECTION_PREFIX_GRD = "GRD_"; +constexpr const char *COLLECTION_PREFIX_GM_SYS = "GM_SYS_"; +const int MAX_COLLECTION_NAME = 511; +const int MAX_ID_LENS = 899; +const int JSON_DEEP_MAX = 4; + +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, int &errCode) +{ + if (collectionName.empty()) { + errCode = -E_INVALID_ARGS; + return false; + } + if (collectionName.length() > MAX_COLLECTION_NAME) { + errCode = -E_OVER_LIMIT; + return false; + } + if (CheckCollectionNamePrefix(collectionName, COLLECTION_PREFIX_GRD) || + CheckCollectionNamePrefix(collectionName, COLLECTION_PREFIX_GM_SYS)) { + GLOGE("Collection name is illegal"); + errCode = -E_INVALID_COLL_NAME_FORMAT; + return false; + } + lowerCaseName = collectionName; + std::transform(lowerCaseName.begin(), lowerCaseName.end(), lowerCaseName.begin(), [](unsigned char c){ + return std::tolower(c); + }); + return true; +} + +int CheckCommon::CheckFilter(JsonObject &filterObj) +{ + if (filterObj.GetDeep() > JSON_DEEP_MAX) { + GLOGE("filter's json deep is deeper than JSON_DEEP_MAX"); + return -E_INVALID_ARGS; + } + int ret = CheckIdFormat(filterObj); + if (ret != E_OK) { + GLOGE("Filter Id format is illegal"); + return ret; + } + if (!filterObj.GetChild().GetNext().IsNull()) { + return -E_INVALID_ARGS; + } + return E_OK; +} + +int CheckCommon::CheckIdFormat(JsonObject &filterJson) +{ + auto filterObjChild = filterJson.GetChild(); + auto idValue = JsonCommon::GetValueByFiled(filterObjChild, KEY_ID); + if (idValue.GetValueType() != ValueObject::ValueType::VALUE_STRING) { + return -E_INVALID_ARGS; + } + if (idValue.GetStringValue().length() > MAX_ID_LENS) { + return -E_OVER_LIMIT; + } + return E_OK; +} + +int CheckCommon::CheckDocument(JsonObject &documentObj) +{ + if (documentObj.GetDeep() > JSON_DEEP_MAX) { + GLOGE("documentObj's json deep is deeper than JSON_DEEP_MAX"); + return -E_INVALID_ARGS; + } + int ret = CheckIdFormat(documentObj); + if (ret != E_OK) { + GLOGE("Document Id format is illegal"); + return ret; + } + if (!documentObj.GetChild().IsNull()) { + auto documentObjChild = documentObj.GetChild(); + if (!JsonCommon::CheckJsonField(documentObjChild)) { + GLOGE("Document json field format is illegal"); + return -E_INVALID_ARGS; + } + } + return E_OK; +} + +bool CheckCommon::CheckProjection(JsonObject &projectionObj, std::vector> &path) +{ + if (projectionObj.GetDeep() > JSON_DEEP_MAX) { + GLOGE("projectionObj's json deep is deeper than JSON_DEEP_MAX"); + return -E_INVALID_ARGS; + } + if (!projectionObj.GetChild().IsNull()) { + auto projectionObjChild = projectionObj.GetChild(); + if (!JsonCommon::CheckProjectionField(projectionObjChild)) { + GLOGE("projection json field format is illegal"); + return false; + } + } + for (int i = 0; i < path.size(); i++) { + for (auto fieldName : path[i]) { + for (int i = 0; i < fieldName.size(); i++) { + if (!((isalpha(fieldName[i])) || (isdigit(fieldName[i])) || ('_' == fieldName[i]))) { + return false; + } + if (i == 0 && (isdigit(fieldName[i]))) { + return false; + } + } + } + } + return true; +} +} // namespace DocumentDB \ No newline at end of file diff --git a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/executor/document/document_check.h b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/executor/document/document_check.h new file mode 100644 index 0000000000000000000000000000000000000000..57769260990a02f236848048ba1e417f175ef46d --- /dev/null +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/executor/document/document_check.h @@ -0,0 +1,42 @@ +/* +* 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 DOCUMENT_CHECK_H +#define DOCUMENT_CHECK_H + +#include +#include +#include "json_common.h" + +namespace DocumentDB { +class JsonCommon; +class CheckCommon +{ +public: + CheckCommon() = default; + ~CheckCommon() = default; + + static bool CheckCollectionName(const std::string &collectionName, std::string &lowerCaseName, int &errCode); + static int CheckFilter(JsonObject &document); + static int CheckIdFormat(JsonObject &data); + static int CheckDocument(JsonObject &document); + static bool CheckProjection(JsonObject &projectionObj, std::vector> &path); +}; +using Key = std::vector; +using Value = std::vector; + +constexpr const char *COLL_PREFIX = "GRD_COLL_"; +} // DocumentDB +#endif // DOCUMENT_CHECK_H \ No newline at end of file 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..58ac1f5a9b69547e8d37ce049c3f4a0a950892a6 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,20 @@ #include "grd_document/grd_document_api.h" #include "grd_base/grd_error.h" #include "grd_type_inner.h" +#include "grd_resultset_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 +37,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 +48,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 +61,66 @@ 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); +} + +int GRD_InsertDoc(GRD_DB *db, const char *collectionName, const char *document, unsigned int flags) +{ + if (db == nullptr || db->store_ == nullptr || collectionName == nullptr || document == nullptr) { + return GRD_INVALID_ARGS; + } + int ret = db->store_->InsertDocument(collectionName, document, flags); + return TrasnferDocErr(ret); +} + +int GRD_DeleteDoc(GRD_DB *db, const char *collectionName, const char *filter, unsigned int flags) +{ + if (db == nullptr || db->store_ == nullptr || filter == nullptr || collectionName == nullptr) { + return GRD_INVALID_ARGS; + } + int ret = db->store_->DeleteDocument(collectionName, filter, flags); + int errCode = TrasnferDocErr(ret); + int deleteCount = 0; + switch (errCode) { + case GRD_OK: + deleteCount = 1; + return deleteCount; + break; + case GRD_NO_DATA: + deleteCount = 0; + return deleteCount; + break; + } + return errCode; +} + +int GRD_FindDoc(GRD_DB *db, const char *collectionName, Query query, unsigned int flags, GRD_ResultSet **resultSet) +{ + if (db == nullptr || db->store_ == nullptr || collectionName == nullptr || resultSet == nullptr || query.filter == nullptr + || query.projection == nullptr) { + return GRD_INVALID_ARGS; + } + GRD_ResultSet *grdResultSet = new (std::nothrow)GRD_ResultSet(); + if (grdResultSet == nullptr) { + GLOGE("Memory allocation failed!" ); + return -E_FAILED_MEMORY_ALLOCATE; + } + int ret = db->store_->FindDocument(collectionName, query.filter, query.projection, flags, grdResultSet); + if (ret != E_OK) { + delete grdResultSet; + *resultSet = nullptr; + return TrasnferDocErr(ret); + } + *resultSet = grdResultSet; + return TrasnferDocErr(ret); } + +int GRD_Flush(GRD_DB *db, unsigned int flags) +{ + return GRD_OK; +} \ No newline at end of file diff --git a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/executor/document/grd_resultset_api.cpp b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/executor/document/grd_resultset_api.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6db93e850e6fc3ed641f9e9e35d34de8d18e7104 --- /dev/null +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/executor/document/grd_resultset_api.cpp @@ -0,0 +1,71 @@ +/* +* 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 "grd_base/grd_error.h" +#include "doc_errno.h" +#include "grd_base/grd_error.h" +#include "grd_resultset_inner.h" +#include "grd_base/grd_resultset_api.h" +#include "log_print.h" + +using namespace DocumentDB; + +int GRD_Next(GRD_ResultSet *resultSet) +{ + if (resultSet == nullptr) { + GLOGE("resultSet is nullptr"); + return GRD_INVALID_ARGS; + }; + std::mutex dbMutex; + std::lock_guard lock(dbMutex); + int ret = resultSet->resultSet_.GetNext(); + return TrasnferDocErr(ret); +} + +int GRD_GetValue(GRD_ResultSet *resultSet, char **value) +{ + if (resultSet == nullptr) { + GLOGE("resultSet is nullptr,cant get value from it"); + return GRD_INVALID_ARGS; + }; + char *val = nullptr; + int ret = resultSet->resultSet_.GetValue(&val); + if (val == nullptr) { + GLOGE("Value that get from resultSet is nullptr"); + return GRD_NOT_AVAILABLE; + } + *value = val; + return TrasnferDocErr(ret); +} + +int GRD_FreeValue(char *value) +{ + if (value == nullptr) { + return GRD_OK; + } + delete[] value; + return GRD_OK; +} + +int GRD_FreeResultSet(GRD_ResultSet *resultSet) +{ + if (resultSet == nullptr) { + return GRD_INVALID_ARGS; + } + resultSet->resultSet_.EraseCollection(); + delete resultSet; + return GRD_OK; +} \ No newline at end of file diff --git a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/executor/include/grd_format_config.h b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/executor/include/grd_format_config.h new file mode 100644 index 0000000000000000000000000000000000000000..06e7bbeea54dae8f45f6899cac23bb9d0e5c92dc --- /dev/null +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/executor/include/grd_format_config.h @@ -0,0 +1,32 @@ +/* +* 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 GRD_FORMAT_CONFIG_H +#define GRD_FORMAT_CONFIG_H + + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +#define COLLECTION_LENS_MAX (512 * 1024) +#define JSON_LENS_MAX (512 * 1024) +#define JSON_DEEP_MAX (4) +#define KEY_ID ("_id") + +#ifdef __cplusplus +} +#endif // __cplusplus +#endif // GRD_FORMAT_CONFIG_H \ No newline at end of file diff --git a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/executor/include/grd_resultset_inner.h b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/executor/include/grd_resultset_inner.h new file mode 100644 index 0000000000000000000000000000000000000000..d840ad79370a4cca20b76e5ed877aadde6d1eda8 --- /dev/null +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/executor/include/grd_resultset_inner.h @@ -0,0 +1,26 @@ +/* +* 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 GRD_RESULTSET_INNER_H +#define GRD_RESULTSET_INNER_H + +#include "doc_errno.h" +#include "grd_base/grd_error.h" +#include "result_set.h" + +typedef struct GRD_ResultSet { + DocumentDB::ResultSet resultSet_; +} GRD_ResultSet; +#endif // GRD_RESULTSET_INNER_H \ No newline at end of file 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..602ca359a09bbc3e64f006ad4362e63e1fe7945d 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 @@ -17,21 +17,24 @@ #define COLLECTION_H #include -#include "doc_common.h" +#include "document_check.h" #include "kv_store_executor.h" namespace DocumentDB { class Collection { public: - Collection(std::string name, KvStoreExecutor *executor); + Collection(const std::string &name, KvStoreExecutor *executor); + Collection(const Collection &a) {}; + Collection() {}; ~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); + bool FindDocument(); + private: std::string name_; KvStoreExecutor *executor_ = nullptr; 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 c481c57396c08363f40b983e57189e01933bfeb2..6f159683b4aa4939307949173f8e37a83b1e4e37 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; @@ -26,5 +25,20 @@ constexpr int E_UNFINISHED = E_BASE + 7; constexpr int E_OUT_OF_MEMORY = E_BASE + 8; constexpr int E_SECUREC_ERROR = E_BASE + 9; constexpr int E_SYSTEM_API_FAIL = E_BASE + 10; +constexpr int E_FILE_OPERATION = E_BASE + 11; +constexpr int E_OVER_LIMIT = E_BASE + 12; +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_COLL_NAME_FORMAT = E_BASE + 18; +constexpr int E_INVALID_JSON_FORMAT = E_BASE + 40; +constexpr int E_JSON_PATH_NOT_EXISTS = E_BASE + 41; +constexpr int E_RESOURCE_BUSY = E_BASE + 50; +constexpr int E_FAILED_MEMORY_ALLOCATE = E_BASE + 51; + +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..0e06db1f181c058b2e8352fa93232ca95fad801e 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,25 +18,33 @@ #include #include +#include -#include "collection.h" #include "kv_store_executor.h" +#include "collection.h" +class GRD_ResultSet; namespace DocumentDB { class DocumentStore { 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); + int InsertDocument(const std::string &collection, const std::string &document, int flag); + int DeleteDocument(const std::string &collection, const std::string &filter, int flag); + int FindDocument(const std::string &collection, const std::string &filter, const std::string &projection, int flags, GRD_ResultSet *grdResultSet); + KvStoreExecutor *GetExecutor(int errCode); + int EraseCollection(const std::string collectionName); private: + int GetViewType(JsonObject &jsonObj, bool &viewType); + std::mutex dbMutex_; KvStoreExecutor *executor_ = nullptr; - std::map collections_; + std::map collections_; }; } // DocumentDB #endif // DOCUMENT_STORE_H \ No newline at end of file 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/include/projection_tree.h b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/interface/include/projection_tree.h new file mode 100644 index 0000000000000000000000000000000000000000..15920bd41d178faff1529555bbba0bede7e6e581 --- /dev/null +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/interface/include/projection_tree.h @@ -0,0 +1,37 @@ +#ifndef PROJECTION_TREE_H +#define PROJECTION_TREE_H + +#include +#include +#include +#include "doc_errno.h" +#include "json_common.h" +#include "log_print.h" + +namespace DocumentDB { +struct ProjectionNode { + std::unordered_map SonNode; + bool isDeepest; + int Deep; + int ViewType; + ProjectionNode() { + Deep = 0; + isDeepest = true; + } + int DeleteProjectionNode(); + ~ProjectionNode () { + DeleteProjectionNode(); + } +}; +class ProjectionTree { +public: + ProjectionTree(); + ~ProjectionTree(); + + int ParseTree(std::vector> &path); + bool SearchTree(std::vector &singlePath, int &index); +private: + ProjectionNode node_; +}; +} // DocumentDB +#endif // PROJECTION_TREE_H \ No newline at end of file diff --git a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/interface/include/result_set.h b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/interface/include/result_set.h new file mode 100644 index 0000000000000000000000000000000000000000..dc87115e27b6e9fcdf544c3fea5d0e831f73e130 --- /dev/null +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/interface/include/result_set.h @@ -0,0 +1,53 @@ +/* +* 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 RESULTSET_H +#define RESULTSET_H + +#include +#include "grd_base/grd_type_export.h" +#include "projection_tree.h" +#include "vector" +#include "doc_errno.h" +#include "json_object.h" +#include "securec.h" +#include "document_check.h" +#include "document_store.h" + +namespace DocumentDB { +class ResultSet { +public: + ResultSet(); + ~ResultSet(); + + int Init(DocumentStore *store, const std::string collectionName, ValueObject &key, std::vector> &path, bool ifShowId, bool viewType); + int GetNext(); + int GetValue(char **value); + int EraseCollection(); +private: + int CutJsonBranch(std::string &jsonData); + int CheckCutNode(JsonObject *node, std::vector singleCutPath, std::vector> &allCutPath); + DocumentStore *store_ = nullptr; + std::string collectionName_; + ValueObject key_; + bool ifShowId_ = false; + bool viewType_ = false; + ProjectionTree projectionTree_; + std::vector> projectionPath_; + int index_ = 0; + std::vector findValue_; +}; +} // DocumentDB +#endif //RESULTSET_H \ No newline at end of file diff --git a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/interface/include/result_set_common.h b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/interface/include/result_set_common.h new file mode 100644 index 0000000000000000000000000000000000000000..dfef9c26d88b963d3204ad1f03e0fefafd8278a6 --- /dev/null +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/interface/include/result_set_common.h @@ -0,0 +1,31 @@ +/* +* 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 RESULTSET_COMMON_H +#define RESULTSET_COMMON_H + +#include +#include "grd_base/grd_type_export.h" +#include "vector" +#include "doc_errno.h" +#include "result_set.h" + +namespace DocumentDB { +class ValueObject; +int InitResultSet(DocumentStore *store, const std::string collectionName, ValueObject &key, std::vector> &path, bool ifShowId, bool viewType, + ResultSet &resultSet); +} // DocumentDB +#endif //RESULTSET_COMMON_H + 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..e76a2239a8f95d77843bef6ea70eb05cca8c0e68 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 "document_check.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() @@ -34,22 +44,82 @@ int Collection::PutDocument(const Key &key, const Value &document) return executor_->PutData(name_, key, document); } +bool Collection::FindDocument() +{ + if (executor_ == nullptr) { + return -E_INVALID_ARGS; + } + int errCode = 0; + return executor_->IsCollectionExists(name_, errCode); +} + int Collection::GetDocument(const Key &key, Value &document) const { - return E_OK; + if (executor_ == nullptr) { + return -E_INVALID_ARGS; + } + return executor_->GetData(name_, key, document); } int Collection::DeleteDocument(const Key &key) { - return E_OK; + if (executor_ == nullptr) { + return -E_INVALID_ARGS; + } + return executor_->DelData(name_, key); } -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..24eaca7608808d242c49a3c52cc61b0242a609bd --- /dev/null +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/interface/src/doc_errno.cpp @@ -0,0 +1,74 @@ +/* +* 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; + case -E_INVALID_COLL_NAME_FORMAT: + outErr = GRD_INVALID_COLLECTION_NAME; + break; + case -E_RESOURCE_BUSY: + outErr = GRD_RESOURCE_BUSY; + break; + case -E_FAILED_MEMORY_ALLOCATE: + outErr = GRD_FAILED_MEMORY_ALLOCATE; + 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..67b2c2d4ca5a2373f8c7d233a66b342e70134a42 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,9 +14,21 @@ */ #include "document_store.h" +#include "collection_option.h" +#include "document_check.h" #include "doc_errno.h" +#include "grd_base/grd_type_export.h" +#include "log_print.h" +#include "result_set_common.h" +#include "grd_resultset_inner.h" namespace DocumentDB { +const int COLLECTION_LENS_MAX = 512 * 1024; +const int JSON_LENS_MAX = 512 * 1024; +const int JSON_DEEP_MAX = 4; +constexpr const char *KEY_ID = "_id"; +const bool caseIsSensitive = true; + DocumentStore::DocumentStore(KvStoreExecutor *executor) : executor_(executor) { } @@ -26,16 +38,78 @@ 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; + int errCode = E_OK; + if (!CheckCommon::CheckCollectionName(name, lowerCaseName, errCode)) { + GLOGE("Check collection name invalid. %d", errCode); + return errCode; + } + + 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; + int errCode = E_OK; + if (!CheckCommon::CheckCollectionName(name, lowerCaseName, errCode)) { + GLOGE("Check collection name invalid. %d", errCode); + return errCode; + } + + if (flags != 0 && flags != CHK_NON_EXIST_COLLECTION) { + GLOGE("Check flags invalid."); + return -E_INVALID_ARGS; + } + + bool ignoreNonExists = (flags != CHK_NON_EXIST_COLLECTION); + 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 +119,240 @@ 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; + int errCode = E_OK; + if (!CheckCommon::CheckCollectionName(collection, lowerCaseCollName, errCode)) { + GLOGE("Check collection name invalid. %d", errCode); + return errCode; + } - Key key(filter.begin(), filter.end()); - Value value(document.begin(), document.end()); + // 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_); + + std::string docId(filter.begin(), filter.end()); + bool isReplace = (flags & GRD_DOC_REPLACE == GRD_DOC_REPLACE); + std::lock_guard lock(dbMutex_); + return coll.UpsertDocument(docId, document, isReplace); +} + +int DocumentStore::InsertDocument(const std::string &collection, const std::string &document, int flag) +{ + if (flag != 0) { + GLOGE("InsertDocument flag is not zero"); + return -E_INVALID_ARGS; + } + std::string lowerCaseCollName; + int errCode = E_OK; + if (!CheckCommon::CheckCollectionName(collection, lowerCaseCollName, errCode)) { + GLOGE("Check collection name invalid. %d", errCode); + return errCode; + } + auto coll = Collection(collection, executor_); + if (document.length() + 1 > JSON_LENS_MAX) { + GLOGE("document's length is larger than JSON_LENS_MAX"); + return -E_OVER_LIMIT; + } + JsonObject documentObj = JsonObject::Parse(document, errCode, caseIsSensitive); + if (errCode != E_OK) { + GLOGE("Document Parsed faild"); + return errCode; + } + errCode = CheckCommon::CheckDocument(documentObj); + if (errCode != E_OK) { + return errCode; + } + auto documentObjChild = documentObj.GetChild(); + auto idValue = JsonCommon::GetValueByFiled(documentObjChild, KEY_ID); + std::string id = idValue.GetStringValue(); + Key key(id.begin(), id.end()); + Value value(document.begin(), document.end()); + std::lock_guard lock(dbMutex_); return coll.PutDocument(key, value); } + +int DocumentStore::DeleteDocument(const std::string &collection, const std::string &filter, int flag) +{ + if (flag != 0) { + GLOGE("DeleteDocument flag is not zero"); + return -E_INVALID_ARGS; + } + std::string lowerCaseCollName; + int errCode = E_OK; + if (!CheckCommon::CheckCollectionName(collection, lowerCaseCollName, errCode)) { + GLOGE("Check collection name invalid. %d", errCode); + return errCode; + } + auto coll = Collection(collection, executor_); + if (filter.empty()) { + GLOGE("Filter is empty"); + return -E_INVALID_ARGS; + } + if (filter.length() + 1 > JSON_LENS_MAX) { + GLOGE("filter's length is larger than JSON_LENS_MAX"); + return -E_OVER_LIMIT; + } + JsonObject filterObj = JsonObject::Parse(filter, errCode, caseIsSensitive); + if (errCode != E_OK) { + GLOGE("filter Parsed faild"); + return errCode; + } + errCode = CheckCommon::CheckFilter(filterObj); + if (errCode != E_OK) { + return errCode; + } + auto filterObjChild = filterObj.GetChild(); + auto idValue = JsonCommon::GetValueByFiled(filterObjChild, KEY_ID); + std::string id = idValue.GetStringValue(); + Key key(id.begin(), id.end()); + std::lock_guard lock(dbMutex_); + return coll.DeleteDocument(key); +} +KvStoreExecutor *DocumentStore::GetExecutor(int errCode) +{ + return executor_; +} +int DocumentStore::FindDocument(const std::string &collection, const std::string &filter, const std::string &projection, + int flags, GRD_ResultSet *grdResultSet) +{ + if (flags != 0 && flags != GRD_DOC_ID_DISPLAY) { + GLOGE("FindDocument flag is illegal"); + return -E_INVALID_ARGS;; + } + std::string lowerCaseCollName; + int errCode = E_OK; + if (!CheckCommon::CheckCollectionName(collection, lowerCaseCollName, errCode)) { + GLOGE("Check collection name invalid. %d", errCode); + return errCode; + } + if (filter.length() + 1 > JSON_LENS_MAX) { + GLOGE("filter's length is larger than JSON_LENS_MAX"); + return -E_OVER_LIMIT; + } + JsonObject filterObj = JsonObject::Parse(filter, errCode, caseIsSensitive); + if (errCode != E_OK) { + GLOGE("filter Parsed faild"); + return errCode; + } + errCode = CheckCommon::CheckFilter(filterObj); + if (errCode != E_OK) { + return errCode; + } + auto filterObjChild = filterObj.GetChild(); + auto idValue = JsonCommon::GetValueByFiled(filterObjChild, KEY_ID); + if (projection.length() + 1 > JSON_LENS_MAX) { + GLOGE("projection's length is larger than JSON_LENS_MAX"); + return -E_OVER_LIMIT; + } + JsonObject projectionObj = JsonObject::Parse(projection, errCode, caseIsSensitive); + if (errCode != E_OK) { + GLOGE("projection Parsed faild"); + return errCode; + } + bool viewType = false; + std::vector> allPath; + if (projection != "{}") { + allPath = JsonCommon::ParsePath(projectionObj); + if (!CheckCommon::CheckProjection(projectionObj, allPath)) { + GLOGE("projection format unvalid"); + return -E_INVALID_ARGS; + } + if (GetViewType(projectionObj, viewType) != E_OK) { + GLOGE("GetViewType faild"); + return -E_INVALID_ARGS; + } + } + bool ifShowId = false; + if (flags == GRD_DOC_ID_DISPLAY) { + ifShowId = true; + } + if (collections_.find(collection) != collections_.end()) { + GLOGE("DB is resource busy"); + return -E_RESOURCE_BUSY; + } + auto coll = Collection(collection, executor_); + std::lock_guard lock(dbMutex_); + if (!coll.FindDocument()) { + GLOGE("no corresponding table name"); + return -E_INVALID_ARGS; + } + int ret = InitResultSet(this, collection, idValue, allPath, ifShowId, viewType, grdResultSet->resultSet_); + if (ret == E_OK) { + collections_[collection] = nullptr; + } + if (ret != E_OK) { + collections_.erase(collection); + } + return ret; +} +int DocumentStore::EraseCollection(const std::string collectionName) { + if (collections_.find(collectionName) != collections_.end()) { + collections_.erase(collectionName); + return E_OK; + } + GLOGE("erase collection failed"); +} +int DocumentStore::GetViewType(JsonObject &jsonObj, bool &viewType) { + auto leafValue = JsonCommon::GetLeafValue(jsonObj); + if (leafValue.size() == 0) { + return E_INVALID_ARGS; + } + bool viewFlag = false; + for (int i = 0; i < leafValue.size(); i++) { + switch (leafValue[i].GetValueType()) { + case ValueObject::ValueType::VALUE_BOOL: + if (leafValue[i].GetBoolValue()) { + if (i != 0 && !viewType) { + return -E_INVALID_ARGS; + } + viewType = true; + } + else { + if (i != 0 && viewType) { + return E_INVALID_ARGS; + } + viewType == false; + } + break; + case ValueObject::ValueType::VALUE_STRING: + if (leafValue[i].GetStringValue() == "") { + if (i != 0 && !viewType) { + return -E_INVALID_ARGS; + } + viewType = true; + } + else { + return -E_INVALID_ARGS; + } + break; + case ValueObject::ValueType::VALUE_NUMBER: + if (leafValue[i].GetIntValue() == 0) { + if (i != 0 && viewType) { + return -E_INVALID_ARGS; + } + viewType = false; + } + else { + if (i != 0 && !viewType) { + return E_INVALID_ARGS; + } + viewType = true; + } + break; + default: + return E_INVALID_ARGS; + } + } + return E_OK; +} } // 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 b386b44dc8055b878c497b85d8449dd8a8c9ed5e..f4f2faac4d8a4ea1e9d49bb287b3da4821fb9d8f 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,43 +22,121 @@ #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); +} + +bool CheckDBCreate(unsigned int flags, const std::string &path) +{ + if ((flags & GRD_DB_OPEN_CREATE) == 0 && !OSAPI::CheckPathExistence(path)) { + return false; + } + return true; +} +} + +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; + } + + if (!CheckDBCreate(flags, path)) { + GLOGE("Open db failed, file no exists."); + 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) { + GLOGE("Memory allocation failed!" ); + return -E_FAILED_MEMORY_ALLOCATE; + } + 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); - return errCode; + errCode = -E_FILE_OPERATION; + return false; } + 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/interface/src/projection_tree.cpp b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/interface/src/projection_tree.cpp new file mode 100644 index 0000000000000000000000000000000000000000..edf6f3568215d97e258f18658f1b67842f093143 --- /dev/null +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/interface/src/projection_tree.cpp @@ -0,0 +1,76 @@ +#include +#include "projection_tree.h" + + +namespace DocumentDB { +const int JSON_DEEP_MAX = 4; + +ProjectionTree::ProjectionTree() { +} + +ProjectionTree::~ProjectionTree() { +} + +int ProjectionTree::ParseTree(std::vector> &path) { + ProjectionNode *node = &node_; + if (node == NULL) { + return E_OK; + } + for (int i = 0; i < path.size(); i++) { + node = &node_; + for (int j = 0; j < path[i].size(); j++) { + if (node->SonNode[path[i][j]] != nullptr) { + node = node->SonNode[path[i][j]]; + if (j < path[i].size() - 1 && node->isDeepest) { + return -E_INVALID_ARGS; + } + if (j == path[i].size() - 1 && !node->isDeepest) { + return -E_INVALID_ARGS; + } + } + else { + auto tempNode = new (std::nothrow) ProjectionNode; + if (tempNode == nullptr) { + GLOGE("Memory allocation failed!" ); + return -E_FAILED_MEMORY_ALLOCATE; + } + tempNode->Deep = node->Deep + 1; + if (tempNode->Deep > JSON_DEEP_MAX) { + delete tempNode; + return -E_INVALID_ARGS; + } + node->isDeepest = false; + node->SonNode[path[i][j]] = tempNode; + node = node->SonNode[path[i][j]]; + } + } + } + return E_OK; +} + +bool ProjectionTree::SearchTree(std::vector &singlePath, int &index) { + ProjectionNode *node = &node_; + for (int i = 0; i < singlePath.size(); i++) { + if (node->isDeepest) { + index = i; + } + if (node->SonNode[singlePath[i]] != nullptr) { + node = node->SonNode[singlePath[i]]; + } + else { + return false; + } + } + return true; +} + +int ProjectionNode::DeleteProjectionNode() { + for (auto item : SonNode) { + if (item.second != nullptr) { + delete item.second; + item.second = nullptr; + } + } + return E_OK; +} +} // namespace DocumentDB \ No newline at end of file diff --git a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/interface/src/result_set.cpp b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/interface/src/result_set.cpp new file mode 100644 index 0000000000000000000000000000000000000000..038ada1d9211182c6fe2b4dfe00834b039f6f803 --- /dev/null +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/interface/src/result_set.cpp @@ -0,0 +1,162 @@ +/* +* 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 "result_set.h" +#include "log_print.h" + +namespace DocumentDB { +constexpr const char *KEY_ID = "_id"; + +ResultSet::ResultSet() +{ + +} +ResultSet::~ResultSet() +{ + +} +int ResultSet::EraseCollection() +{ + if (store_ != nullptr) { + store_->EraseCollection(collectionName_); + } + return E_OK; +} +int ResultSet::Init(DocumentStore *store, const std::string collectionName, ValueObject &key, std::vector> &path, bool ifShowId, bool viewType) +{ + store_ = store; + collectionName_ = collectionName; + key_ = key; + projectionPath_ = path; + if (projectionTree_.ParseTree(path) == -E_INVALID_ARGS) { + GLOGE("Parse ProjectionTree failed"); + return -E_INVALID_ARGS; + } + ifShowId_ = ifShowId; + viewType_ = viewType; + findValue_.reserve(1 + 1); + return E_OK; +} + +int ResultSet::GetNext() +{ + index_++; + if (index_ != 1) { + if (findValue_.size() != 0) { + findValue_.pop_back(); + } + return -E_NO_DATA; + } + std::string idValue = key_.GetStringValue(); + if (idValue.empty()) { + GLOGE("id is empty"); + return -E_NO_DATA; + } + Key key(idValue.begin(), idValue.end()); + Value document; + int errCode = 0; + auto coll = Collection(collectionName_, store_->GetExecutor(errCode)); + errCode = coll.GetDocument(key, document); + if (errCode == -E_NOT_FOUND) { + GLOGE("Cant get value from db"); + return -E_NO_DATA; + } + std::string jsonData(document.begin(), document.end()); + CutJsonBranch(jsonData); + findValue_.emplace_back(jsonData); + return E_OK; +} + +int ResultSet::GetValue(char **value) +{ + if (findValue_.size() == 0) { + GLOGE("The value vector in resultSet is empty"); + return -E_NO_DATA; + } + auto jsonData = findValue_.back(); + char *jsonstr = new char[jsonData.size() + 1]; + if (jsonstr == nullptr) { + GLOGE("Memory allocation failed!" ); + return -E_FAILED_MEMORY_ALLOCATE; + } + errno_t err = strcpy_s(jsonstr, jsonData.size() + 1, jsonData.c_str()); + if (err != 0) { + GLOGE("strcpy_s failed"); + delete[] jsonstr; + return -E_NO_DATA;; + } + *value = jsonstr; + return E_OK; +} + +int ResultSet::CheckCutNode(JsonObject *node, std::vector singlePath, std::vector> &allCutPath) +{ + if (node == nullptr) { + GLOGE("No node to cut"); + return -E_NO_DATA; + } + singlePath.emplace_back(node->GetItemFiled()); + int index = 0; + if (!projectionTree_.SearchTree(singlePath, index) && index == 0) { + allCutPath.emplace_back(singlePath); + } + if (!node->GetChild().IsNull()) { + auto nodeNew = node->GetChild(); + CheckCutNode(&nodeNew, singlePath, allCutPath); + } + if (!node->GetNext().IsNull()) { + singlePath.pop_back(); + auto nodeNew = node->GetNext(); + CheckCutNode(&nodeNew, singlePath, allCutPath); + } + return E_OK; +} +int ResultSet::CutJsonBranch(std::string &jsonData) +{ + int errCode; + JsonObject cjsonObj = JsonObject::Parse(jsonData, errCode, true); + if (errCode != E_OK) { + GLOGE("jsonData Parsed faild"); + return errCode; + } + std::vector> allCutPath; + bool idFlag = false; + if (viewType_) { + std::vector singlePath; + auto cjsonObjChild = cjsonObj.GetChild(); + errCode = CheckCutNode(&cjsonObjChild, singlePath, allCutPath); + if (errCode != E_OK) { + GLOGE("The node in CheckCutNode is nullptr"); + return errCode; + } + for (int i = 0; i < allCutPath.size(); i++) { + if (!ifShowId_ || allCutPath[i][0] != KEY_ID) { + cjsonObj.DeleteItemDeeplyOnTarget(allCutPath[i]); + } + } + } + if (!viewType_) { + for (int i = 0; i < projectionPath_.size(); i++) { + cjsonObj.DeleteItemDeeplyOnTarget(projectionPath_[i]); + } + if (!ifShowId_) { + std::vector idPath; + idPath.emplace_back(KEY_ID); + cjsonObj.DeleteItemDeeplyOnTarget(idPath); + } + } + jsonData = cjsonObj.Print(); + return E_OK; +} +} // namespace DocumentDB diff --git a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/interface/src/result_set_common.cpp b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/interface/src/result_set_common.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ad195a6b65a6085fa0e97c467631c0f136a3a0ba --- /dev/null +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/interface/src/result_set_common.cpp @@ -0,0 +1,30 @@ +/* +* 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" +#include "result_set_common.h" +#include +#include + +namespace DocumentDB { +class ValueObject; +int InitResultSet(DocumentStore *store, const std::string collectionName, ValueObject &key, std::vector> &path, bool ifShowId, bool viewType, + ResultSet &resultSet) +{ + return resultSet.Init(store, collectionName, key, path, ifShowId, viewType); +} +} + 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 new file mode 100644 index 0000000000000000000000000000000000000000..12510f1dc5c85f1784a0c0a5c8d29f9651240a6e --- /dev/null +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/oh_adapter/include/json_object.h @@ -0,0 +1,113 @@ +/* +* 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 JSON_OBJECT_H +#define JSON_OBJECT_H + +#include +#include +#include +#include + +#include "cJSON.h" + +namespace DocumentDB { +class ValueObject { +public: + enum class ValueType { + VALUE_NULL = 0, + VALUE_BOOL, + VALUE_NUMBER, + VALUE_STRING, + }; + + ValueObject() = default; + explicit ValueObject(bool val); + explicit ValueObject(double val); + explicit ValueObject(const char *val); + explicit ValueObject(const std::string &val); + + ValueType GetValueType() const; + bool GetBoolValue() const; + int64_t GetIntValue() const; + double GetDoubleValue() const; + std::string GetStringValue() const; + +private: + ValueType valueType = ValueType::VALUE_NULL; + union { + bool boolValue; + double doubleValue; + }; + std::string stringValue; +}; +using JsonFieldPath = std::vector; + +using ResultValue = ValueObject; +using JsonFieldPath = std::vector; + +class JsonObject { +public: + static JsonObject Parse(const std::string &jsonStr, int &errCode, bool caseSensitive = false); + + ~JsonObject (); + + std::string Print() const; + + JsonObject GetObjectItem(const std::string &field, int &errCode); + JsonObject GetArrayItem(int index, int &errCode); + + 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; + void SetItemValue(const ValueObject &value) const; + + std::string GetItemFiled() const; + std::string GetItemFiled(int &errCode) 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); + int DeleteItemDeeplyOnTarget(const JsonFieldPath &path); + bool IsNull() const; + int GetDeep(); + enum class Type { + JSON_LEAF, + JSON_OBJECT, + JSON_ARRAY + }; + Type GetType() const; + +private: + JsonObject(); + int Init(const std::string &str); + + int GetDeep(cJSON *cjson); + int CheckNumber(cJSON *cjson, int &errCode); + + cJSON *cjson_ = nullptr; + int jsonDeep_ = 0; + bool isOwner_ = false; + bool caseSensitive_ = false; +}; +} // DocumentDB +#endif // JSON_OBJECT_H + 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..0f81f5b1f2ac08ca2da726bb2ef8f2f348392742 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 @@ -17,7 +17,7 @@ #define KV_STORE_EXECUTOR_H #include -#include "doc_common.h" +#include "document_check.h" namespace DocumentDB { class KvStoreExecutor { @@ -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..c1df107236329fd3961755f57886aca64e9a8e14 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 "doc_common.h" +#include "db_config.h" +#include "document_check.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/json_operator.h b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/oh_adapter/json_operator.h deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 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 new file mode 100644 index 0000000000000000000000000000000000000000..2d3ed07e9d2e4a2cec3d25c5a5d1a77aa4c0c47d --- /dev/null +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/src/oh_adapter/src/json_object.cpp @@ -0,0 +1,544 @@ +/* +* 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 "json_object.h" +#include +#include "doc_errno.h" +#include "log_print.h" + +namespace DocumentDB { + +namespace { +const int COLLECTION_LENS_MAX = 512 * 1024; +const int JSON_LENS_MAX = 512 * 1024; + +bool IsNumber(const std::string &str) +{ + return std::all_of(str.begin(), str.end(), [](char c) { + return std::isdigit(c); + }); +} +} + +ValueObject::ValueObject(bool val) +{ + valueType = ValueType::VALUE_BOOL; + boolValue = val; +} + +ValueObject::ValueObject(double val) +{ + valueType = ValueType::VALUE_NUMBER; + doubleValue = val; +} + +ValueObject::ValueObject(const char *val) +{ + valueType = ValueType::VALUE_STRING; + stringValue = val; +} + +ValueObject::ValueObject(const std::string &val) +{ + valueType = ValueType::VALUE_STRING; + stringValue = val; +} + +ValueObject::ValueType ValueObject::GetValueType() const +{ + return valueType; +} + +bool ValueObject::GetBoolValue() const +{ + return boolValue; +} + +int64_t ValueObject::GetIntValue() const +{ + return static_cast(doubleValue + 0.5); +} + +double ValueObject::GetDoubleValue() const +{ + return doubleValue; +} + +std::string ValueObject::GetStringValue() const +{ + return stringValue; +} + +JsonObject JsonObject::Parse(const std::string &jsonStr, int &errCode, bool caseSensitive) +{ + JsonObject obj; + errCode = obj.Init(jsonStr); + obj.caseSensitive_ = caseSensitive; + return obj; +} + +JsonObject::JsonObject() +{ + cjson_ = nullptr; +} + +JsonObject::~JsonObject() +{ + if (isOwner_) { + cJSON_Delete(cjson_); + } +} + +bool JsonObject::IsNull() const +{ + if (cjson_ == nullptr) { + return true; + } + 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() +{ + if (cjson_ == nullptr) { + GLOGE("cJson is nullptr,deep is 0"); + return 0; + } + if (jsonDeep_ != 0) { + return jsonDeep_; + } + jsonDeep_ = GetDeep(cjson_); + return jsonDeep_; + +} +int JsonObject::GetDeep(cJSON *cjson) +{ + if (cjson->child == nullptr) { + jsonDeep_ = 0; + return 0; // leaf node + } + + int depth = -1; + cJSON *child = cjson->child; + while (child != nullptr) { + depth = std::max(depth, GetDeep(child) + 1); + child = child->next; + } + jsonDeep_ = depth; + return depth; +} + + +int JsonObject::CheckNumber(cJSON *item, int &errCode) +{ + if (item != NULL && cJSON_IsNumber(item)) { + double value = cJSON_GetNumberValue(item); + if (value > __DBL_MAX__ || value < -__DBL_MAX__) { + errCode = E_INVALID_ARGS; + } + } + if (item->child != nullptr) { + return CheckNumber(item->child, errCode); + } + if (item->next != nullptr) { + return CheckNumber(item->next, errCode); + } + return E_OK; +} + +int JsonObject::Init(const std::string &str) +{ + const char *end = NULL; + isOwner_ = true; + cjson_ = cJSON_ParseWithOpts(str.c_str(), &end, true); + if (cjson_ == nullptr) { + GLOGE("Json's format is wrong"); + return -E_INVALID_JSON_FORMAT; + } + + if (cjson_->type != cJSON_Object) { + GLOGE("after Parse,cjson_'s type is not cJSON_Object"); + return -E_INVALID_ARGS; + } + + int ret = 0; + CheckNumber(cjson_, ret); + if (ret == E_INVALID_ARGS) { + GLOGE("Int value is larger than double"); + return -E_INVALID_ARGS; + } + return E_OK; +} + +std::string JsonObject::Print() const +{ + if (cjson_ == nullptr) { + return ""; + } + char *ret = cJSON_PrintUnformatted(cjson_); + std::string str = ret; + cJSON_free(ret); + return str; +} + +JsonObject JsonObject::GetObjectItem(const std::string &field, int &errCode) +{ + if (cjson_ == nullptr || (cjson_->type & cJSON_Object) != cJSON_Object) { + errCode = -E_INVALID_ARGS; + return JsonObject(); + } + + JsonObject item; + item.caseSensitive_ = caseSensitive_; + if (caseSensitive_) { + item.cjson_ = cJSON_GetObjectItemCaseSensitive(cjson_, field.c_str()); + } else { + item.cjson_ = cJSON_GetObjectItem(cjson_, field.c_str()); + } + if (item.cjson_ == nullptr) { + errCode = -E_NOT_FOUND; + } + return item; +} + +JsonObject JsonObject::GetArrayItem(int index, int &errCode) +{ + if (cjson_ == nullptr || (cjson_->type & cJSON_Array) != cJSON_Array) { + errCode = -E_INVALID_ARGS; + return JsonObject(); + } + + JsonObject item; + item.caseSensitive_ = caseSensitive_; + item.cjson_ = cJSON_GetArrayItem(cjson_, index); + if (item.cjson_ == nullptr) { + errCode = -E_NOT_FOUND; + } + return item; +} + +JsonObject JsonObject::GetNext() const +{ + if (cjson_ == nullptr) { + return JsonObject(); + } + JsonObject next; + next.caseSensitive_ = caseSensitive_; + if (cjson_->next == nullptr) { + return JsonObject(); + } + next.cjson_ = cjson_->next; + return next; +} + +JsonObject JsonObject::GetChild() const +{ + if (cjson_ == nullptr) { + return JsonObject(); + } + JsonObject child; + child.caseSensitive_ = caseSensitive_; + if (cjson_->child == nullptr) { + return JsonObject(); + } + child.cjson_ = cjson_->child; + return child; +} + +int JsonObject::DeleteItemFromObject(const std::string &field) +{ + if (field.empty()) { + return E_OK; + } + cJSON_DeleteItemFromObjectCaseSensitive(cjson_, field.c_str()); + 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) { + return ValueObject(); + } + + ValueObject value; + switch (cjson_->type) { + case cJSON_False: + case cJSON_True: + return ValueObject(cjson_->type == cJSON_True); + case cJSON_NULL: + return ValueObject(); + case cJSON_Number: + return ValueObject(cjson_->valuedouble); + case cJSON_String: + return ValueObject(cjson_->valuestring); + case cJSON_Array: + case cJSON_Object: + default: + GLOGW("Invalid json type: %d", cjson_->type); + break; + } + + 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) { + cJSON *tail = cjson_; + while(tail->next != nullptr) { + tail = tail->next; + } + + int index = 0; + cJSON *head = cjson_; + while (head->prev != tail) { + head = head->prev; + index++; + } + return std::to_string(index); + } else { + return cjson_->string; + } +} + +std::string JsonObject::GetItemFiled(int &errCode) const +{ + if (cjson_ == nullptr) { + errCode = E_INVALID_ARGS; + return ""; + } + if (cjson_->string == nullptr) { + errCode = E_INVALID_ARGS; + return ""; + } + errCode = E_OK; + return cjson_->string; +} + +cJSON *GetChild(cJSON *cjson, const std::string &field, bool caseSens) +{ + if (cjson->type == cJSON_Object) { + if (caseSens) { + return cJSON_GetObjectItemCaseSensitive(cjson, field.c_str()); + } else { + return cJSON_GetObjectItem(cjson, field.c_str()); + } + } else if (cjson->type == cJSON_Array) { + if (!IsNumber(field)) { + GLOGW("Invalid json field path, expect array index."); + return nullptr; + } + return cJSON_GetArrayItem(cjson, std::stoi(field)); + } + + GLOGW("Invalid json field type, expect object or array."); + return nullptr; +} + +cJSON *MoveToPath(cJSON *cjson, const JsonFieldPath &jsonPath, bool caseSens) +{ + for (const auto &field : jsonPath) { + cjson = GetChild(cjson, field, caseSens); + if (cjson == nullptr) { + break; + } + } + return cjson; +} + +bool JsonObject::IsFieldExists(const JsonFieldPath &jsonPath) const +{ + return (MoveToPath(cjson_, jsonPath, caseSensitive_) != nullptr); +} + +JsonObject JsonObject::FindItem(const JsonFieldPath &jsonPath, int &errCode) const +{ + if (jsonPath.empty()) { + 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; + item.caseSensitive_ = caseSensitive_; + item.cjson_ = findItem; + return item; +} + +ValueObject JsonObject::GetObjectByPath(const JsonFieldPath &jsonPath, int &errCode) const +{ + JsonObject objGot = FindItem(jsonPath, errCode); + if (errCode != E_OK) { + GLOGE("Get json value object failed. %d", errCode); + return {}; + } + return objGot.GetItemValue(); +} + +int JsonObject::DeleteItemOnTarget(const JsonFieldPath &path) +{ + if (path.empty()) { + return -E_INVALID_ARGS; + } + + std::string fieldName = path.back(); + JsonFieldPath patherPath = path; + patherPath.pop_back(); + + int errCode = E_OK; + cJSON *nodeFather = MoveToPath(cjson_, patherPath, caseSensitive_); + if (nodeFather == nullptr) { + GLOGE("Delete item failed, json field path not found."); + return -E_JSON_PATH_NOT_EXISTS; + } + + if (nodeFather->type == cJSON_Object) { + if (caseSensitive_) { + cJSON_DeleteItemFromObjectCaseSensitive(nodeFather, fieldName.c_str()); + } else { + cJSON_DeleteItemFromObject(nodeFather, fieldName.c_str()); + } + } else if (nodeFather->type == cJSON_Array) { + if (!IsNumber(fieldName)) { + GLOGW("Invalid json field path, expect array index."); + return -E_JSON_PATH_NOT_EXISTS; + } + cJSON_DeleteItemFromArray(nodeFather, std::stoi(fieldName)); + } + + return E_OK; +} + +int JsonObject::DeleteItemDeeplyOnTarget(const JsonFieldPath &path) +{ + if (path.empty()) { + return -E_INVALID_ARGS; + } + + std::string fieldName = path.back(); + JsonFieldPath patherPath = path; + patherPath.pop_back(); + + int errCode = E_OK; + cJSON *nodeFather = MoveToPath(cjson_, patherPath, caseSensitive_); + if (nodeFather == nullptr) { + GLOGE("Delete item failed, json field path not found."); + return -E_JSON_PATH_NOT_EXISTS; + } + + if (nodeFather->type == cJSON_Object) { + if (caseSensitive_) { + cJSON_DeleteItemFromObjectCaseSensitive(nodeFather, fieldName.c_str()); + if (nodeFather->child == nullptr && path.size() > 1) { + JsonFieldPath fatherPath(path.begin(), path.end() - 1); + DeleteItemDeeplyOnTarget(fatherPath); + } + } else { + cJSON_DeleteItemFromObject(nodeFather, fieldName.c_str()); + if (nodeFather->child == nullptr && path.size() > 1) { + JsonFieldPath fatherPath(path.begin(), path.end() - 1); + DeleteItemDeeplyOnTarget(fatherPath); + } + } + } else if (nodeFather->type == cJSON_Array) { + if (!IsNumber(fieldName)) { + GLOGW("Invalid json field path, expect array index."); + return -E_JSON_PATH_NOT_EXISTS; + } + cJSON_DeleteItemFromArray(nodeFather, std::stoi(fieldName)); + if (nodeFather->child == nullptr && path.size() > 1) { + JsonFieldPath fatherPath(path.begin(), path.end() - 1); + DeleteItemDeeplyOnTarget(fatherPath); + } + } + + return E_OK; +} +} // 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..937a2793808080d53f3e5cc69ac4c61d3f5fdf82 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 "document_check.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,11 @@ 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); + if (errCode == -E_ERROR) { + GLOGE("Cant find the collection"); + return -E_INVALID_ARGS; + } return errCode; } return E_OK; @@ -50,51 +107,154 @@ 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::DelData(const std::string &collName, const Key &key) +{ + if (dbHandle_ == nullptr) { + GLOGE("Invalid db handle."); + return -E_ERROR; + } + int errCode = 0; + if (!IsCollectionExists(collName, errCode)) { + return -E_INVALID_ARGS; + } + Value valueRet; + if (GetData(collName, key, valueRet) != E_OK) { + return -E_NO_DATA; + } + std::string sql = "DELETE FROM '" + collName + "' WHERE key=?;"; + 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); + if (errCode == -E_ERROR) { + GLOGE("Cant find the collection"); + return -E_NO_DATA; + } + } + return errCode; } -int SqliteStoreExecutor::CreateCollection(const std::string &name, int flag) +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 '" + name + "' (key BLOB PRIMARY KEY, value BLOB);"; + 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_delete_test.cpp b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/test/unittest/api/ documentdb_delete_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..42d4d2d7463cbb2bed72d0edaac6a18dd3fd0fdd --- /dev/null +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/test/unittest/api/ documentdb_delete_test.cpp @@ -0,0 +1,326 @@ +/* +* 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 "grd_base/grd_db_api.h" +#include "grd_document/grd_document_api.h" +#include "grd_base/grd_error.h" +#include "grd_base/grd_type_export.h" +#include "grd_type_inner.h" +#include "grd_resultset_inner.h" +#include "grd_base/grd_resultset_api.h" + +using namespace testing::ext; +namespace { +constexpr const char *COLLECTION_NAME = "student"; +constexpr const char *NULL_JSON_STR = "{}"; +const int MAX_COLLECTION_LENS = 511; +std::string path = "./document.db"; +GRD_DB *g_db = nullptr; +} + +class DocumentDeleteApiTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); + void InsertDoc(const char *collectionName, const char *document); +}; +void DocumentDeleteApiTest::SetUpTestCase(void) +{ + std::string path = "./document.db"; + int status = GRD_DBOpen(path.c_str(), nullptr, GRD_DB_OPEN_CREATE, &g_db); + EXPECT_EQ(status, GRD_OK); +} + +void DocumentDeleteApiTest::TearDownTestCase(void) +{ + EXPECT_EQ(GRD_DBClose(g_db, 0), GRD_OK); + remove(path.c_str()); +} + +void DocumentDeleteApiTest::SetUp(void) +{ + /** + * @tc.steps:step1. Create Collection + * @tc.expected: step1. GRD_OK + */ + EXPECT_EQ(GRD_CreateCollection(g_db, "student", "", 0), GRD_OK); + /** + * @tc.steps:step2. Insert many document in order to delete + * @tc.expected: step2. GRD_OK + */ + const char *document1 = + "{ \ + \"_id\" : \"1\", \ + \"name\": \"xiaoming\", \ + \"address\": \"beijing\", \ + \"age\" : 15, \ + \"friend\" : {\"name\" : \"David\", \"sex\" : \"female\", \"age\" : 90}, \ + \"subject\": [\"math\", \"English\", \"music\"] \ + }"; + const char *document2 = + "{ \ + \"_id\" : \"2\", \ + \"name\": \"ori\", \ + \"address\": \"beijing\", \ + \"age\" : 15, \ + \"friend\" : {\"name\" : \"David\", \"sex\" : \"female\", \"age\" : 90}, \ + \"subject\": [\"math\", \"English\", \"music\"] \ + }"; + const char *document3 = + "{ \ + \"_id\" : \"3\", \ + \"name\": \"David\", \ + \"address\": \"beijing\", \ + \"age\" : 15, \ + \"friend\" : {\"name\" : \"David\", \"sex\" : \"female\", \"age\" : 90}, \ + \"subject\": [\"math\", \"English\", \"music\"] \ + }"; + DocumentDeleteApiTest::InsertDoc(COLLECTION_NAME, document1); + DocumentDeleteApiTest::InsertDoc(COLLECTION_NAME, document2); + DocumentDeleteApiTest::InsertDoc(COLLECTION_NAME, document3); +} + +void DocumentDeleteApiTest::TearDown(void) +{ + /** + * @tc.steps:step1. Call GRD_DropCollection to drop the collection + * @tc.expected: step1. GRD_OK + */ + EXPECT_EQ(GRD_DropCollection(g_db, COLLECTION_NAME, 0), GRD_OK); +} + +static void ChkDeleteResWithFilter(const char *filter) +{ + /** + * @tc.steps:step1. Try to find the deleted document + * @tc.expected: step1. GRD_OK + */ + Query query; + query.filter = filter; + const char *projection = "{}"; + query.projection = projection; + GRD_ResultSet *resultSet = nullptr; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, 0, &resultSet), GRD_OK); + /** + * @tc.steps:step2. The resultset should be NULL + * @tc.expected: step2. GRD_OK + */ + EXPECT_EQ(GRD_Next(resultSet), GRD_NO_DATA); + EXPECT_EQ(GRD_FreeResultSet(resultSet), GRD_OK); +} + +void DocumentDeleteApiTest::InsertDoc(const char *collectionName, const char *document) +{ + EXPECT_EQ(GRD_InsertDoc(g_db, collectionName, document, 0), GRD_OK); +} + +/** + * @tc.name: DocumentDelete001 + * @tc.desc: Delete with NULL filter + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentDeleteApiTest, DeleteDBTest001, TestSize.Level1) +{ + /** + * @tc.steps:step1. Delete all the document + * @tc.expected: step1. GRD_INVALID_ARGS + */ + EXPECT_EQ(GRD_DeleteDoc(g_db, COLLECTION_NAME, NULL_JSON_STR, 0), GRD_INVALID_ARGS); +} + +/** + * @tc.name: DocumentDelete002 + * @tc.desc: Delete with filter which has no _id + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentDeleteApiTest, DeleteDBTest002, TestSize.Level1) +{ + /** + * @tc.steps:step1. Delete with filter which has no _id + * @tc.expected: step1. GRD_INVALID_ARGS + */ + const char *filter = "{\"age\" : 15}"; + EXPECT_EQ(GRD_DeleteDoc(g_db, COLLECTION_NAME, filter, 0), GRD_INVALID_ARGS); +} + +/** + * @tc.name: DocumentDelete003 + * @tc.desc: Delete with filter which has more than one fileds. + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentDeleteApiTest, DeleteDBTest003, TestSize.Level1) +{ + /** + * @tc.steps:step1. Delete with filter which has more than one fileds. + * @tc.expected: step1. GRD_INVALID_ARGS + */ + const char *filter = "{\"_id\" : \"1\", \"age\" : 15}"; + EXPECT_EQ(GRD_DeleteDoc(g_db, COLLECTION_NAME, filter, 0), GRD_INVALID_ARGS); +} + +/** + * @tc.name: DocumentDelete004 + * @tc.desc: Test delete with invalid input + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentDeleteApiTest, DeleteDBTest004, TestSize.Level1) +{ + /** + * @tc.steps:step1. Test delete with un-zero flags + * @tc.expected: step1. GRD_INVALID_ARGS + */ + const char *filter1 = "{\"_id\" : \"1\"}"; + EXPECT_EQ(GRD_DeleteDoc(g_db, COLLECTION_NAME, filter1, 1), GRD_INVALID_ARGS); + /** + * @tc.steps:step2. Test delete with NULL collection name + * @tc.expected: step2. GRD_INVALID_ARGS + */ + // const char *filter2 = "{\"_id\" : \"1\"}"; + // EXPECT_EQ(GRD_DeleteDoc(g_db, NULL, filter2, 0), GRD_INVALID_ARGS); + /** + * @tc.steps:step3. Test delete with empty collection name + * @tc.expected: step3. GRD_INVALID_ARGS + */ + // const char *filter1 = "{\"_id\" : \"1\"}"; + // EXPECT_EQ(GRD_DeleteDoc(g_db, "", filter1, 1), GRD_INVALID_ARGS); +} + +/** + * @tc.name: DocumentDelete005 + * @tc.desc: Test delete with same collection name + * but one is uppercase(delete) and the other is lowercase(insert) + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentDeleteApiTest, DeleteDBTest005, TestSize.Level1) +{ + /** + * @tc.step1: Test delete with same collection name + * but one is uppercase(delete) and the other is lowercase(insert) + * @tc.expected: step1. GRD_INVALID_ARGS + */ + const char *filter = "{\"_id\" : \"1\"}"; + EXPECT_EQ(GRD_DeleteDoc(g_db, COLLECTION_NAME, filter, 0), 1); + /** + * @tc.step2: Check whether doc has been deleted compeletely + * @tc.expected: step2. GRD_OK + */ + ChkDeleteResWithFilter(filter); +} + +/** + * @tc.name: DocumentDelete006 + * @tc.desc: Test delete after calling find interface + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentDeleteApiTest, DeleteDBTest006, TestSize.Level1) +{ + /** + * @tc.step1: Create filter with _id and get the record according to filter condition. + * @tc.expected: step1. GRD_OK + */ + const char *filter = "{\"_id\" : \"1\"}"; + GRD_ResultSet *resultSet = nullptr; + Query query = {filter, "{}"}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, 0, &resultSet), GRD_OK); + EXPECT_EQ(GRD_DeleteDoc(g_db, COLLECTION_NAME, filter, 0), 1); + /** + * @tc.step2: Invoke GRD_Next to get the next matching value. Release resultSet. + * @tc.expected: step2. Cannot get next record, return GRD_NO_DATA. + */ + EXPECT_EQ(GRD_Next(resultSet), GRD_NO_DATA); + EXPECT_EQ(GRD_FreeResultSet(resultSet), GRD_OK); +} + +/** + * @tc.name: DocumentDelete007 + * @tc.desc: Test delete with too long collectionName. + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentDeleteApiTest, DeleteDBTest007, TestSize.Level1) +{ + const char *filter = "{\"_id\" : \"1\"}"; + string collectionName1(MAX_COLLECTION_LENS, 'a'); + EXPECT_EQ(GRD_CreateCollection(g_db, collectionName1.c_str(), "", 0), GRD_OK); + EXPECT_EQ(GRD_DeleteDoc(g_db, collectionName1.c_str(), filter, 0), 0); + EXPECT_EQ(GRD_DropCollection(g_db, collectionName1.c_str(), 0), GRD_OK); + + string collectionName2(MAX_COLLECTION_LENS + 1, 'a'); + EXPECT_EQ(GRD_DeleteDoc(g_db, collectionName2.c_str(), filter, 0), GRD_OVER_LIMIT); +} + + +/** + * @tc.name: DocumentDelete008 + * @tc.desc: Test delete with invalid NULL input for all parameters. + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentDeleteApiTest, DeleteDBTest008, TestSize.Level1) +{ + /** + * @tc.steps:step1. Delete with filter which has more than one fileds. + * @tc.expected: step1. GRD_INVALID_ARGS + */ + const char *filter = "{\"_id\" : \"1\"}"; + EXPECT_EQ(GRD_DeleteDoc(NULL, COLLECTION_NAME, filter, 0), GRD_INVALID_ARGS); + EXPECT_EQ(GRD_DeleteDoc(g_db, NULL, filter, 0), GRD_INVALID_ARGS); + EXPECT_EQ(GRD_DeleteDoc(g_db, "", filter, 0), GRD_INVALID_ARGS); + EXPECT_EQ(GRD_DeleteDoc(g_db, COLLECTION_NAME, NULL, 0), GRD_INVALID_ARGS); + EXPECT_EQ(GRD_DeleteDoc(g_db, COLLECTION_NAME, "", 0), GRD_INVALID_ARGS); + EXPECT_EQ(GRD_DeleteDoc(g_db, "notExisted", filter, 0), GRD_INVALID_ARGS); +} + +/** + * @tc.name: DocumentDelete010 + * @tc.desc: Test delete document when filter _id is int and string + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentDeleteApiTest, DeleteDBTest010, TestSize.Level1) +{ + /** + * @tc.steps:step1. Test delete document when filter _id is int and string. + * @tc.expected: step1. GRD_INVALID_ARGS + */ + std::vector filterVec = {R"({"_id" : 1})", R"({"_id":[1, 2]})", + R"({"_id" : {"t1" : 1}})", R"({"_id":null})", R"({"_id":true})", R"({"_id" : 1.333})", R"({"_id" : -2.0})"}; + for (const auto &item : filterVec) { + EXPECT_EQ(GRD_DeleteDoc(g_db, COLLECTION_NAME, item.c_str(), 0), GRD_INVALID_ARGS); + } + const char *filter = "{\"_id\" : \"1\"}"; + EXPECT_EQ(GRD_DeleteDoc(g_db, COLLECTION_NAME, filter, 0), 1); + +} + diff --git a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/test/unittest/api/ documentdb_find_test.cpp b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/test/unittest/api/ documentdb_find_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0051493bbfe6176822c3d1b856fc82ca14706dcf --- /dev/null +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/test/unittest/api/ documentdb_find_test.cpp @@ -0,0 +1,1449 @@ +/* +* 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 "grd_base/grd_db_api.h" +#include "grd_document/grd_document_api.h" +#include "grd_base/grd_error.h" +#include "grd_base/grd_type_export.h" +#include "grd_type_inner.h" +#include "grd_base/grd_resultset_api.h" +#include "doc_errno.h" +#include "log_print.h" +#include "documentdb_test_utils.h" +#include "grd_resultset_inner.h" + +using namespace testing::ext; +namespace { +std::string path = "./document.db"; +GRD_DB *g_db = nullptr; +constexpr const char *COLLECTION_NAME = "student"; +constexpr const char *NULL_JSON_STR = "{}"; +const int E_OK = 0; +const int MAX_COLLECTION_NAME = 511; +const int INT_MAX = 2147483647; +const int INT_MIN = -2147483648; +const int MAX_ID_LENS = 899; +static const char *g_document1 = "{\"_id\" : \"1\", \"name\":\"doc1\",\"item\":\"journal\",\"personInfo\":\ + {\"school\":\"AB\", \"age\" : 51}}"; +static const char *g_document2 = "{\"_id\" : \"2\", \"name\":\"doc2\",\"item\": 1, \"personInfo\":\ + [1, \"my string\", {\"school\":\"AB\", \"age\" : 51}, true, {\"school\":\"CD\", \"age\" : 15}, false]}"; +static const char *g_document3 = "{\"_id\" : \"3\", \"name\":\"doc3\",\"item\":\"notebook\",\"personInfo\":\ + [{\"school\":\"C\", \"age\" : 5}]}"; +static const char *g_document4 = "{\"_id\" : \"4\", \"name\":\"doc4\",\"item\":\"paper\",\"personInfo\":\ + {\"grade\" : 1, \"school\":\"A\", \"age\" : 18}}"; +static const char *g_document5 = "{\"_id\" : \"5\", \"name\":\"doc5\",\"item\":\"journal\",\"personInfo\":\ + [{\"sex\" : \"woma\", \"school\" : \"B\", \"age\" : 15}, {\"school\":\"C\", \"age\" : 35}]}"; +static const char *g_document6 = "{\"_id\" : \"6\", \"name\":\"doc6\",\"item\":false,\"personInfo\":\ + [{\"school\":\"B\", \"teacher\" : \"mike\",\"age\" : 15}, {\"school\":\"C\", \"teacher\" : \"moon\",\"age\" : 20}]}"; +static const char *g_document7 = "{\"_id\" : \"7\", \"name\":\"doc7\",\"item\":\"fruit\",\"other_Info\":\ + [{\"school\":\"BX\", \"age\" : 15}, {\"school\":\"C\", \"age\" : 35}]}"; +static const char *g_document8 = "{\"_id\" : \"8\", \"name\":\"doc8\",\"item\":true,\"personInfo\":\ + [{\"school\":\"B\", \"age\" : 15}, {\"school\":\"C\", \"age\" : 35}]}"; +static const char *g_document9 = "{\"_id\" : \"9\", \"name\":\"doc9\",\"item\": true}"; +static const char *g_document10 = "{\"_id\" : \"10\", \"name\":\"doc10\", \"parent\" : \"kate\"}"; +static const char *g_document11 = "{\"_id\" : \"11\", \"name\":\"doc11\", \"other\" : \"null\"}"; +static const char *g_document12 = "{\"_id\" : \"12\", \"name\":\"doc12\",\"other\" : null}"; +static const char *g_document13 = "{\"_id\" : \"13\", \"name\":\"doc13\",\"item\" : \"shoes\",\"personInfo\":\ + {\"school\":\"AB\", \"age\" : 15}}"; +static const char *g_document14 = "{\"_id\" : \"14\", \"name\":\"doc14\",\"item\" : true,\"personInfo\":\ + [{\"school\":\"B\", \"age\" : 15}, {\"school\":\"C\", \"age\" : 85}]}"; +static const char *g_document15 = "{\"_id\" : \"15\", \"name\":\"doc15\",\"personInfo\":[{\"school\":\"C\", \"age\" : 5}]}"; +static const char *g_document16 = "{\"_id\" : \"16\", \"name\":\"doc16\", \"nested1\":{\"nested2\":{\"nested3\":\ + {\"nested4\":\"ABC\", \"field2\":\"CCC\"}}}}"; +static const char *g_document17 = "{\"_id\" : \"17\", \"name\":\"doc17\",\"personInfo\":\"oh,ok\"}"; +static const char *g_document18 = "{\"_id\" : \"18\", \"name\":\"doc18\",\"item\" : \"mobile phone\",\"personInfo\":\ + {\"school\":\"DD\", \"age\":66}, \"color\":\"blue\"}"; +static const char *g_document19 = "{\"_id\" : \"19\", \"name\":\"doc19\",\"ITEM\" : true,\"PERSONINFO\":\ + {\"school\":\"AB\", \"age\":15}}"; +static const char *g_document20 = "{\"_id\" : \"20\", \"name\":\"doc20\",\"ITEM\" : true,\"personInfo\":\ + [{\"SCHOOL\":\"B\", \"AGE\":15}, {\"SCHOOL\":\"C\", \"AGE\":35}]}"; +static std::vectorg_data = {g_document1, g_document2, g_document3, g_document4, g_document5, g_document6, g_document7, + g_document8, g_document9, g_document10, g_document11, g_document12, g_document13, g_document14, g_document15, g_document16, g_document17, g_document18, g_document19, g_document20}; + +static void InsertData(GRD_DB *g_db, const char *collectionName) +{ + for (const auto &item : g_data) { + EXPECT_EQ(GRD_InsertDoc(g_db, collectionName, item, 0), GRD_OK); + } +} + +static void CompareValue(const char *value, const char *targetValue) +{ + int errCode; + DocumentDB::JsonObject valueObj = DocumentDB::JsonObject::Parse(value, errCode); + EXPECT_EQ(errCode, E_OK); + DocumentDB::JsonObject targetValueObj = DocumentDB::JsonObject::Parse(targetValue, errCode); + EXPECT_EQ(errCode, E_OK); + EXPECT_EQ(valueObj.Print(), targetValueObj.Print()); +} +} + +class DocumentFindApiTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); + void InsertDoc(const char *collectionName, const char *document); +}; +void DocumentFindApiTest::SetUpTestCase(void) +{ + std::string path = "./document.db"; + int status = GRD_DBOpen(path.c_str(), nullptr, GRD_DB_OPEN_CREATE, &g_db); + EXPECT_EQ(status, GRD_OK); + EXPECT_EQ(GRD_CreateCollection(g_db, COLLECTION_NAME, "", 0), GRD_OK); + EXPECT_NE(g_db, nullptr); +} + +void DocumentFindApiTest::TearDownTestCase(void) +{ + EXPECT_EQ(GRD_DBClose(g_db, 0), GRD_OK); + remove(path.c_str()); +} + +void DocumentFindApiTest::SetUp(void) +{ + InsertData(g_db, "student"); +} + +void DocumentFindApiTest::TearDown(void) +{ +} + + +/** + * @tc.name: DocumentFindApiTest001 + * @tc.desc: Test Insert document db + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentFindApiTest, DocumentFindApiTest001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Create filter with _id and get the record according to filter condition. + * @tc.expected: step1. Succeed to get the record, the matching record is g_document6. + */ + const char *filter = "{\"_id\" : \"6\"}"; + GRD_ResultSet *resultSet = nullptr; + Query query = {filter, "{}"}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, 1, &resultSet), GRD_OK); + EXPECT_EQ(GRD_Next(resultSet), GRD_OK); + char *value = NULL; + EXPECT_EQ(GRD_GetValue(resultSet, &value), GRD_OK); + CompareValue(value, g_document6); + EXPECT_EQ(GRD_FreeValue(value), GRD_OK); + /** + * @tc.steps: step2. Invoke GRD_Next to get the next matching value. Release resultSet. + * @tc.expected: step2. Cannot get next record, return GRD_NO_DATA. + */ + EXPECT_EQ(GRD_Next(resultSet), GRD_NO_DATA); + EXPECT_EQ(GRD_GetValue(resultSet, &value), GRD_NOT_AVAILABLE); + EXPECT_EQ(GRD_FreeResultSet(resultSet), GRD_OK); +} + +/** + * @tc.name: DocumentFindApiTest002 + * @tc.desc: Test filter with multiple fields and _id. + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentFindApiTest, DocumentFindApiTest002, TestSize.Level1) +{ + /** + * @tc.steps: step1. Create filter with multiple and _id. and get the record according to filter condition. + * @tc.expected: step1. Faild to get the record, the result is GRD_INVALID_ARGS, GRD_GetValue return GRD_NOT_AVAILABLE and GRD_Next return GRD_NO_DATA. + */ + const char *filter = "{\"_id\" : \"6\", \"name\":\"doc6\"}"; + GRD_ResultSet *resultSet = nullptr; + Query query = {filter, "{}"}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, 1, &resultSet), GRD_INVALID_ARGS); + /** + * @tc.steps: step2. Invoke GRD_Next to get the next matching value. Release resultSet. + * @tc.expected: step2. GRD_GetValue return GRD_INVALID_ARGS and GRD_Next return GRD_INVALID_ARGS. + */ + EXPECT_EQ(GRD_Next(resultSet), GRD_INVALID_ARGS); + char *value = NULL; + EXPECT_EQ(GRD_GetValue(resultSet, &value), GRD_INVALID_ARGS); + EXPECT_EQ(GRD_FreeValue(value), GRD_OK); + EXPECT_EQ(GRD_FreeResultSet(resultSet), GRD_INVALID_ARGS); +} + +/** + * @tc.name: DocumentFindApiTest004 + * @tc.desc: test filter with string filter without _id. + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentFindApiTest, DocumentFindApiTest004, TestSize.Level1) +{ + /** + * @tc.steps: step1. Create filter without _id and get the record according to filter condition. + * @tc.expected: step1. Faild to get the record, the result is GRD_INVALID_ARGS, + */ + const char *filter = "{\"name\":\"doc6\"}"; + GRD_ResultSet *resultSet = nullptr; + Query query = {filter, "{}"}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, 1, &resultSet), GRD_INVALID_ARGS); + + /** + * @tc.steps: step2. Invoke GRD_Next to get the next matching value. Release resultSet. + * @tc.expected: step2. GRD_GetValue return GRD_INVALID_ARGS and GRD_Next return GRD_INVALID_ARGS. + */ + char *value = NULL; + EXPECT_EQ(GRD_Next(resultSet), GRD_INVALID_ARGS); + EXPECT_EQ(GRD_GetValue(resultSet, &value), GRD_INVALID_ARGS); + EXPECT_EQ(GRD_FreeValue(value), GRD_OK); + EXPECT_EQ(GRD_FreeResultSet(resultSet), GRD_INVALID_ARGS); +} + +/** + * @tc.name: DocumentFindApiTest005 + * @tc.desc: test filter field with other word. + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentFindApiTest, DocumentFindApiTest005, TestSize.Level1) +{ + /** + * @tc.steps: step1. Create filter with _id and number + * @tc.expected: step1. Faild to get the record, the result is GRD_INVALID_ARGS, + */ + GRD_ResultSet *resultSet1 = nullptr; + const char *filter1 = "{\"_id\" : \"1\", \"info\" : 1}"; + Query query1 = {filter1, "{}"}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query1, 1, &resultSet1), GRD_INVALID_ARGS); + + /** + * @tc.steps: step2. Create filter with two _id + * @tc.expected: step2. Faild to get the record, the result is GRD_INVALID_ARGS, + */ + GRD_ResultSet *resultSet2 = nullptr; + const char *filter2 = "{\"_id\" : \"1\", \"_id\" : \"2\"}"; + Query query2 = {filter2, "{}"}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query2, 1, &resultSet2), GRD_INVALID_ARGS); + + /** + * @tc.steps: step3. Create filter with array and _id + * @tc.expected: step3. Faild to get the record, the result is GRD_INVALID_ARGS, + */ + GRD_ResultSet *resultSet3 = nullptr; + const char *filter3 = "{\"_id\" : \"1\", \"info\" : [\"2\", 1]}"; + Query query3 = {filter3, "{}"}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query3, 1, &resultSet3), GRD_INVALID_ARGS); + + /** + * @tc.steps: step4. Create filter with object and _id + * @tc.expected: step4. Faild to get the record, the result is GRD_INVALID_ARGS, + */ + GRD_ResultSet *resultSet4 = nullptr; + const char *filter4 = "{\"_id\" : \"1\", \"info\" : {\"info_val\" : \"1\"}}"; + Query query4 = {filter4, "{}"}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query4, 1, &resultSet4), GRD_INVALID_ARGS); + + /** + * @tc.steps: step5. Create filter with bool and _id + * @tc.expected: step5. Faild to get the record, the result is GRD_INVALID_ARGS, + */ + GRD_ResultSet *resultSet5 = nullptr; + const char *filter5 = "{\"_id\" : \"1\", \"info\" : true}"; + Query query5 = {filter5, "{}"}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query5, 1, &resultSet5), GRD_INVALID_ARGS); + + /** + * @tc.steps: step6. Create filter with null and _id + * @tc.expected: step6. Faild to get the record, the result is GRD_INVALID_ARGS, + */ + GRD_ResultSet *resultSet6 = nullptr; + const char *filter6 = "{\"_id\" : \"1\", \"info\" : null}"; + Query query6 = {filter6, "{}"}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query6, 1, &resultSet6), GRD_INVALID_ARGS); +} + +/** + * @tc.name: DocumentFindApiTest006 + * @tc.desc: test filter field with id which has different type of value. + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentFindApiTest, DocumentFindApiTest006, TestSize.Level1) +{ + /** + * @tc.steps: step1. Create filter with _id which value is string + * @tc.expected: step1. Faild to get the record, the result is GRD_INVALID_ARGS, + */ + GRD_ResultSet *resultSet1 = nullptr; + const char *filter1 = "{\"_id\" : \"valstring\"}"; + Query query1 = {filter1, "{}"}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query1, 1, &resultSet1), GRD_OK); + EXPECT_EQ(GRD_FreeResultSet(resultSet1), GRD_OK); + + /** + * @tc.steps: step2. Create filter with _id which value is number + * @tc.expected: step2. Faild to get the record, the result is GRD_INVALID_ARGS, + */ + GRD_ResultSet *resultSet2 = nullptr; + const char *filter2 = "{\"_id\" : 1}"; + Query query2 = {filter2, "{}"}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query2, 1, &resultSet2), GRD_INVALID_ARGS); + + /** + * @tc.steps: step3. Create filter with _id which value is array + * @tc.expected: step3. Faild to get the record, the result is GRD_INVALID_ARGS, + */ + GRD_ResultSet *resultSet3 = nullptr; + const char *filter3 = "{\"_id\" : [\"2\", 1]}"; + Query query3 = {filter3, "{}"}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query3, 1, &resultSet3), GRD_INVALID_ARGS); + + /** + * @tc.steps: step4. Create filter with _id which value is object + * @tc.expected: step4. Faild to get the record, the result is GRD_INVALID_ARGS, + */ + GRD_ResultSet *resultSet4 = nullptr; + const char *filter4 = "{\"_id\" : {\"info_val\" : \"1\"}}"; + Query query4 = {filter4, "{}"}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query4, 1, &resultSet4), GRD_INVALID_ARGS); + + /** + * @tc.steps: step5. Create filter with _id which value is bool + * @tc.expected: step5. Faild to get the record, the result is GRD_INVALID_ARGS, + */ + GRD_ResultSet *resultSet5 = nullptr; + const char *filter5 = "{\"_id\" : true}"; + Query query5 = {filter5, "{}"}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query5, 1, &resultSet5), GRD_INVALID_ARGS); + + /** + * @tc.steps: step6. Create filter with _id which value is null + * @tc.expected: step6. Faild to get the record, the result is GRD_INVALID_ARGS, + */ + GRD_ResultSet *resultSet6 = nullptr; + const char *filter6 = "{\"_id\" : null}"; + Query query6 = {filter6, "{}"}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query6, 1, &resultSet6), GRD_INVALID_ARGS); +} + +/** + * @tc.name: DocumentFindApiTest016 + * @tc.desc: Test filter with collection Name is invalid. + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentFindApiTest, DocumentFindApiTest016, TestSize.Level1) +{ + const char *colName1 = "grd_type"; + const char *colName2 = "GM_SYS_sysfff"; + GRD_ResultSet *resultSet = NULL; + const char *filter = "{\"_id\" : \"1\"}"; + Query query = {filter, "{}"}; + EXPECT_EQ(GRD_FindDoc(g_db, colName1, query, 1, &resultSet), GRD_INVALID_FORMAT); + EXPECT_EQ(GRD_FindDoc(g_db, colName2, query, 1, &resultSet), GRD_INVALID_FORMAT); +} + +// /** +// * @tc.name: DocumentFindApiTest017 +// * @tc.desc: Test filter field with large filter +// * @tc.type: FUNC +// * @tc.require: +// * @tc.author: mazhao +// */ +// HWTEST_F(DocumentFindApiTest, DocumentFindApiTest017, TestSize.Level1) +// { +// GRD_ResultSet *resultSet = nullptr; +// string documentPart1 = "{\"_id\" : \"18\", \"item\" :\" "; +// string documentPart2 = "\" }"; +// string jsonVal = string(512 * 1024 - documentPart1.size() - documentPart2.size() - 1, 'k'); +// string document = documentPart1 + jsonVal + documentPart2; +// string jsonVal1 = string(512 * 1024 - documentPart1.size() - documentPart2.size(), 'k'); +// string document1 = documentPart1 + jsonVal1 + documentPart2; + +// Query query = {document.c_str(), "{}"}; +// EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, 1, &resultSet), GRD_OK); +// EXPECT_EQ(GRD_Next(resultSet), GRD_NO_DATA); +// char *value = NULL; +// EXPECT_EQ(GRD_GetValue(resultSet, &value), GRD_NOT_AVAILABLE); +// EXPECT_EQ(GRD_FreeResultSet(resultSet), GRD_OK); + +// query = {document1.c_str(), "{}"}; +// EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, 1, &resultSet), GRD_OVER_LIMIT); +// } + +/** + * @tc.name: DocumentFindApiTest019 + * @tc.desc: Test filter field with no result + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentFindApiTest, DocumentFindApiTest019, TestSize.Level1) +{ + const char *filter = "{\"_id\" : \"100\"}"; + GRD_ResultSet *resultSet = nullptr; + Query query = {filter, "{}"}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, 1, &resultSet), GRD_OK); + EXPECT_EQ(GRD_Next(resultSet), GRD_NO_DATA); + char *value = NULL; + EXPECT_EQ(GRD_GetValue(resultSet, &value), GRD_NOT_AVAILABLE); + EXPECT_EQ(GRD_FreeResultSet(resultSet), GRD_OK); +} + +/** + * @tc.name: DocumentFindApiTest023 + * @tc.desc: Test filter field with double find. + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentFindApiTest, DocumentFindApiTest023, TestSize.Level1) +{ + /** + * @tc.steps: step1. Create filter with _id and get the record according to filter condition. + * @tc.expected: step1. succeed to get the record, the matching record is g_document6. + */ + const char *filter = "{\"_id\" : \"6\"}"; + GRD_ResultSet *resultSet = nullptr; + GRD_ResultSet *resultSet2 = nullptr; + Query query = {filter, "{}"}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, 1, &resultSet), GRD_OK); + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, 1, &resultSet2), GRD_RESOURCE_BUSY); + + EXPECT_EQ(GRD_Next(resultSet), GRD_OK); + char *value = NULL; + EXPECT_EQ(GRD_GetValue(resultSet, &value), GRD_OK); + CompareValue(value, g_document6); + EXPECT_EQ(GRD_FreeValue(value), GRD_OK); + /** + * @tc.steps: step2. Invoke GRD_Next to get the next matching value. Release resultSet. + * @tc.expected: step2. Cannot get next record, return GRD_NO_DATA. + */ + EXPECT_EQ(GRD_Next(resultSet), GRD_NO_DATA); + EXPECT_EQ(GRD_GetValue(resultSet, &value), GRD_NOT_AVAILABLE); + EXPECT_EQ(GRD_FreeResultSet(resultSet), GRD_OK); + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, 1, &resultSet), GRD_OK); + EXPECT_EQ(GRD_Next(resultSet), GRD_OK); + value = nullptr; + EXPECT_EQ(GRD_GetValue(resultSet, &value), GRD_OK); + CompareValue(value, g_document6); + EXPECT_EQ(GRD_FreeValue(value), GRD_OK); + EXPECT_EQ(GRD_FreeResultSet(resultSet), GRD_OK); +} + +/** + * @tc.name: DocumentFindApiTest024 + * @tc.desc: Test filter field with multi collections + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentFindApiTest, DocumentFindApiTest024, TestSize.Level1) +{ + const char *filter = "{\"_id\" : \"6\"}"; + GRD_ResultSet *resultSet = nullptr; + GRD_ResultSet *resultSet2 = nullptr; + Query query = {filter, "{}"}; + const char* collectionName = "DocumentFindApiTest024"; + EXPECT_EQ(GRD_CreateCollection(g_db, collectionName, "", 0), GRD_OK); + InsertData(g_db, collectionName); + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, 1, &resultSet), GRD_OK); + EXPECT_EQ(GRD_FindDoc(g_db, collectionName, query, 1, &resultSet2), GRD_OK); + + EXPECT_EQ(GRD_Next(resultSet), GRD_OK); + char *value = NULL; + EXPECT_EQ(GRD_GetValue(resultSet, &value), GRD_OK); + CompareValue(value, g_document6); + EXPECT_EQ(GRD_FreeValue(value), GRD_OK); + + EXPECT_EQ(GRD_Next(resultSet2), GRD_OK); + char *value2 = NULL; + EXPECT_EQ(GRD_GetValue(resultSet2, &value2), GRD_OK); + CompareValue(value2, g_document6); + EXPECT_EQ(GRD_FreeValue(value2), GRD_OK); + + EXPECT_EQ(GRD_Next(resultSet), GRD_NO_DATA); + EXPECT_EQ(GRD_Next(resultSet2), GRD_NO_DATA); + EXPECT_EQ(GRD_GetValue(resultSet, &value), GRD_NOT_AVAILABLE); + EXPECT_EQ(GRD_GetValue(resultSet2, &value), GRD_NOT_AVAILABLE); + EXPECT_EQ(GRD_FreeResultSet(resultSet), GRD_OK); + EXPECT_EQ(GRD_FreeResultSet(resultSet2), GRD_OK); + + EXPECT_EQ(GRD_DropCollection(g_db, collectionName, 0), GRD_OK); +} + +/** + * @tc.name: DocumentFindApiTest025 + * @tc.desc: Test nested projection, with viewType equals to 1. + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentFindApiTest, DocumentFindApiTest025, TestSize.Level1) +{ + /** + * @tc.steps: step1. Create filter to match g_document16, _id flag is 0. + * Create projection to display name,nested4. + * @tc.expected: step1. resultSet init successfuly, the result is GRD_OK, + */ + const char *filter = "{\"_id\" : \"16\"}"; + GRD_ResultSet *resultSet = nullptr; + const char *projectionInfo = "{\"name\": true, \"nested1.nested2.nested3.nested4\":true}"; + const char *targetDocument = "{\"name\":\"doc16\", \"nested1\":{\"nested2\":{\"nested3\":\ + {\"nested4\":\"ABC\"}}}}"; + Query query = {filter, projectionInfo}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, 0, &resultSet), GRD_OK); + EXPECT_EQ(GRD_Next(resultSet), GRD_OK); + char *value = nullptr; + EXPECT_EQ(GRD_GetValue(resultSet, &value), GRD_OK); + CompareValue(value, targetDocument); + EXPECT_EQ(GRD_FreeValue(value), GRD_OK); + /** + * @tc.steps: step2. After loop, cannot get more record. + * @tc.expected: step2. Return GRD_NO_DATA. + */ + EXPECT_EQ(GRD_Next(resultSet), GRD_NO_DATA); + EXPECT_EQ(GRD_GetValue(resultSet, &value), GRD_NOT_AVAILABLE); + EXPECT_EQ(GRD_FreeResultSet(resultSet), GRD_OK); + /** + * @tc.steps: step3. Create filter to match g_document16, _id flag is 0; + * Create projection to display name、nested4 with different projection format. + * @tc.expected: step3. succeed to get the record. + */ + projectionInfo = "{\"name\": true, \"nested1\":{\"nested2\":{\"nested3\":{\"nested4\":true}}}}"; + query = {filter, projectionInfo}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, 0, &resultSet), GRD_OK); + EXPECT_EQ(GRD_Next(resultSet), GRD_OK); + EXPECT_EQ(GRD_GetValue(resultSet, &value), GRD_OK); + EXPECT_EQ(GRD_FreeValue(value), GRD_OK); + /** + * @tc.steps: step4. After loop, cannot get more record. + * @tc.expected: step4. return GRD_NO_DATA. + */ + EXPECT_EQ(GRD_Next(resultSet), GRD_NO_DATA); + EXPECT_EQ(GRD_GetValue(resultSet, &value), GRD_NOT_AVAILABLE); + EXPECT_EQ(GRD_FreeResultSet(resultSet), GRD_OK); + /** + * @tc.steps: step5. Create filter to match g_document16, _id flag is 0. + * Create projection to conceal name,nested4 with different projection format. + * @tc.expected: step5. succeed to get the record. + */ + projectionInfo = "{\"name\": 0, \"nested1.nested2.nested3.nested4\":0}"; + targetDocument = "{\"nested1\":{\"nested2\":{\"nested3\":{\"field2\":\"CCC\"}}}}"; + query = {filter, projectionInfo}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, 0, &resultSet), GRD_OK); + EXPECT_EQ(GRD_Next(resultSet), GRD_OK); + EXPECT_EQ(GRD_GetValue(resultSet, &value), GRD_OK); + CompareValue(value, targetDocument); + EXPECT_EQ(GRD_FreeValue(value), GRD_OK); + EXPECT_EQ(GRD_FreeResultSet(resultSet), GRD_OK); +} +#include +/** + * @tc.name: DocumentFindApiTest026 + * @tc.desc: Test nested projection, with _id flag equals to 1. Projection is 5 level. + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentFindApiTest, DocumentFindApiTest026, TestSize.Level1) +{ + /** + * @tc.steps: step1. Create filter to match g_document16, _id flag is 0. + * Create projection to display name,nested5 + * @tc.expected: step1. Error GRD_INVALID_ARGS. + */ + const char *filter = "{\"_id\" : \"16\"}"; + GRD_ResultSet *resultSet = nullptr; + const char *projectionInfo = "{\"name\": true, \"nested1.nested2.nested3.nested4.nested5\":true}"; + Query query = {filter, projectionInfo}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, 0, &resultSet), GRD_INVALID_ARGS); + // EXPECT_EQ(GRD_Next(resultSet), GRD_INVALID_ARGS); + // char *value = nullptr; + // EXPECT_EQ(GRD_GetValue(resultSet, &value), GRD_INVALID_ARGS); + /** + * @tc.steps: step2. After loop, cannot get more record. + * @tc.expected: step2. Return GRD_NO_DATA. + */ + // EXPECT_EQ(GRD_Next(resultSet), GRD_INVALID_ARGS); + // EXPECT_EQ(GRD_FreeResultSet(resultSet), GRD_INVALID_ARGS); +} + +/** + * @tc.name: DocumentFindApiTest027 + * @tc.desc: Test projection with invalid field, _id field equals to 1. + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentFindApiTest, DocumentFindApiTest027, TestSize.Level1) +{ + /** + * @tc.steps: step1. Create filter to match g_document7, _id flag is 0. + * Create projection to display name, other _info and non existing field. + * @tc.expected: step1. Match the g_document7 and display name, other_info + */ + const char *filter = "{\"_id\" : \"7\"}"; + GRD_ResultSet *resultSet = nullptr; + const char *projectionInfo = "{\"name\": true, \"other_Info\":true, \"non_exist_field\":true}"; + const char *targetDocument = "{\"name\": \"doc7\", \"other_Info\":[{\"school\":\"BX\", \"age\":15},\ + {\"school\":\"C\", \"age\":35}]}"; + Query query = {filter, projectionInfo}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, 0, &resultSet), GRD_OK); + EXPECT_EQ(GRD_Next(resultSet), GRD_OK); + char *value = nullptr; + EXPECT_EQ(GRD_GetValue(resultSet, &value), GRD_OK); + CompareValue(value, targetDocument); + EXPECT_EQ(GRD_FreeValue(value), GRD_OK); + EXPECT_EQ(GRD_FreeResultSet(resultSet), GRD_OK); + + /** + * @tc.steps: step2. Create filter to match g_document7, _id flag is 0. + * Create projection to display name, other _info and existing field with space. + * @tc.expected: step2. Return GRD_INVALID_ARGS. + */ + projectionInfo = "{\"name\": true, \"other_Info\":true, \" item \":true}"; + query = {filter, projectionInfo}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, 0, &resultSet), GRD_INVALID_ARGS); + + /** + * @tc.steps: step3. Create filter to match g_document7, _id flag is 0. + * Create projection to display name, other _info and existing field with different case. + * @tc.expected: step3. Match the g_document7 and display name, other_Info. + */ + projectionInfo = "{\"name\": true, \"other_Info\":true, \"ITEM\": true}"; + query = {filter, projectionInfo}; + resultSet = nullptr; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, 0, &resultSet), GRD_OK); + EXPECT_EQ(GRD_Next(resultSet), GRD_OK); + EXPECT_EQ(GRD_GetValue(resultSet, &value), GRD_OK); + CompareValue(value, targetDocument); + EXPECT_EQ(GRD_FreeValue(value), GRD_OK); + EXPECT_EQ(GRD_FreeResultSet(resultSet), GRD_OK); +} + +/** + * @tc.name: DocumentFindApiTest028 + * @tc.desc: Test projection with invalid field in Array,_id field equals to 1. + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentFindApiTest, DocumentFindApiTest028, TestSize.Level1) +{ + /** + * @tc.steps: step1. Create filter to match g_document7, _id flag is 0. + * Create projection to display name, non existing field in array. + * @tc.expected: step1. Match the g_document7 and display name, other_info. + */ + const char *filter = "{\"_id\" : \"7\"}"; + GRD_ResultSet *resultSet = nullptr; + const char *projectionInfo = "{\"name\": true, \"other_Info.non_exist_field\":true}"; + const char *targetDocument = "{\"name\": \"doc7\"}"; + Query query = {filter, projectionInfo}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, 0, &resultSet), GRD_OK); + EXPECT_EQ(GRD_Next(resultSet), GRD_OK); + char *value = nullptr; + EXPECT_EQ(GRD_GetValue(resultSet, &value), GRD_OK); + CompareValue(value, targetDocument); + EXPECT_EQ(GRD_FreeValue(value), GRD_OK); + EXPECT_EQ(GRD_FreeResultSet(resultSet), GRD_OK); + + /** + * @tc.steps: step2. Create filter to match g_document7, _id flag is 0. + * Create projection to display name, other _info and existing field with space. + * @tc.expected: step2. Return GRD_INVALID_ARGS. + */ + projectionInfo = "{\"name\": true, \"other_Info\":{\"non_exist_field\":true}}"; + query = {filter, projectionInfo}; + resultSet = nullptr; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, 0, &resultSet), GRD_OK); + EXPECT_EQ(GRD_Next(resultSet), GRD_OK); + EXPECT_EQ(GRD_GetValue(resultSet, &value), GRD_OK); + CompareValue(value, targetDocument); + EXPECT_EQ(GRD_FreeValue(value), GRD_OK); + EXPECT_EQ(GRD_FreeResultSet(resultSet), GRD_OK); + + /** + * @tc.steps: step3. Create filter to match g_document7, _id flag is 0. + * Create projection to display name, non existing field in array with index format. + * @tc.expected: step3. Match the g_document7 and display name, other_Info. + */ + projectionInfo = "{\"name\": true, \"other_Info.0\": true}"; + query = {filter, projectionInfo}; + resultSet = nullptr; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, 0, &resultSet), GRD_INVALID_ARGS); + EXPECT_EQ(GRD_FreeResultSet(resultSet), GRD_INVALID_ARGS); +} + +/** + * @tc.name: DocumentFindApiTest029 + * @tc.desc: Test projection with path conflict._id field equals to 0. + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentFindApiTest, DocumentFindApiTest029, TestSize.Level1) +{ + /** + * @tc.steps: step1. Create filter to match g_document4, _id flag is 0. + * Create projection to display conflict path. + * @tc.expected: step1. Return GRD_INVALID_ARGS. + */ + const char *filter = "{\"_id\" : \"4\"}"; + GRD_ResultSet *resultSet = nullptr; + const char *projectionInfo = "{\"personInfo\": true, \"personInfo.grade\": true}"; + Query query = {filter, projectionInfo}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, 0, &resultSet), GRD_INVALID_ARGS); +} + +/** + * @tc.name: DocumentFindApiTest030 + * @tc.desc: Test _id flag and field.None exist field. + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentFindApiTest, DocumentFindApiTest030, TestSize.Level1) +{ + /** + * @tc.steps: step1. Create filter to match g_document7, _id flag is 0. + * @tc.expected: step1. Match the g_document7 and return empty json. + */ + const char *filter = "{\"_id\" : \"7\"}"; + GRD_ResultSet *resultSet = nullptr; + const char *projectionInfo = "{\"non_exist_field\":true}"; + int flag = 0; + const char *targetDocument = "{}"; + Query query = {filter, projectionInfo}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, flag, &resultSet), GRD_OK); + EXPECT_EQ(GRD_Next(resultSet), GRD_OK); + char *value = nullptr; + EXPECT_EQ(GRD_GetValue(resultSet, &value), GRD_OK); + CompareValue(value, targetDocument); + EXPECT_EQ(GRD_FreeValue(value), GRD_OK); + EXPECT_EQ(GRD_FreeResultSet(resultSet), GRD_OK); + + /** + * @tc.steps: step2. Create filter to match g_document7, _id flag is 1. + * @tc.expected: step2. Match g_document7, and return a json with _id. + */ + resultSet = nullptr; + flag = 1; + targetDocument = "{\"_id\": \"7\"}"; + query = {filter, projectionInfo}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, flag, &resultSet), GRD_OK); + EXPECT_EQ(GRD_Next(resultSet), GRD_OK); + value = NULL; + EXPECT_EQ(GRD_GetValue(resultSet, &value), GRD_OK); + CompareValue(value, targetDocument); + EXPECT_EQ(GRD_FreeValue(value), GRD_OK); + EXPECT_EQ(GRD_FreeResultSet(resultSet), GRD_OK); +} + +/** + * @tc.name: DocumentFindApiTest031 + * @tc.desc: Test _id flag and field.Exist field with 1 value. + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentFindApiTest, DocumentFindApiTest031, TestSize.Level1) +{ + /** + * @tc.steps: step1. Create filter to match g_document7, _id flag is 0. + * @tc.expected: step1. Match the g_document7 and return json with name, item. + */ + const char *filter = "{\"_id\" : \"7\"}"; + GRD_ResultSet *resultSet = nullptr; + const char *projectionInfo = "{\"name\":true, \"item\":true}"; + int flag = 0; + const char *targetDocument = "{\"name\":\"doc7\", \"item\":\"fruit\"}"; + Query query = {filter, projectionInfo}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, flag, &resultSet), GRD_OK); + EXPECT_EQ(GRD_Next(resultSet), GRD_OK); + char *value = nullptr; + EXPECT_EQ(GRD_GetValue(resultSet, &value), GRD_OK); + CompareValue(value, targetDocument); + EXPECT_EQ(GRD_FreeValue(value), GRD_OK); + EXPECT_EQ(GRD_FreeResultSet(resultSet), GRD_OK); + + /** + * @tc.steps: step2. Create filter to match g_document7, _id flag is 1. + * @tc.expected: step2. Match g_document7, and return a json with _id. + */ + resultSet = nullptr; + flag = 1; + projectionInfo = "{\"name\": 1, \"item\": 1}"; + targetDocument = "{\"_id\":\"7\", \"name\":\"doc7\", \"item\":\"fruit\"}"; + query = {filter, projectionInfo}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, flag, &resultSet), GRD_OK); + EXPECT_EQ(GRD_Next(resultSet), GRD_OK); + value = NULL; + EXPECT_EQ(GRD_GetValue(resultSet, &value), GRD_OK); + CompareValue(value, targetDocument); + EXPECT_EQ(GRD_FreeValue(value), GRD_OK); + EXPECT_EQ(GRD_FreeResultSet(resultSet), GRD_OK); +} + +/** + * @tc.name: DocumentFindApiTest032 + * @tc.desc: Test _id flag and field.Exist field with 1 value. + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentFindApiTest, DocumentFindApiTest032, TestSize.Level1) +{ + /** + * @tc.steps: step1. Create filter to match g_document7, _id flag is 0. + * @tc.expected: step1. Match the g_document7 and return json with name, item. + */ + const char *filter = "{\"_id\" : \"7\"}"; + GRD_ResultSet *resultSet = nullptr; + const char *projectionInfo = "{\"name\":true, \"item\":true}"; + int flag = 0; + const char *targetDocument = "{\"name\":\"doc7\", \"item\":\"fruit\"}"; + Query query = {filter, projectionInfo}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, flag, &resultSet), GRD_OK); + EXPECT_EQ(GRD_Next(resultSet), GRD_OK); + char *value = nullptr; + EXPECT_EQ(GRD_GetValue(resultSet, &value), GRD_OK); + CompareValue(value, targetDocument); + EXPECT_EQ(GRD_FreeValue(value), GRD_OK); + EXPECT_EQ(GRD_FreeResultSet(resultSet), GRD_OK); + /** + * @tc.steps: step2. Create filter to match g_document7, _id flag is 1. + * @tc.expected: step2. Match g_document7, and return a json with _id. + */ + resultSet = nullptr; + flag = 1; + projectionInfo = "{\"name\": 1, \"item\": 1}"; + targetDocument = "{\"_id\":\"7\", \"name\":\"doc7\", \"item\":\"fruit\"}"; + query = {filter, projectionInfo}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, flag, &resultSet), GRD_OK); + EXPECT_EQ(GRD_Next(resultSet), GRD_OK); + value = NULL; + EXPECT_EQ(GRD_GetValue(resultSet, &value), GRD_OK); + CompareValue(value, targetDocument); + EXPECT_EQ(GRD_FreeValue(value), GRD_OK); + EXPECT_EQ(GRD_FreeResultSet(resultSet), GRD_OK); + /** + * @tc.steps: step3. Create filter to match g_document7, _id flag is 1.Projection value is not 0. + * @tc.expected: step3. Match g_document7, and return a json with name, item and _id. + */ + resultSet = nullptr; + flag = 1; + projectionInfo = "{\"name\": 10, \"item\": 10}"; + targetDocument = "{\"_id\":\"7\", \"name\":\"doc7\", \"item\":\"fruit\"}"; + query = {filter, projectionInfo}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, flag, &resultSet), GRD_OK); + EXPECT_EQ(GRD_Next(resultSet), GRD_OK); + value = NULL; + EXPECT_EQ(GRD_GetValue(resultSet, &value), GRD_OK); + CompareValue(value, targetDocument); + EXPECT_EQ(GRD_FreeValue(value), GRD_OK); + EXPECT_EQ(GRD_FreeResultSet(resultSet), GRD_OK); +} + +/** + * @tc.name: DocumentFindApiTest033 + * @tc.desc: Test _id flag and field.Exist field with 0 value. + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentFindApiTest, DocumentFindApiTest033, TestSize.Level1) +{ + /** + * @tc.steps: step1. Create filter to match g_document7, _id flag is 0. + * @tc.expected: step1. Match the g_document7 and return json with name, item and _id + */ + const char *filter = "{\"_id\" : \"7\"}"; + GRD_ResultSet *resultSet = nullptr; + const char *projectionInfo = "{\"name\":false, \"item\":false}"; + int flag = 0; + const char *targetDocument = "{\"other_Info\":[{\"school\":\"BX\", \"age\" : 15}, {\"school\":\"C\", \"age\" : 35}]}";; + Query query = {filter, projectionInfo}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, flag, &resultSet), GRD_OK); + EXPECT_EQ(GRD_Next(resultSet), GRD_OK); + char *value = nullptr; + EXPECT_EQ(GRD_GetValue(resultSet, &value), GRD_OK); + CompareValue(value, targetDocument); + EXPECT_EQ(GRD_FreeValue(value), GRD_OK); + EXPECT_EQ(GRD_FreeResultSet(resultSet), GRD_OK); + /** + * @tc.steps: step2. Create filter to match g_document7, _id flag is 1. + * @tc.expected: step2. Match g_document7, and return a json without name and item. + */ + resultSet = nullptr; + flag = 1; + projectionInfo = "{\"name\": 0, \"item\": 0}"; + targetDocument = "{\"_id\": \"7\", \"other_Info\":[{\"school\":\"BX\", \"age\" : 15}, {\"school\":\"C\", \"age\" : 35}]}";; + query = {filter, projectionInfo}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, flag, &resultSet), GRD_OK); + EXPECT_EQ(GRD_Next(resultSet), GRD_OK); + value = NULL; + EXPECT_EQ(GRD_GetValue(resultSet, &value), GRD_OK); + CompareValue(value, targetDocument); + EXPECT_EQ(GRD_FreeValue(value), GRD_OK); + EXPECT_EQ(GRD_FreeResultSet(resultSet), GRD_OK); +} + +/** + * @tc.name: DocumentFindApiTest034 + * @tc.desc: Test projection with nonexist field in nested structure. + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentFindApiTest, DocumentFindApiTest034, TestSize.Level1) +{ + /** + * @tc.steps: step1. Create filter to match g_document4, _id flag is 0. + * @tc.expected: step1. Match the g_document4 and return json without name + */ + const char *filter = "{\"_id\" : \"4\"}"; + GRD_ResultSet *resultSet = nullptr; + const char *projectionInfo = "{\"name\": 1, \"personInfo.grade1\": 1, \ + \"personInfo.shool1\": 1, \"personInfo.age1\": 1}"; + int flag = 0; + const char *targetDocument = "{\"name\":\"doc4\"}"; + Query query = {filter, projectionInfo}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, flag, &resultSet), GRD_OK); + EXPECT_EQ(GRD_Next(resultSet), GRD_OK); + char *value = nullptr; + EXPECT_EQ(GRD_GetValue(resultSet, &value), GRD_OK); + CompareValue(value, targetDocument); + EXPECT_EQ(GRD_FreeValue(value), GRD_OK); + EXPECT_EQ(GRD_FreeResultSet(resultSet), GRD_OK); + + /** + * @tc.steps: step2. Create filter to match g_document4, _id flag is 0, display part of fields in nested structure. + * @tc.expected: step2. Match the g_document4 and return json without name + */ + projectionInfo = "{\"name\": false, \"personInfo.grade1\": false, \ + \"personInfo.shool1\": false, \"personInfo.age1\": false}"; + const char *targetDocument2 = "{\"item\":\"paper\",\"personInfo\":{\"grade\" : 1, \"school\":\"A\", \"age\" : 18}}"; + query = {filter, projectionInfo}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, flag, &resultSet), GRD_OK); + EXPECT_EQ(GRD_Next(resultSet), GRD_OK); + EXPECT_EQ(GRD_GetValue(resultSet, &value), GRD_OK); + CompareValue(value, targetDocument2); + EXPECT_EQ(GRD_FreeValue(value), GRD_OK); + EXPECT_EQ(GRD_FreeResultSet(resultSet), GRD_OK); + + /** + * @tc.steps: step3. Create filter to match g_document4, _id flag is 0, display part of fields in nested structure. + * @tc.expected: step3. Match the g_document4 and return json with name, personInfo.school and personInfo.age. + */ + projectionInfo = "{\"name\": 1, \"personInfo.school\": 1, \"personInfo.age\": 1}"; + const char *targetDocument3 = "{\"name\":\"doc4\", \"personInfo\": {\"school\":\"A\", \"age\" : 18}}"; + query = {filter, projectionInfo}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, flag, &resultSet), GRD_OK); + EXPECT_EQ(GRD_Next(resultSet), GRD_OK); + EXPECT_EQ(GRD_GetValue(resultSet, &value), GRD_OK); + CompareValue(value, targetDocument3); + EXPECT_EQ(GRD_FreeValue(value), GRD_OK); + EXPECT_EQ(GRD_FreeResultSet(resultSet), GRD_OK); + + /** + * @tc.steps: step4. Create filter to match g_document4, _id flag is 0, display part of fields in nested structure. + * @tc.expected: step4. Match the g_document4 and return json with name, personInfo.school + */ + projectionInfo = "{\"name\": 1, \"personInfo.school\": 1, \"personInfo.age1\": 1}"; + const char *targetDocument4 = "{\"name\":\"doc4\", \"personInfo\": {\"school\":\"A\"}}"; + query = {filter, projectionInfo}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, flag, &resultSet), GRD_OK); + EXPECT_EQ(GRD_Next(resultSet), GRD_OK); + EXPECT_EQ(GRD_GetValue(resultSet, &value), GRD_OK); + CompareValue(value, targetDocument4); + EXPECT_EQ(GRD_FreeValue(value), GRD_OK); + EXPECT_EQ(GRD_FreeResultSet(resultSet), GRD_OK); +} + +/** + * @tc.name: DocumentFindApiTest035 + * @tc.desc: test filter with id string filter + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentFindApiTest, DocumentFindApiTest035, TestSize.Level1) +{ + /** + * @tc.steps: step1. Create filter with _id and get the record according to filter condition. + * @tc.expected: step1. succeed to get the record, the matching record is g_document17 + */ + const char *filter = "{\"_id\" : \"17\"}"; + GRD_ResultSet *resultSet = nullptr; + int flag = 0; + Query query = {filter, "{}"}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, GRD_DOC_ID_DISPLAY, &resultSet), GRD_OK); + EXPECT_EQ(GRD_Next(resultSet), GRD_OK); + char *value = nullptr; + EXPECT_EQ(GRD_GetValue(resultSet, &value), GRD_OK); + CompareValue(value, g_document17); + EXPECT_EQ(GRD_FreeValue(value), GRD_OK); + /** + * @tc.steps: step2. Invoke GRD_Next to get the next matching value. Release resultSet. + * @tc.expected: step2. Cannot get next record, return GRD_NO_DATA. + */ + EXPECT_EQ(GRD_Next(resultSet), GRD_NO_DATA); + EXPECT_EQ(GRD_GetValue(resultSet, &value), GRD_NOT_AVAILABLE); + EXPECT_EQ(GRD_FreeResultSet(resultSet), GRD_OK); +} + +/** + * @tc.name: DocumentFindApiTest036 + * @tc.desc: Test with invalid collectionName. + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentFindApiTest, DocumentFindApiTest036, TestSize.Level1) +{ + /** + * @tc.steps: step1. Test with invalid collectionName. + * @tc.expected: step1. Return GRD_INVALID_ARGS. + */ + const char *filter = "{\"_id\" : \"17\"}"; + GRD_ResultSet *resultSet = nullptr; + int flag = 0; + Query query = {filter, "{}"}; + EXPECT_EQ(GRD_FindDoc(g_db, "", query, 0, &resultSet), GRD_INVALID_ARGS); + EXPECT_EQ(GRD_FindDoc(g_db, NULL, query, 0, &resultSet), GRD_INVALID_ARGS); +} + +/** + * @tc.name: DocumentFindApiTest037 + * @tc.desc: Test filed with different value. + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentFindApiTest, DocumentFindApiTest037, TestSize.Level1) +{ + /** + * @tc.steps: step1. Test filed with different value.some are 1, other are 0. + * @tc.expected: step1. Return GRD_INVALID_ARGS. + */ + const char *filter = "{\"_id\" : \"4\"}"; + GRD_ResultSet *resultSet = nullptr; + const char *projectionInfo = "{\"name\":1, \"personInfo\":0, \"item\":1}"; + Query query = {filter, projectionInfo}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, 0, &resultSet), GRD_INVALID_ARGS); + + /** + * @tc.steps: step2. Test filed with different value.some are 2, other are 0. + * @tc.expected: step2. Return GRD_INVALID_ARGS. + */ + projectionInfo = "{\"name\":2, \"personInfo\":0, \"item\":2}"; + query = {filter, projectionInfo}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, 0, &resultSet), GRD_INVALID_ARGS); + + /** + * @tc.steps: step3. Test filed with different value.some are 0, other are true. + * @tc.expected: step3. Return GRD_INVALID_ARGS. + */ + projectionInfo = "{\"name\":true, \"personInfo\":0, \"item\":true}"; + query = {filter, projectionInfo}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, 0, &resultSet), GRD_INVALID_ARGS); + + /** + * @tc.steps: step4. Test filed with different value.some are 0, other are "". + * @tc.expected: step4. Return GRD_INVALID_ARGS. + */ + projectionInfo = "{\"name\":\"\", \"personInfo\":0, \"item\":\"\"}"; + query = {filter, projectionInfo}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, 0, &resultSet), GRD_INVALID_ARGS); + + /** + * @tc.steps: step5. Test filed with different value.some are 1, other are false. + * @tc.expected: step5. Return GRD_INVALID_ARGS. + */ + projectionInfo = "{\"name\":false, \"personInfo\":1, \"item\":false"; + query = {filter, projectionInfo}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, 0, &resultSet), GRD_INVALID_FORMAT); + + /** + * @tc.steps: step6. Test filed with different value.some are -1.123, other are false. + * @tc.expected: step6. Return GRD_INVALID_ARGS. + */ + projectionInfo = "{\"name\":false, \"personInfo\":-1.123, \"item\":false"; + query = {filter, projectionInfo}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, 0, &resultSet), GRD_INVALID_FORMAT); + + /** + * @tc.steps: step7. Test filed with different value.some are true, other are false. + * @tc.expected: step7. Return GRD_INVALID_ARGS. + */ + projectionInfo = "{\"name\":false, \"personInfo\":true, \"item\":false"; + query = {filter, projectionInfo}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, 0, &resultSet), GRD_INVALID_FORMAT); +} + +/** + * @tc.name: DocumentFindApiTest038 + * @tc.desc: Test field with false value. + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentFindApiTest, DocumentFindApiTest038, TestSize.Level1) +{ + /** + * @tc.steps: step1. Test field with different false value. Some are false, other are 0. flag is 0. + * @tc.expected: step1. Match the g_document6 and return empty json. + */ + const char *filter = "{\"_id\" : \"6\"}"; + GRD_ResultSet *resultSet = nullptr; + const char *projectionInfo = "{\"name\":false, \"personInfo\": 0, \"item\":0}"; + int flag = 0; + const char *targetDocument = "{}"; + Query query = {filter, projectionInfo}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, flag, &resultSet), GRD_OK); + EXPECT_EQ(GRD_Next(resultSet), GRD_OK); + char *value = nullptr; + EXPECT_EQ(GRD_GetValue(resultSet, &value), GRD_OK); + CompareValue(value, targetDocument); + EXPECT_EQ(GRD_FreeValue(value), GRD_OK); + EXPECT_EQ(GRD_FreeResultSet(resultSet), GRD_OK); + + /** + * @tc.steps: step2. Test field with different false value.Some are false, others are 0. flag is 1. + * @tc.expected: step2. Match g_document6, Return json with _id. + */ + targetDocument = "{\"_id\": \"6\"}"; + query = {filter, projectionInfo}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, 1, &resultSet), GRD_OK); + EXPECT_EQ(GRD_Next(resultSet), GRD_OK); + EXPECT_EQ(GRD_GetValue(resultSet, &value), GRD_OK); + CompareValue(value, targetDocument); + EXPECT_EQ(GRD_FreeValue(value), GRD_OK); + EXPECT_EQ(GRD_FreeResultSet(resultSet), GRD_OK); +} + +/** + * @tc.name: DocumentFindApiTest039 + * @tc.desc: Test field with true value. + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentFindApiTest, DocumentFindApiTest039, TestSize.Level1) +{ + /** + * @tc.steps: step1. Test field with different true value. Some are true, other are 1. flag is 0. + * @tc.expected: step1. Match the g_document18 and return json with name, item, personInfo.age and color. + */ + const char *filter = "{\"_id\" : \"18\"}"; + GRD_ResultSet *resultSet = nullptr; + const char *projectionInfo = "{\"name\":true, \"personInfo.age\": \"\", \"item\":1, \"color\":10, \"nonExist\" : -100}"; + const char *targetDocument = "{\"name\":\"doc18\", \"item\":\"mobile phone\", \"personInfo\":\ + {\"age\":66}, \"color\":\"blue\"}"; + int flag = 0; + Query query = {filter, projectionInfo}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, flag, &resultSet), GRD_OK); + EXPECT_EQ(GRD_Next(resultSet), GRD_OK); + char *value = nullptr; + EXPECT_EQ(GRD_GetValue(resultSet, &value), GRD_OK); + CompareValue(value, targetDocument); + EXPECT_EQ(GRD_FreeValue(value), GRD_OK); + EXPECT_EQ(GRD_FreeResultSet(resultSet), GRD_OK); + + /** + * @tc.steps: step2. Test field with different true value.Some are false, others are 0. flag is 1. + * @tc.expected: step2. Match g_document18, Return json with name, item, personInfo.age, color and _id. + */ + targetDocument = "{\"_id\" : \"18\", \"name\":\"doc18\",\"item\" : \"mobile phone\",\"personInfo\":\ + {\"age\":66}, \"color\":\"blue\"}"; + query = {filter, projectionInfo}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, 1, &resultSet), GRD_OK); + EXPECT_EQ(GRD_Next(resultSet), GRD_OK); + EXPECT_EQ(GRD_GetValue(resultSet, &value), GRD_OK); + CompareValue(value, targetDocument); + EXPECT_EQ(GRD_FreeValue(value), GRD_OK); + EXPECT_EQ(GRD_FreeResultSet(resultSet), GRD_OK); +} + +/** + * @tc.name: DocumentFindApiTest040 + * @tc.desc: Test field with invalid value. + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentFindApiTest, DocumentFindApiTest040, TestSize.Level1) +{ + /** + * @tc.steps: step1. Test field with invalid value.Value is array. + * @tc.expected: step1. Match the g_document18 and return GRD_INVALID_ARGS. + */ + const char *filter = "{\"_id\" : \"18\"}"; + GRD_ResultSet *resultSet = nullptr; + const char *projectionInfo = "{\"personInfo\":[true, 1]}"; + int flag = 1; + Query query = {filter, projectionInfo}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, flag, &resultSet), GRD_INVALID_ARGS); + + /** + * @tc.steps: step2. Test field with invalid value.Value is null. + * @tc.expected: step2. Match the g_document18 and return GRD_INVALID_ARGS. + */ + projectionInfo = "{\"personInfo\":null}"; + query = {filter, projectionInfo}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, flag, &resultSet), GRD_INVALID_ARGS); + + /** + * @tc.steps: step3. Test field with invalid value.Value is invalid string. + * @tc.expected: step3. Match the g_document18 and return GRD_INVALID_ARGS. + */ + projectionInfo = "{\"personInfo\":\"invalid string.\"}"; + query = {filter, projectionInfo}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, flag, &resultSet), GRD_INVALID_ARGS); +} + +/** + * @tc.name: DocumentFindApiTest042 + * @tc.desc: Test field with no existed uppercase filter + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentFindApiTest, DocumentFindApiTest042, TestSize.Level1) +{ + /** + * @tc.steps: step1. Test field with no existed uppercase filter. + * @tc.expected: step1. not match any item. + */ + const char *filter = "{\"_iD\" : \"18\"}"; + GRD_ResultSet *resultSet = nullptr; + const char *projectionInfo = "{\"Name\":true, \"personInfo.age\": \"\", \"item\":1, \"COLOR\":10, \"nonExist\" : -100}"; + int flag = 0; + Query query = {filter, projectionInfo}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, flag, &resultSet), GRD_INVALID_ARGS); + EXPECT_EQ(GRD_Next(resultSet), GRD_INVALID_ARGS); + EXPECT_EQ(GRD_FreeResultSet(resultSet), GRD_INVALID_ARGS); + + /** + * @tc.steps: step2. Test field with upper projection. + * @tc.expected: step2. Match g_document18, Return json with item, personInfo.age, color and _id. + */ + const char *filter1 = "{\"_id\" : \"18\"}"; + const char* targetDocument = "{\"_id\" : \"18\", \"item\" : \"mobile phone\",\"personInfo\":\ + {\"age\":66}}"; + query = {filter1, projectionInfo}; + char *value = nullptr; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, 1, &resultSet), GRD_OK); + EXPECT_EQ(GRD_Next(resultSet), GRD_OK); + EXPECT_EQ(GRD_GetValue(resultSet, &value), GRD_OK); + CompareValue(value, targetDocument); + EXPECT_EQ(GRD_FreeValue(value), GRD_OK); + EXPECT_EQ(GRD_Next(resultSet), GRD_NO_DATA); + EXPECT_EQ(GRD_FreeResultSet(resultSet), GRD_OK); +} + +/** + * @tc.name: DocumentFindApiTest044 + * @tc.desc: Test field with uppercase projection + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentFindApiTest, DocumentFindApiTest044, TestSize.Level1) +{ + /** + * @tc.steps: step1. Test with false uppercase projection + * @tc.expected: step1. Match g_document18, Return json with item, personInfo.age and _id. + */ + const char *filter = "{\"_id\" : \"18\"}"; + GRD_ResultSet *resultSet = nullptr; + const char *projectionInfo = "{\"Name\":0, \"personInfo.age\": false, \"personInfo.SCHOOL\": false, \"item\":\ + false, \"COLOR\":false, \"nonExist\" : false}"; + const char *targetDocument = "{\"_id\" : \"18\", \"name\":\"doc18\", \"personInfo\":\ + {\"school\":\"DD\"}, \"color\":\"blue\"}"; + int flag = 0; + Query query = {filter, projectionInfo}; + char *value = nullptr; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, 1, &resultSet), GRD_OK); + EXPECT_EQ(GRD_Next(resultSet), GRD_OK); + EXPECT_EQ(GRD_GetValue(resultSet, &value), GRD_OK); + CompareValue(value, targetDocument); + EXPECT_EQ(GRD_FreeValue(value), GRD_OK); + EXPECT_EQ(GRD_Next(resultSet), GRD_NO_DATA); + EXPECT_EQ(GRD_FreeResultSet(resultSet), GRD_OK); +} + +/** + * @tc.name: DocumentFindApiTest045 + * @tc.desc: Test field with too long collectionName + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentFindApiTest, DocumentFindApiTest045, TestSize.Level1) +{ + /** + * @tc.steps: step1. Test with false uppercase projection + * @tc.expected: step1. Match g_document18, Return json with item, personInfo.age and _id. + */ + const char *filter = "{\"_id\" : \"18\"}"; + GRD_ResultSet *resultSet = nullptr; + int flag = 0; + Query query = {filter, "{}"}; + string collectionName1(MAX_COLLECTION_NAME, 'a'); + ASSERT_EQ(GRD_CreateCollection(g_db, collectionName1.c_str(), "", 0), GRD_OK); + EXPECT_EQ(GRD_FindDoc(g_db, collectionName1.c_str(), query, 1, &resultSet), GRD_OK); + EXPECT_EQ(GRD_FreeResultSet(resultSet), GRD_OK); + ASSERT_EQ(GRD_DropCollection(g_db, collectionName1.c_str(), 0), GRD_OK); + + string collectionName2(MAX_COLLECTION_NAME + 1, 'a'); + EXPECT_EQ(GRD_FindDoc(g_db, collectionName2.c_str(), query, 1, &resultSet), GRD_OVER_LIMIT); + EXPECT_EQ(GRD_FindDoc(g_db, "", query, 1, &resultSet), GRD_INVALID_ARGS); +} + +/** + * @tc.name: DocumentFindApiTest052 + * @tc.desc: Test field when id string len is large than max + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentFindApiTest, DocumentFindApiTest052, TestSize.Level1) +{ + /** + * @tc.steps: step1. Test with false uppercase projection + * @tc.expected: step1. Match g_document18, Return json with item, personInfo.age and _id. + */ + const char *filter = "{\"_id\" : \"18\"}"; + GRD_ResultSet *resultSet = nullptr; + int flag = 0; + Query query = {filter, "{}"}; + string collectionName1(MAX_COLLECTION_NAME, 'a'); + ASSERT_EQ(GRD_CreateCollection(g_db, collectionName1.c_str(), "", 0), GRD_OK); + EXPECT_EQ(GRD_FindDoc(g_db, collectionName1.c_str(), query, 1, &resultSet), GRD_OK); + EXPECT_EQ(GRD_FreeResultSet(resultSet), GRD_OK); + ASSERT_EQ(GRD_DropCollection(g_db, collectionName1.c_str(), 0), GRD_OK); + + string collectionName2(MAX_COLLECTION_NAME + 1, 'a'); + EXPECT_EQ(GRD_FindDoc(g_db, collectionName2.c_str(), query, 1, &resultSet), GRD_OVER_LIMIT); + EXPECT_EQ(GRD_FindDoc(g_db, "", query, 1, &resultSet), GRD_INVALID_ARGS); +} + +/** + * @tc.name: DocumentFindApiTest053 + * @tc.desc: Test with invalid flags + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentFindApiTest, DocumentFindApiTest053, TestSize.Level1) +{ + /** + * @tc.steps: step1. Test with invalid flags which is 3. + * @tc.expected: step1. Return GRD_INVALID_ARGS. + */ + const char *filter = "{\"_id\" : \"18\"}"; + GRD_ResultSet *resultSet = nullptr; + const char *projectionInfo = "{}"; + Query query = {filter, projectionInfo}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, 3, &resultSet), GRD_INVALID_ARGS); + + /** + * @tc.steps:step1.parameter flags is int_max + * @tc.expected:step1.GRD_INVALID_ARGS + */ + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, INT_MAX, &resultSet), GRD_INVALID_ARGS); + + /** + * @tc.steps:step1.parameter flags is INT_MIN + * @tc.expected:step1.GRD_INVALID_ARGS + */ + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, INT_MIN, &resultSet), GRD_INVALID_ARGS); +} + +/** + * @tc.name: DocumentFindApiTest054 + * @tc.desc: Test with null g_db and resultSet, filter. + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentFindApiTest, DocumentFindApiTest054, TestSize.Level1) +{ + /** + * @tc.steps: step1. Test with null g_db. + * @tc.expected: step1. Return GRD_INVALID_ARGS. + */ + const char *filter = "{\"_id\" : \"18\"}"; + GRD_ResultSet *resultSet = nullptr; + const char *projectionInfo = "{}"; + Query query = {filter, projectionInfo}; + EXPECT_EQ(GRD_FindDoc(nullptr, COLLECTION_NAME, query, 0, &resultSet), GRD_INVALID_ARGS); + + /** + * @tc.steps: step2. Test with null resultSet. + * @tc.expected: step2. Return GRD_INVALID_ARGS. + */ + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, 0, nullptr), GRD_INVALID_ARGS); + + /** + * @tc.steps: step1. Test with query that has two nullptr data. + * @tc.expected: step1. Return GRD_INVALID_ARGS. + */ + query = {nullptr, nullptr}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, 0, &resultSet), GRD_INVALID_ARGS); +} + +/** + * @tc.name: DocumentFindApiTest055 + * @tc.desc: Find doc, but filter' _id value lens is larger than MAX_ID_LENS + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentFindApiTest, DocumentFindApiTest055, TestSize.Level1) +{ + /** + * @tc.steps:step1.Find doc, but filter' _id value lens is larger than MAX_ID_LENS + * @tc.expected:step1.GRD_OVER_LIMIT. + */ + string document1 = "{\"_id\" : "; + string document2 = "\""; + string document4 = "\""; + string document5 = "}"; + string document_midlle(MAX_ID_LENS + 1, 'k'); + string filter = document1 + document2 + document_midlle + document4 + document5; + GRD_ResultSet *resultSet = nullptr; + const char *projectionInfo = "{}"; + Query query = {filter.c_str(), projectionInfo}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, 0, &resultSet), GRD_OVER_LIMIT); + + /** + * @tc.steps:step1.Find doc, filter' _id value lens is equal as MAX_ID_LENS + * @tc.expected:step1.GRD_OK. + */ + string document_midlle2(MAX_ID_LENS, 'k'); + filter = document1 + document2 + document_midlle2 + document4 + document5; + query = {filter.c_str(), projectionInfo}; + EXPECT_EQ(GRD_FindDoc(g_db, COLLECTION_NAME, query, 0, &resultSet), GRD_OK); + EXPECT_EQ(GRD_FreeResultSet(resultSet), GRD_OK); +} \ No newline at end of file diff --git a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/test/unittest/api/ documentdb_insert_test.cpp b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/test/unittest/api/ documentdb_insert_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..393c4bbc4753e73e3c59af5548392cdbbba175a4 --- /dev/null +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/test/unittest/api/ documentdb_insert_test.cpp @@ -0,0 +1,754 @@ +/* +* 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 "grd_base/grd_db_api.h" +#include "grd_document/grd_document_api.h" +#include "grd_base/grd_error.h" +using namespace testing::ext; +namespace { +std::string path = "./document.db"; +GRD_DB *g_db = nullptr; +const char *RIGHT_COLLECTION_NAME = "student"; +const char *NO_EXIST_COLLECTION_NAME = "no_exisit"; +const int INT_MAX = 2147483647; +const int INT_MIN = -2147483648; +const int MAX_COLLECTION_LENS = 511; +const int MAX_ID_LENS = 899; + +static void TestInsertDocIntoCertainColl(const char *collectionName, const char *projection, int expectedResult) +{ + /** * @tc.steps: step1. Create Collection + * @tc.expected: step1. GRD_OK + */ + EXPECT_EQ(GRD_CreateCollection(g_db, collectionName, "", 0), expectedResult); + /** + * @tc.steps: step2. Insert projection into colloction. + * @tc.expected: step2. GRD_OK + */ + EXPECT_EQ(GRD_InsertDoc(g_db, collectionName, projection, 0), expectedResult); + /** + * @tc.steps: step3. Call GRD_DroCollection to drop the collection. + * @tc.expected: step3. GRD_OK + */ + EXPECT_EQ(GRD_DropCollection(g_db, collectionName, 0), expectedResult); +} + +const char* SetRandomDocument(int i) +{ + string document1 = "{\"_id\" : "; + string document2 = "\""; + string document3 = {'2','6'}; + string document4 = "\""; + string document5 = ", \"name\" : \"Ori\"}"; + string document = document1 + document2 + document3 + document4 + document5; + return document.c_str(); +} +} + +class DocumentInsertApiTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; +void DocumentInsertApiTest::SetUpTestCase(void) +{ + std::string path = "./document.db"; + int status = GRD_DBOpen(path.c_str(), nullptr, GRD_DB_OPEN_CREATE, &g_db); + EXPECT_EQ(status, GRD_OK); + EXPECT_EQ(GRD_CreateCollection(g_db, "student", "", 0), GRD_OK); + EXPECT_NE(g_db, nullptr); +} + +void DocumentInsertApiTest::TearDownTestCase(void) +{ + EXPECT_EQ(GRD_DBClose(g_db, 0), GRD_OK); + remove(path.c_str()); +} + +void DocumentInsertApiTest::SetUp(void) +{ +} + +void DocumentInsertApiTest::TearDown(void) +{ +} + + +/** + * @tc.name: DocumentInsertApiTest001 + * @tc.desc: Insert documents into collection which dose not exist + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentInsertApiTest, DocumentInsertApiTest001, TestSize.Level1) +{ + /** + * @tc.steps:step1.Insert document into collection which dose not exist + * @tc.expected:step1.GRD_INVALID_ARGS + */ + const char *document1 = "{\"_id\" : \"1\", \"name\" : \"Ori\"}"; + EXPECT_EQ(GRD_InsertDoc(g_db, NO_EXIST_COLLECTION_NAME, document1, 0), GRD_INVALID_ARGS); +} + +/** + * @tc.name: DocumentInsertApiTest002 + * @tc.desc: Insert documents into collection which _id is not string + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentInsertApiTest, DocumentInsertApiTest002, TestSize.Level1) +{ + /** + * @tc.steps:step1.Insert a document whose _id is integer + * @tc.expected:step1.GRD_INVALID_ARGS + */ + const char *document1 = "{\"_id\" : 2, \"name\" : \"Ori\"}"; + EXPECT_EQ(GRD_InsertDoc(g_db, RIGHT_COLLECTION_NAME, document1, 0), GRD_INVALID_ARGS); + /** + * @tc.steps:step2.Insert a document whose _id is bool + * @tc.expected:step2.GRD_INVALID_ARGS + */ + const char *document2 = "{\"_id\" : true, \"name\" : \"Chuan\"}"; + EXPECT_EQ(GRD_InsertDoc(g_db, RIGHT_COLLECTION_NAME, document2, 0), GRD_INVALID_ARGS); + /** + * @tc.steps:step2.Insert a document whose _id is NULL + * @tc.expected:step2.GRD_INVALID_ARGS + */ + const char *document3 = "{\"_id\" : null, \"name\" : \"Chuan\"}"; + EXPECT_EQ(GRD_InsertDoc(g_db, RIGHT_COLLECTION_NAME, document3, 0), GRD_INVALID_ARGS); + /** + * @tc.steps:step2.Insert a document whose _id is ARRAY + * @tc.expected:step2.GRD_INVALID_ARGS + */ + const char *document4 = "{\"_id\" : [\"2\"], \"name\" : \"Chuan\"}"; + EXPECT_EQ(GRD_InsertDoc(g_db, RIGHT_COLLECTION_NAME, document4, 0), GRD_INVALID_ARGS); + /** + * @tc.steps:step2.Insert a document whose _id is OBJECT + * @tc.expected:step2.GRD_INVALID_ARGS + */ + const char *document5 = "{\"_id\" : {\"val\" : \"2\"}, \"name\" : \"Chuan\"}"; + EXPECT_EQ(GRD_InsertDoc(g_db, RIGHT_COLLECTION_NAME, document5, 0), GRD_INVALID_ARGS); +} + + +/** + * @tc.name: DocumentInsertApiTest003 + * @tc.desc: Insert a document whose _id has appeared before + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentInsertApiTest, DocumentInsertApiTest003, TestSize.Level1) +{ + /** + * @tc.steps:step1.Insert a document whose _id is string + * @tc.expected:step1.GRD_OK + */ + const char *document1 = "{\"_id\" : \"3\", \"name\" : \"Ori\"}"; + EXPECT_EQ(GRD_InsertDoc(g_db, RIGHT_COLLECTION_NAME, document1, 0), GRD_OK); + + /** + * @tc.steps:step2.Insert a document whose _id has appeared before + * @tc.expected:step2.GRD_DATA_CONFLICT + */ + // const char *document2 = "{\"_id\" : \"3\", \"name\" : \"Chuan\"}"; + // EXPECT_EQ(GRD_InsertDoc(g_db, RIGHT_COLLECTION_NAME, document2, 0), GRD_DATA_CONFLICT); +} + +/** + * @tc.name: DocumentInsertApiTest004 + * @tc.desc: Test Insert with null parameter. parameter db is NULL + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentInsertApiTest, DocumentInsertApiTest004, TestSize.Level1) +{ + /** + * @tc.steps:step1.step1.parameter db is NULL + * @tc.expected:step1.GRD_INVALID_ARGS + */ + const char *document1 = "{\"_id\" : \"4\", \"name\" : \"Ori\"}"; + EXPECT_EQ(GRD_InsertDoc(NULL, RIGHT_COLLECTION_NAME, document1, 0), GRD_INVALID_ARGS); +} + +/** + * @tc.name: DocumentInsertApiTest005 + * @tc.desc: Test insert with null parameter. parameter collectionName is NULL. + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentInsertApiTest, DocumentInsertApiTest005, TestSize.Level1) +{ + /** + * @tc.steps:step1.Parameter collectionName is NULL + * @tc.expected:step1.GRD_INVALID_ARGS + */ + const char *document1 = "{\"_id\" : \"5\", \"name\" : \"Ori\"}"; + EXPECT_EQ(GRD_InsertDoc(g_db, NULL, document1, 0), GRD_INVALID_ARGS); + /** + * @tc.steps:step2.Parameter collectionName is empty string + * @tc.expected:step2.GRD_INVALID_ARGS + */ + const char *document2 = "{\"_id\" : \"5\", \"name\" : \"Chuang\"}"; + EXPECT_EQ(GRD_InsertDoc(g_db, "", document2, 0), GRD_INVALID_ARGS); +} + +/** + * @tc.name: DocumentInsertApiTest006 + * @tc.desc: parameter flags is not zero + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentInsertApiTest, DocumentInsertApiTest006, TestSize.Level1) +{ + /** + * @tc.steps:step1.parameter flags is not zero + * @tc.expected:step1.GRD_INVALID_ARGS + */ + const char *document1 = "{\"_id\" : \"6\", \"name\" : \"Ori\"}"; + EXPECT_EQ(GRD_InsertDoc(g_db, RIGHT_COLLECTION_NAME, document1, 1), GRD_INVALID_ARGS); +} + +/** + * @tc.name: DocumentInsertApiTest007 + * @tc.desc: parameter flags is INT_MAX + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentInsertApiTest, DocumentInsertApiTest007, TestSize.Level1) +{ + /** + * @tc.steps:step1.parameter flags is int_max + * @tc.expected:step1.GRD_INVALID_ARGS + */ + const char *document1 = "{\"_id\" : \"7\", \"name\" : \"Ori\"}"; + EXPECT_EQ(GRD_InsertDoc(g_db, RIGHT_COLLECTION_NAME, document1, INT_MAX), GRD_INVALID_ARGS); +} + +/** + * @tc.name: DocumentInsertApiTest008 + * @tc.desc: parameter flags is int_min + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentInsertApiTest, DocumentInsertApiTest008, TestSize.Level1) +{ + /** + * @tc.steps:step1.parameter flags is int_min + * @tc.expected:step1.GRD_INVALID_ARGS + */ + const char *document1 = "{\"_id\" : \"8\", \"name\" : \"Ori\"}"; + EXPECT_EQ(GRD_InsertDoc(g_db, RIGHT_COLLECTION_NAME, document1, INT_MIN), GRD_INVALID_ARGS); +} + +/** + * @tc.name: DocumentInsertApiTest009 + * @tc.desc: parameter collectionName and document is NULL or invalid + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentInsertApiTest, DocumentInsertApiTest009, TestSize.Level1) +{ + /** + * @tc.steps:step1.parameter collectionName and document is NULL; + * @tc.expected:step1.GRD_INVALID_ARGS + */ + EXPECT_EQ(GRD_InsertDoc(g_db, NULL, NULL, 0), GRD_INVALID_ARGS); + /** + * @tc.steps:step2.parameter collectionName is larger than max_collectionName_lens; + * @tc.expected:step2.GRD_OVER_LIMIT + */ + const char *document1 = "{\"_id\" : \"9\", \"name\" : \"Ori\"}"; + std::string collectionName2(MAX_COLLECTION_LENS + 1, 'a'); + EXPECT_EQ(GRD_InsertDoc(g_db, collectionName2.c_str(), document1, 0), GRD_OVER_LIMIT); +} + +/** + * @tc.name: DocumentInsertApiTest010 + * @tc.desc: parameter collectionName contains irregular charactor + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentInsertApiTest, DocumentInsertApiTest010, TestSize.Level1) +{ + /** + * @tc.steps:step1.Create Collection whose parameter collectionName contains irregular charactor + * @tc.expected:step1.GRD_OK + */ + const char *collectionName = "collction@!#"; + const char *document1 = "{\"_id\" : \"10\", \"name\" : \"Ori\"}"; + TestInsertDocIntoCertainColl(collectionName, document1, GRD_OK); +} + +/** + * @tc.name: DocumentInsertApiTest011 + * @tc.desc: parameter collectionName is longer than 256 charactors + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentInsertApiTest, DocumentInsertApiTest011, TestSize.Level1) +{ + /** + * @tc.steps:step1.Create Collection whose parameter collectionName contains irregular charactor + * @tc.expected:step1.GRD_OK + */ + string collectionName(257, 'k'); + const char *document1 = "{\"_id\" : \"10\", \"name\" : \"Ori\"}"; + TestInsertDocIntoCertainColl(collectionName.c_str(), document1, GRD_OK); +} + +/** + * @tc.name: DocumentInsertApiTest014 + * @tc.desc: Inserted document's JSON depth is larger than 4, which is 5. + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentInsertApiTest, DocumentInsertApiTest014, TestSize.Level1) +{ + /** + * @tc.steps:step1.document's JSON depth is larger than 4, which is 5. + * @tc.expected:step1.GRD_INVALID_ARGS + */ + const char *document1 = "{\"level1\" : {\"level2\" : {\"level3\" : {\"level4\": {\"level5\" : 1}}, \"level3_2\" : \"level3_2_val\"\ + }},\"_id\":\"14\"}"; + EXPECT_EQ(GRD_InsertDoc(g_db, RIGHT_COLLECTION_NAME, document1, 0), GRD_INVALID_ARGS); + /** + * @tc.steps:step1.document's JSON depth is larger than 4, which is 5.But with array type. + * @tc.expected:step1.GRD_INVALID_ARGS + */ + const char *document2 = "{\"level1\" : {\"level2\" : {\"level3\" : [{ \"level5\" : \"level5_1val\", \"level5_2\":\ + \"level5_2_val\"}, \"level4_val1\",\"level4_val2\"], \"level3_2\" : \"level3_2_val\"\ + }},\"_id\":\"14\"}"; + EXPECT_EQ(GRD_InsertDoc(g_db, RIGHT_COLLECTION_NAME, document2, 0), GRD_INVALID_ARGS); + /** + * @tc.steps:step1.document's JSON depth is 4 + * @tc.expected:step1.GRD_OK + */ + const char *document3 = "{\"level1\" : {\"level2\" : {\"level3\" : { \"level4\" : \"level5_1val\"}, \"level3_2\" : \"level3_2_val\"\ + }},\"_id\":\"14\"}"; + EXPECT_EQ(GRD_InsertDoc(g_db, RIGHT_COLLECTION_NAME, document3, 0), GRD_OK); +} + +/** + * @tc.name: DocumentInsertApiTest015 + * @tc.desc: Inserted document with all kinds of size + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentInsertApiTest, DocumentInsertApiTest015, TestSize.Level1) +{ + /** + * @tc.steps:step1.document's JSON is bigger than 512k - 1 + * @tc.expected:step1.GRD_INVALID_ARGS + */ + string documentPart1 = "{ \"_id\" : \"15\", \"textVal\" : \" "; + string documentPart2 = "\" }"; + string jsonVal = string(512 * 1024 - documentPart1.size() - documentPart2.size(), 'k'); + string document = documentPart1 + jsonVal + documentPart2; + //EXPECT_EQ(GRD_InsertDoc(g_db, RIGHT_COLLECTION_NAME, document.c_str(), 0), GRD_OVER_LIMIT); + /** + * @tc.steps:step2.Insert document's JSON is a large data but lower than 512k - 1 + * @tc.expected:step2.GRD_OK + */ + string jsonVal2 = string(512 * 1024 - 1 - documentPart1.size() - documentPart2.size(), 'k'); + string document2 = documentPart1 + jsonVal2 + documentPart2; + EXPECT_EQ(GRD_InsertDoc(g_db, RIGHT_COLLECTION_NAME, document2.c_str(), 0), GRD_OK); +} + +/** + * @tc.name: DocumentInsertApiTest016 + * @tc.desc: document JSON string contains irregular char + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentInsertApiTest, DocumentInsertApiTest016, TestSize.Level1) +{ + /** + * @tc.steps:step1.document JSON string contains irregular char. + * @tc.expected:step1.GRD_OK + */ + const char *document1 = "{\"_id\" : \"16\", \"name\" : \"!@#Ori\"}"; + EXPECT_EQ(GRD_InsertDoc(g_db, RIGHT_COLLECTION_NAME, document1, 0), GRD_OK); +} + +/** + * @tc.name: DocumentInsertApiTest017 + * @tc.desc: document JSON string contains invalid value type such as BLOB type + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentInsertApiTest, DocumentInsertApiTest017, TestSize.Level1) +{ + /** + * @tc.steps:step1.document JSON string contains invalid value type such as BLOB type. + * @tc.expected:step1.GRD_INVALID_FORMAT. + */ + const char *document = "{\"_id\" : \"17\", \"level1\" : {\"level2\" : {\"level3\" : {\"level4\" : x'1234'\ + } } }, \"level1_2\" : \"level1_2Val\"}"; + EXPECT_EQ(GRD_InsertDoc(g_db, RIGHT_COLLECTION_NAME, document, 0), GRD_INVALID_FORMAT); +} + +/** + * @tc.name: DocumentInsertApiTest018 + * @tc.desc: The Inserted document is not JSON format + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentInsertApiTest, DocumentInsertApiTest018, TestSize.Level1) +{ + /** + * @tc.steps:step1.The Inserted document is not JSON format + * @tc.expected:step1.GRD_INVALID_FORMAT. + */ + const char *document = "some random string not JSON format"; + EXPECT_EQ(GRD_InsertDoc(g_db, RIGHT_COLLECTION_NAME, document, 0), GRD_INVALID_FORMAT); +} + +/** + * @tc.name: DocumentInsertApiTest019 + * @tc.desc: Insert a normal documents + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentInsertApiTest, DocumentInsertApiTest019, TestSize.Level1) +{ + /** + * @tc.steps:step1.Insert a normal documents which _id is in the end of the string + * @tc.expected:step1.GRD_OK. + */ + const char *document1 = "{\"name\" : \"Jack\", \"age\" : 18, \"friend\" : {\"name\" : \" lucy\"}, \"_id\" : \"19\"}"; + EXPECT_EQ(GRD_InsertDoc(g_db, RIGHT_COLLECTION_NAME, document1, 0), GRD_OK); +} + +/** + * @tc.name: DocumentInsertApiTest022 + * @tc.desc: parameter collectionName is equal to 256 charactors + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentInsertApiTest, DocumentInsertApiTest022, TestSize.Level1) +{ + /** + * @tc.steps:step1.parameter collectionName is equal to 256 charactors + * @tc.expected:step1.GRD_OK. + */ + string collectionName = string(256, 'k'); + string collectionName1(MAX_COLLECTION_LENS, 'a'); + const char *document1 = "{\"_id\" : \"22\", \"name\" : \"Ori\"}"; + TestInsertDocIntoCertainColl(collectionName.c_str(), document1, GRD_OK); +} + +/** + * @tc.name: DocumentInsertApiTest023 + * @tc.desc: parameter collectionName contains upper & lower case charactors, + * numbers and underline + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentInsertApiTest, DocumentInsertApiTest023, TestSize.Level1) +{ + /** + * @tc.steps:step1.parameter collectionName contains upper & lower case charactors, + * numbers and underline + * @tc.expected:step1.GRD_OK. + */ + string collectionName = "Aads_sd__23Asb_"; + const char *document1 = "{\"_id\" : \"23\", \"name\" : \"Ori\"}"; + TestInsertDocIntoCertainColl(collectionName.c_str(), document1, GRD_OK); +} + +/** + * @tc.name: DocumentInsertApiTest024 + * @tc.desc: parameter collectionName's head is GRD_ or GM_SYS_ + * numbers and underline + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentInsertApiTest, DocumentInsertApiTest024, TestSize.Level1) +{ + /** + * @tc.steps:step1.parameter collectionName's head is GRD_ + * @tc.expected:step1.GRD_INVALID_FORMAT. + */ + string collectionName = "GRD_collectionName"; + const char *document1 = "{\"_id\" : \"24\", \"name\" : \"Ori\"}"; + GRD_CreateCollection(g_db, collectionName.c_str(), "", 0); + EXPECT_EQ(GRD_InsertDoc(g_db, collectionName.c_str(), document1, 0), GRD_INVALID_FORMAT); + + /** + * @tc.steps:step2.parameter collectionName's head is GM_SYS_ + * @tc.expected:step2.GRD_INVALID_FORMAT. + */ + collectionName = "GM_SYS__collectionName"; + const char *document2 = "{\"_id\" : \"24_2\", \"name\" : \"Ori\"}"; + GRD_CreateCollection(g_db, collectionName.c_str(), "", 0); + EXPECT_EQ(GRD_InsertDoc(g_db, collectionName.c_str(), document2, 0), GRD_INVALID_FORMAT); + + /** + * @tc.steps:step3.parameter collectionName's head is grd_ + * @tc.expected:step3.GRD_INVALID_FORMAT. + */ + collectionName = "grd_collectionName"; + const char *document3 = "{\"_id\" : \"24_3\", \"name\" : \"Ori\"}"; + GRD_CreateCollection(g_db, collectionName.c_str(), "", 0); + EXPECT_EQ(GRD_InsertDoc(g_db, collectionName.c_str(), document3, 0), GRD_INVALID_FORMAT); + + /** + * @tc.steps:step4.parameter collectionName's head is gm_sys_ + * @tc.expected:step4.GRD_INVALID_FORMAT. + */ + collectionName = "gm_sys_collectionName"; + const char *document4 = "{\"_id\" : \"24_4\", \"name\" : \"Ori\"}"; + GRD_CreateCollection(g_db, collectionName.c_str(), "", 0); + EXPECT_EQ(GRD_InsertDoc(g_db, collectionName.c_str(), document4, 0), GRD_INVALID_FORMAT); + + /** + * @tc.steps:step5.parameter collectionName's head is gM_sYs_ that has Uppercase and lowercase at the same time. + * @tc.expected:step5.GRD_INVALID_FORMAT. + */ + collectionName = "gM_sYs_collectionName"; + const char *document5 = "{\"_id\" : \"24_5\", \"name\" : \"Ori\"}"; + GRD_CreateCollection(g_db, collectionName.c_str(), "", 0); + EXPECT_EQ(GRD_InsertDoc(g_db, collectionName.c_str(), document5, 0), GRD_INVALID_FORMAT); + + /** + * @tc.steps:step6.parameter collectionName's head is gRd_ that has Uppercase and lowercase at the same time. + * @tc.expected:step6.GRD_INVALID_FORMAT. + */ + collectionName = "gRd_collectionName"; + const char *document6 = "{\"_id\" : \"24_6\", \"name\" : \"Ori\"}"; + GRD_CreateCollection(g_db, collectionName.c_str(), "", 0); + EXPECT_EQ(GRD_InsertDoc(g_db, collectionName.c_str(), document6, 0), GRD_INVALID_FORMAT); + + /** + * @tc.steps:step7.parameter collectionName's head is grd@ that has no '_' + * @tc.expected:step7.GRD_INVALID_FORMAT. + */ + collectionName = "gRd@collectionName"; + const char *document7 = "{\"_id\" : \"24_7\", \"name\" : \"Ori\"}"; + GRD_CreateCollection(g_db, collectionName.c_str(), "", 0); + EXPECT_EQ(GRD_InsertDoc(g_db, collectionName.c_str(), document7, 0), GRD_OK); +} + +/** + * @tc.name: DocumentInsertApiTest025 + * @tc.desc: Insert document whose depth is 4, which is allowed + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentInsertApiTest, DocumentInsertApiTest025, TestSize.Level1) +{ + /** + * @tc.steps:step1.documents JSON depth is 4, which is allowed. + * @tc.expected:step1.GRD_OK. + */ + const char *document1 = "{\"_id\" : \"25_0\", \"level1\" : { \"level2\" : {\"level3\" :\ + {\"level4\" : \"level4Val\" } } } , \"level1_2\" : \"level1_2Val\" }"; + EXPECT_EQ(GRD_InsertDoc(g_db, RIGHT_COLLECTION_NAME, document1, 0), GRD_OK); + /** + * @tc.steps:step2.documents JSON depth is exactly 4. + * @tc.expected:step2.GRD_OK. + */ + const char *document2 = "{\"_id\" : \"25_1\", \"class_name\" : \"计算机科学一班\", \"signed_info\" : true, \"student_info\" : [{\"name\":\"张三\", \ + \"age\" : 18, \"sex\" : \"男\"}, { \"newName1\" : [\"qw\", \"dr\", 0, \"ab\"] }]}"; + EXPECT_EQ(GRD_InsertDoc(g_db, RIGHT_COLLECTION_NAME, document2, 0), GRD_OK); + /** + * @tc.steps:step3.documents JSON depth is exactly 4, but the last field in array contains leading number + * @tc.expected:step3.GRD_INVALID_ARGS. + */ + const char *document3 = "{\"_id\" : \"25_2\", \"class_name\" : \"计算机科学一班\", \"signed_info\" : true, \"student_info\" : [{\"name\":\"张三\", \ + \"age\" : 18, \"sex\" : \"男\"}, [\"qw\", \"dr\", 0, \"ab\", {\"0ab\" : null}]]}"; + EXPECT_EQ(GRD_InsertDoc(g_db, RIGHT_COLLECTION_NAME, document3, 0), GRD_INVALID_ARGS); + /** + * @tc.steps:step4.documents JSON depth is exactly 5. + * @tc.expected:step4.GRD_INVALID_ARGS. + */ + const char *document4 = "{\"_id\" : \"25_3\", \"class_name\" : \"计算机科学一班\", \"signed_info\" : true, \"student_info\" : [{\"name\":\"张三\", \ + \"age\" : 18, \"sex\" : \"男\"}, { \"newName1\" : [\"qw\", \"dr\", 0, \"ab\", {\"level5\" : 1}] }]}"; + EXPECT_EQ(GRD_InsertDoc(g_db, RIGHT_COLLECTION_NAME, document4, 0), GRD_INVALID_ARGS); +} + +/** + * @tc.name: DocumentInsertApiTest026 + * @tc.desc: Insert 100 normal documents continuously + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentInsertApiTest, DocumentInsertApiTest, TestSize.Level1) +{ + /** + * @tc.steps:step1.Insert 100 normal documents continuously + * @tc.expected:step1.GRD_OK. + */ + string document1 = "{\"_id\" : "; + string document2 = "\""; + string document4 = "\""; + string document5 = ", \"name\" : \"Ori\"}"; + for (int i = 0; i < 5; i++) { + string document_midlle = {'2','6' + i}; + string document = document1 + document2 + document_midlle + document4 + document5; + EXPECT_EQ(GRD_InsertDoc(g_db, RIGHT_COLLECTION_NAME, document.c_str(), 0), GRD_OK); + } +} + +/** + * @tc.name: DocumentInsertApiTest035 + * @tc.desc: Insert a document whose value contains + * upper &lower case charactors, numbers and underline. + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentInsertApiTest, DocumentInsertApiTest035, TestSize.Level1) +{ + /** + * @tc.steps:step1.Insert a document whose value contains + * upper &lower case charactors, numbers and underline. + * @tc.expected:step1.GRD_OK. + */ + const char *document1 = "{\"_id\" : \"35\", \"A_aBdk_324_\" : \"value\", \"name\" : \"Chuan\"}"; + EXPECT_EQ(GRD_InsertDoc(g_db, RIGHT_COLLECTION_NAME, document1, 0), GRD_OK); + /** + * @tc.steps:step1.Insert a document whose value contains + * upper &lower case charactors, numbers and underline. + * But the field started with number, which is not allowed. + * @tc.expected:step1.GRD_OK. + */ + const char *document2 = "{\"_id\" : \"35_2\", \"1A_aBdk_324_\" : \"value\", \"name\" : \"Chuan\"}"; + EXPECT_EQ(GRD_InsertDoc(g_db, RIGHT_COLLECTION_NAME, document2, 0), GRD_INVALID_ARGS); +} + +/** + * @tc.name: DocumentInsertApiTest036 + * @tc.desc: Insert a document whose value contains + * string, number, bool, null, array and object type + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentInsertApiTest, DocumentInsertApiTest036, TestSize.Level1) +{ + /** + * @tc.steps:step1.Insert a document whose value contains + * string, number, bool, null, array and object type + * @tc.expected:step1.GRD_OK. + */ + const char *document1 = "{\"_id\" : \"36_0\", \"stringType\" : \"stringVal\", \"numType\" : 1, \"BoolType\" : true,\ + \"nullType\" : null, \"arrayType\" : [1, 2, 3, 4], \"objectType\" : {\"A\" : 3}}"; + EXPECT_EQ(GRD_InsertDoc(g_db, RIGHT_COLLECTION_NAME, document1, 0), GRD_OK); +} + +/** + * @tc.name: DocumentInsertApiTest038 + * @tc.desc: Insert document whose value is over the range of double + * string, number, bool, null, array and object type + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentInsertApiTest, DocumentInsertApiTest038, TestSize.Level1) +{ + /** + * @tc.steps:step1.Insert document whose value is over the range of double + * @tc.expected:step1.GRD_INVALID_ARGS. + */ + const char *document1 = R"({"_id" : "38_0", "field2" : 1.79769313486232e308})"; + EXPECT_EQ(GRD_InsertDoc(g_db, RIGHT_COLLECTION_NAME, document1, 0), GRD_INVALID_ARGS); + /** + * @tc.steps:step2.Insert document whose value is over the range of double + * @tc.expected:step2.GRD_INVALID_ARGS. + */ + const char *document2 = R"({"_id" : "38_1", "t1" : {"field2" : 1.79769313486232e308}})"; + EXPECT_EQ(GRD_InsertDoc(g_db, RIGHT_COLLECTION_NAME, document2, 0), GRD_INVALID_ARGS); + /** + * @tc.steps:step3.Insert document whose value is over the range of double + * @tc.expected:step3.GRD_INVALID_ARGS. + */ + const char *document3 = R"({"_id" : "38_2", "t1" : [1, 2, 1.79769313486232e308]})"; + EXPECT_EQ(GRD_InsertDoc(g_db, RIGHT_COLLECTION_NAME, document3, 0), GRD_INVALID_ARGS); + /** + * @tc.steps:step4.Insert document whose value is over the range of double + * @tc.expected:step4.GRD_INVALID_ARGS. + */ + const char *document4 = R"({"_id" : "38_3", "t1" : [1, 2, -1.7976931348623167E+308]})"; + EXPECT_EQ(GRD_InsertDoc(g_db, RIGHT_COLLECTION_NAME, document2, 0), GRD_INVALID_ARGS); + /** + * @tc.steps:step5.Insert document with minimum double value + * @tc.expected:step5.GRD_INVALID_ARGS. + */ + const char *document5 = R"({"_id" : "38_4", "t1" : [1, 2, -1.79769313486231570E+308]})"; + EXPECT_EQ(GRD_InsertDoc(g_db, RIGHT_COLLECTION_NAME, document5, 0), GRD_OK); + /** + * @tc.steps:step6.Insert document with maxium double value + * @tc.expected:step6.GRD_INVALID_ARGS. + */ + const char *document6 = R"({"_id" : "38_5", "t1" : [1, 2, 1.79769313486231570E+308]})"; + EXPECT_EQ(GRD_InsertDoc(g_db, RIGHT_COLLECTION_NAME, document6, 0), GRD_OK); +} + +/** + * @tc.name: DocumentInsertApiTest039 + * @tc.desc: Insert a filter which _id value's lens is larger than MAX_ID_LENS + * @tc.type: FUNC + * @tc.require: + * @tc.author: mazhao + */ +HWTEST_F(DocumentInsertApiTest, DocumentInsertApiTest039, TestSize.Level1) +{ + /** + * @tc.steps:step1.Insert a filter which _id value's lens is larger than MAX_ID_LENS. + * @tc.expected:step1.GRD_OVER_LIMIT. + */ + string document1 = "{\"_id\" : "; + string document2 = "\""; + string document4 = "\""; + string document5 = ", \"name\" : \"Ori\"}"; + string document_midlle(MAX_ID_LENS + 1, 'k'); + string document = document1 + document2 + document_midlle + document4 + document5; + EXPECT_EQ(GRD_InsertDoc(g_db, RIGHT_COLLECTION_NAME, document.c_str(), 0), GRD_OVER_LIMIT); + + /** + * @tc.steps:step1.Insert a filter which _id value's lens is equal as MAX_ID_LENS. + * @tc.expected:step1.GRD_OK. + */ + string document_midlle2(MAX_ID_LENS, 'k'); + document = document1 + document2 + document_midlle2 + document4 + document5; + EXPECT_EQ(GRD_InsertDoc(g_db, RIGHT_COLLECTION_NAME, document.c_str(), 0), GRD_OK); +} \ No newline at end of file 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..382fe5ef71dd3a0cd036b1b37e1141412748e7f2 --- /dev/null +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/test/unittest/api/documentdb_api_test.cpp @@ -0,0 +1,620 @@ +/* +* 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_ONLY, &db); + EXPECT_EQ(status, GRD_INVALID_ARGS); + + 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..a15df4df50029f45055142b9ed59b2bc9d47c19f --- /dev/null +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/test/unittest/api/documentdb_collection_test.cpp @@ -0,0 +1,220 @@ +/* +* 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'); + EXPECT_EQ(GRD_CreateCollection(g_db, overLenName.c_str(), "", 0), GRD_OVER_LIMIT); + EXPECT_EQ(GRD_DropCollection(g_db, overLenName.c_str(), 0), GRD_OVER_LIMIT); + + std::vector invalidName = { + nullptr, + "", + }; + + 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); + } + + std::vector invalidNameFormat = { + "GRD_123", + "grd_123", + "GM_SYS_123", + "gm_sys_123" + }; + + for (auto *it : invalidNameFormat) { + GLOGD("CollectionTest002: create collection with name: %s", it); + EXPECT_EQ(GRD_CreateCollection(g_db, it, "", 0), GRD_INVALID_FORMAT); + EXPECT_EQ(GRD_DropCollection(g_db, it, 0), GRD_INVALID_FORMAT); + } +} + +/** + * @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_OK); + EXPECT_EQ(GRD_CreateCollection(g_db, "Student", "", CHK_EXIST_COLLECTION), GRD_DATA_CONFLICT); +} + +/** + * @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})"", 0), GRD_INVALID_ARGS); + + EXPECT_EQ(GRD_DropCollection(g_db, "student", 0), GRD_OK); + EXPECT_EQ(GRD_DropCollection(g_db, "student", 0), GRD_OK); + EXPECT_EQ(GRD_DropCollection(g_db, "student", CHK_NON_EXIST_COLLECTION), GRD_NO_DATA); + + // 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/api/documentdb_test_utils.h b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/test/unittest/api/documentdb_test_utils.h new file mode 100644 index 0000000000000000000000000000000000000000..a513875ef87a922f39132229a2e9e620f2bda17c --- /dev/null +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/test/unittest/api/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/api/doucumentdb_api_test.cpp b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/test/unittest/api/doucumentdb_api_test.cpp deleted file mode 100644 index 63ca9cab9ef11810fe59557d23b9a7b073eb8eef..0000000000000000000000000000000000000000 --- a/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/test/unittest/api/doucumentdb_api_test.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/* -* 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 "log_print.h" -#include "grd_base/grd_db_api.h" -#include "grd_base/grd_error.h" -#include "grd_document/grd_document_api.h" - -using namespace DocumentDB; -using namespace testing::ext; - -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) -{ -} - -/** - * @tc.name: OpenDBTest001 - * @tc.desc: Test open document db - * @tc.type: FUNC - * @tc.require: - * @tc.author: lianhuix - */ -HWTEST_F(DocumentDBApiTest, OpenDBTest001, TestSize.Level1) -{ - 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); - - EXPECT_EQ(GRD_DropCollection(db, "student", 0), GRD_OK); - - status = GRD_DBClose(db, 0); - EXPECT_EQ(status, GRD_OK); - db = nullptr; -} - -/** - * @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); -} 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/oh_adapter/documentdb_jsonobject_test.cpp b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/test/unittest/oh_adapter/documentdb_jsonobject_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fa16a27a019fc874f28bff8beef2a4bc71208ffa --- /dev/null +++ b/services/distributeddataservice/service/data_share/gaussdb_rd_Simple/test/unittest/oh_adapter/documentdb_jsonobject_test.cpp @@ -0,0 +1,68 @@ +/* +* 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 "documentdb_test_utils.h" +#include "doc_errno.h" +#include "json_object.h" + +using namespace DocumentDB; +using namespace testing::ext; +using namespace DocumentDBUnitTest; + +class DocumentDBJsonObjectTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void DocumentDBJsonObjectTest::SetUpTestCase(void) +{ +} + +void DocumentDBJsonObjectTest::TearDownTestCase(void) +{ +} + +void DocumentDBJsonObjectTest::SetUp(void) +{ +} + +void DocumentDBJsonObjectTest::TearDown(void) +{ +} + +/** + * @tc.name: OpenDBTest001 + * @tc.desc: Test open document db + * @tc.type: FUNC + * @tc.require: + * @tc.author: lianhuix + */ +HWTEST_F(DocumentDBJsonObjectTest, JsonObjectTest001, TestSize.Level0) +{ + const std::string config = R""({"a":123,"b":{"c":234,"d":"12345"}})""; + + int ret = E_OK; + JsonObject conf = JsonObject::Parse(config, ret); + EXPECT_EQ(ret, E_OK); + + ValueObject obj = conf.GetObjectByPath({"b", "c"}, ret); + + EXPECT_EQ(obj.GetValueType(), ValueObject::ValueType::VALUE_NUMBER); + EXPECT_EQ(obj.GetIntValue(), 234); +} \ No newline at end of file