• 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 #ifdef RELATIONAL_STORE
16 #include "relational_schema_object.h"
17 
18 #include <algorithm>
19 
20 #include "db_common.h"
21 #include "json_object.h"
22 #include "schema_constant.h"
23 #include "schema_utils.h"
24 
25 namespace DistributedDB {
IsSchemaValid() const26 bool RelationalSchemaObject::IsSchemaValid() const
27 {
28     return isValid_;
29 }
30 
GetSchemaType() const31 SchemaType RelationalSchemaObject::GetSchemaType() const
32 {
33     return schemaType_;
34 }
35 
ToSchemaString() const36 std::string RelationalSchemaObject::ToSchemaString() const
37 {
38     return schemaString_;
39 }
40 
ParseFromSchemaString(const std::string & inSchemaString)41 int RelationalSchemaObject::ParseFromSchemaString(const std::string &inSchemaString)
42 {
43     if (isValid_) {
44         return -E_NOT_PERMIT;
45     }
46 
47     if (inSchemaString.empty() || inSchemaString.size() > SchemaConstant::SCHEMA_STRING_SIZE_LIMIT) {
48         LOGE("[RelationalSchema][Parse] SchemaSize=%zu is invalid.", inSchemaString.size());
49         return -E_INVALID_ARGS;
50     }
51     JsonObject schemaObj;
52     int errCode = schemaObj.Parse(inSchemaString);
53     if (errCode != E_OK) {
54         LOGE("[RelationalSchema][Parse] Schema json string parse failed: %d.", errCode);
55         return errCode;
56     }
57 
58     errCode = ParseRelationalSchema(schemaObj);
59     if (errCode != E_OK) {
60         LOGE("[RelationalSchema][Parse] Parse to relational schema failed: %d.", errCode);
61         return errCode;
62     }
63 
64     schemaType_ = SchemaType::RELATIVE;
65     schemaString_ = schemaObj.ToString();
66     isValid_ = true;
67     return E_OK;
68 }
69 
GenerateSchemaString()70 void RelationalSchemaObject::GenerateSchemaString()
71 {
72     schemaString_ = {};
73     schemaString_ += "{";
74     schemaString_ += R"("SCHEMA_VERSION":")" + schemaVersion_ + R"(",)";
75     schemaString_ += R"("SCHEMA_TYPE":"RELATIVE",)";
76     if (schemaVersion_ == SchemaConstant::SCHEMA_SUPPORT_VERSION_V2_1) {
77         std::string modeString = tableMode_ == DistributedTableMode::COLLABORATION ?
78             SchemaConstant::KEYWORD_TABLE_COLLABORATION : SchemaConstant::KEYWORD_TABLE_SPLIT_DEVICE;
79         schemaString_ += R"("TABLE_MODE":")" + modeString + R"(",)";
80     }
81     schemaString_ += R"("TABLES":[)";
82     for (auto it = tables_.begin(); it != tables_.end(); it++) {
83         if (it != tables_.begin()) {
84             schemaString_ += ",";
85         }
86         schemaString_ += it->second.ToTableInfoString(schemaVersion_);
87     }
88     schemaString_ += R"(])";
89     schemaString_ += "}";
90 }
91 
AddRelationalTable(const TableInfo & tb)92 void RelationalSchemaObject::AddRelationalTable(const TableInfo &tb)
93 {
94     tables_[tb.GetTableName()] = tb;
95     isValid_ = true;
96     if (tb.GetPrimaryKey().size() > 1) { // Table with composite primary keys
97         // Composite primary keys are supported since version 2.1
98         schemaVersion_ = SchemaConstant::SCHEMA_CURRENT_VERSION;
99     }
100     GenerateSchemaString();
101 }
102 
RemoveRelationalTable(const std::string & tableName)103 void RelationalSchemaObject::RemoveRelationalTable(const std::string &tableName)
104 {
105     tables_.erase(tableName);
106     GenerateSchemaString();
107 }
108 
GetTables() const109 const std::map<std::string, TableInfo> &RelationalSchemaObject::GetTables() const
110 {
111     return tables_;
112 }
113 
GetTableNames() const114 std::vector<std::string> RelationalSchemaObject::GetTableNames() const
115 {
116     std::vector<std::string> tableNames;
117     for (const auto &it : tables_) {
118         tableNames.emplace_back(it.first);
119     }
120     return tableNames;
121 }
122 
GetTable(const std::string & tableName) const123 TableInfo RelationalSchemaObject::GetTable(const std::string &tableName) const
124 {
125     auto it = tables_.find(tableName);
126     if (it != tables_.end()) {
127         return it->second;
128     }
129     return {};
130 }
131 
GetSchemaVersion() const132 std::string RelationalSchemaObject::GetSchemaVersion() const
133 {
134     return schemaVersion_;
135 }
136 
GetTableMode() const137 DistributedTableMode RelationalSchemaObject::GetTableMode() const
138 {
139     return tableMode_;
140 }
141 
SetTableMode(DistributedTableMode mode)142 void RelationalSchemaObject::SetTableMode(DistributedTableMode mode)
143 {
144     tableMode_ = mode;
145     if (tableMode_ == DistributedTableMode::COLLABORATION) {
146         schemaVersion_ = SchemaConstant::SCHEMA_CURRENT_VERSION;
147     }
148     GenerateSchemaString();
149 }
150 
CompareAgainstSchemaObject(const std::string & inSchemaString,std::map<std::string,int> & cmpRst) const151 int RelationalSchemaObject::CompareAgainstSchemaObject(const std::string &inSchemaString,
152     std::map<std::string, int> &cmpRst) const
153 {
154     return E_OK;
155 }
156 
CompareAgainstSchemaObject(const RelationalSchemaObject & inSchemaObject,std::map<std::string,int> & cmpRst) const157 int RelationalSchemaObject::CompareAgainstSchemaObject(const RelationalSchemaObject &inSchemaObject,
158     std::map<std::string, int> &cmpRst) const
159 {
160     return E_OK;
161 }
162 
163 namespace {
GetMemberFromJsonObject(const JsonObject & inJsonObject,const std::string & fieldName,FieldType expectType,bool isNecessary,FieldValue & fieldValue)164 int GetMemberFromJsonObject(const JsonObject &inJsonObject, const std::string &fieldName, FieldType expectType,
165     bool isNecessary, FieldValue &fieldValue)
166 {
167     if (!inJsonObject.IsFieldPathExist(FieldPath {fieldName})) {
168         if (isNecessary) {
169             LOGE("[RelationalSchema][Parse] Get schema %s not exist. isNecessary: %d", fieldName.c_str(), isNecessary);
170             return -E_SCHEMA_PARSE_FAIL;
171         }
172         return -E_NOT_FOUND;
173     }
174 
175     FieldType fieldType;
176     int errCode = inJsonObject.GetFieldTypeByFieldPath(FieldPath {fieldName}, fieldType);
177     if (errCode != E_OK) {
178         LOGE("[RelationalSchema][Parse] Get schema %s fieldType failed: %d.", fieldName.c_str(), errCode);
179         return -E_SCHEMA_PARSE_FAIL;
180     }
181 
182     if (fieldType != expectType) {
183         LOGE("[RelationalSchema][Parse] Expect %s fieldType %d but: %d.", fieldName.c_str(),
184             static_cast<int>(expectType), static_cast<int>(fieldType));
185         return -E_SCHEMA_PARSE_FAIL;
186     }
187 
188     errCode = inJsonObject.GetFieldValueByFieldPath(FieldPath {fieldName}, fieldValue);
189     if (errCode != E_OK) {
190         LOGE("[RelationalSchema][Parse] Get schema %s value failed: %d.", fieldName.c_str(), errCode);
191         return -E_SCHEMA_PARSE_FAIL;
192     }
193     return E_OK;
194 }
195 }
196 
ParseRelationalSchema(const JsonObject & inJsonObject)197 int RelationalSchemaObject::ParseRelationalSchema(const JsonObject &inJsonObject)
198 {
199     int errCode = ParseCheckSchemaVersion(inJsonObject);
200     if (errCode != E_OK) {
201         return errCode;
202     }
203     errCode = ParseCheckSchemaType(inJsonObject);
204     if (errCode != E_OK) {
205         return errCode;
206     }
207     errCode = ParseCheckTableMode(inJsonObject);
208     if (errCode != E_OK) {
209         return errCode;
210     }
211     return ParseCheckSchemaTableDefine(inJsonObject);
212 }
213 
214 namespace {
IsSchemaVersionValid(const std::string & version)215 inline bool IsSchemaVersionValid(const std::string &version)
216 {
217     std::string stripedVersion = SchemaUtils::Strip(version);
218     return stripedVersion == SchemaConstant::SCHEMA_SUPPORT_VERSION_V2 ||
219         stripedVersion == SchemaConstant::SCHEMA_SUPPORT_VERSION_V2_1;
220 }
221 }
222 
ParseCheckSchemaVersion(const JsonObject & inJsonObject)223 int RelationalSchemaObject::ParseCheckSchemaVersion(const JsonObject &inJsonObject)
224 {
225     FieldValue fieldValue;
226     int errCode = GetMemberFromJsonObject(inJsonObject, SchemaConstant::KEYWORD_SCHEMA_VERSION,
227         FieldType::LEAF_FIELD_STRING, true, fieldValue);
228     if (errCode != E_OK) {
229         return errCode;
230     }
231 
232     if (IsSchemaVersionValid(fieldValue.stringValue)) {
233         schemaVersion_ = fieldValue.stringValue;
234         return E_OK;
235     }
236 
237     LOGE("[RelationalSchema][Parse] Unexpected SCHEMA_VERSION=%s.", fieldValue.stringValue.c_str());
238     return -E_SCHEMA_PARSE_FAIL;
239 }
240 
ParseCheckSchemaType(const JsonObject & inJsonObject)241 int RelationalSchemaObject::ParseCheckSchemaType(const JsonObject &inJsonObject)
242 {
243     FieldValue fieldValue;
244     int errCode = GetMemberFromJsonObject(inJsonObject, SchemaConstant::KEYWORD_SCHEMA_TYPE,
245         FieldType::LEAF_FIELD_STRING, true, fieldValue);
246     if (errCode != E_OK) {
247         return errCode;
248     }
249 
250     if (SchemaUtils::Strip(fieldValue.stringValue) != SchemaConstant::KEYWORD_TYPE_RELATIVE) {
251         LOGE("[RelationalSchema][Parse] Unexpected SCHEMA_TYPE=%s.", fieldValue.stringValue.c_str());
252         return -E_SCHEMA_PARSE_FAIL;
253     }
254     schemaType_ = SchemaType::RELATIVE;
255     return E_OK;
256 }
257 
258 namespace {
IsTableModeValid(const std::string & mode)259 inline bool IsTableModeValid(const std::string &mode)
260 {
261     std::string stripedMode = SchemaUtils::Strip(mode);
262     return stripedMode == SchemaConstant::KEYWORD_TABLE_SPLIT_DEVICE ||
263         stripedMode == SchemaConstant::KEYWORD_TABLE_COLLABORATION;
264 }
265 }
266 
ParseCheckTableMode(const JsonObject & inJsonObject)267 int RelationalSchemaObject::ParseCheckTableMode(const JsonObject &inJsonObject)
268 {
269     if (schemaVersion_ == SchemaConstant::SCHEMA_SUPPORT_VERSION_V2) {
270         return E_OK; // version 2 has no table mode, no parsing required
271     }
272 
273     FieldValue fieldValue;
274     int errCode = GetMemberFromJsonObject(inJsonObject, SchemaConstant::KEYWORD_TABLE_MODE,
275         FieldType::LEAF_FIELD_STRING, true, fieldValue);
276     if (errCode != E_OK) {
277         return errCode;
278     }
279 
280     if (!IsTableModeValid(fieldValue.stringValue)) {
281         LOGE("[RelationalSchema][Parse] Unexpected TABLE_MODE=%s.", fieldValue.stringValue.c_str());
282         return -E_SCHEMA_PARSE_FAIL;
283     }
284 
285     tableMode_ = SchemaUtils::Strip(fieldValue.stringValue) == SchemaConstant::KEYWORD_TABLE_SPLIT_DEVICE ?
286         DistributedDB::SPLIT_BY_DEVICE : DistributedTableMode::COLLABORATION;
287     return E_OK;
288 }
289 
ParseCheckSchemaTableDefine(const JsonObject & inJsonObject)290 int RelationalSchemaObject::ParseCheckSchemaTableDefine(const JsonObject &inJsonObject)
291 {
292     FieldType fieldType;
293     int errCode = inJsonObject.GetFieldTypeByFieldPath(FieldPath {SchemaConstant::KEYWORD_SCHEMA_TABLE}, fieldType);
294     if (errCode != E_OK) {
295         LOGE("[RelationalSchema][Parse] Get schema TABLES fieldType failed: %d.", errCode);
296         return -E_SCHEMA_PARSE_FAIL;
297     }
298     if (FieldType::LEAF_FIELD_ARRAY != fieldType) {
299         LOGE("[RelationalSchema][Parse] Expect TABLES fieldType ARRAY but %s.",
300             SchemaUtils::FieldTypeString(fieldType).c_str());
301         return -E_SCHEMA_PARSE_FAIL;
302     }
303     std::vector<JsonObject> tables;
304     errCode = inJsonObject.GetObjectArrayByFieldPath(FieldPath{SchemaConstant::KEYWORD_SCHEMA_TABLE}, tables);
305     if (errCode != E_OK) {
306         LOGE("[RelationalSchema][Parse] Get schema TABLES value failed: %d.", errCode);
307         return -E_SCHEMA_PARSE_FAIL;
308     }
309     for (const JsonObject &table : tables) {
310         errCode = ParseCheckTableInfo(table);
311         if (errCode != E_OK) {
312             LOGE("[RelationalSchema][Parse] Parse schema TABLES failed: %d.", errCode);
313             return -E_SCHEMA_PARSE_FAIL;
314         }
315     }
316     return E_OK;
317 }
318 
ParseCheckTableInfo(const JsonObject & inJsonObject)319 int RelationalSchemaObject::ParseCheckTableInfo(const JsonObject &inJsonObject)
320 {
321     TableInfo resultTable;
322     int errCode = ParseCheckTableName(inJsonObject, resultTable);
323     if (errCode != E_OK) {
324         return errCode;
325     }
326     errCode = ParseCheckTableDefine(inJsonObject, resultTable);
327     if (errCode != E_OK) {
328         return errCode;
329     }
330     errCode = ParseCheckTableAutoInc(inJsonObject, resultTable);
331     if (errCode != E_OK) {
332         return errCode;
333     }
334     errCode = ParseCheckTablePrimaryKey(inJsonObject, resultTable);
335     if (errCode != E_OK) {
336         return errCode;
337     }
338     errCode = ParseCheckTableIndex(inJsonObject, resultTable);
339     if (errCode != E_OK) {
340         return errCode;
341     }
342     errCode = ParseCheckTableUnique(inJsonObject, resultTable);
343     if (errCode != E_OK) {
344         return errCode;
345     }
346     tables_[resultTable.GetTableName()] = resultTable;
347     return E_OK;
348 }
349 
ParseCheckTableName(const JsonObject & inJsonObject,TableInfo & resultTable)350 int RelationalSchemaObject::ParseCheckTableName(const JsonObject &inJsonObject, TableInfo &resultTable)
351 {
352     FieldValue fieldValue;
353     int errCode = GetMemberFromJsonObject(inJsonObject, "NAME", FieldType::LEAF_FIELD_STRING,
354         true, fieldValue);
355     if (errCode == E_OK) {
356         if (!DBCommon::CheckIsAlnumAndUnderscore(fieldValue.stringValue)) {
357             LOGE("[RelationalSchema][Parse] Invalid characters in table name, err=%d.", errCode);
358             return -E_SCHEMA_PARSE_FAIL;
359         }
360         resultTable.SetTableName(fieldValue.stringValue);
361     }
362     return errCode;
363 }
364 
ParseCheckTableDefine(const JsonObject & inJsonObject,TableInfo & resultTable)365 int RelationalSchemaObject::ParseCheckTableDefine(const JsonObject &inJsonObject, TableInfo &resultTable)
366 {
367     std::map<FieldPath, FieldType> tableFields;
368     int errCode = inJsonObject.GetSubFieldPathAndType(FieldPath {"DEFINE"}, tableFields);
369     if (errCode != E_OK) {
370         LOGE("[RelationalSchema][Parse] Get schema TABLES DEFINE failed: %d.", errCode);
371         return -E_SCHEMA_PARSE_FAIL;
372     }
373 
374     for (const auto &field : tableFields) {
375         if (field.second != FieldType::INTERNAL_FIELD_OBJECT) {
376             LOGE("[RelationalSchema][Parse] Expect schema TABLES DEFINE fieldType INTERNAL OBJECT but : %s.",
377                 SchemaUtils::FieldTypeString(field.second).c_str());
378             return -E_SCHEMA_PARSE_FAIL;
379         }
380 
381         JsonObject fieldObj;
382         errCode = inJsonObject.GetObjectByFieldPath(field.first, fieldObj);
383         if (errCode != E_OK) {
384             LOGE("[RelationalSchema][Parse] Get table field object failed. %d", errCode);
385             return errCode;
386         }
387 
388         if (!DBCommon::CheckIsAlnumAndUnderscore(field.first[1])) {
389             LOGE("[RelationalSchema][Parse] Invalid characters in field name, err=%d.", errCode);
390             return -E_SCHEMA_PARSE_FAIL;
391         }
392 
393         FieldInfo fieldInfo;
394         fieldInfo.SetFieldName(field.first[1]); // 1 : table name element in path
395         errCode = ParseCheckTableFieldInfo(fieldObj, field.first, fieldInfo);
396         if (errCode != E_OK) {
397             LOGE("[RelationalSchema][Parse] Parse table field info failed. %d", errCode);
398             return -E_SCHEMA_PARSE_FAIL;
399         }
400         resultTable.AddField(fieldInfo);
401     }
402     return E_OK;
403 }
404 
ParseCheckTableFieldInfo(const JsonObject & inJsonObject,const FieldPath & path,FieldInfo & field)405 int RelationalSchemaObject::ParseCheckTableFieldInfo(const JsonObject &inJsonObject, const FieldPath &path,
406     FieldInfo &field)
407 {
408     FieldValue fieldValue;
409     int errCode = GetMemberFromJsonObject(inJsonObject, "COLUMN_ID", FieldType::LEAF_FIELD_INTEGER, true, fieldValue);
410     if (errCode != E_OK) {
411         return errCode;
412     }
413     field.SetColumnId(fieldValue.integerValue);
414 
415     errCode = GetMemberFromJsonObject(inJsonObject, "TYPE", FieldType::LEAF_FIELD_STRING, true, fieldValue);
416     if (errCode != E_OK) {
417         return errCode;
418     }
419     field.SetDataType(fieldValue.stringValue);
420 
421     errCode = GetMemberFromJsonObject(inJsonObject, "NOT_NULL", FieldType::LEAF_FIELD_BOOL, true, fieldValue);
422     if (errCode != E_OK) {
423         return errCode;
424     }
425     field.SetNotNull(fieldValue.boolValue);
426 
427     errCode = GetMemberFromJsonObject(inJsonObject, "DEFAULT", FieldType::LEAF_FIELD_STRING, false, fieldValue);
428     if (errCode == E_OK) {
429         field.SetDefaultValue(fieldValue.stringValue);
430     } else if (errCode != -E_NOT_FOUND) {
431         return errCode;
432     }
433 
434     return E_OK;
435 }
436 
ParseCheckTableAutoInc(const JsonObject & inJsonObject,TableInfo & resultTable)437 int RelationalSchemaObject::ParseCheckTableAutoInc(const JsonObject &inJsonObject, TableInfo &resultTable)
438 {
439     FieldValue fieldValue;
440     int errCode = GetMemberFromJsonObject(inJsonObject, "AUTOINCREMENT", FieldType::LEAF_FIELD_BOOL, false, fieldValue);
441     if (errCode == E_OK) {
442         resultTable.SetAutoIncrement(fieldValue.boolValue);
443     } else if (errCode != -E_NOT_FOUND) {
444         return errCode;
445     }
446     return E_OK;
447 }
448 
ParseCheckTablePrimaryKey(const JsonObject & inJsonObject,TableInfo & resultTable)449 int RelationalSchemaObject::ParseCheckTablePrimaryKey(const JsonObject &inJsonObject, TableInfo &resultTable)
450 {
451     if (!inJsonObject.IsFieldPathExist(FieldPath {"PRIMARY_KEY"})) {
452         return E_OK;
453     }
454 
455     FieldType type;
456     int errCode = inJsonObject.GetFieldTypeByFieldPath(FieldPath {"PRIMARY_KEY"}, type);
457     if (errCode != E_OK) {
458         return errCode;
459     }
460 
461     if (type == FieldType::LEAF_FIELD_STRING) { // Compatible with schema 2.0
462         FieldValue fieldValue;
463         errCode = GetMemberFromJsonObject(inJsonObject, "PRIMARY_KEY", FieldType::LEAF_FIELD_STRING, false, fieldValue);
464         if (errCode == E_OK) {
465             resultTable.SetPrimaryKey(fieldValue.stringValue, 1);
466         }
467     } else if (type == FieldType::LEAF_FIELD_ARRAY) {
468         CompositeFields multiPrimaryKey;
469         errCode = inJsonObject.GetStringArrayByFieldPath(FieldPath {"PRIMARY_KEY"}, multiPrimaryKey);
470         if (errCode == E_OK) {
471             int index = 1; // primary key index
472             for (const auto &item : multiPrimaryKey) {
473                 resultTable.SetPrimaryKey(item, index++);
474             }
475         }
476     } else {
477         errCode = -E_SCHEMA_PARSE_FAIL;
478     }
479     return errCode;
480 }
481 
ParseCheckTableIndex(const JsonObject & inJsonObject,TableInfo & resultTable)482 int RelationalSchemaObject::ParseCheckTableIndex(const JsonObject &inJsonObject, TableInfo &resultTable)
483 {
484     if (!inJsonObject.IsFieldPathExist(FieldPath {"INDEX"})) { // INDEX is not necessary
485         return E_OK;
486     }
487     std::map<FieldPath, FieldType> tableFields;
488     int errCode = inJsonObject.GetSubFieldPathAndType(FieldPath {"INDEX"}, tableFields);
489     if (errCode != E_OK) {
490         LOGE("[RelationalSchema][Parse] Get schema TABLES INDEX failed: %d.", errCode);
491         return -E_SCHEMA_PARSE_FAIL;
492     }
493 
494     for (const auto &field : tableFields) {
495         if (field.second != FieldType::LEAF_FIELD_ARRAY) {
496             LOGE("[RelationalSchema][Parse] Expect schema TABLES INDEX fieldType ARRAY but : %s.",
497                 SchemaUtils::FieldTypeString(field.second).c_str());
498             return -E_SCHEMA_PARSE_FAIL;
499         }
500         CompositeFields indexDefine;
501         errCode = inJsonObject.GetStringArrayByFieldPath(field.first, indexDefine);
502         if (errCode != E_OK) {
503             LOGE("[RelationalSchema][Parse] Get schema TABLES INDEX field value failed: %d.", errCode);
504             return -E_SCHEMA_PARSE_FAIL;
505         }
506         resultTable.AddIndexDefine(field.first[1], indexDefine); // 1 : second element in path
507     }
508     return E_OK;
509 }
510 
ParseCheckTableUnique(const JsonObject & inJsonObject,TableInfo & resultTable)511 int RelationalSchemaObject::ParseCheckTableUnique(const JsonObject &inJsonObject, TableInfo &resultTable)
512 {
513     if (!inJsonObject.IsFieldPathExist(FieldPath {"UNIQUE"})) { // UNIQUE is not necessary
514         return E_OK;
515     }
516 
517     std::vector<CompositeFields> uniques;
518     int errCode = inJsonObject.GetArrayContentOfStringOrStringArray(FieldPath {"UNIQUE"}, uniques);
519     if (errCode != E_OK) {
520         LOGE("[RelationalSchema][Parse] Get schema TABLES UNIQUE failed: %d.", errCode);
521         return -E_SCHEMA_PARSE_FAIL;
522     }
523     resultTable.SetUniqueDefine(uniques);
524     return E_OK;
525 }
526 }
527 #endif