1 /*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #ifndef OMIT_JSON
17 #include <gtest/gtest.h>
18
19 #include "db_common.h"
20 #include "db_constant.h"
21 #include "distributeddb_data_generate_unit_test.h"
22 #include "distributeddb_tools_unit_test.h"
23 #include "log_print.h"
24 #include "platform_specific.h"
25 #include "sqlite_import.h"
26 #include "store_types.h"
27
28 using namespace testing::ext;
29 using namespace DistributedDB;
30 using namespace DistributedDBUnitTest;
31
32 namespace {
33 std::string g_testDir;
34 std::string g_identifier;
35
36 KvStoreDelegateManager g_mgr(APP_ID, USER_ID);
37 DBStatus g_kvNbDelegateStatus = INVALID_ARGS;
38 KvStoreNbDelegate *g_kvNbDelegatePtr = nullptr;
39 auto g_kvNbDelegateCallback = bind(&DistributedDBToolsUnitTest::KvStoreNbDelegateCallback,
40 std::placeholders::_1, std::placeholders::_2, std::ref(g_kvNbDelegateStatus), std::ref(g_kvNbDelegatePtr));
41
42 const std::string BASE_SCHEMA_STRING = "{\"SCHEMA_VERSION\" : \"1.0\","
43 "\"SCHEMA_MODE\" : \"COMPATIBLE\","
44 "\"SCHEMA_DEFINE\" : {"
45 "\"name\" : \"STRING\","
46 "\"id\" : \"INTEGER\","
47 "\"father\" : {"
48 "\"name\" : \"STRING\","
49 "\"id\" : \"INTEGER\""
50 "},"
51 "\"phone\" : \"INTEGER\""
52 "},"
53 "\"SCHEMA_INDEXES\" : ";
54
55 const std::string JSON_VALUE ="{\"name\":\"Tom\","
56 "\"id\":10,"
57 "\"father\":{\"name\":\"Jim\", \"id\":20},"
58 "\"phone\":20}";
59
GenerateSchemaString(std::string & schema,const std::string & indexString)60 void GenerateSchemaString(std::string &schema, const std::string &indexString)
61 {
62 schema = BASE_SCHEMA_STRING + indexString + "}";
63 }
64
GetKvStoreDirectory(const std::string & userId,const std::string & appId,const std::string & storeId)65 std::string GetKvStoreDirectory(const std::string &userId, const std::string &appId, const std::string &storeId)
66 {
67 string identifier = DBCommon::GenerateIdentifierId(storeId, appId, userId);
68 string hashIdentifierName = DBCommon::TransferHashString(identifier);
69 string identifierName = DBCommon::TransferStringToHex(hashIdentifierName);
70 string filePath = g_testDir + "/" + identifierName + "/" + DBConstant::SINGLE_SUB_DIR + "/main/";
71 filePath += DBConstant::SINGLE_VER_DATA_STORE + DBConstant::DB_EXTENSION;
72 return filePath;
73 }
74
CheckIndexFromDbFile(const::std::string & filePath,const std::string & indexName)75 bool CheckIndexFromDbFile(const::std::string &filePath, const std::string &indexName)
76 {
77 sqlite3 *db = nullptr;
78 if (sqlite3_open_v2(filePath.c_str(), &db, SQLITE_OPEN_URI | SQLITE_OPEN_READWRITE, nullptr) != SQLITE_OK) {
79 LOGD("DB open failed %s", filePath.c_str());
80 if (db != nullptr) {
81 (void)sqlite3_close_v2(db);
82 }
83 return false;
84 }
85
86 std::string querySQL = "select sql from sqlite_master where name = '" + indexName + "'";
87 int errCode = sqlite3_exec(db, querySQL.c_str(), nullptr, nullptr, nullptr);
88 (void)sqlite3_close_v2(db);
89 if (errCode == SQLITE_OK) {
90 return true;
91 }
92 return false;
93 }
94 }
95
96 class DistributedDBStorageIndexOptimizeTest : public testing::Test {
97 public:
98 static void SetUpTestCase(void);
99 static void TearDownTestCase(void);
100 void SetUp();
101 void TearDown();
102 };
103
SetUpTestCase(void)104 void DistributedDBStorageIndexOptimizeTest::SetUpTestCase(void)
105 {
106 DistributedDBToolsUnitTest::TestDirInit(g_testDir);
107 std::string origIdentifier = USER_ID + "-" + APP_ID + "-" + STORE_ID_1;
108 std::string identifier = DBCommon::TransferHashString(origIdentifier);
109 g_identifier = DBCommon::TransferStringToHex(identifier);
110 std::string dir = g_testDir + g_identifier + "/" + DBConstant::SINGLE_SUB_DIR;
111 DIR *dirTmp = opendir(dir.c_str());
112 if (dirTmp == nullptr) {
113 OS::MakeDBDirectory(dir);
114 } else {
115 closedir(dirTmp);
116 }
117
118 KvStoreConfig config;
119 config.dataDir = g_testDir;
120 g_mgr.SetKvStoreConfig(config);
121 }
122
TearDownTestCase(void)123 void DistributedDBStorageIndexOptimizeTest::TearDownTestCase(void)
124 {
125 if (DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir) != 0) {
126 LOGE("rm test db files error!");
127 }
128 }
129
SetUp(void)130 void DistributedDBStorageIndexOptimizeTest::SetUp(void)
131 {
132 DistributedDBToolsUnitTest::PrintTestCaseInfo();
133 }
134
TearDown(void)135 void DistributedDBStorageIndexOptimizeTest::TearDown(void)
136 {
137 }
138
139 /**
140 * @tc.name: ParseAndCheckUnionIndex001
141 * @tc.desc: Test the Json union index parse and check function Open function
142 * @tc.type: FUNC
143 * @tc.require: AR000F3OPD
144 * @tc.author: xushaohua
145 */
146 HWTEST_F(DistributedDBStorageIndexOptimizeTest, ParseAndCheckUnionIndex001, TestSize.Level1)
147 {
148 /**
149 * @tc.steps: step1. Create a correct shema string include a correct union index.
150 */
151 std::string schema1;
152 GenerateSchemaString(schema1, "[[\"name\", \"father.name\", \"father.id\", \"id\", \"phone\"]]");
153
154 /**
155 * @tc.steps: step2. Call SchemaObject.ParseFromSchemaString to parse the string.
156 * @tc.expected: step2. Expect return E_OK.
157 */
158 SchemaObject so1;
159 EXPECT_EQ(so1.ParseFromSchemaString(schema1), E_OK);
160
161 /**
162 * @tc.steps: step3. Create a correct shema string include a single index and a union index
163 */
164 std::string schema2;
165 GenerateSchemaString(schema2, "[[\"name\", \"father.name\", \"father.id\", \"id\", \"phone\"], \"id\"]");
166
167 /**
168 * @tc.steps: step4. Call SchemaObject.ParseFromSchemaString to parse the string.
169 * @tc.expected: step4. Expect return E_OK.
170 */
171 SchemaObject so2;
172 EXPECT_EQ(so2.ParseFromSchemaString(schema2), E_OK);
173
174 /**
175 * @tc.steps: step5. Create a shema string include a single index and a union index, and the two index has
176 the same sort column.
177 */
178 std::string schema3;
179 GenerateSchemaString(schema3, "[[\"name\", \"father.name\", \"father.id\", \"id\", \"phone\"], \"name\"]");
180
181 /**
182 * @tc.steps: step6. Call SchemaObject.ParseFromSchemaString to parse the string.
183 * @tc.expected: step6. Expect return E_SCHEMA_PARSE_FAIL.
184 */
185 SchemaObject so3;
186 EXPECT_EQ(so3.ParseFromSchemaString(schema3), -E_SCHEMA_PARSE_FAIL);
187
188 /**
189 * @tc.steps: step7. Create a shema string include a single index with a not exist column
190 */
191 std::string schema4;
192 GenerateSchemaString(schema4, "[[\"name\", \"father.name\", \"father.id\", \"id\", \"tel\"]]");
193
194 /**
195 * @tc.steps: step8. Call SchemaObject.ParseFromSchemaString to parse the string.
196 * @tc.expected: step8. Expect return -E_SCHEMA_PARSE_FAIL.
197 */
198 SchemaObject so4;
199 EXPECT_EQ(so4.ParseFromSchemaString(schema4), -E_SCHEMA_PARSE_FAIL);
200
201 /**
202 * @tc.steps: step9. Create a shema string include a single index with all columns not exists
203 */
204 std::string schema5;
205 GenerateSchemaString(schema5, "[[\"name1\", \"father.name2\", \"father1.id\", \"id2\", \"tel\"]]");
206
207 /**
208 * @tc.steps: step10. Call SchemaObject.ParseFromSchemaString to parse the string.
209 * @tc.expected: step10. Expect return -E_SCHEMA_PARSE_FAIL.
210 */
211 SchemaObject so5;
212 EXPECT_EQ(so5.ParseFromSchemaString(schema5), -E_SCHEMA_PARSE_FAIL);
213 }
214
215 /**
216 * @tc.name: UnionIndexCreatTest001
217 * @tc.desc: Test the Json uoin index create function
218 * @tc.type: FUNC
219 * @tc.require: AR000F3OPD
220 * @tc.author: xushaohua
221 */
222 HWTEST_F(DistributedDBStorageIndexOptimizeTest, UnionIndexCreatTest001, TestSize.Level1)
223 {
224 /**
225 * @tc.steps: step1. Create a correct shema string include a correct union index.
226 */
227 std::string schema;
228 GenerateSchemaString(schema, "[[\"name\", \"father.name\"]]");
229
230 /**
231 * @tc.steps: step2. Create a kvStore with the schema string.
232 */
233 KvStoreNbDelegate::Option option;
234 option.schema = schema;
235 g_mgr.GetKvStore(STORE_ID_1, option, g_kvNbDelegateCallback);
236 ASSERT_TRUE(g_kvNbDelegatePtr != nullptr);
237 g_mgr.CloseKvStore(g_kvNbDelegatePtr);
238 g_kvNbDelegatePtr = nullptr;
239 EXPECT_TRUE(CheckIndexFromDbFile(GetKvStoreDirectory(USER_ID, APP_ID, STORE_ID_1), "$.name"));
240 }
241
242 /**
243 * @tc.name: SuggestIndexTest001
244 * @tc.desc: Test the Suggest index verify function
245 * @tc.type: FUNC
246 * @tc.require: AR000F3OPE
247 * @tc.author: xushaohua
248 */
249 HWTEST_F(DistributedDBStorageIndexOptimizeTest, SuggestIndexTest001, TestSize.Level1)
250 {
251 std::string schema;
252 GenerateSchemaString(schema, "[\"name\", \"id\"]");
253 SchemaObject schemaObject1;
254 ASSERT_EQ(schemaObject1.ParseFromSchemaString(schema), E_OK);
255
256 /**
257 * @tc.steps: step1. Create a Query and call GreaterThan().SuggestIndex(), then check the query1
258 * @tc.expected: step1. query1 is valid.
259 */
260 Query query1 = Query::Select().GreaterThan("id", 1).SuggestIndex("id");
261 QueryObject queryObject1(query1);
262 queryObject1.SetSchema(schemaObject1);
263 queryObject1.Init();
264 EXPECT_TRUE(queryObject1.IsValid());
265
266 /**
267 * @tc.steps: step2. Create a Query and call SuggestIndex().SuggestIndex(), then check the query2
268 * @tc.expected: step2. query1 is invalid.
269 */
270 SchemaObject schemaObject2;
271 ASSERT_EQ(schemaObject2.ParseFromSchemaString(schema), E_OK);
272 Query query2 = Query::Select().SuggestIndex("id").SuggestIndex("id");
273 QueryObject queryObject2(query2);
274 queryObject2.SetSchema(schemaObject2);
275 queryObject2.Init();
276 EXPECT_FALSE(queryObject2.IsValid());
277
278 /**
279 * @tc.steps: step3. Create a Query and call SuggestIndex().GreaterThan(), then check the query3
280 * @tc.expected: step4. query3 is invalid.
281 */
282 SchemaObject schemaObject3;
283 ASSERT_EQ(schemaObject3.ParseFromSchemaString(schema), E_OK);
284 Query query3 = Query::Select().SuggestIndex("id").GreaterThan("id", 1);
285 QueryObject queryObject3(query3);
286 queryObject3.SetSchema(schemaObject3);
287 queryObject3.Init();
288 EXPECT_FALSE(queryObject3.IsValid());
289 }
290
291 /**
292 * @tc.name: SuggestIndexTest002
293 * @tc.desc: Test the Query parse sql the SuggestIndex
294 * @tc.type: FUNC
295 * @tc.require: AR000F3OPE
296 * @tc.author: xushaohua
297 */
298 HWTEST_F(DistributedDBStorageIndexOptimizeTest, SuggestIndexTest002, TestSize.Level1)
299 {
300 /**
301 * @tc.steps: step1. Create a schema include index name, id, phone.
302 */
303 std::string schema;
304 GenerateSchemaString(schema, "[\"name\", \"id\", \"phone\"]");
305
306 /**
307 * @tc.steps: step2. Create Query,call GreaterThan("id").GreaterThan("phone").SuggestIndex("id")
308 */
309 SchemaObject schemaObject1;
310 ASSERT_EQ(schemaObject1.ParseFromSchemaString(schema), E_OK);
311 Query query1 = Query::Select().GreaterThan("id", 1).And().GreaterThan("phone", 1).SuggestIndex("id");
312
313 /**
314 * @tc.steps: step3. Create QueryObject with query1 and call GetQuerySql to check the sql
315 * @tc.expected: step3. the sql contains "INDEXED BY $.id".
316 */
317 QueryObject queryObject1(query1);
318 queryObject1.SetSchema(schemaObject1);
319 int errCode = E_OK;
320 SqliteQueryHelper helper = queryObject1.GetQueryHelper(errCode);
321 ASSERT_EQ(errCode, E_OK);
322 std::string sql1;
323 ASSERT_EQ(helper.GetQuerySql(sql1, false), E_OK);
324 size_t pos = sql1.find("INDEXED BY '$.id'", 0);
325 ASSERT_TRUE(pos != std::string::npos);
326
327 SqliteQueryHelper helper1 = queryObject1.GetQueryHelper(errCode);
328 ASSERT_EQ(errCode, E_OK);
329 /**
330 * @tc.steps: step4. Create a kvStore with the schema string.
331 */
332 KvStoreNbDelegate::Option option;
333 option.schema = schema;
334 g_mgr.GetKvStore(STORE_ID_1, option, g_kvNbDelegateCallback);
335 ASSERT_TRUE(g_kvNbDelegatePtr != nullptr);
336
337 /**
338 * @tc.steps: step5. put a valid value
339 */
340 Value value(JSON_VALUE.begin(), JSON_VALUE.end());
341 ASSERT_EQ(g_kvNbDelegatePtr->Put(KEY_1, value), OK);
342 std::vector<Entry> entries;
343
344 /**
345 * @tc.steps: step6. GetEntries with the query1
346 * @tc.expected: step6. GetEntries return OK, and the out value is the given value.
347 */
348 ASSERT_EQ(errCode, E_OK);
349 ASSERT_EQ(g_kvNbDelegatePtr->GetEntries(query1, entries), OK);
350 EXPECT_TRUE(value == entries[0].value);
351 ASSERT_EQ(errCode, E_OK);
352 EXPECT_EQ(g_mgr.CloseKvStore(g_kvNbDelegatePtr), OK);
353 g_kvNbDelegatePtr = nullptr;
354 SqliteQueryHelper helper2 = queryObject1.GetQueryHelper(errCode);
355 ASSERT_EQ(errCode, E_OK);
356 /**
357 * @tc.steps: step7. Create Query,call GreaterThan("id").SuggestIndex("car")
358 */
359 SchemaObject schemaObject3;
360 ASSERT_EQ(schemaObject3.ParseFromSchemaString(schema), E_OK);
361 Query query3 = Query::Select().GreaterThan("id", 1).SuggestIndex("car");
362
363 /**
364 * @tc.steps: step8. Create QueryObject with query3 and call GetQuerySql to check the sql
365 * @tc.expected: step8. the sql not contains "INDEXED BY $.car".
366 */
367 QueryObject queryObject3(query3);
368 queryObject3.SetSchema(schemaObject3);
369 SqliteQueryHelper helper3 = queryObject1.GetQueryHelper(errCode);
370 ASSERT_EQ(errCode, E_OK);
371 std::string sql3;
372 ASSERT_EQ(helper3.GetQuerySql(sql3, false), E_OK);
373 pos = sql3.find("INDEXED BY '$.car'", 0);
374 ASSERT_TRUE(pos == std::string::npos);
375 }
376 #endif