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 #ifndef SCHEMA_OBJECT_H
17 #define SCHEMA_OBJECT_H
18 
19 #include <map>
20 #include <set>
21 
22 #ifndef OMIT_FLATBUFFER
23 #include <flatbuffers/idl.h>
24 #endif // OMIT_FLATBUFFER
25 #include "db_types.h"
26 #include "ischema.h"
27 #include "macro_utils.h"
28 #include "relational_schema_object.h"
29 #include "value_object.h"
30 
31 namespace DistributedDB {
32 using IndexName = FieldPath;
33 using IndexFieldInfo = std::pair<FieldPath, FieldType>;
34 using IndexInfo = std::vector<IndexFieldInfo>;
35 template<typename T> using PairConstPointer = std::pair<const T *, const T *>;
36 
37 struct IndexDifference {
38     std::map<IndexName, IndexInfo> change;
39     std::map<IndexName, IndexInfo> increase;
40     std::set<IndexName> decrease;
41 };
42 
43 class SchemaObject : public ISchema {
44 public:
45     static std::string GetExtractFuncName(SchemaType inSchemaType);
46     static std::string GenerateExtractSQL(SchemaType inSchemaType, const FieldPath &inFieldpath, FieldType inFieldType,
47         uint32_t skipSize, const std::string &accessStr = "");
48 
49     // Support default constructor, copy constructor and copy assignment
50     SchemaObject();
51     ~SchemaObject() = default;
52     SchemaObject(const SchemaObject &);
53     SchemaObject& operator=(const SchemaObject &);
54 #ifdef RELATIONAL_STORE
55     explicit SchemaObject(const TableInfo &tableInfo);  // The construct func can only be used for query.
56 #endif  // RELATIONAL_STORE
57 
58     // Move constructor and move assignment is not need currently
59     SchemaObject(SchemaObject &&) = delete;
60     SchemaObject& operator=(SchemaObject &&) = delete;
61 
62     // Should be called on an invalid SchemaObject, create new SchemaObject if need to reparse
63     int ParseFromSchemaString(const std::string &inSchemaString) override;
64 
65     bool IsSchemaValid() const override;
66     SchemaType GetSchemaType() const override;
67 
68     // For Json-Schema : Unnecessary spacing will be removed and fieldname resorted by lexicographical order
69     // For FlatBuffer-Schema : Original binary schema(Base64 decoded if need)
70     std::string ToSchemaString() const override;
71 
72     uint32_t GetSkipSize() const;
73     std::map<IndexName, IndexInfo> GetIndexInfo() const;
74     bool IsIndexExist(const IndexName &indexName) const;
75 
76     // Return E_OK if queryale. outType will be set if path exist no matter binary or not
77     int CheckQueryableAndGetFieldType(const FieldPath &inPath, FieldType &outType) const;
78 
79     // Attention: it doesn't return E_OK. instead:
80     // E_JSON_PARSE_FAIL : the inSchemaString is not an valid json
81     // E_SCHEMA_PARSE_FAIL : the inSchemaString is not an valid schema
82     // E_SCHEMA_EQUAL_EXACTLY : the inSchema is exactly equal to this SchemaObject
83     // E_SCHEMA_UNEQUAL_COMPATIBLE : the inSchema is not equal to but only index differ with this SchemaObject
84     // E_SCHEMA_UNEQUAL_COMPATIBLE_UPGRADE : the inSchema is not equal to but can upgrade from this SchemaObject
85     // E_SCHEMA_UNEQUAL_INCOMPATIBLE : the inSchema is not equal to and can not upgrade from this SchemaObject
86     int CompareAgainstSchemaString(const std::string &inSchemaString) const;
87     int CompareAgainstSchemaString(const std::string &inSchemaString, IndexDifference &indexDiffer) const;
88     int CompareAgainstSchemaObject(const SchemaObject &inSchemaObject) const;
89     int CompareAgainstSchemaObject(const SchemaObject &inSchemaObject, IndexDifference &indexDiffer) const;
90 
91     // Attention: it doesn't return E_OK. instead:
92     // E_VALUE_MATCH : Value match schema(no matter strict or compatible mode) without any change
93     // E_VALUE_MATCH_AMENDED : Value match schema(no matter strict or compatible mode) with some amendment
94     // E_VALUE_MISMATCH_FEILD_COUNT : Value contain more field then schema when in strict mode
95     // E_VALUE_MISMATCH_FEILD_TYPE : Type of some fields of value mismatch schema
96     // E_VALUE_MISMATCH_CONSTRAINT : Some fields of value violate the NotNull constraint against schema
97     // E_VALUE_MISMATCH_OTHER_REASON : Value mismatch schema because of other reason unmentioned
98     int CheckValueAndAmendIfNeed(ValueSource sourceType, ValueObject &inValue) const;
99 
100     // Currently only for flatBuffer-type schema and value.
101     // Accept the original entry-value, return E_OK or E_FLATBUFFER_VERIFY_FAIL.
102     int VerifyValue(ValueSource sourceType, const Value &inValue) const;
103     int VerifyValue(ValueSource sourceType, const RawValue &inValue) const;
104 
105     // Accept the original value from database. The cache will not be expanded. Return E_OK if nothing error.
106     // The ExtractValue is with nice performance by carefully not use std-class to avoid memory allocation.
107     // But currently it can only deal with path with $. prefix and only one depth. However, meet current demand.
108     int ExtractValue(ValueSource sourceType, RawString inPath, const RawValue &inValue, TypeValue &outExtract,
109         std::vector<uint8_t> *cache) const;
110 private:
111     enum class SchemaMode {
112         STRICT,
113         COMPATIBLE,
114     };
115     using SchemaDefine = std::map<FieldPath, SchemaAttribute>;
116 
117     // For Json-Schema : Parsing related methods.
118     int ParseJsonSchema(const JsonObject &inJsonObject);
119     int CheckMetaFieldCountAndType(const JsonObject &inJsonObject) const;
120     int ParseCheckSchemaVersionMode(const JsonObject &inJsonObject);
121     int ParseCheckSchemaDefine(const JsonObject &inJsonObject);
122     int CheckSchemaDefineItemDecideAttribute(const JsonObject &inJsonObject, const FieldPath &inPath, FieldType inType,
123         SchemaAttribute &outAttr) const;
124     int ParseCheckSchemaIndexes(const JsonObject &inJsonObject);
125     int ParseCheckSchemaSkipSize(const JsonObject &inJsonObject);
126 
127     // For both Json-Schema and FlatBuffer-Schema.
128     int ParseCheckEachIndexFromStringArray(const std::vector<std::string> &inStrArray);
129     int CheckFieldPathIndexableThenSave(const std::vector<FieldPath> &inPathVec, IndexInfo &infoToSave);
130 
131     // CompareAgainstSchemaObject related sub methods
132     int CompareSchemaVersionMode(const SchemaObject &newSchema) const;
133     int CompareSchemaSkipSize(const SchemaObject &newSchema) const;
134     int CompareSchemaDefine(const SchemaObject &newSchema) const;
135     int CompareSchemaDefineByDepth(const SchemaDefine &oldDefine, const SchemaDefine &newDefine) const;
136     int CompareSchemaAttribute(const SchemaAttribute &oldAttr, const SchemaAttribute &newAttr) const;
137     int CompareSchemaDefaultValue(const SchemaAttribute &oldAttr, const SchemaAttribute &newAttr) const;
138     int CompareSchemaIndexes(const SchemaObject &newSchema, IndexDifference &indexDiffer) const;
139 
140     // CheckValueAndAmendIfNeed related sub methods
141     int CheckValue(const ValueObject &inValue, std::set<FieldPath> &lackingPaths) const;
142     int AmendValueIfNeed(ValueObject &inValue, const std::set<FieldPath> &lackingPaths, bool &amended) const;
143 
144     // It is better using a class to represent flatBuffer-Schema related other than more private method(As well as for
145     // Json-Schema in the future refactor). Delegation is chosen other than inheritance for accessing SchemaObject.
146     // Choose inner-class other than friend-class to avoid forward declaration and using pointer.
147     class FlatBufferSchema {
148     public:
FlatBufferSchema(SchemaObject & owner)149         explicit FlatBufferSchema(SchemaObject &owner) : owner_(owner) {};
150         ~FlatBufferSchema() = default;
151         DISABLE_COPY_ASSIGN_MOVE(FlatBufferSchema);
152         // Copy-Constructor can not define due to Const-Ref member. Code standard require copy assignment be deleted.
153         void CopyFrom(const FlatBufferSchema &other);
154 
155         std::string GetDescription() const;
156 
157         // Judge whether it's flatbuffer type schema, no matter whether it is Base64 encoded, provide a decoded one.
158         static bool IsFlatBufferSchema(const std::string &inOriginal, std::string &outDecoded);
159 
160         // Accept a decoded and verified flatbuffer-schema, then parse its content
161         int ParseFlatBufferSchema(const std::string &inDecoded);
162 
163         // Compare based on self.
164         // return E_SCHEMA_EQUAL_EXACTLY or E_SCHEMA_UNEQUAL_COMPATIBLE_UPGRADE or E_SCHEMA_UNEQUAL_INCOMPATIBLE
165         int CompareFlatBufferDefine(const FlatBufferSchema &other) const;
166 
167         // Accept a no-skipsize(so byte-aligned) value, return E_OK or E_FLATBUFFER_VERIFY_FAIL.
168         int VerifyFlatBufferValue(const RawValue &inValue, bool tryNoSizePrefix) const;
169 
170         // Accept a no-skipsize(so byte-aligned) value.
171         int ExtractFlatBufferValue(RawString inPath, const RawValue &inValue, TypeValue &outExtract,
172             bool tryNoSizePrefix) const;
173     private:
174 #ifndef OMIT_FLATBUFFER
175         using RawIndexInfos = std::map<std::string, std::string>; // First the fieldName, second the index-attr value.
176 
177         const reflection::Schema *GetSchema() const;
178 
179         int ParseCheckRootTableAttribute(const reflection::Object &rootTable);
180         int ParseCheckRootTableDefine(const reflection::Schema &schema, const reflection::Object &rootTable,
181             RawIndexInfos &indexCollect);
182         int ParseCheckFieldInfo(const reflection::Schema &schema, const reflection::Field &field,
183             const FieldPath &path, RawIndexInfos &indexCollect);
184         void CollectRawIndexInfos(const reflection::Field &field, RawIndexInfos &indexCollect) const;
185         int ParseCheckStructDefine(const reflection::Schema &schema, const reflection::Field &field,
186             const FieldPath &path);
187         int ParseCheckIndexes(const RawIndexInfos &indexCollect);
188 
189         int CompareTableOrStructDefine(const PairConstPointer<reflection::Schema> &bothSchema,
190             const PairConstPointer<reflection::Object> &bothObject, bool isRoot, std::set<std::string> &compared) const;
191         int CompareStruct(const PairConstPointer<reflection::Schema> &bothSchema,
192             const PairConstPointer<reflection::Field> &bothField, std::set<std::string> &compared) const;
193 #endif
194         SchemaObject &owner_;
195         std::string description_;
196     };
197 
198     bool isValid_ = false;
199     SchemaType schemaType_ = SchemaType::NONE; // Default NONE
200     FlatBufferSchema flatbufferSchema_;
201     std::string schemaString_; // The minified and valid schemaString
202 
203     std::string schemaVersion_;
204     SchemaMode schemaMode_ = SchemaMode::STRICT; // Only for Json-Schema, Consider refactor into JsonSchema class
205     uint32_t schemaSkipSize_ = 0;
206     std::map<IndexName, IndexInfo> schemaIndexes_;
207     std::map<uint32_t, SchemaDefine> schemaDefine_; // SchemaDefine classified by the depth of fieldpath
208 };
209 } // namespace DistributedDB
210 
211 #endif // SCHEMA_OBJECT_H
212