1 /*
2 * Copyright (c) 2023 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 "cloud/schema_mgr.h"
17
18 #include <unordered_set>
19
20 #include "cloud/cloud_db_constant.h"
21 #include "cloud/cloud_storage_utils.h"
22 #include "cloud/cloud_store_types.h"
23 #include "db_common.h"
24 #include "db_errno.h"
25 namespace DistributedDB {
SchemaMgr()26 SchemaMgr::SchemaMgr()
27 {
28 }
29
ChkSchema(const TableName & tableName,RelationalSchemaObject & localSchema)30 int SchemaMgr::ChkSchema(const TableName &tableName, RelationalSchemaObject &localSchema)
31 {
32 if (cloudSchema_ == nullptr) {
33 LOGE("Cloud schema has not been set");
34 return -E_SCHEMA_MISMATCH;
35 }
36 TableInfo tableInfo = localSchema.GetTable(tableName);
37 if (tableInfo.Empty()) {
38 LOGE("Local schema does not contain certain table [ %s size = %d ]",
39 DBCommon::StringMiddleMasking(tableName).c_str(), tableName.size());
40 return -E_SCHEMA_MISMATCH;
41 }
42 if (tableInfo.GetTableSyncType() != TableSyncType::CLOUD_COOPERATION) {
43 LOGE("Sync type of local table [ %s size = %d ] is not CLOUD_COOPERATION",
44 DBCommon::StringMiddleMasking(tableName).c_str(), tableName.size());
45 return -E_NOT_SUPPORT;
46 }
47 TableSchema cloudTableSchema;
48 int ret = GetCloudTableSchema(tableName, cloudTableSchema);
49 if (ret != E_OK) {
50 LOGE("Cloud schema does not contain certain table [ %s size = %d ]:%d",
51 DBCommon::StringMiddleMasking(tableName).c_str(), tableName.size(), ret);
52 return -E_SCHEMA_MISMATCH;
53 }
54 std::map<int, FieldName> primaryKeys = tableInfo.GetPrimaryKey();
55 FieldInfoMap localFields = tableInfo.GetFields();
56 return CompareFieldSchema(primaryKeys, localFields, cloudTableSchema.fields);
57 }
58
CompareFieldSchema(std::map<int,FieldName> & primaryKeys,FieldInfoMap & localFields,std::vector<Field> & cloudFields)59 int SchemaMgr::CompareFieldSchema(std::map<int, FieldName> &primaryKeys, FieldInfoMap &localFields,
60 std::vector<Field> &cloudFields)
61 {
62 std::unordered_set<std::string> cloudColNames;
63 for (const Field &cloudField : cloudFields) {
64 if (localFields.find(cloudField.colName) == localFields.end()) {
65 LOGE("Column name mismatch between local and cloud schema");
66 return -E_SCHEMA_MISMATCH;
67 }
68 if (IsAssetPrimaryField(cloudField)) {
69 LOGE("Asset type can not be primary field");
70 return -E_SCHEMA_MISMATCH;
71 }
72 FieldInfo &localField = localFields[cloudField.colName];
73 if (!CompareType(localField, cloudField)) {
74 LOGE("Type mismatch between local and cloud schema");
75 return -E_SCHEMA_MISMATCH;
76 }
77 if (!CompareNullable(localField, cloudField)) {
78 LOGE("The nullable property is mismatched between local and cloud schema");
79 return -E_SCHEMA_MISMATCH;
80 }
81 if (!ComparePrimaryField(primaryKeys, cloudField)) {
82 LOGE("The primary key property is mismatched between local and cloud schema");
83 return -E_SCHEMA_MISMATCH;
84 }
85 cloudColNames.emplace(cloudField.colName);
86 }
87 if (!primaryKeys.empty() && !(primaryKeys.size() == 1 && primaryKeys[0] == CloudDbConstant::ROW_ID_FIELD_NAME)) {
88 LOGE("Local schema contain extra primary key:%d", -E_SCHEMA_MISMATCH);
89 return -E_SCHEMA_MISMATCH;
90 }
91 for (const auto &[fieldName, fieldInfo] : localFields) {
92 if (!fieldInfo.HasDefaultValue() &&
93 fieldInfo.IsNotNull() &&
94 cloudColNames.find(fieldName) == cloudColNames.end()) {
95 LOGE("Column from local schema is not within cloud schema but doesn't have default value");
96 return -E_SCHEMA_MISMATCH;
97 }
98 }
99 return E_OK;
100 }
101
IsAssetPrimaryField(const Field & cloudField)102 bool SchemaMgr::IsAssetPrimaryField(const Field &cloudField)
103 {
104 return cloudField.primary && (cloudField.type == TYPE_INDEX<Assets> || cloudField.type == TYPE_INDEX<Asset>);
105 }
106
CompareType(const FieldInfo & localField,const Field & cloudField)107 bool SchemaMgr::CompareType(const FieldInfo &localField, const Field &cloudField)
108 {
109 StorageType localType = localField.GetStorageType();
110 switch (cloudField.type) {
111 case TYPE_INDEX<std::monostate>:
112 case TYPE_INDEX<bool>:
113 // BOOL type should be stored as NUMERIC type,
114 // but we regard it as NULL type for historic reason
115 return localType == StorageType::STORAGE_TYPE_NULL;
116 case TYPE_INDEX<int64_t>:
117 return localType == StorageType::STORAGE_TYPE_INTEGER;
118 case TYPE_INDEX<double>:
119 return localType == StorageType::STORAGE_TYPE_REAL;
120 case TYPE_INDEX<std::string>:
121 return localType == StorageType::STORAGE_TYPE_TEXT;
122 case TYPE_INDEX<Bytes>:
123 case TYPE_INDEX<Asset>:
124 case TYPE_INDEX<Assets>:
125 return localType == StorageType::STORAGE_TYPE_BLOB;
126 default:
127 return false;
128 }
129 }
130
CompareNullable(const FieldInfo & localField,const Field & cloudField)131 bool SchemaMgr::CompareNullable(const FieldInfo &localField, const Field &cloudField)
132 {
133 return localField.IsNotNull() == !cloudField.nullable;
134 }
135
ComparePrimaryField(std::map<int,FieldName> & localPrimaryKeys,const Field & cloudField)136 bool SchemaMgr::ComparePrimaryField(std::map<int, FieldName> &localPrimaryKeys, const Field &cloudField)
137 {
138 // whether the corresponding field in local schema is primary key
139 bool isLocalFieldPrimary = false;
140 for (const auto &kvPair : localPrimaryKeys) {
141 if (DBCommon::CaseInsensitiveCompare(kvPair.second, cloudField.colName)) {
142 isLocalFieldPrimary = true;
143 localPrimaryKeys.erase(kvPair.first);
144 break;
145 }
146 }
147 return isLocalFieldPrimary == cloudField.primary;
148 }
149
SetCloudDbSchema(const DataBaseSchema & schema,RelationalSchemaObject & localSchema)150 void SchemaMgr::SetCloudDbSchema(const DataBaseSchema &schema, RelationalSchemaObject &localSchema)
151 {
152 DataBaseSchema cloudSchema = schema;
153 for (TableSchema &table : cloudSchema.tables) {
154 std::string tableName = table.name;
155 TableInfo tableInfo = localSchema.GetTable(tableName);
156 if (tableInfo.Empty()) {
157 LOGD("Local schema does not contain certain table [%s size = %d]",
158 DBCommon::StringMiddleMasking(tableName).c_str(), tableName.size());
159 continue;
160 }
161 FieldInfoMap localFields = tableInfo.GetFields();
162
163 // remove the fields that are not found in local schema from cloud schema
164 for (auto it = table.fields.begin(); it != table.fields.end();) {
165 if (localFields.find((*it).colName) == localFields.end()) {
166 it = table.fields.erase(it);
167 LOGI("Column name mismatch between local and cloud schema, colName: %s", (*it).colName.c_str());
168 } else {
169 ++it;
170 }
171 }
172 }
173 SetCloudDbSchema(cloudSchema);
174 }
175
SetCloudDbSchema(const DataBaseSchema & schema)176 void SchemaMgr::SetCloudDbSchema(const DataBaseSchema &schema)
177 {
178 DataBaseSchema cloudSchema = schema;
179 DataBaseSchema cloudSharedSchema;
180 for (const auto &tableSchema : cloudSchema.tables) {
181 if (tableSchema.name.empty() || tableSchema.sharedTableName.empty()) {
182 continue;
183 }
184 bool hasPrimaryKey = DBCommon::HasPrimaryKey(tableSchema.fields);
185 auto sharedTableFields = tableSchema.fields;
186 Field ownerField = { CloudDbConstant::CLOUD_OWNER, TYPE_INDEX<std::string>, hasPrimaryKey, true };
187 Field privilegeField = { CloudDbConstant::CLOUD_PRIVILEGE, TYPE_INDEX<std::string>, false, true };
188 sharedTableFields.push_back(ownerField);
189 sharedTableFields.push_back(privilegeField);
190 TableSchema sharedTableSchema = { tableSchema.sharedTableName, tableSchema.sharedTableName, sharedTableFields };
191 cloudSharedSchema.tables.push_back(sharedTableSchema);
192 }
193 for (const auto &sharedTableSchema : cloudSharedSchema.tables) {
194 cloudSchema.tables.push_back(sharedTableSchema);
195 }
196 cloudSchema_ = std::make_shared<DataBaseSchema>(cloudSchema);
197 }
198
GetCloudDbSchema()199 std::shared_ptr<DataBaseSchema> SchemaMgr::GetCloudDbSchema()
200 {
201 return cloudSchema_;
202 }
203
GetCloudTableSchema(const TableName & tableName,TableSchema & retSchema)204 int SchemaMgr::GetCloudTableSchema(const TableName &tableName, TableSchema &retSchema)
205 {
206 if (cloudSchema_ == nullptr) {
207 return -E_SCHEMA_MISMATCH;
208 }
209 for (const TableSchema &tableSchema : cloudSchema_->tables) {
210 if (DBCommon::CaseInsensitiveCompare(tableSchema.name, tableName)) {
211 retSchema = tableSchema;
212 return E_OK;
213 }
214 }
215 return -E_NOT_FOUND;
216 }
217
IsSharedTable(const std::string & tableName)218 bool SchemaMgr::IsSharedTable(const std::string &tableName)
219 {
220 if (cloudSchema_ == nullptr) {
221 return false;
222 }
223 if (sharedTableMap_.find(tableName) != sharedTableMap_.end()) {
224 return sharedTableMap_[tableName];
225 }
226 for (const auto &tableSchema : (*cloudSchema_).tables) {
227 if (DBCommon::CaseInsensitiveCompare(tableName, tableSchema.sharedTableName)) {
228 sharedTableMap_[tableName] = true;
229 return true;
230 }
231 }
232 sharedTableMap_[tableName] = false;
233 return false;
234 }
235
GetSharedTableOriginNames()236 std::map<std::string, std::string> SchemaMgr::GetSharedTableOriginNames()
237 {
238 if (cloudSchema_ == nullptr) {
239 LOGW("[SchemaMgr] Not found cloud schema!");
240 return {};
241 }
242 std::map<std::string, std::string> res;
243 for (const auto &item : cloudSchema_->tables) {
244 if (item.sharedTableName.empty() || CloudStorageUtils::IsSharedTable(item)) {
245 continue;
246 }
247 res[item.name] = item.sharedTableName;
248 }
249 return res;
250 }
251 } // namespace DistributedDB