• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "SqliteSharedResultSet"
16 #include "sqlite_shared_result_set.h"
17 
18 #include <rdb_errno.h>
19 
20 #include <cstdint>
21 #include <memory>
22 #include <mutex>
23 #include <tuple>
24 #include <cinttypes>
25 
26 #include "logger.h"
27 #include "rdb_sql_utils.h"
28 #include "rdb_sql_statistic.h"
29 #include "result_set.h"
30 #include "share_block.h"
31 #include "sqlite_connection.h"
32 #include "sqlite_statement.h"
33 #include "sqlite_utils.h"
34 #include "sqlite_errno.h"
35 
36 namespace OHOS {
37 namespace NativeRdb {
38 using namespace OHOS::Rdb;
39 using namespace std::chrono;
40 constexpr int64_t TIME_OUT = 1500;
SqliteSharedResultSet(Time start,Conn conn,std::string sql,const Values & args,const std::string & path)41 SqliteSharedResultSet::SqliteSharedResultSet(Time start, Conn conn, std::string sql, const Values &args,
42     const std::string &path)
43     : AbsSharedResultSet(path), conn_(std::move(conn)), qrySql_(std::move(sql)), bindArgs_(args)
44 {
45     if (conn_ == nullptr) {
46         isClosed_ = true;
47         return;
48     }
49 
50     auto prepareBegin = steady_clock::now();
51     auto [statement, errCode] = PrepareStep();
52     if (errCode != E_OK) {
53         LOG_ERROR("step resultset ret %{public}d", errCode);
54         return;
55     }
56     statement_ = statement;
57     auto countBegin = steady_clock::now();
58     rowCount_ = InitRowCount();
59     auto endTime = steady_clock::now();
60     int64_t totalCost = duration_cast<milliseconds>(endTime - start).count();
61     if (totalCost >= TIME_OUT) {
62         int64_t acquireCost = duration_cast<milliseconds>(prepareBegin - start).count();
63         int64_t prepareCost = duration_cast<milliseconds>(countBegin - prepareBegin).count();
64         int64_t countCost = duration_cast<milliseconds>(endTime - countBegin).count();
65         LOG_WARN("total[%{public}" PRId64 "]<%{public}" PRId64 ",%{public}" PRId64 ",%{public}" PRId64
66                  "> rowCount[%{public}d] sql[%{public}s] path[%{public}s]",
67             totalCost, acquireCost, prepareCost, countCost, rowCount_, qrySql_.c_str(),
68             SqliteUtils::Anonymous(path).c_str());
69     }
70 }
71 
InitRowCount()72 int SqliteSharedResultSet::InitRowCount()
73 {
74     if (statement_ == nullptr) {
75         return NO_COUNT;
76     }
77     int32_t count = NO_COUNT;
78     int32_t status = E_OK;
79     int32_t retry = 0;
80     do {
81         status = statement_->Step();
82         if (status == E_SQLITE_BUSY || status == E_SQLITE_LOCKED) {
83             retry++;
84             usleep(RETRY_INTERVAL);
85             continue;
86         }
87         count++;
88     } while (status == E_OK || ((status == E_SQLITE_BUSY || status == E_SQLITE_LOCKED) && retry < MAX_RETRY_TIMES));
89     if (status != E_NO_MORE_ROWS) {
90         lastErr_ = status;
91         count = NO_COUNT;
92     }
93     statement_->Reset();
94     return count;
95 }
96 
PrepareStep()97 std::pair<std::shared_ptr<Statement>, int> SqliteSharedResultSet::PrepareStep()
98 {
99     if (conn_ == nullptr) {
100         LOG_ERROR("Already close.");
101         lastErr_ = E_ALREADY_CLOSED;
102         return { nullptr, E_ALREADY_CLOSED };
103     }
104 
105     if (statement_ != nullptr) {
106         return { statement_, E_OK };
107     }
108 
109     auto type = SqliteUtils::GetSqlStatementType(qrySql_);
110     if (type == SqliteUtils::STATEMENT_ERROR) {
111         LOG_ERROR("invalid sql_ %{public}s!", qrySql_.c_str());
112         lastErr_ = E_INVALID_ARGS;
113         return { nullptr, E_INVALID_ARGS };
114     }
115 
116     auto [errCode, statement] = conn_->CreateStatement(qrySql_, conn_);
117     if (statement == nullptr) {
118         lastErr_ = errCode;
119         return { statement, errCode };
120     }
121 
122     if (!statement->ReadOnly()) {
123         LOG_ERROR("failed, %{public}s is not query sql!", SqliteUtils::Anonymous(qrySql_).c_str());
124         lastErr_ = E_NOT_SELECT;
125         return { nullptr, E_NOT_SELECT };
126     }
127 
128     errCode = statement->Bind(bindArgs_);
129     if (errCode != E_OK) {
130         LOG_ERROR("Bind arg faild! Ret is %{public}d", errCode);
131         statement->Reset();
132         statement = nullptr;
133         lastErr_ = errCode;
134         return { nullptr, errCode };
135     }
136     return { statement, E_OK };
137 }
138 
~SqliteSharedResultSet()139 SqliteSharedResultSet::~SqliteSharedResultSet() {}
140 
GetColumnNames()141 std::pair<int, std::vector<std::string>> SqliteSharedResultSet::GetColumnNames()
142 {
143     if (isClosed_) {
144         LOG_ERROR("fail, result set has been closed, ret %{public}d, sql %{public}s",
145             E_ALREADY_CLOSED, qrySql_.c_str());
146         return { E_ALREADY_CLOSED, {} };
147     }
148 
149     auto [statement, errCode] = PrepareStep();
150     if (statement == nullptr) {
151         return { errCode, {} };
152     }
153 
154     // Get the total number of columns
155     auto columnCount = statement->GetColumnCount();
156     std::vector<std::string> colNames;
157     for (int i = 0; i < columnCount; i++) {
158         auto [ret, name] = statement->GetColumnName(i);
159         if (ret != E_OK) {
160             return { ret, {} };
161         }
162         colNames.push_back(name);
163     }
164 
165     return { E_OK, std::move(colNames) };
166 }
167 
Close()168 int SqliteSharedResultSet::Close()
169 {
170     AbsSharedResultSet::Close();
171     statement_ = nullptr;
172     conn_ = nullptr;
173     rowCount_ = NO_COUNT;
174     auto qrySql = std::move(qrySql_);
175     auto bindArgs = std::move(bindArgs_);
176     return E_OK;
177 }
178 
OnGo(int oldPosition,int newPosition)179 int SqliteSharedResultSet::OnGo(int oldPosition, int newPosition)
180 {
181     if (isClosed_) {
182         LOG_ERROR("fail, result set has been closed, ret %{public}d, sql %{public}s",
183             E_ALREADY_CLOSED, qrySql_.c_str());
184         return E_ALREADY_CLOSED;
185     }
186     if (GetBlock() == nullptr) {
187         return E_ERROR;
188     }
189     if ((uint32_t)newPosition < GetBlock()->GetStartPos() || (uint32_t)newPosition >= GetBlock()->GetLastPos()
190         || oldPosition == rowCount_) {
191         return FillBlock(newPosition);
192     }
193     return E_OK;
194 }
195 
196 /**
197  * Calculate a proper start position to fill the block.
198  */
PickFillBlockStartPosition(int resultSetPosition,int blockCapacity) const199 int SqliteSharedResultSet::PickFillBlockStartPosition(int resultSetPosition, int blockCapacity) const
200 {
201     return std::max(resultSetPosition - blockCapacity / PICK_POS, 0);
202 }
203 
FillBlock(int requiredPos)204 int SqliteSharedResultSet::FillBlock(int requiredPos)
205 {
206     auto block = GetBlock();
207     if (block == nullptr) {
208         LOG_ERROR("FillSharedBlock GetBlock failed.");
209         return E_ERROR;
210     }
211     ClearBlock();
212     int startPos = isOnlyFillBlock_ ? requiredPos : PickFillBlockStartPosition(requiredPos, blockCapacity_);
213     auto errCode = ExecuteForSharedBlock(block.get(), startPos, requiredPos);
214     if (errCode != E_OK) {
215         return errCode;
216     }
217     blockCapacity_ = block->GetRowNum();
218     if ((block->GetStartPos() == block->GetLastPos() && (uint32_t)rowCount_ != block->GetStartPos())
219         || ((uint32_t)requiredPos < block->GetStartPos() || block->GetLastPos() <= (uint32_t)requiredPos)
220         || block->GetStartPos() > 0) {
221         LOG_WARN("blockRowNum=%{public}d, requiredPos= %{public}d, startPos_= %{public}" PRIu32
222              ", lastPos_= %{public}" PRIu32 ", blockPos_= %{public}" PRIu32 ".",
223             rowCount_, requiredPos, block->GetStartPos(), block->GetLastPos(), block->GetBlockPos());
224     }
225     return E_OK;
226 }
227 
SetBlock(AppDataFwk::SharedBlock * block)228 void SqliteSharedResultSet::SetBlock(AppDataFwk::SharedBlock *block)
229 {
230     AbsSharedResultSet::SetBlock(block);
231     rowNum_ = NO_COUNT;
232 }
233 
234 /**
235  * If isOnlyFillResultSetBlockInput is true, use the input requiredPos to fill the block, otherwise pick the value
236  * from requirePos and blockCapacity_.
237  */
SetFillBlockForwardOnly(bool isOnlyFillResultSetBlockInput)238 void SqliteSharedResultSet::SetFillBlockForwardOnly(bool isOnlyFillResultSetBlockInput)
239 {
240     isOnlyFillBlock_ = isOnlyFillResultSetBlockInput;
241 }
242 
Finalize()243 void SqliteSharedResultSet::Finalize()
244 {
245     Close();
246 }
247 /**
248  * Executes a statement and populates the specified with a range of results.
249  */
ExecuteForSharedBlock(AppDataFwk::SharedBlock * block,int start,int required)250 int32_t SqliteSharedResultSet::ExecuteForSharedBlock(AppDataFwk::SharedBlock *block, int start, int required)
251 {
252     auto [statement, errCode] = PrepareStep();
253     if (errCode != E_OK) {
254         LOG_ERROR("PrepareStep error = %{public}d ", errCode);
255         return errCode;
256     }
257 
258     auto code = block->Clear();
259     if (code != AppDataFwk::SharedBlock::SHARED_BLOCK_OK) {
260         LOG_ERROR("Clear %{public}d.", code);
261         return E_ERROR;
262     }
263 
264     SharedBlockInfo blockInfo(block);
265     blockInfo.requiredPos = required;
266     blockInfo.columnNum = statement->GetColumnCount();
267     blockInfo.isCountAllRows = false;
268     blockInfo.startPos = start;
269     code = block->SetColumnNum(blockInfo.columnNum);
270     if (code != AppDataFwk::SharedBlock::SHARED_BLOCK_OK) {
271         LOG_ERROR("SetColumnNum %{public}d.", code);
272         return E_ERROR;
273     }
274     errCode = statement->FillBlockInfo(&blockInfo);
275     if (errCode != E_OK) {
276         LOG_ERROR("Fill shared block failed, ret is %{public}d", errCode);
277         return errCode;
278     }
279 
280     block->SetStartPos(blockInfo.startPos);
281     block->SetBlockPos(required - blockInfo.startPos);
282     block->SetLastPos(blockInfo.startPos + block->GetRowNum());
283     return E_OK;
284 }
285 } // namespace NativeRdb
286 } // namespace OHOS