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 #include "distributeddb_schema_test_tools.h"
16 #include <fstream>
17 #include "distributeddb_nb_test_tools.h"
18 
19 using namespace std;
20 using namespace DistributedDB;
21 using namespace DistributedDBDataGenerator;
22 
GenerateFixedLenJsonSchemaRecord(const unsigned long serialNo,const EntrySize & entrySize,const uint8_t keyFilledChr,const uint8_t valueFilledChr)23 Entry DistributedDBSchemaTestTools::GenerateFixedLenJsonSchemaRecord(const unsigned long serialNo,
24     const EntrySize &entrySize, const uint8_t keyFilledChr, const uint8_t valueFilledChr)
25 {
26     Entry entry;
27     std::string serialNoStr = std::to_string(serialNo);
28     entry.key.assign(entrySize.keySize - serialNoStr.length(), keyFilledChr);
29     for (unsigned long index = 0; index < serialNoStr.size(); ++index) {
30         entry.key.push_back(serialNoStr[index]);
31     }
32 
33     string val = string("{") + "\"field" + std::to_string(FIRST_FIELD) + "\":" + std::to_string(serialNo) + ",";
34     for (unsigned int fieldIndex = SECOND_FIELD; fieldIndex <= THIRTIETH_FIELD; fieldIndex++) {
35         if (fieldIndex == SIXTH_FIELD) {
36             val += "\"field" + std::to_string(fieldIndex) + "\":" + to_string(static_cast<double>(serialNo)) + ",";
37         } else {
38             val += "\"field" + std::to_string(fieldIndex) + "\":" +
39                 "\"SchemaPerfTest" + std::to_string(fieldIndex) + "_" + std::to_string(serialNo) + "\",";
40         }
41     }
42     val.back() = '}';
43     size_t found = val.rfind("PerfTest");
44     if (found != string::npos) {
45         string insert = "PerfTest";
46         insert += string(entrySize.valSize - val.length() - 1, valueFilledChr); // consider skipsize 1 byte 'a' below
47         val.replace(found, strlen("PerfTest"), insert);
48     }
49     string scmVal = "a" + val;
50     Value schemaVal(scmVal.begin(), scmVal.end());
51     entry.value = schemaVal;
52     return entry;
53 }
54 
GenerateFixedJsonSchemaRecords(const int recordNum,const EntrySize & entrySize,const uint8_t keyFilledChr,const uint8_t valueFilledChr,vector<Key> & allKeys)55 vector<Entry> DistributedDBSchemaTestTools::GenerateFixedJsonSchemaRecords(const int recordNum,
56     const EntrySize &entrySize, const uint8_t keyFilledChr, const uint8_t valueFilledChr, vector<Key> &allKeys)
57 {
58     vector<Entry> entries;
59     for (int serialNo = TEST_START_CNT; serialNo <= recordNum; ++serialNo) {
60         Entry entry = GenerateFixedLenJsonSchemaRecord(serialNo, entrySize, keyFilledChr, valueFilledChr);
61         allKeys.push_back(entry.key);
62         entries.push_back(entry);
63     }
64     return entries;
65 }
66 
GenerateFixedLenSchemaPerfRecord(const uint64_t presetRecordsCnt,const uint64_t serialNo,const RecordInfo & recordInfo,const string & valueSkipString)67 Entry DistributedDBSchemaTestTools::GenerateFixedLenSchemaPerfRecord(
68     const uint64_t presetRecordsCnt, const uint64_t serialNo, const RecordInfo &recordInfo,
69     const string &valueSkipString)
70 {
71     Entry entry;
72     std::string serialNoStr = std::to_string(serialNo);
73     entry.key.assign(recordInfo.keyLength - serialNoStr.length(), recordInfo.keyFilledChr);
74     for (unsigned long index = 0; index < serialNoStr.size(); ++index) {
75         entry.key.push_back(serialNoStr[index]);
76     }
77 
78     string val = string("{") + "\"field" + std::to_string(FIRST_FIELD) + "\":" + std::to_string(serialNo) + ",";
79     unsigned int fieldIndex;
80     for (fieldIndex = SECOND_FIELD; fieldIndex <= THIRD_FIELD; fieldIndex++) {
81         val += "\"field" + std::to_string(fieldIndex) + "\":" +
82             "\"SchemaPerfTest" + std::to_string(fieldIndex) + "_" + serialNoStr + "\",";
83     }
84     if (serialNo <= static_cast<uint64_t>(presetRecordsCnt) / 2) { // 2 is an half of records.
85         for (fieldIndex = FOURTH_FIELD; fieldIndex <= FIFTH_FIELD; fieldIndex++) {
86             val += "\"field" + std::to_string(fieldIndex) + "\":" +
87                 "\"SchemaPerfTest" + std::to_string(fieldIndex) + "_" + serialNoStr + "\",";
88         }
89     }
90     for (; fieldIndex <= THIRTIETH_FIELD; fieldIndex++) {
91         if (fieldIndex == SIXTH_FIELD) {
92             val += "\"field" + std::to_string(fieldIndex) + "\":" + to_string(static_cast<double>(serialNo)) + ",";
93         } else {
94             val += "\"field" + std::to_string(fieldIndex) + "\":" +
95                 "\"SchemaPerfTest" + std::to_string(fieldIndex) + "\",";
96         }
97     }
98     val.back() = '}';
99     size_t found = val.rfind("PerfTest");
100     if (found != string::npos) {
101         string insert = "PerfTest";
102         if (recordInfo.valueLength > val.length()) {
103             insert += string(recordInfo.valueLength - val.length(), recordInfo.valueFilledChr);
104             val.replace(found, strlen("PerfTest"), insert);
105         } else {
106             MST_LOG("[GenerateFixedLenSchemaPerfRecord] recordInfo.valueLength(%u) is too short, " \
107                 "it should be more than %zu.", recordInfo.valueLength, val.length());
108             return entry;
109         }
110     }
111     string scmVal = valueSkipString + val;
112     Value schemaVal(scmVal.begin(), scmVal.end());
113     entry.value = schemaVal;
114     return entry;
115 }
116 
SchemaIndexQuery(const DBParameters & parameters,const string & dbPath,const string & schemaIndex,const Option & option)117 bool DistributedDBSchemaTestTools::SchemaIndexQuery(const DBParameters &parameters,
118     const string &dbPath, const string &schemaIndex, const Option &option)
119 {
120     int count = 0;
121     string identifier = parameters.userId + "-" + parameters.appId + "-" + parameters.storeId;
122     string hashIdentifierRes = TransferStringToHashHexString(identifier);
123     const string dbName = dbPath + hashIdentifierRes + DATABASE_INFOR_FILE;
124     EncrypteAttribute attribute = {option.isEncryptedDb, option.passwd};
125 
126     const string SCHEMA_INDEX_QUERY_SQL = "select count(*) from sqlite_master where name= \'" +
127         schemaIndex + "\' and type = \'index\'";
128 
129     DistributedTestTools::QuerySpecifiedData(dbName, SCHEMA_INDEX_QUERY_SQL, attribute, count);
130     return (count == 1);
131 }
132 
GenerateCombinationSchemaValue(const vector<vector<string>> & fieldValue)133 vector<string> DistributedDBSchemaTestTools::GenerateCombinationSchemaValue(const vector<vector<string>> &fieldValue)
134 {
135     vector<string> schemasValue;
136     int valueNum = fieldValue.size();
137     for (int index = 0; index < valueNum; index++) {
138         string valueStr;
139         string field1Val = fieldValue[index][INDEX_ZEROTH];
140         if (field1Val != "null") {
141             valueStr = valueStr + "{\"field1\":\"" + field1Val + "\"";
142         } else {
143             valueStr = valueStr + "{\"field1\":" + field1Val;
144         }
145 
146         valueStr = valueStr + ",\"field2\":{\"field3\":" + fieldValue[index][INDEX_FIRST] + ",\"field4\":{\"field5\":" +
147         fieldValue[index][INDEX_SECOND] + ",\"field6\":{\"field7\":" + fieldValue[index][INDEX_THIRD] + ",\"field8\":" +
148         fieldValue[index][INDEX_FORTH] + "}}}}";
149         schemasValue.push_back(valueStr);
150     }
151     return schemasValue;
152 }
GenerateCombineSchemaEntries(std::vector<DistributedDB::Entry> & entries,const std::vector<vector<std::string>> & fieldValues,const std::vector<uint8_t> & keyPrefix,int startPoint)153 void DistributedDBSchemaTestTools::GenerateCombineSchemaEntries(std::vector<DistributedDB::Entry> &entries,
154     const std::vector<vector<std::string>> &fieldValues, const std::vector<uint8_t> &keyPrefix, int startPoint)
155 {
156     entries.clear();
157     vector<Key> keys;
158     int recordNo = fieldValues.size();
159     for (int cnt = startPoint; cnt < (startPoint + recordNo); cnt++) {
160         Key key = keyPrefix;
161         std::string keyNo = std::to_string(cnt);
162         key.insert(key.end(), keyNo.begin(), keyNo.end());
163         keys.push_back(key);
164     }
165     vector<string> valueStr = GenerateCombinationSchemaValue(fieldValues);
166     for (int index = 0; index < recordNo; index++) {
167         Entry entry;
168         Value value(valueStr[index].begin(), valueStr[index].end());
169         entry.key = keys[index];
170         entry.value = value;
171         entries.push_back(entry);
172     }
173 }
174 
CombinationCheckQueryResult(KvStoreNbDelegate & delegate,const Query & query,vector<Entry> & expectEntry,const DBStatus status,bool canGetCount)175 bool DistributedDBSchemaTestTools::CombinationCheckQueryResult(KvStoreNbDelegate &delegate, const Query &query,
176     vector<Entry> &expectEntry, const DBStatus status, bool canGetCount)
177 {
178     vector<Entry> entries;
179     bool result = false;
180     result = (delegate.GetEntries(query, entries) == status);
181     if (!expectEntry.empty()) {
182         if (entries.size() != expectEntry.size()) {
183             MST_LOG("The entries from query is %zd, The expectEntry is %zd, they are not equal",
184                 entries.size(), expectEntry.size());
185             return false;
186         }
187         for (vector<Entry>::size_type index = 0; index < entries.size(); index++) {
188             if (entries[index].key != expectEntry[index].key || entries[index].value != expectEntry[index].value) {
189                 string keyGot(entries[index].key.begin(), entries[index].key.end());
190                 string keyExpect(expectEntry[index].key.begin(), expectEntry[index].key.end());
191                 MST_LOG("entry key compare failed, expectKey:%s, gotKey:%s, line:%d", keyExpect.c_str(), keyGot.c_str(),
192                     __LINE__);
193                 return false;
194             }
195         }
196     }
197     KvStoreResultSet *resultSet = nullptr;
198     if (status != NOT_FOUND) {
199         result = (delegate.GetEntries(query, resultSet) == status) && result;
200     } else {
201         result = (delegate.GetEntries(query, resultSet) == OK) && result;
202         if (resultSet != nullptr) {
203             Entry entry;
204             result = (resultSet->GetEntry(entry) == status) && result;
205         }
206     }
207     int expectCnt = expectEntry.size();
208     if (resultSet != nullptr) {
209         result = (resultSet->GetCount() == expectCnt) && result;
210         result = (delegate.CloseResultSet(resultSet) == DBStatus::OK) && result;
211     }
212     if (canGetCount) {
213         int cnt = 0;
214         result = (delegate.GetCount(query, cnt) == status) && result;
215         result = (cnt == expectCnt) && result;
216     }
217     return result;
218 }
219 
GenSchemaValue(Value notSchemaValue)220 Value DistributedDBSchemaTestTools::GenSchemaValue(Value notSchemaValue)
221 {
222     // emit the first 'v' character
223     string notSchemaVal(notSchemaValue.begin() + INDEX_FIRST, notSchemaValue.end());
224     string valueRes = "{" + VALUE_MATCH_1 + "," + VALUE_MATCH_2 + ",\"field17\":" + notSchemaVal + "}";
225     Value valueSchema(valueRes.begin(), valueRes.end());
226     return valueSchema;
227 }
228 
PreInsertRecords(KvStoreNbDelegate * & delegate,vector<Key> & keys,const std::vector<std::string> & values,int beginNumber)229 bool DistributedDBSchemaTestTools::PreInsertRecords(KvStoreNbDelegate *&delegate, vector<Key> &keys,
230     const std::vector<std::string> &values, int beginNumber)
231 {
232     bool result = true;
233     for (vector<string>::size_type index = 0; index < values.size(); index++) {
234         keys.push_back({'k', static_cast<uint8_t>(index + 49 + beginNumber)}); // 49 is the ASCII number of 'a'
235     }
236     for (vector<string>::size_type index = 0; index < values.size(); index++) {
237         Value value(values[index].begin(), values[index].end());
238         result = (DistributedDBNbTestTools::Put(*delegate, keys[index], value) == OK);
239     }
240     return result;
241 }
242 
243 // insert query condition between brackets.
SpliceQueryMethod(int flag,Query & query)244 static void SpliceQueryMethod(int flag, Query &query)
245 {
246     switch (flag) {
247         case 0: // query condition 0
248             query.LessThan("$.field2.field4.field5", "100").And().NotEqualTo("$.field2.field4.field6.field7", "-100");
249             break;
250         case 1: // query condition 1
251             query.EqualTo("$.field2.field3", "true").Or().NotLike("$.field1", "%c");
252             break;
253         case 2: // query condition 2
254             query.Like("$.field1", "ab%");
255             break;
256         case 3: // query condition 3
257             query.GreaterThanOrEqualTo("$.field2.field4.field6.field8", "0");
258             break;
259         default: // other query condition
260             break;
261     }
262 }
263 // Generate rand query by the number of brackets.
GenerateRandQuery(Query & query,int beginNum,int endNum)264 void DistributedDBSchemaTestTools::GenerateRandQuery(Query &query, int beginNum, int endNum)
265 {
266     int left = 0;
267     int right = 0;
268     for (int cnt = 0; cnt < beginNum + endNum; cnt++) {
269         int bracketTest = GetRandInt(0, 1);
270         if (bracketTest == 0 && left < beginNum) {
271             left++;
272             if (cnt != 0) {
273                 query.Or();
274             }
275             query.BeginGroup();
276         } else if (bracketTest == 1 && right < endNum) {
277             right++;
278             query.EndGroup();
279         } else {
280             cnt--;
281             continue;
282         }
283         int flag = GetRandInt(0, 4); // add query condition 0-4 to brackets.
284         if ((bracketTest == 0 && cnt < beginNum + endNum - 1) || (bracketTest == 1 && cnt == 0)) {
285             SpliceQueryMethod(flag, query);
286         } else if (bracketTest == 1 && cnt < beginNum + endNum - 1) {
287             query.And();
288             SpliceQueryMethod(flag, query);
289         }
290     }
291 }
292 
TransformToSchemaEntry(std::vector<DistributedDB::Entry> & entries,const std::vector<DistributedDB::Key> & keys,const std::vector<std::string> & schemasValue)293 bool DistributedDBSchemaTestTools::TransformToSchemaEntry(std::vector<DistributedDB::Entry> &entries,
294     const std::vector<DistributedDB::Key> &keys, const std::vector<std::string> &schemasValue)
295 {
296     if (keys.size() != schemasValue.size()) {
297         MST_LOG("The number of keys is not equal to the value strings' number!");
298         return false;
299     }
300     for (vector<string>::size_type index = 0; index < schemasValue.size(); index++) {
301         Value value(schemasValue[index].begin(), schemasValue[index].end());
302         entries.push_back({keys[index], value});
303     }
304     return true;
305 }
306 
GenerateSpecificSchemaEntries(const int startPoint,const int recordNumber,std::vector<DistributedDB::Entry> & entries,const std::vector<std::vector<std::string>> & values)307 void DistributedDBSchemaTestTools::GenerateSpecificSchemaEntries(const int startPoint, const int recordNumber,
308     std::vector<DistributedDB::Entry> &entries, const std::vector<std::vector<std::string>> &values)
309 {
310     entries.clear();
311     int switchFactor;
312     vector<Key> keys;
313     vector<string> schemasValue;
314     std::string indexNumStr, field1, field3, field5, field7, field8, value, switchFactorStr;
315     for (int indexNum = startPoint; indexNum < startPoint + recordNumber; indexNum++) {
316         indexNumStr = std::to_string(indexNum);
317         field1 = "\"field1\":\"SubscribeRemoteQueryTest" + indexNumStr + "\"";
318         field3 = "\"field3\":false";
319         field5 = "\"field5\":" + indexNumStr;
320         field7 = "\"field7\":" + indexNumStr;
321         field8 = "\"field8\":" + indexNumStr + ".1234";
322         for (auto &iter : values) {
323             switchFactorStr = iter[INDEX_ZEROTH];
324             switchFactor = switchFactorStr.back() - '0';
325             switch (switchFactor) {
326                 case 1: // field 1 has assigned value
327                     field1 = "\"field1\":\"" + iter[INDEX_FIRST] + "\"";
328                     break;
329                 case 3: // field 3 has assigned value
330                     field3 = "\"field3\":" + iter[INDEX_FIRST];
331                     break;
332                 case 5: // field 5 has assigned value
333                     field5 = "\"field5\":" + iter[INDEX_FIRST];
334                     break;
335                 case 7: // field 7 has assigned value
336                     field7 = "\"field7\":" + iter[INDEX_FIRST];
337                     break;
338                 case 8: // field 8 has assigned value
339                     field8 = "\"field8\":" + iter[INDEX_FIRST];
340                     break;
341                 default:
342                     MST_LOG("invalid field !");
343                     break;
344             }
345         }
346         value = "{" + field1 + ",\"field2\":{" + field3 + ",\"field4\":{" + field5 + ",\"field6\":{"
347             + field7 + "," + field8 + "}}}}";
348         indexNumStr = "k" + indexNumStr;
349         Key key(indexNumStr.begin(), indexNumStr.end());
350         keys.push_back(key);
351         schemasValue.push_back(value);
352     }
353     DistributedDBSchemaTestTools::TransformToSchemaEntry(entries, keys, schemasValue);
354 }
355