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
BatchInsertWithConflictResolution(const std::string & table,const ValuesBuckets & rows,Resolution resolution)107 std::pair<int, int64_t> TransDB::BatchInsertWithConflictResolution(
108 const std::string &table, const ValuesBuckets &rows, Resolution resolution)
109 {
110 if (rows.RowSize() == 0) {
111 return { E_OK, 0 };
112 }
113
114 auto sqlArgs = SqliteSqlBuilder::GenerateSqls(table, rows, maxArgs_, resolution);
115 if (sqlArgs.size() != 1 || sqlArgs.front().second.size() != 1) {
116 auto [fields, values] = rows.GetFieldsAndValues();
117 LOG_ERROR("invalid args, table=%{public}s, rows:%{public}zu, fields:%{public}zu, max:%{public}d.",
118 table.c_str(), rows.RowSize(), fields != nullptr ? fields->size() : 0, maxArgs_);
119 return { E_INVALID_ARGS, -1 };
120 }
121 auto &[sql, bindArgs] = sqlArgs.front();
122 auto [errCode, statement] = GetStatement(sql);
123 if (statement == nullptr) {
124 LOG_ERROR("statement is nullptr, errCode:0x%{public}x, args:%{public}zu, table:%{public}s.", errCode,
125 bindArgs.size(), table.c_str());
126 return { errCode, -1 };
127 }
128 auto args = std::ref(bindArgs.front());
129 errCode = statement->Execute(args);
130 if (errCode != E_OK) {
131 LOG_ERROR("failed,errCode:%{public}d,table:%{public}s,args:%{public}zu,resolution:%{public}d.", errCode,
132 table.c_str(), args.get().size(), static_cast<int32_t>(resolution));
133 return { errCode, errCode == E_SQLITE_CONSTRAINT ? int64_t(statement->Changes()) : -1 };
134 }
135 return { E_OK, int64_t(statement->Changes()) };
136 }
137
Update(const std::string & table,const Row & row,const std::string & where,const Values & args,Resolution resolution)138 std::pair<int, int> TransDB::Update(
139 const std::string &table, const Row &row, const std::string &where, const Values &args, Resolution resolution)
140 {
141 DISTRIBUTED_DATA_HITRACE(std::string(__FUNCTION__));
142 auto clause = SqliteUtils::GetConflictClause(static_cast<int>(resolution));
143 if (table.empty() || row.IsEmpty() || clause == nullptr) {
144 return { E_INVALID_ARGS, 0 };
145 }
146
147 std::string sql("UPDATE");
148 sql.append(clause).append(" ").append(table).append(" SET ");
149 std::vector<ValueObject> totalArgs;
150 totalArgs.reserve(row.values_.size() + args.size());
151 const char *split = "";
152 for (auto &[key, val] : row.values_) {
153 sql.append(split);
154 if (val.GetType() == ValueObject::TYPE_ASSETS) {
155 sql.append(key).append("=merge_assets(").append(key).append(", ?)");
156 } else if (val.GetType() == ValueObject::TYPE_ASSET) {
157 sql.append(key).append("=merge_asset(").append(key).append(", ?)");
158 } else {
159 sql.append(key).append("=?");
160 }
161 totalArgs.push_back(val);
162 split = ",";
163 }
164
165 if (!where.empty()) {
166 sql.append(" WHERE ").append(where);
167 }
168
169 totalArgs.insert(totalArgs.end(), args.begin(), args.end());
170 auto [errCode, statement] = GetStatement(sql);
171 if (statement == nullptr) {
172 return { errCode, 0 };
173 }
174
175 errCode = statement->Execute(totalArgs);
176 if (errCode != E_OK) {
177 return { errCode, 0 };
178 }
179 return { errCode, int32_t(statement->Changes()) };
180 }
181
Delete(int & deletedRows,const std::string & table,const std::string & whereClause,const Values & args)182 int TransDB::Delete(int &deletedRows, const std::string &table, const std::string &whereClause, const Values &args)
183 {
184 DISTRIBUTED_DATA_HITRACE(std::string(__FUNCTION__));
185 if (table.empty()) {
186 return E_INVALID_ARGS;
187 }
188
189 std::string sql;
190 sql.append("DELETE FROM ").append(table);
191 if (!whereClause.empty()) {
192 sql.append(" WHERE ").append(whereClause);
193 }
194 auto [errCode, statement] = GetStatement(sql);
195 if (statement == nullptr) {
196 return errCode;
197 }
198 errCode = statement->Execute(args);
199 if (errCode != E_OK) {
200 return errCode;
201 }
202 deletedRows = statement->Changes();
203 return E_OK;
204 }
205
QuerySql(const std::string & sql,const Values & args)206 std::shared_ptr<AbsSharedResultSet> TransDB::QuerySql(const std::string &sql, const Values &args)
207 {
208 #if !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM) && !defined(ANDROID_PLATFORM) && !defined(IOS_PLATFORM)
209 auto start = std::chrono::steady_clock::now();
210 return std::make_shared<SqliteSharedResultSet>(start, conn_.lock(), sql, args, name_);
211 #else
212 (void)sql;
213 (void)args;
214 return nullptr;
215 #endif
216 }
217
QueryByStep(const std::string & sql,const Values & args,bool preCount)218 std::shared_ptr<ResultSet> TransDB::QueryByStep(const std::string &sql, const Values &args, bool preCount)
219 {
220 auto start = std::chrono::steady_clock::now();
221 return std::make_shared<StepResultSet>(start, conn_.lock(), sql, args, true, true);
222 }
223
Execute(const std::string & sql,const Values & args,int64_t trxId)224 std::pair<int32_t, ValueObject> TransDB::Execute(const std::string &sql, const Values &args, int64_t trxId)
225 {
226 (void)trxId;
227 ValueObject object;
228 int sqlType = SqliteUtils::GetSqlStatementType(sql);
229 if (!SqliteUtils::IsSupportSqlForExecute(sqlType) && !SqliteUtils::IsSpecial(sqlType)) {
230 LOG_ERROR("Not support the sql:app self can check the SQL");
231 return { E_INVALID_ARGS, object };
232 }
233
234 auto [errCode, statement] = GetStatement(sql);
235 if (errCode != E_OK) {
236 return { errCode, object };
237 }
238
239 errCode = statement->Execute(args);
240 if (errCode != E_OK) {
241 LOG_ERROR("failed,app self can check the SQL, error:0x%{public}x.", errCode);
242 return { errCode, object };
243 }
244
245 if (sqlType == SqliteUtils::STATEMENT_INSERT) {
246 int64_t outValue = statement->Changes() > 0 ? statement->LastInsertRowId() : -1;
247 return { errCode, ValueObject(outValue) };
248 }
249
250 if (sqlType == SqliteUtils::STATEMENT_UPDATE) {
251 int outValue = statement->Changes();
252 return { errCode, ValueObject(outValue) };
253 }
254
255 if (sqlType == SqliteUtils::STATEMENT_PRAGMA) {
256 if (statement->GetColumnCount() == 1) {
257 return statement->GetColumn(0);
258 }
259 }
260
261 if (sqlType == SqliteUtils::STATEMENT_DDL) {
262 statement->Reset();
263 statement->Prepare("PRAGMA schema_version");
264 auto [err, version] = statement->ExecuteForValue();
265 if (vSchema_ < static_cast<int64_t>(version)) {
266 LOG_INFO("db:%{public}s exe DDL schema<%{public}" PRIi64 "->%{public}" PRIi64
267 "> app self can check the SQL.",
268 SqliteUtils::Anonymous(name_).c_str(), vSchema_, static_cast<int64_t>(version));
269 vSchema_ = version;
270 }
271 }
272 return { errCode, object };
273 }
274
GetVersion(int & version)275 int TransDB::GetVersion(int &version)
276 {
277 return E_NOT_SUPPORT;
278 }
279
SetVersion(int version)280 int TransDB::SetVersion(int version)
281 {
282 return E_NOT_SUPPORT;
283 }
284
Sync(const SyncOption & option,const std::vector<std::string> & tables,const AsyncDetail & async)285 int TransDB::Sync(const SyncOption &option, const std::vector<std::string> &tables, const AsyncDetail &async)
286 {
287 if (option.mode != TIME_FIRST || tables.empty()) {
288 return E_INVALID_ARGS;
289 }
290 return RdbStore::Sync(option, tables, async);
291 }
292
GetStatement(const std::string & sql) const293 std::pair<int32_t, std::shared_ptr<Statement>> TransDB::GetStatement(const std::string &sql) const
294 {
295 auto connection = conn_.lock();
296 if (connection == nullptr) {
297 return { E_ALREADY_CLOSED, nullptr };
298 }
299 return connection->CreateStatement(sql, connection);
300 }
301 } // namespace OHOS::NativeRdb