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 #ifdef RELATIONAL_STORE
16 #include "cloud/cloud_storage_utils.h"
17 #include "db_common.h"
18 #include "tracker_table.h"
19 #include "schema_constant.h"
20 
21 namespace DistributedDB {
Init(const TrackerSchema & schema)22 void TrackerTable::Init(const TrackerSchema &schema)
23 {
24     tableName_ = schema.tableName;
25     extendColName_ = schema.extendColName;
26     trackerColNames_ = schema.trackerColNames;
27 }
28 
GetTableName() const29 std::string TrackerTable::GetTableName() const
30 {
31     return tableName_;
32 }
33 
GetTrackerColNames() const34 const std::set<std::string> &TrackerTable::GetTrackerColNames() const
35 {
36     return trackerColNames_;
37 }
38 
GetAssignValSql(bool isDelete) const39 const std::string TrackerTable::GetAssignValSql(bool isDelete) const
40 {
41     if (extendColName_.empty()) {
42         return "''";
43     }
44     return isDelete ? ("OLD." + extendColName_) : ("NEW." + extendColName_);
45 }
46 
GetExtendAssignValSql(bool isDelete) const47 const std::string TrackerTable::GetExtendAssignValSql(bool isDelete) const
48 {
49     if (extendColName_.empty()) {
50         return "";
51     }
52     return isDelete ? (", extend_field = OLD." + extendColName_) : (", extend_field = NEW." + extendColName_);
53 }
54 
GetDiffTrackerValSql() const55 const std::string TrackerTable::GetDiffTrackerValSql() const
56 {
57     if (trackerColNames_.empty()) {
58         return "0";
59     }
60     std::string sql = " CASE WHEN (";
61     size_t index = 0;
62     for (const auto &colName: trackerColNames_) {
63         sql += "(NEW." + colName + " IS NOT OLD." + colName + ")";
64         if (index < trackerColNames_.size() - 1) {
65             sql += " OR ";
66         }
67         index++;
68     }
69     sql += ") THEN 1 ELSE 0 END";
70     return sql;
71 }
72 
GetDiffIncCursorSql(const std::string & tableName) const73 const std::string TrackerTable::GetDiffIncCursorSql(const std::string &tableName) const
74 {
75     if (trackerColNames_.empty()) {
76         return "";
77     }
78     std::string sql = ", cursor = CASE WHEN (";
79     size_t index = 0;
80     for (const auto &colName: trackerColNames_) {
81         sql += "(NEW." + colName + " IS NOT OLD." + colName + ")";
82         if (index < trackerColNames_.size() - 1) {
83             sql += " OR ";
84         }
85         index++;
86     }
87     sql += ") THEN " + CloudStorageUtils::GetSelectIncCursorSql(tableName);
88     sql += " ELSE cursor END ";
89     return sql;
90 }
91 
GetExtendName() const92 const std::string TrackerTable::GetExtendName() const
93 {
94     return extendColName_;
95 }
96 
ToString() const97 std::string TrackerTable::ToString() const
98 {
99     std::string attrStr;
100     attrStr += "{";
101     attrStr += R"("NAME": ")" + tableName_ + "\",";
102     attrStr += R"("EXTEND_NAME": ")" + extendColName_ + "\",";
103     attrStr += R"("TRACKER_NAMES": [)";
104     for (const auto &colName: trackerColNames_) {
105         attrStr += "\"" + colName + "\",";
106     }
107     attrStr.pop_back();
108     attrStr += "]}";
109     return attrStr;
110 }
111 
GetDropTempTriggerSql() const112 const std::vector<std::string> TrackerTable::GetDropTempTriggerSql() const
113 {
114     if (IsEmpty()) {
115         return {};
116     }
117     std::vector<std::string> dropSql;
118     dropSql.push_back(GetDropTempTriggerSql(TriggerMode::TriggerModeEnum::INSERT));
119     dropSql.push_back(GetDropTempTriggerSql(TriggerMode::TriggerModeEnum::UPDATE));
120     dropSql.push_back(GetDropTempTriggerSql(TriggerMode::TriggerModeEnum::DELETE));
121     return dropSql;
122 }
123 
GetDropTempTriggerSql(TriggerMode::TriggerModeEnum mode) const124 const std::string TrackerTable::GetDropTempTriggerSql(TriggerMode::TriggerModeEnum mode) const
125 {
126     return "DROP TRIGGER IF EXISTS " + GetTempTriggerName(mode);
127 }
128 
GetCreateTempTriggerSql(TriggerMode::TriggerModeEnum mode) const129 const std::string TrackerTable::GetCreateTempTriggerSql(TriggerMode::TriggerModeEnum mode) const
130 {
131     switch (mode) {
132         case TriggerMode::TriggerModeEnum::INSERT:
133             return GetTempInsertTriggerSql();
134         case TriggerMode::TriggerModeEnum::UPDATE:
135             return GetTempUpdateTriggerSql();
136         case TriggerMode::TriggerModeEnum::DELETE:
137             return GetTempDeleteTriggerSql();
138         default:
139             return {};
140     }
141 }
142 
GetTempTriggerName(TriggerMode::TriggerModeEnum mode) const143 const std::string TrackerTable::GetTempTriggerName(TriggerMode::TriggerModeEnum mode) const
144 {
145     return DBConstant::RELATIONAL_PREFIX + tableName_ + "_ON_" + TriggerMode::GetTriggerModeString(mode) + "_TEMP";
146 }
147 
GetTempInsertTriggerSql(bool incFlag) const148 const std::string TrackerTable::GetTempInsertTriggerSql(bool incFlag) const
149 {
150     // This trigger is built on the log table
151     std::string sql = "CREATE TEMP TRIGGER IF NOT EXISTS " + DBConstant::RELATIONAL_PREFIX + tableName_;
152     sql += "_ON_INSERT_TEMP AFTER INSERT ON " + DBConstant::RELATIONAL_PREFIX + tableName_ + "_log";
153     sql += " WHEN (SELECT 1 FROM " + DBConstant::RELATIONAL_PREFIX + "metadata" +
154         " WHERE key = 'log_trigger_switch' AND value = 'false')\n";
155     sql += "BEGIN\n";
156     if (incFlag) {
157         sql += CloudStorageUtils::GetCursorIncSqlWhenAllow(tableName_) + "\n";
158     } else {
159         sql += CloudStorageUtils::GetCursorIncSql(tableName_) + "\n";
160     }
161     sql += "UPDATE " + DBConstant::RELATIONAL_PREFIX + tableName_ + "_log" + " SET ";
162     sql += "cursor=" + CloudStorageUtils::GetSelectIncCursorSql(tableName_) + " WHERE";
163     sql += " hash_key = NEW.hash_key;\n";
164     if (!IsEmpty()) {
165         sql += "SELECT server_observer('" + tableName_ + "', 1);";
166     }
167     sql += "\nEND;";
168     return sql;
169 }
170 
GetTempUpdateTriggerSql(bool incFlag) const171 const std::string TrackerTable::GetTempUpdateTriggerSql(bool incFlag) const
172 {
173     std::string sql = "CREATE TEMP TRIGGER IF NOT EXISTS " + DBConstant::RELATIONAL_PREFIX + tableName_;
174     sql += "_ON_UPDATE_TEMP AFTER UPDATE ON " + tableName_;
175     sql += " WHEN (SELECT 1 FROM " + DBConstant::RELATIONAL_PREFIX + "metadata" +
176         " WHERE key = 'log_trigger_switch' AND value = 'false')\n";
177     sql += "BEGIN\n";
178     if (incFlag) {
179         sql += CloudStorageUtils::GetCursorIncSqlWhenAllow(tableName_) + "\n";
180     } else {
181         sql += CloudStorageUtils::GetCursorIncSql(tableName_) + "\n";
182     }
183     sql += "UPDATE " + DBConstant::RELATIONAL_PREFIX + tableName_ + "_log" + " SET ";
184     if (!IsEmpty()) {
185         sql += "extend_field=" + GetAssignValSql() + ",";
186     }
187     sql += "cursor=" + CloudStorageUtils::GetSelectIncCursorSql(tableName_) + " WHERE";
188     sql += " data_key = OLD." + std::string(DBConstant::SQLITE_INNER_ROWID) + ";\n";
189     if (!IsEmpty()) {
190         sql += "SELECT server_observer('" + tableName_ + "', " + GetDiffTrackerValSql() + ");";
191     }
192     sql += "\nEND;";
193     return sql;
194 }
195 
GetTempDeleteTriggerSql(bool incFlag) const196 const std::string TrackerTable::GetTempDeleteTriggerSql(bool incFlag) const
197 {
198     std::string sql = "CREATE TEMP TRIGGER IF NOT EXISTS " + DBConstant::RELATIONAL_PREFIX + tableName_;
199     sql += "_ON_DELETE_TEMP AFTER DELETE ON " + tableName_ +
200         " WHEN (SELECT 1 FROM " + DBConstant::RELATIONAL_PREFIX + "metadata" +
201         " WHERE key = 'log_trigger_switch' AND value = 'false')\n";
202     sql += "BEGIN\n";
203     if (IsEmpty() && incFlag) {
204         sql += " SELECT 1;\n";
205         sql += "\nEND;";
206         return sql;
207     }
208     if (!incFlag) {
209         sql += CloudStorageUtils::GetCursorIncSql(tableName_) + "\n";
210     }
211     sql += "UPDATE " + DBConstant::RELATIONAL_PREFIX + tableName_ + "_log" + " SET ";
212     if (!IsEmpty()) {
213         sql += "extend_field=" + GetAssignValSql(true) + ",";
214     }
215     if (!incFlag) {
216         sql += "cursor=" + CloudStorageUtils::GetSelectIncCursorSql(tableName_);
217     }
218     if (!IsEmpty() && incFlag) {
219         sql.pop_back();
220     }
221     sql += " WHERE data_key = OLD." + std::string(DBConstant::SQLITE_INNER_ROWID) + ";\n";
222     if (!IsEmpty()) {
223         sql += "SELECT server_observer('" + tableName_ + "', 1);";
224     }
225     sql += "\nEND;";
226     return sql;
227 }
228 
SetTableName(const std::string & tableName)229 void TrackerTable::SetTableName(const std::string &tableName)
230 {
231     tableName_ = tableName;
232 }
233 
SetExtendName(const std::string & colName)234 void TrackerTable::SetExtendName(const std::string &colName)
235 {
236     extendColName_ = colName;
237 }
238 
SetTrackerNames(const std::set<std::string> & trackerNames)239 void TrackerTable::SetTrackerNames(const std::set<std::string> &trackerNames)
240 {
241     trackerColNames_ = std::move(trackerNames);
242 }
243 
IsEmpty() const244 bool TrackerTable::IsEmpty() const
245 {
246     return trackerColNames_.empty();
247 }
248 
IsTableNameEmpty() const249 bool TrackerTable::IsTableNameEmpty() const
250 {
251     return tableName_.empty();
252 }
253 
IsChanging(const TrackerSchema & schema)254 bool TrackerTable::IsChanging(const TrackerSchema &schema)
255 {
256     if (tableName_ != schema.tableName || extendColName_ != schema.extendColName) {
257         return true;
258     }
259     if (trackerColNames_.size() != schema.trackerColNames.size()) {
260         return true;
261     }
262     for (const auto &col: trackerColNames_) {
263         if (schema.trackerColNames.find(col) == schema.trackerColNames.end()) {
264             return true;
265         }
266     }
267     return false;
268 }
269 
ReBuildTempTrigger(sqlite3 * db,TriggerMode::TriggerModeEnum mode,const AfterBuildAction & action)270 int TrackerTable::ReBuildTempTrigger(sqlite3 *db, TriggerMode::TriggerModeEnum mode, const AfterBuildAction &action)
271 {
272     sqlite3_stmt *stmt = nullptr;
273     int errCode = SQLiteUtils::GetStatement(db, "SELECT 1 FROM sqlite_temp_master where name='" +
274         GetTempTriggerName(mode) + "' and type='trigger' COLLATE NOCASE;", stmt);
275     if (errCode != E_OK) {
276         LOGE("Failed to select temp trigger mode:%d err:%d", mode, errCode);
277         return errCode;
278     }
279     errCode = SQLiteUtils::StepWithRetry(stmt);
280     bool isExists = (errCode == SQLiteUtils::MapSQLiteErrno(SQLITE_ROW)) ? true : false;
281     int ret = E_OK;
282     SQLiteUtils::ResetStatement(stmt, true, ret);
283     if (errCode != SQLiteUtils::MapSQLiteErrno(SQLITE_ROW) && errCode != SQLiteUtils::MapSQLiteErrno(SQLITE_DONE)) {
284         LOGE("Select temp trigger step mode:%d err:%d", mode, errCode);
285         return errCode;
286     }
287     errCode = SQLiteUtils::ExecuteRawSQL(db, GetDropTempTriggerSql(mode));
288     if (errCode != E_OK) {
289         LOGE("Failed to drop temp trigger mode:%d err:%d", mode, errCode);
290         return errCode;
291     }
292     errCode = SQLiteUtils::ExecuteRawSQL(db, GetCreateTempTriggerSql(mode));
293     if (errCode != E_OK) {
294         LOGE("Failed to create temp trigger mode:%d err:%d", mode, errCode);
295         return errCode;
296     }
297     if (action != nullptr) {
298         errCode = action();
299         if (errCode != E_OK) {
300             return errCode;
301         }
302     }
303     if (!isExists) {
304         errCode = SQLiteUtils::ExecuteRawSQL(db, GetDropTempTriggerSql(mode));
305         if (errCode != E_OK) {
306             LOGE("Failed to clear temp trigger mode:%d err:%d", mode, errCode);
307         }
308     }
309     return errCode;
310 }
311 }
312 #endif