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