• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "schema_utils.h"
17 
18 #include <cctype>
19 #include <cfloat>
20 #include <cmath>
21 #include <map>
22 
23 #include "db_common.h"
24 #include "db_errno.h"
25 #include "log_print.h"
26 #include "schema_constant.h"
27 
28 namespace DistributedDB {
29 namespace {
IsLegalFieldCharacter(char character)30     bool IsLegalFieldCharacter(char character)
31     {
32         return (std::isalnum(character) || character == '_');
33     }
34 
35     // TYPE, [NOT NULL,] [DEFAULT X]
36     // DEFAULT at last
37     // State transition matrix
38     const int STATE_TRANSFER[8][6] = { // 5 type input and 7 type state
39         // blank, NOT NULL, DEFAULT, OTHER AlNUM, COMMA
40         {0, -1, -1, 1, -1},     // state 0: empty
41         {1, -1, -1, 1, 2},      // state 1: only type
42         {2, 3, 5, -1, -1},      // state 2: alnum ,
43         {3, -1, -1, -1, 4},     // state 3: alnum , notnull
44         {4, -1, 5, -1, -1},     // state 4: alnum , notnull ,
45         {6, -1, -1, -1, -1},    // state 5: finish with DEFAULT
46         {6, -1, -1, 7, -1},     // state 6: finish with DEFAULT and blank
47         {7, 7, 7, 7, 7},        // state 7: finish with DEFAULT and blank and no matter what value
48     };
49     enum StateTransferColNum {
50         COLUMN_ILLEGAL = -1,
51         COLUMN_BLANK,
52         COLUMN_NOT_NULL,
53         COLUMN_DEFAULT,
54         COLUMN_OTHER_ALNUM,
55         COLUMN_COMMA,
56     };
57 } // namespace
58 
59 // compare function can make sure not to cross the border, pos < oriContent.size() - 1
60 // Get symbol type and Converts to the corresponding column of the state transition matrix
MakeTrans(const std::string & oriContent,size_t & pos)61 int SchemaUtils::MakeTrans(const std::string &oriContent, size_t &pos)
62 {
63     if (isspace(oriContent[pos])) {
64         return COLUMN_BLANK;
65     } else if (oriContent.compare(pos, strlen(SchemaConstant::KEYWORD_ATTR_NOT_NULL),
66         SchemaConstant::KEYWORD_ATTR_NOT_NULL) == 0) {
67         pos = pos + strlen(SchemaConstant::KEYWORD_ATTR_NOT_NULL) - 1;
68         return COLUMN_NOT_NULL;
69     } else if (oriContent.compare(pos, strlen(SchemaConstant::KEYWORD_ATTR_DEFAULT),
70         SchemaConstant::KEYWORD_ATTR_DEFAULT) == 0) {
71         pos = pos + strlen(SchemaConstant::KEYWORD_ATTR_DEFAULT) - 1;
72         return COLUMN_DEFAULT;
73     } else if (std::isalnum(oriContent[pos]) || oriContent[pos] == '\'' ||
74         oriContent[pos] == '+' || oriContent[pos] == '-') {
75         return COLUMN_OTHER_ALNUM;
76     } else if (oriContent[pos] == ',') {
77         return COLUMN_COMMA;
78     } else {
79         return COLUMN_ILLEGAL;
80     }
81 }
82 
83 // Use DFA to check and Parsing
84 // You can get the corresponding state meaning in the state transition matrix STATE_TRANSFER
SplitSchemaAttribute(const std::string & inAttrString,std::vector<std::string> & outAttrString)85 int SchemaUtils::SplitSchemaAttribute(const std::string &inAttrString, std::vector<std::string> &outAttrString)
86 {
87     int state = 0;
88     outAttrString.resize(3); // attribute have 3 type keywords
89     for (size_t i = 0; i < inAttrString.size(); i++) {
90         int id = MakeTrans(inAttrString, i);
91         if (id < 0) {
92             LOGD("Split Schema Attribute err, Contains unrecognized content [%c]", inAttrString[i]);
93             return -E_SCHEMA_PARSE_FAIL;
94         }
95         state = STATE_TRANSFER[state][id];
96         if (state < 0) {
97             LOGD("Split Schema Attribute err, err state [%d]", state);
98             return -E_SCHEMA_PARSE_FAIL;
99         }
100         switch (state) {
101             case 1: // state 1 :Indicates that only type information is currently available
102                 outAttrString[0].push_back(inAttrString[i]);
103                 break;
104             case 3: // state 3 :Gets the NOT_NULL keyword
105                 outAttrString[1] = SchemaConstant::KEYWORD_ATTR_NOT_NULL;
106                 break;
107             case 7: // state 7 :Contains complete information
108                 // Get default string. Now transfer matrix can ensure > 1, but you should pay attention when fix it
109                 if (i <= 1) {
110                     LOGE("default string size must be over 1.");
111                     return -E_SCHEMA_PARSE_FAIL;
112                 }
113                 outAttrString[2] = inAttrString.substr(i - 1); // 2 is index
114                 return E_OK;
115             default:
116                 break;
117         }
118     }
119     // Only these states are legal, The meaning of the state can be seen in the matrix STATE_TRANSFER explanation
120     if (!(state == 1 || state == 3 || state == 7)) { // 1 is state; 3 is state; 7 is state;
121         LOGD("Split Schema Attribute err, err state [%d]", state);
122         return -E_SCHEMA_PARSE_FAIL;
123     }
124     return E_OK;
125 }
126 
TransToBool(const std::string & defaultContent,SchemaAttribute & outAttr)127 int SchemaUtils::TransToBool(const std::string &defaultContent, SchemaAttribute &outAttr)
128 {
129     // Have been trim
130     if (defaultContent.compare(SchemaConstant::KEYWORD_ATTR_VALUE_TRUE) == 0) {
131         outAttr.defaultValue.boolValue = true;
132         return E_OK;
133     } else if (defaultContent.compare(SchemaConstant::KEYWORD_ATTR_VALUE_FALSE) == 0) {
134         outAttr.defaultValue.boolValue = false;
135         return E_OK;
136     }
137     LOGE("Default value can not transform to bool!!");
138     return -E_SCHEMA_PARSE_FAIL;
139 }
140 
TransToString(const std::string & defaultContent,SchemaAttribute & outAttr)141 int SchemaUtils::TransToString(const std::string &defaultContent, SchemaAttribute &outAttr)
142 {
143     // Have been trim, Strip leading and trailing '
144     if (defaultContent.size() > 1 && defaultContent.front() == '\'' && defaultContent.back() == '\'') {
145         outAttr.defaultValue.stringValue = defaultContent.substr(1, defaultContent.size() - 2); // 2: trim trailing
146         if (outAttr.defaultValue.stringValue.size() > SchemaConstant::SCHEMA_DEFAULT_STRING_SIZE_LIMIT) {
147             return -E_SCHEMA_PARSE_FAIL;
148         }
149         return E_OK;
150     }
151     LOGE("Substandard format! Default value can not transform to string!!");
152     return -E_SCHEMA_PARSE_FAIL;
153 }
154 
TransToInteger(const std::string & defaultContent,SchemaAttribute & outAttr)155 int SchemaUtils::TransToInteger(const std::string &defaultContent, SchemaAttribute &outAttr)
156 {
157     // defaultContent can not be null
158     if (defaultContent.empty()) {
159         return -E_SCHEMA_PARSE_FAIL;
160     }
161     int transRes = strtol(defaultContent.c_str(), nullptr, 10); // 10: decimal
162     std::string resReview = std::to_string(transRes);
163     if (defaultContent.compare(defaultContent.find_first_not_of("+- "), defaultContent.size(),
164         resReview, resReview.find_first_not_of("+- "), resReview.size()) == 0) {
165         // Check the sign of the number
166         if ((defaultContent[0] == '-' && resReview[0] == '-') ||
167             (defaultContent[0] != '-' && resReview[0] != '-') ||
168             transRes == 0) {
169             outAttr.defaultValue.integerValue = transRes;
170             return E_OK;
171         }
172     }
173     LOGE("Default value can not transform to Integer!!");
174     return -E_SCHEMA_PARSE_FAIL;
175 }
176 
TransToLong(const std::string & defaultContent,SchemaAttribute & outAttr)177 int SchemaUtils::TransToLong(const std::string &defaultContent, SchemaAttribute &outAttr)
178 {
179     // defaultContent can not be null
180     if (defaultContent.empty()) {
181         return -E_SCHEMA_PARSE_FAIL;
182     }
183     int64_t transRes = strtoll(defaultContent.c_str(), nullptr, 10); // 10: decimal
184     std::string resReview = std::to_string(transRes);
185     if (defaultContent.compare(defaultContent.find_first_not_of("+- "), defaultContent.size(),
186         resReview, resReview.find_first_not_of("+- "), resReview.size()) == 0) {
187         // Check the sign of the number
188         if ((defaultContent[0] == '-' && resReview[0] == '-') ||
189             (defaultContent[0] != '-' && resReview[0] != '-') ||
190             transRes == 0) {
191             outAttr.defaultValue.longValue = transRes;
192             return E_OK;
193         }
194     }
195 
196     LOGE("Default value[%s] can not transform to LONG!!", resReview.c_str());
197     return -E_SCHEMA_PARSE_FAIL;
198 }
199 
TransToDouble(const std::string & defaultContent,SchemaAttribute & outAttr)200 int SchemaUtils::TransToDouble(const std::string &defaultContent, SchemaAttribute &outAttr)
201 {
202     // defaultContent can not be null
203     if (defaultContent.empty()) {
204         return -E_SCHEMA_PARSE_FAIL;
205     }
206 
207     // Disable scientific notation
208     int dotCount = 0;
209     for (const auto &iter : defaultContent) {
210         if (!(std::isdigit(iter) || iter == '.' || iter == '-' || iter == '+')) {
211             LOGE("Default value to double, exist invalid symbol[%c]", iter);
212             return -E_SCHEMA_PARSE_FAIL;
213         }
214         if (iter == '.') {
215             dotCount++;
216         }
217         if (dotCount > 1) {
218             LOGE("Default value to double, exist invalid extra dot");
219             return -E_SCHEMA_PARSE_FAIL;
220         }
221     }
222 
223     char *end = nullptr;
224     double transRes = std::strtod(defaultContent.c_str(), &end);
225     // Double exist problems with accuracy, overflow is subject to the legality of the c++ conversion.
226     if (transRes > -HUGE_VAL && transRes < HUGE_VAL && std::isfinite(transRes)) {
227         // Cleared blank
228         if (end != &defaultContent.back() + 1) {
229             LOGD("Termination of parsing due to exception symbol");
230             return -E_SCHEMA_PARSE_FAIL;
231         }
232         outAttr.defaultValue.doubleValue = transRes;
233         return E_OK;
234     }
235     LOGE("Default value can not transform to double, overflow double max!");
236     return -E_SCHEMA_PARSE_FAIL;
237 }
238 
TransformDefaultValue(std::string & defaultContent,SchemaAttribute & outAttr)239 int SchemaUtils::TransformDefaultValue(std::string &defaultContent, SchemaAttribute &outAttr)
240 {
241     TrimFiled(defaultContent);
242     if (defaultContent.compare(SchemaConstant::KEYWORD_ATTR_VALUE_NULL) == 0 && outAttr.hasNotNullConstraint) {
243         LOGE("NOT NULL and DEFAULT null Simultaneously");
244         return -E_SCHEMA_PARSE_FAIL;
245     } else if (defaultContent.compare(SchemaConstant::KEYWORD_ATTR_VALUE_NULL) == 0) {
246         outAttr.hasDefaultValue = false;
247         return E_OK;
248     }
249 
250     int errCode = E_OK;
251     switch (outAttr.type) {
252         case FieldType::LEAF_FIELD_BOOL:
253             errCode = TransToBool(defaultContent, outAttr);
254             break;
255         case FieldType::LEAF_FIELD_INTEGER:
256             errCode = TransToInteger(defaultContent, outAttr);
257             break;
258         case FieldType::LEAF_FIELD_LONG:
259             errCode = TransToLong(defaultContent, outAttr);
260             break;
261         case FieldType::LEAF_FIELD_DOUBLE:
262             errCode = TransToDouble(defaultContent, outAttr);
263             break;
264         case FieldType::LEAF_FIELD_STRING:
265             errCode = TransToString(defaultContent, outAttr);
266             break;
267         default:
268             LOGE("Unrecognized or unsupported type, please check!!");
269             errCode = -E_SCHEMA_PARSE_FAIL;
270             break;
271     }
272 
273     LOGD("SchemaAttribute type is [%d], transfer result is [%d]", static_cast<int>(outAttr.type), errCode);
274     return errCode;
275 }
276 
ParseAndCheckSchemaAttribute(const std::string & inAttrString,SchemaAttribute & outAttr,bool useAffinity)277 int SchemaUtils::ParseAndCheckSchemaAttribute(const std::string &inAttrString, SchemaAttribute &outAttr,
278     bool useAffinity)
279 {
280     if (inAttrString.empty()) {
281         return -E_SCHEMA_PARSE_FAIL;
282     }
283     std::string tempinAttrString = inAttrString;
284     TrimFiled(tempinAttrString);
285 
286     std::vector<std::string> attrContext;
287     int errCode = SplitSchemaAttribute(inAttrString, attrContext);
288     if (errCode != E_OK) {
289         LOGD("Syntax error, please check!");
290         return errCode;
291     }
292     errCode = ParseSchemaAttribute(attrContext, outAttr, useAffinity);
293     if (errCode != E_OK) {
294         LOGD("Grammatical error, please check!");
295         return errCode;
296     }
297 
298     return E_OK;
299 }
300 
ParseSchemaAttribute(std::vector<std::string> & attrContext,SchemaAttribute & outAttr,bool useAffinity)301 int SchemaUtils::ParseSchemaAttribute(std::vector<std::string> &attrContext, SchemaAttribute &outAttr, bool useAffinity)
302 {
303     // Currently supported types
304     static const std::map<std::string, FieldType> FIELD_TYPE_DIC = {
305         {SchemaConstant::KEYWORD_TYPE_BOOL, FieldType::LEAF_FIELD_BOOL},
306         {SchemaConstant::KEYWORD_TYPE_INTEGER, FieldType::LEAF_FIELD_INTEGER},
307         {SchemaConstant::KEYWORD_TYPE_LONG, FieldType::LEAF_FIELD_LONG},
308         {SchemaConstant::KEYWORD_TYPE_DOUBLE, FieldType::LEAF_FIELD_DOUBLE},
309         {SchemaConstant::KEYWORD_TYPE_STRING, FieldType::LEAF_FIELD_STRING},
310     };
311 
312     // After split attribute? attrContext include 3 type field
313     if (attrContext.size() < 3) {
314         LOGE("No parsing preprocessing!!");
315         return -E_SCHEMA_PARSE_FAIL;
316     }
317     TrimFiled(attrContext[0]);
318     if (!useAffinity) {
319         if (FIELD_TYPE_DIC.find(attrContext[0]) == FIELD_TYPE_DIC.end()) {
320             LOGE("Errno schema field type [%s]!!", attrContext[0].c_str());
321             return -E_SCHEMA_PARSE_FAIL;
322         } else {
323             outAttr.type = FIELD_TYPE_DIC.at(attrContext[0]);
324         }
325     } else {
326         outAttr.type = FieldType::LEAF_FIELD_NULL;
327         outAttr.customFieldType = attrContext[0];
328     }
329 
330     outAttr.hasNotNullConstraint = !attrContext[1].empty();
331 
332     // if DEFAULT value context exist, fix hasDefaultValue flag, 2nd represents the default value
333     if (attrContext[2].empty()) {
334         outAttr.hasDefaultValue = false;
335     } else {
336         outAttr.hasDefaultValue = true;
337         int errCode = TransformDefaultValue(attrContext[2], outAttr); // 2nd element is DEFAULT value
338         if (errCode != E_OK) {
339             LOGE("Default value is malformed!!");
340             return -E_SCHEMA_PARSE_FAIL;
341         }
342     }
343     return E_OK;
344 }
345 
346 namespace {
347 // Check prefix and attempt to find any illegal, returns E_OK if nothing illegal and an hasPrefix indicator.
CheckDollarDotPrefix(const std::string & inPathStr,bool & hasPrefix)348 int CheckDollarDotPrefix(const std::string &inPathStr, bool &hasPrefix)
349 {
350     if (inPathStr.empty()) {
351         return -E_SCHEMA_PARSE_FAIL;
352     }
353     if (inPathStr.size() >= std::string("$.").size()) {
354         // In this case, $. prefix may exist, but also may not exist.
355         if (inPathStr[0] == '$' && inPathStr[1] == '.') { // 1 for second char
356             // $. prefix may exist
357             hasPrefix = true;
358             return E_OK;
359         }
360         if (inPathStr[0] == '$' && inPathStr[1] != '.') { // 1 for second char
361             return -E_SCHEMA_PARSE_FAIL;
362         }
363         if (inPathStr[1] == '$') { // 1 for second char
364             return -E_SCHEMA_PARSE_FAIL;
365         }
366     }
367     // here, inPathStr not empty, has at least one char, should not begin with '.'
368     if (inPathStr[0] == '.') {
369         return -E_SCHEMA_PARSE_FAIL;
370     }
371     hasPrefix = false;
372     return E_OK;
373 }
374 }
375 
ParseAndCheckFieldPath(const std::string & inPathString,FieldPath & outPath,bool permitPrefix)376 int SchemaUtils::ParseAndCheckFieldPath(const std::string &inPathString, FieldPath &outPath, bool permitPrefix)
377 {
378     std::string tempInPathString = inPathString;
379     TrimFiled(tempInPathString);
380     bool hasPrefix = false;
381     int errCode = CheckDollarDotPrefix(tempInPathString, hasPrefix);
382     if (errCode != E_OK) {
383         LOGE("CheckDollarDotPrefix Fail.");
384         return errCode;
385     }
386 
387     if (!permitPrefix && hasPrefix) {
388         LOGE("Not permit $. prefix.");
389         return -E_SCHEMA_PARSE_FAIL;
390     }
391 
392     if (!hasPrefix) {
393         tempInPathString = std::string("$.") + tempInPathString;
394     }
395 
396     for (size_t curPos = 1; curPos < tempInPathString.size();) {
397         if (curPos + 1 == tempInPathString.size()) {
398             LOGE("Dot at end will generate empty illegal path!");
399             return -E_SCHEMA_PARSE_FAIL;
400         }
401         size_t nextPointPos = tempInPathString.find_first_of(".", curPos + 1);
402         outPath.push_back(tempInPathString.substr(curPos + 1, nextPointPos - curPos - 1));
403         curPos = nextPointPos;
404     }
405 
406     if (outPath.size() > SchemaConstant::SCHEMA_FEILD_PATH_DEPTH_MAX) {
407         LOGE("Parse Schema Index  depth illegality!");
408         return -E_SCHEMA_PARSE_FAIL;
409     }
410 
411     for (const auto &iter : outPath) {
412         if (CheckFieldName(iter) != E_OK) {
413             LOGE("Parse Schema Index field illegality!");
414             return -E_SCHEMA_PARSE_FAIL;
415         }
416     }
417     return E_OK;
418 }
419 
CheckFieldName(const FieldName & inName)420 int SchemaUtils::CheckFieldName(const FieldName &inName)
421 {
422     if (inName.empty() || inName.size() > SchemaConstant::SCHEMA_FEILD_NAME_LENGTH_MAX) {
423         LOGE("Schema FieldName have invalid size!");
424         return -E_SCHEMA_PARSE_FAIL;
425     }
426 
427     // The first letter must be a number or an underscore
428     if (!(std::isalpha(inName[0]) || inName[0] == '_')) {
429         LOGE("Schema FieldName begin with un support symbol!");
430         return -E_SCHEMA_PARSE_FAIL;
431     }
432 
433     // Must consist of numeric underscore letters
434     for (const auto &iter : inName) {
435         if (!(IsLegalFieldCharacter(iter))) {
436             LOGE("Schema FieldName exist un support symbol!");
437             return -E_SCHEMA_PARSE_FAIL;
438         }
439     }
440 
441     return E_OK;
442 }
443 
StripNameSpace(const std::string & inFullName)444 std::string SchemaUtils::StripNameSpace(const std::string &inFullName)
445 {
446     auto pos = inFullName.find_last_of('.');
447     if (pos == std::string::npos) { // No '.', so no namespace
448         return inFullName;
449     }
450     return inFullName.substr(pos + 1);
451 }
452 
SchemaTypeString(SchemaType inType)453 std::string SchemaUtils::SchemaTypeString(SchemaType inType)
454 {
455     static std::map<SchemaType, std::string> schemaTypeMapString {
456         {SchemaType::NONE, "NONE"},
457         {SchemaType::JSON, "JSON-SCHEMA"},
458         {SchemaType::FLATBUFFER, "FLATBUFFER-SCHEMA"},
459         {SchemaType::RELATIVE, "RELATIVE"},
460         {SchemaType::UNRECOGNIZED, "UNRECOGNIZED"},
461     };
462     return schemaTypeMapString[inType];
463 }
464 
FieldPathString(const FieldPath & inPath)465 std::string SchemaUtils::FieldPathString(const FieldPath &inPath)
466 {
467     std::string outString = "$";
468     for (const auto &entry : inPath) {
469         outString += ".";
470         outString += entry;
471     }
472     return outString;
473 }
474 
TransTrackerSchemaToLower(const TrackerSchema & srcSchema,TrackerSchema & destSchema)475 void SchemaUtils::TransTrackerSchemaToLower(const TrackerSchema &srcSchema, TrackerSchema &destSchema)
476 {
477     std::string tableName(srcSchema.tableName.length(), ' ');
478     std::transform(srcSchema.tableName.begin(), srcSchema.tableName.end(), tableName.begin(), ::tolower);
479     destSchema.tableName = tableName;
480     for (const auto &extendColName : srcSchema.extendColNames) {
481         destSchema.extendColNames.insert(DBCommon::ToLowerCase(extendColName));
482     }
483     for (const auto &srcName : srcSchema.trackerColNames) {
484         destSchema.trackerColNames.insert(DBCommon::ToLowerCase(srcName));
485     }
486 }
487 } // namespace DistributedDB
488