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