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 std::tie(lastErr_, rowCount_) = statement->Count();
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
PrepareStep()72 std::pair<std::shared_ptr<Statement>, int> SqliteSharedResultSet::PrepareStep()
73 {
74 if (conn_ == nullptr) {
75 LOG_ERROR("Already close.");
76 lastErr_ = E_ALREADY_CLOSED;
77 return { nullptr, E_ALREADY_CLOSED };
78 }
79
80 if (statement_ != nullptr) {
81 return { statement_, E_OK };
82 }
83
84 auto type = SqliteUtils::GetSqlStatementType(qrySql_);
85 if (type == SqliteUtils::STATEMENT_ERROR) {
86 LOG_ERROR("invalid sql_ %{public}s!", qrySql_.c_str());
87 lastErr_ = E_INVALID_ARGS;
88 return { nullptr, E_INVALID_ARGS };
89 }
90
91 auto [errCode, statement] = conn_->CreateStatement(qrySql_, conn_);
92 if (statement == nullptr) {
93 lastErr_ = errCode;
94 return { statement, errCode };
95 }
96
97 if (!statement->ReadOnly()) {
98 LOG_ERROR("failed, %{public}s is not query sql!", SqliteUtils::Anonymous(qrySql_).c_str());
99 lastErr_ = E_NOT_SELECT;
100 return { nullptr, E_NOT_SELECT };
101 }
102
103 errCode = statement->Bind(bindArgs_);
104 if (errCode != E_OK) {
105 LOG_ERROR("Bind arg faild! Ret is %{public}d", errCode);
106 statement->Reset();
107 statement = nullptr;
108 lastErr_ = errCode;
109 return { nullptr, errCode };
110 }
111 return { statement, E_OK };
112 }
113
~SqliteSharedResultSet()114 SqliteSharedResultSet::~SqliteSharedResultSet() {}
115
GetColumnNames()116 std::pair<int, std::vector<std::string>> SqliteSharedResultSet::GetColumnNames()
117 {
118 if (isClosed_) {
119 LOG_ERROR("fail, result set has been closed, ret %{public}d, sql %{public}s",
120 E_ALREADY_CLOSED, qrySql_.c_str());
121 return { E_ALREADY_CLOSED, {} };
122 }
123
124 auto [statement, errCode] = PrepareStep();
125 if (statement == nullptr) {
126 return { errCode, {} };
127 }
128
129 // Get the total number of columns
130 auto columnCount = statement->GetColumnCount();
131 std::vector<std::string> colNames;
132 for (int i = 0; i < columnCount; i++) {
133 auto [ret, name] = statement->GetColumnName(i);
134 if (ret != E_OK) {
135 return { ret, {} };
136 }
137 colNames.push_back(name);
138 }
139
140 return { E_OK, std::move(colNames) };
141 }
142
Close()143 int SqliteSharedResultSet::Close()
144 {
145 AbsSharedResultSet::Close();
146 statement_ = nullptr;
147 conn_ = nullptr;
148 rowCount_ = NO_COUNT;
149 auto qrySql = std::move(qrySql_);
150 auto bindArgs = std::move(bindArgs_);
151 return E_OK;
152 }
153
OnGo(int oldPosition,int newPosition)154 int SqliteSharedResultSet::OnGo(int oldPosition, int newPosition)
155 {
156 if (isClosed_) {
157 LOG_ERROR("fail, result set has been closed, ret %{public}d, sql %{public}s",
158 E_ALREADY_CLOSED, qrySql_.c_str());
159 return E_ALREADY_CLOSED;
160 }
161 if (GetBlock() == nullptr) {
162 return E_ERROR;
163 }
164 if ((uint32_t)newPosition < GetBlock()->GetStartPos() || (uint32_t)newPosition >= GetBlock()->GetLastPos()
165 || oldPosition == rowCount_) {
166 return FillBlock(newPosition);
167 }
168 return E_OK;
169 }
170
171 /**
172 * Calculate a proper start position to fill the block.
173 */
PickFillBlockStartPosition(int resultSetPosition,int blockCapacity) const174 int SqliteSharedResultSet::PickFillBlockStartPosition(int resultSetPosition, int blockCapacity) const
175 {
176 return std::max(resultSetPosition - blockCapacity / PICK_POS, 0);
177 }
178
FillBlock(int requiredPos)179 int SqliteSharedResultSet::FillBlock(int requiredPos)
180 {
181 auto block = GetBlock();
182 if (block == nullptr) {
183 LOG_ERROR("FillSharedBlock GetBlock failed.");
184 return E_ERROR;
185 }
186 ClearBlock();
187 int startPos = isOnlyFillBlock_ ? requiredPos : PickFillBlockStartPosition(requiredPos, blockCapacity_);
188 auto errCode = ExecuteForSharedBlock(block.get(), startPos, requiredPos);
189 if (errCode != E_OK) {
190 return errCode;
191 }
192 blockCapacity_ = block->GetRowNum();
193 if ((block->GetStartPos() == block->GetLastPos() && (uint32_t)rowCount_ != block->GetStartPos())
194 || ((uint32_t)requiredPos < block->GetStartPos() || block->GetLastPos() <= (uint32_t)requiredPos)
195 || block->GetStartPos() > 0) {
196 LOG_WARN("blockRowNum=%{public}d, requiredPos= %{public}d, startPos_= %{public}" PRIu32
197 ", lastPos_= %{public}" PRIu32 ", blockPos_= %{public}" PRIu32 ".",
198 rowCount_, requiredPos, block->GetStartPos(), block->GetLastPos(), block->GetBlockPos());
199 }
200 return E_OK;
201 }
202
SetBlock(AppDataFwk::SharedBlock * block)203 void SqliteSharedResultSet::SetBlock(AppDataFwk::SharedBlock *block)
204 {
205 AbsSharedResultSet::SetBlock(block);
206 rowNum_ = NO_COUNT;
207 }
208
209 /**
210 * If isOnlyFillResultSetBlockInput is true, use the input requiredPos to fill the block, otherwise pick the value
211 * from requirePos and blockCapacity_.
212 */
SetFillBlockForwardOnly(bool isOnlyFillResultSetBlockInput)213 void SqliteSharedResultSet::SetFillBlockForwardOnly(bool isOnlyFillResultSetBlockInput)
214 {
215 isOnlyFillBlock_ = isOnlyFillResultSetBlockInput;
216 }
217
Finalize()218 void SqliteSharedResultSet::Finalize()
219 {
220 Close();
221 }
222 /**
223 * Executes a statement and populates the specified with a range of results.
224 */
ExecuteForSharedBlock(AppDataFwk::SharedBlock * block,int start,int required)225 int32_t SqliteSharedResultSet::ExecuteForSharedBlock(AppDataFwk::SharedBlock *block, int start, int required)
226 {
227 auto [statement, errCode] = PrepareStep();
228 if (errCode != E_OK) {
229 LOG_ERROR("PrepareStep error = %{public}d ", errCode);
230 return errCode;
231 }
232
233 auto code = block->Clear();
234 if (code != AppDataFwk::SharedBlock::SHARED_BLOCK_OK) {
235 LOG_ERROR("Clear %{public}d.", code);
236 return E_ERROR;
237 }
238
239 SharedBlockInfo blockInfo(block);
240 blockInfo.requiredPos = required;
241 blockInfo.columnNum = statement->GetColumnCount();
242 blockInfo.isCountAllRows = false;
243 blockInfo.startPos = start;
244 code = block->SetColumnNum(blockInfo.columnNum);
245 if (code != AppDataFwk::SharedBlock::SHARED_BLOCK_OK) {
246 LOG_ERROR("SetColumnNum %{public}d.", code);
247 return E_ERROR;
248 }
249 errCode = statement->FillBlockInfo(&blockInfo);
250 if (errCode != E_OK) {
251 LOG_ERROR("Fill shared block failed, ret is %{public}d", errCode);
252 return errCode;
253 }
254
255 block->SetStartPos(blockInfo.startPos);
256 block->SetBlockPos(required - blockInfo.startPos);
257 block->SetLastPos(blockInfo.startPos + block->GetRowNum());
258 return E_OK;
259 }
260 } // namespace NativeRdb
261 } // namespace OHOS