1 /*
2  * Copyright (c) 2024 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 #define LOG_TAG "TransDB"
16 #include "trans_db.h"
17 
18 #include "logger.h"
19 #include "rdb_sql_statistic.h"
20 #include "rdb_trace.h"
21 #include "sqlite_sql_builder.h"
22 #include "sqlite_utils.h"
23 #include "step_result_set.h"
24 #if !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM) && !defined(ANDROID_PLATFORM) && !defined(IOS_PLATFORM)
25 #include "sqlite_shared_result_set.h"
26 #endif
27 namespace OHOS::NativeRdb {
28 using namespace OHOS::Rdb;
29 using namespace DistributedRdb;
TransDB(std::shared_ptr<Connection> conn,const std::string & name)30 TransDB::TransDB(std::shared_ptr<Connection> conn, const std::string &name) : conn_(conn), name_(name)
31 {
32     maxArgs_ = conn->GetMaxVariable();
33 }
34 
Insert(const std::string & table,const Row & row,Resolution resolution)35 std::pair<int, int64_t> TransDB::Insert(const std::string &table, const Row &row, Resolution resolution)
36 {
37     DISTRIBUTED_DATA_HITRACE(std::string(__FUNCTION__));
38     auto conflictClause = SqliteUtils::GetConflictClause(static_cast<int>(resolution));
39     if (table.empty() || row.IsEmpty() || conflictClause == nullptr) {
40         return { E_INVALID_ARGS, -1 };
41     }
42 
43     std::string sql("INSERT");
44     sql.append(conflictClause).append(" INTO ").append(table).append("(");
45     std::vector<ValueObject> args;
46     args.reserve(row.values_.size());
47     const char *split = "";
48     for (const auto &[key, val] : row.values_) {
49         sql.append(split).append(key);
50         if (val.GetType() == ValueObject::TYPE_ASSETS && resolution == ConflictResolution::ON_CONFLICT_REPLACE) {
51             return { E_INVALID_ARGS, -1 };
52         }
53         SqliteSqlBuilder::UpdateAssetStatus(val, AssetValue::STATUS_INSERT);
54         args.push_back(val); // columnValue
55         split = ",";
56     }
57 
58     sql.append(") VALUES (");
59     if (!args.empty()) {
60         sql.append(SqliteSqlBuilder::GetSqlArgs(args.size()));
61     }
62 
63     sql.append(")");
64     int64_t rowid = -1;
65     auto [errCode, statement] = GetStatement(sql);
66     if (statement == nullptr) {
67         return { errCode, rowid };
68     }
69     errCode = statement->Execute(args);
70     if (errCode != E_OK) {
71         return { errCode, rowid };
72     }
73     rowid = statement->Changes() > 0 ? statement->LastInsertRowId() : -1;
74     return { errCode, rowid };
75 }
76 
BatchInsert(const std::string & table,const RefRows & rows)77 std::pair<int, int64_t> TransDB::BatchInsert(const std::string &table, const RefRows &rows)
78 {
79     if (rows.RowSize() == 0) {
80         return { E_OK, 0 };
81     }
82 
83     auto batchInfo = SqliteSqlBuilder::GenerateSqls(table, rows, maxArgs_);
84     if (table.empty() || batchInfo.empty()) {
85         LOG_ERROR("empty,table=%{public}s,rows:%{public}zu,max:%{public}d.", table.c_str(), rows.RowSize(), maxArgs_);
86         return { E_INVALID_ARGS, -1 };
87     }
88 
89     for (const auto &[sql, batchArgs] : batchInfo) {
90         auto [errCode, statement] = GetStatement(sql);
91         if (statement == nullptr) {
92             return { errCode, -1 };
93         }
94         for (const auto &args : batchArgs) {
95             errCode = statement->Execute(args);
96             if (errCode == E_OK) {
97                 continue;
98             }
99             LOG_ERROR("failed(0x%{public}x) db:%{public}s table:%{public}s args:%{public}zu", errCode,
100                 SqliteUtils::Anonymous(name_).c_str(), table.c_str(), args.size());
101             return { errCode, -1 };
102         }
103     }
104     return { E_OK, int64_t(rows.RowSize()) };
105 }
106 
Update(const std::string & table,const Row & row,const std::string & where,const Values & args,Resolution resolution)107 std::pair<int, int> TransDB::Update(const std::string &table, const Row &row, const std::string &where,
108     const Values &args, Resolution resolution)
109 {
110     DISTRIBUTED_DATA_HITRACE(std::string(__FUNCTION__));
111     auto clause = SqliteUtils::GetConflictClause(static_cast<int>(resolution));
112     if (table.empty() || row.IsEmpty() || clause == nullptr) {
113         return { E_INVALID_ARGS, 0 };
114     }
115 
116     std::string sql("UPDATE");
117     sql.append(clause).append(" ").append(table).append(" SET ");
118     std::vector<ValueObject> totalArgs;
119     totalArgs.reserve(row.values_.size() + args.size());
120     const char *split = "";
121     for (auto &[key, val] : row.values_) {
122         sql.append(split);
123         if (val.GetType() == ValueObject::TYPE_ASSETS) {
124             sql.append(key).append("=merge_assets(").append(key).append(", ?)");
125         } else if (val.GetType() == ValueObject::TYPE_ASSET) {
126             sql.append(key).append("=merge_asset(").append(key).append(", ?)");
127         } else {
128             sql.append(key).append("=?");
129         }
130         totalArgs.push_back(val);
131         split = ",";
132     }
133 
134     if (!where.empty()) {
135         sql.append(" WHERE ").append(where);
136     }
137 
138     totalArgs.insert(totalArgs.end(), args.begin(), args.end());
139     auto [errCode, statement] = GetStatement(sql);
140     if (statement == nullptr) {
141         return { errCode, 0 };
142     }
143 
144     errCode = statement->Execute(totalArgs);
145     if (errCode != E_OK) {
146         return { errCode, 0 };
147     }
148     return { errCode, int32_t(statement->Changes()) };
149 }
150 
Delete(int & deletedRows,const std::string & table,const std::string & whereClause,const Values & args)151 int TransDB::Delete(int &deletedRows, const std::string &table, const std::string &whereClause, const Values &args)
152 {
153     DISTRIBUTED_DATA_HITRACE(std::string(__FUNCTION__));
154     if (table.empty()) {
155         return E_INVALID_ARGS;
156     }
157 
158     std::string sql;
159     sql.append("DELETE FROM ").append(table);
160     if (!whereClause.empty()) {
161         sql.append(" WHERE ").append(whereClause);
162     }
163     auto [errCode, statement] = GetStatement(sql);
164     if (statement == nullptr) {
165         return errCode;
166     }
167     errCode = statement->Execute(args);
168     if (errCode != E_OK) {
169         return errCode;
170     }
171     deletedRows = statement->Changes();
172     return E_OK;
173 }
174 
QuerySql(const std::string & sql,const Values & args)175 std::shared_ptr<AbsSharedResultSet> TransDB::QuerySql(const std::string &sql, const Values &args)
176 {
177 #if !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM) && !defined(ANDROID_PLATFORM) && !defined(IOS_PLATFORM)
178     auto start = std::chrono::steady_clock::now();
179     return std::make_shared<SqliteSharedResultSet>(start, conn_.lock(), sql, args, name_);
180 #else
181     (void)sql;
182     (void)args;
183     return nullptr;
184 #endif
185 }
186 
QueryByStep(const std::string & sql,const Values & args,bool preCount)187 std::shared_ptr<ResultSet> TransDB::QueryByStep(const std::string &sql, const Values &args, bool preCount)
188 {
189     auto start = std::chrono::steady_clock::now();
190     return std::make_shared<StepResultSet>(start, conn_.lock(), sql, args, true, true);
191 }
192 
Execute(const std::string & sql,const Values & args,int64_t trxId)193 std::pair<int32_t, ValueObject> TransDB::Execute(const std::string &sql, const Values &args, int64_t trxId)
194 {
195     (void)trxId;
196     ValueObject object;
197     int sqlType = SqliteUtils::GetSqlStatementType(sql);
198     if (!SqliteUtils::IsSupportSqlForExecute(sqlType) && !SqliteUtils::IsSpecial(sqlType)) {
199         LOG_ERROR("Not support the sql:%{public}s", sql.c_str());
200         return { E_INVALID_ARGS, object };
201     }
202 
203     auto [errCode, statement] = GetStatement(sql);
204     if (errCode != E_OK) {
205         return { errCode, object };
206     }
207 
208     errCode = statement->Execute(args);
209     if (errCode != E_OK) {
210         LOG_ERROR("failed,sql:%{public}s, error:0x%{public}x.", sql.c_str(), errCode);
211         return { errCode, object };
212     }
213 
214     if (sqlType == SqliteUtils::STATEMENT_INSERT) {
215         int64_t outValue = statement->Changes() > 0 ? statement->LastInsertRowId() : -1;
216         return { errCode, ValueObject(outValue) };
217     }
218 
219     if (sqlType == SqliteUtils::STATEMENT_UPDATE) {
220         int outValue = statement->Changes();
221         return { errCode, ValueObject(outValue) };
222     }
223 
224     if (sqlType == SqliteUtils::STATEMENT_PRAGMA) {
225         if (statement->GetColumnCount() == 1) {
226             return statement->GetColumn(0);
227         }
228     }
229 
230     if (sqlType == SqliteUtils::STATEMENT_DDL) {
231         statement->Reset();
232         statement->Prepare("PRAGMA schema_version");
233         auto [err, version] = statement->ExecuteForValue();
234         if (vSchema_ < static_cast<int64_t>(version)) {
235             LOG_INFO("db:%{public}s exe DDL schema<%{public}" PRIi64 "->%{public}" PRIi64 "> sql:%{public}s.",
236                 SqliteUtils::Anonymous(name_).c_str(), vSchema_, static_cast<int64_t>(version), sql.c_str());
237             vSchema_ = version;
238         }
239     }
240     return { errCode, object };
241 }
242 
GetVersion(int & version)243 int TransDB::GetVersion(int &version)
244 {
245     return E_NOT_SUPPORT;
246 }
247 
SetVersion(int version)248 int TransDB::SetVersion(int version)
249 {
250     return E_NOT_SUPPORT;
251 }
252 
Sync(const SyncOption & option,const std::vector<std::string> & tables,const AsyncDetail & async)253 int TransDB::Sync(const SyncOption &option, const std::vector<std::string> &tables, const AsyncDetail &async)
254 {
255     if (option.mode != TIME_FIRST || tables.empty()) {
256         return E_INVALID_ARGS;
257     }
258     return RdbStore::Sync(option, tables, async);
259 }
260 
GetStatement(const std::string & sql) const261 std::pair<int32_t, std::shared_ptr<Statement>> TransDB::GetStatement(const std::string &sql) const
262 {
263     auto connection = conn_.lock();
264     if (connection == nullptr) {
265         return { E_ALREADY_CLOSED, nullptr };
266     }
267     return connection->CreateStatement(sql, connection);
268 }
269 } // namespace OHOS::NativeRdb