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