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