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