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 #define LOG_TAG "StepResultSet"
16 #include "step_result_set.h"
17
18 #include <unistd.h>
19
20 #include "logger.h"
21 #include "rdb_errno.h"
22 #include "sqlite3sym.h"
23 #include "connection_pool.h"
24 #include "sqlite_errno.h"
25 #include "sqlite_statement.h"
26 #include "sqlite_utils.h"
27 #include "value_object.h"
28
29 namespace OHOS {
30 namespace NativeRdb {
31 using namespace OHOS::Rdb;
32
33 constexpr int64_t TIME_OUT = 1500;
StepResultSet(Time start,Conn conn,const std::string & sql,const Values & args,bool safe)34 StepResultSet::StepResultSet(Time start, Conn conn, const std::string &sql, const Values &args, bool safe)
35 : AbsResultSet(safe), conn_(std::move(conn)), sql_(sql), args_(args)
36 {
37 if (conn_ == nullptr) {
38 isClosed_ = true;
39 return;
40 }
41
42 auto prepareStart = std::chrono::steady_clock::now();
43 auto errCode = PrepareStep();
44 if (errCode != E_OK) {
45 LOG_ERROR("step resultset ret %{public}d", errCode);
46 return;
47 }
48 auto prepareEnd = std::chrono::steady_clock::now();
49 auto statement = GetStatement();
50 if (statement == nullptr) {
51 return;
52 }
53 std::tie(lastErr_, rowCount_) = statement->Count();
54 if (lastErr_ == E_NOT_SUPPORT && rowCount_ == Statement::INVALID_COUNT) {
55 isSupportCountRow_ = false;
56 lastErr_ = E_OK;
57 }
58 auto queryEnd = std::chrono::steady_clock::now();
59 int64_t totalCost = std::chrono::duration_cast<std::chrono::milliseconds>(queryEnd - start).count();
60 if (totalCost >= TIME_OUT) {
61 int64_t acquireCost = std::chrono::duration_cast<std::chrono::milliseconds>(prepareStart - start).count();
62 int64_t prepareCost = std::chrono::duration_cast<std::chrono::milliseconds>(prepareEnd - prepareStart).count();
63 int64_t countCost = std::chrono::duration_cast<std::chrono::milliseconds>(queryEnd - prepareEnd).count();
64 LOG_WARN("total[%{public}" PRId64 "]<%{public}" PRId64 ",%{public}" PRId64 ",%{public}" PRId64
65 "> count[%{public}d] sql[%{public}s]",
66 totalCost, acquireCost, prepareCost, countCost, rowCount_, SqliteUtils::Anonymous(sql_).c_str());
67 }
68 }
69
~StepResultSet()70 StepResultSet::~StepResultSet()
71 {
72 Close();
73 }
74
InitRowCount()75 int StepResultSet::InitRowCount()
76 {
77 auto statement = GetStatement();
78 if (statement == nullptr) {
79 return NO_COUNT;
80 }
81 int32_t count = NO_COUNT;
82 int32_t status = E_OK;
83 int32_t retry = 0;
84 do {
85 status = statement->Step();
86 if (status == E_SQLITE_BUSY || status == E_SQLITE_LOCKED) {
87 retry++;
88 usleep(STEP_QUERY_RETRY_INTERVAL);
89 continue;
90 }
91 count++;
92 } while (status == E_OK ||
93 ((status == E_SQLITE_BUSY || status == E_SQLITE_LOCKED) && retry < STEP_QUERY_RETRY_MAX_TIMES));
94 if (status != E_NO_MORE_ROWS) {
95 lastErr_ = status;
96 count = NO_COUNT;
97 }
98 statement->Reset();
99 return count;
100 }
101 /**
102 * Obtain session and prepare precompile statement for step query
103 */
PrepareStep()104 int StepResultSet::PrepareStep()
105 {
106 std::lock_guard<decltype(globalMtx_)> lockGuard(globalMtx_);
107 if (statement_ != nullptr) {
108 return E_OK;
109 }
110
111 if (isClosed_ || conn_ == nullptr) {
112 lastErr_ = E_ALREADY_CLOSED;
113 return lastErr_;
114 }
115
116 auto type = SqliteUtils::GetSqlStatementType(sql_);
117 if (type == SqliteUtils::STATEMENT_ERROR) {
118 LOG_ERROR("invalid sql_ %{public}s!", SqliteUtils::Anonymous(sql_).c_str());
119 lastErr_ = E_INVALID_ARGS;
120 return lastErr_;
121 }
122
123 auto [errCode, statement] = conn_->CreateStatement(sql_, conn_);
124 if (statement == nullptr || errCode != E_OK) {
125 lastErr_ = errCode;
126 return E_STATEMENT_NOT_PREPARED;
127 }
128
129 if (!statement->ReadOnly()) {
130 LOG_ERROR("failed, %{public}s is not query sql!", SqliteUtils::Anonymous(sql_).c_str());
131 lastErr_ = E_NOT_SELECT;
132 return lastErr_;
133 }
134
135 errCode = statement->Bind(args_);
136 if (errCode != E_OK) {
137 LOG_ERROR("Bind arg faild! Ret is %{public}d", errCode);
138 statement->Reset();
139 statement = nullptr;
140 lastErr_ = errCode;
141 return lastErr_;
142 }
143
144 statement_ = std::move(statement);
145 return E_OK;
146 }
147
GetColumnNames()148 std::pair<int, std::vector<std::string>> StepResultSet::GetColumnNames()
149 {
150 int errCode = PrepareStep();
151 if (errCode != E_OK) {
152 LOG_ERROR("get all column names Step ret %{public}d", errCode);
153 return { errCode, {} };
154 }
155
156 auto statement = GetStatement();
157 if (statement == nullptr) {
158 LOG_ERROR("Statement is nullptr.");
159 return { E_ALREADY_CLOSED, {} };
160 }
161 auto colCount = statement->GetColumnCount();
162 std::vector<std::string> names;
163 for (int i = 0; i < colCount; i++) {
164 auto [code, colName] = statement->GetColumnName(i);
165 if (code) {
166 LOG_ERROR("GetColumnName ret %{public}d", code);
167 return { code, {} };
168 }
169 names.push_back(colName);
170 }
171
172 return { E_OK, std::move(names) };
173 }
174
GetColumnType(int columnIndex,ColumnType & columnType)175 int StepResultSet::GetColumnType(int columnIndex, ColumnType &columnType)
176 {
177 if (isClosed_) {
178 return E_ALREADY_CLOSED;
179 }
180 if (rowPos_ == INIT_POS || ((isSupportCountRow_ || rowCount_ != Statement::INVALID_COUNT) && IsEnded().second)) {
181 LOG_ERROR("query not executed.");
182 return E_ROW_OUT_RANGE;
183 }
184 auto statement = GetStatement();
185 if (statement == nullptr) {
186 LOG_ERROR("Statement is nullptr.");
187 return E_ALREADY_CLOSED;
188 }
189
190 auto [errCode, outPutType] = statement->GetColumnType(columnIndex);
191 if (errCode != E_OK) {
192 LOG_ERROR("GetColumnType ret %{public}d", errCode);
193 return errCode;
194 }
195 columnType = static_cast<ColumnType>(outPutType);
196 return E_OK;
197 }
198
199 /**
200 * Moves the result set to a specified position
201 */
GoToRow(int position)202 int StepResultSet::GoToRow(int position)
203 {
204 if (isClosed_) {
205 return E_ALREADY_CLOSED;
206 }
207
208 if (isSupportCountRow_ && position >= rowCount_) {
209 rowPos_ = (position >= rowCount_ && rowCount_ != 0) ? rowCount_ : rowPos_;
210 LOG_ERROR("position[%{public}d] rowCount[%{public}d] rowPos_[%{public}d]!", position, rowCount_, rowPos_);
211 return E_ROW_OUT_RANGE;
212 }
213
214 if (position < 0) {
215 return E_ROW_OUT_RANGE;
216 }
217
218 if (position < rowPos_) {
219 Reset();
220 return GoToRow(position);
221 }
222 while (position != rowPos_) {
223 int errCode = GoToNextRow();
224 if (errCode != E_OK) {
225 LOG_WARN("GoToNextRow ret %{public}d", errCode);
226 return errCode;
227 }
228 }
229 return E_OK;
230 }
231
232 /**
233 * Move the result set to the next row
234 */
GoToNextRow()235 int StepResultSet::GoToNextRow()
236 {
237 if (isClosed_) {
238 LOG_ERROR("resultSet closed.");
239 return E_ALREADY_CLOSED;
240 }
241
242 int errCode = PrepareStep();
243 if (errCode != E_OK) {
244 return errCode;
245 }
246
247 auto statement = GetStatement();
248 if (statement == nullptr) {
249 LOG_ERROR("Statement is nullptr.");
250 return E_ALREADY_CLOSED;
251 }
252
253 int retryCount = 0;
254 errCode = statement->Step();
255
256 while (errCode == E_SQLITE_LOCKED || errCode == E_SQLITE_BUSY) {
257 // The table is locked, retry
258 if (retryCount > STEP_QUERY_RETRY_MAX_TIMES) {
259 LOG_ERROR("Step in busy ret is %{public}d", errCode);
260 return E_STEP_RESULT_QUERY_EXCEEDED;
261 } else {
262 // Sleep to give the thread holding the lock a chance to finish
263 usleep(STEP_QUERY_RETRY_INTERVAL);
264 errCode = statement->Step();
265 retryCount++;
266 }
267 }
268
269 if (errCode == E_OK) {
270 rowPos_++;
271 return E_OK;
272 } else if (errCode == E_NO_MORE_ROWS) {
273 if (isSupportCountRow_ || rowCount_ != Statement::INVALID_COUNT) {
274 rowPos_ = rowCount_ != 0 ? rowCount_ : rowPos_;
275 } else {
276 ++rowPos_;
277 rowCount_ = rowPos_;
278 }
279 return E_ROW_OUT_RANGE;
280 } else {
281 Reset();
282 rowPos_ = rowCount_;
283 return errCode;
284 }
285 }
286
Close()287 int StepResultSet::Close()
288 {
289 if (isClosed_) {
290 return E_OK;
291 }
292 isClosed_ = true;
293 {
294 std::lock_guard<decltype(globalMtx_)> lockGuard(globalMtx_);
295 conn_ = nullptr;
296 statement_ = nullptr;
297 auto args = std::move(args_);
298 auto sql = std::move(sql_);
299 }
300 Reset();
301 return E_OK;
302 }
303
GetRowCount(int & count)304 int StepResultSet::GetRowCount(int &count)
305 {
306 if (isSupportCountRow_ || rowCount_ != Statement::INVALID_COUNT) {
307 return AbsResultSet::GetRowCount(count);
308 }
309 int ret = E_OK;
310 while (ret == E_OK) {
311 ret = GoToNextRow();
312 if (ret == E_ROW_OUT_RANGE) {
313 rowCount_ = rowPos_;
314 break;
315 }
316 if (ret != E_OK) {
317 LOG_ERROR("Get row cnt err %{public}d, rowCount_ %{public}d, rowPos_ %{public}d", ret, rowCount_, rowPos_);
318 return ret;
319 }
320 };
321 count = rowCount_;
322 Reset();
323 return E_OK;
324 }
325
326 /**
327 * Reset the statement
328 */
Reset()329 int StepResultSet::Reset()
330 {
331 rowPos_ = INIT_POS;
332 auto statement = GetStatement();
333 if (statement != nullptr) {
334 return statement->Reset();
335 }
336 return E_OK;
337 }
338
Get(int32_t col,ValueObject & value)339 int StepResultSet::Get(int32_t col, ValueObject &value)
340 {
341 if (isClosed_) {
342 return E_ALREADY_CLOSED;
343 }
344 return GetValue(col, value);
345 }
346
GetSize(int columnIndex,size_t & size)347 int StepResultSet::GetSize(int columnIndex, size_t &size)
348 {
349 if (isClosed_) {
350 return E_ALREADY_CLOSED;
351 }
352 if (rowPos_ == INIT_POS || ((isSupportCountRow_ || rowCount_ != Statement::INVALID_COUNT) && IsEnded().second)) {
353 size = 0;
354 return E_ROW_OUT_RANGE;
355 }
356
357 auto statement = GetStatement();
358 if (statement == nullptr) {
359 LOG_ERROR("Statement is nullptr.");
360 return E_ALREADY_CLOSED;
361 }
362 auto errCode = E_ERROR;
363 std::tie(errCode, size) = statement->GetSize(columnIndex);
364 return errCode;
365 }
366
367 template<typename T>
GetValue(int32_t col,T & value)368 int StepResultSet::GetValue(int32_t col, T &value)
369 {
370 auto [errCode, object] = GetValueObject(col, ValueObject::TYPE_INDEX<decltype(value)>);
371 if (errCode != E_OK) {
372 LOG_ERROR("ret is %{public}d", errCode);
373 return errCode;
374 }
375 value = static_cast<T>(object);
376 return E_OK;
377 }
378
GetValueObject(int32_t col,size_t index)379 std::pair<int, ValueObject> StepResultSet::GetValueObject(int32_t col, size_t index)
380 {
381 if (rowPos_ == INIT_POS || ((isSupportCountRow_ || rowCount_ != Statement::INVALID_COUNT) && IsEnded().second)) {
382 return { E_ROW_OUT_RANGE, ValueObject() };
383 }
384 auto statement = GetStatement();
385 if (statement == nullptr) {
386 return { E_ALREADY_CLOSED, ValueObject() };
387 }
388 auto [ret, value] = statement->GetColumn(col);
389 if (index < ValueObject::TYPE_MAX && value.value.index() != index) {
390 return { E_INVALID_COLUMN_TYPE, ValueObject() };
391 }
392 return { ret, std::move(value) };
393 }
394
GetStatement()395 std::shared_ptr<Statement> StepResultSet::GetStatement()
396 {
397 std::lock_guard<decltype(globalMtx_)> lockGuard(globalMtx_);
398 if (isClosed_ || conn_ == nullptr) {
399 return nullptr;
400 }
401
402 return statement_;
403 }
404 } // namespace NativeRdb
405 } // namespace OHOS
406