1/*
2 * Copyright (c) 2021-2022 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
16#include "sqlite_helper.h"
17
18#include "accesstoken_log.h"
19#include "sqlite3ext.h"
20#include <sys/types.h>
21
22namespace OHOS {
23namespace Security {
24namespace AccessToken {
25namespace {
26static constexpr OHOS::HiviewDFX::HiLogLabel LABEL = {LOG_CORE, SECURITY_DOMAIN_ACCESSTOKEN, "SqliteHelper"};
27}
28
29SqliteHelper::SqliteHelper(const std::string& dbName, const std::string& dbPath, int32_t version)
30    : dbName_(dbName), dbPath_(dbPath), currentVersion_(version), db_(nullptr)
31{}
32
33SqliteHelper::~SqliteHelper()
34{}
35
36void SqliteHelper::Open() __attribute__((no_sanitize("cfi")))
37{
38    if (db_ != nullptr) {
39        ACCESSTOKEN_LOG_WARN(LABEL, "Db s already open");
40        return;
41    }
42    if (dbName_.empty() || dbPath_.empty() || currentVersion_ < 0) {
43        ACCESSTOKEN_LOG_ERROR(LABEL, "Param invalid, dbName: %{public}s, "
44                              "dbPath: %{public}s, currentVersion: %{public}d",
45                              dbName_.c_str(), dbPath_.c_str(), currentVersion_);
46        return;
47    }
48    // set soft heap limit as 10KB
49    const int32_t heapLimit = 10 * 1024;
50    sqlite3_soft_heap_limit64(heapLimit);
51    std::string fileName = dbPath_ + dbName_;
52    int32_t res = sqlite3_open(fileName.c_str(), &db_);
53    if (res != SQLITE_OK) {
54        ACCESSTOKEN_LOG_ERROR(LABEL, "Failed to open db: %{public}s", sqlite3_errmsg(db_));
55        return;
56    }
57
58    int32_t version = GetVersion();
59    if (version == currentVersion_) {
60        return;
61    }
62
63    BeginTransaction();
64    if (version == DataBaseVersion::VERISION_0) {
65        OnCreate();
66    } else {
67        if (version < currentVersion_) {
68            OnUpdate(version);
69        }
70    }
71    SetVersion();
72    CommitTransaction();
73}
74
75void SqliteHelper::Close()
76{
77    if (db_ == nullptr) {
78        ACCESSTOKEN_LOG_WARN(LABEL, "Do open data base first!");
79        return;
80    }
81    int32_t ret = sqlite3_close(db_);
82    if (ret != SQLITE_OK) {
83        ACCESSTOKEN_LOG_WARN(LABEL, "Sqlite3_close error, ret=%{public}d", ret);
84        return;
85    }
86    db_ = nullptr;
87}
88
89int32_t SqliteHelper::BeginTransaction() const
90{
91    if (db_ == nullptr) {
92        ACCESSTOKEN_LOG_WARN(LABEL, "Do open data base first!");
93        return GENERAL_ERROR;
94    }
95    char* errorMessage = nullptr;
96    int32_t result = 0;
97    int32_t ret = sqlite3_exec(db_, "BEGIN;", nullptr, nullptr, &errorMessage);
98    if (ret != SQLITE_OK) {
99        ACCESSTOKEN_LOG_ERROR(LABEL, "Failed, errorMsg: %{public}s", errorMessage);
100        result = GENERAL_ERROR;
101    }
102    sqlite3_free(errorMessage);
103    return result;
104}
105
106int32_t SqliteHelper::CommitTransaction() const
107{
108    if (db_ == nullptr) {
109        ACCESSTOKEN_LOG_WARN(LABEL, "Do open data base first!");
110        return GENERAL_ERROR;
111    }
112    char* errorMessage = nullptr;
113    int32_t result = 0;
114    int32_t ret = sqlite3_exec(db_, "COMMIT;", nullptr, nullptr, &errorMessage);
115    if (ret != SQLITE_OK) {
116        ACCESSTOKEN_LOG_ERROR(LABEL, "Failed, errorMsg: %{public}s", errorMessage);
117        result = GENERAL_ERROR;
118    }
119    sqlite3_free(errorMessage);
120    sqlite3_db_cacheflush(db_);
121    return result;
122}
123
124int32_t SqliteHelper::RollbackTransaction() const
125{
126    if (db_ == nullptr) {
127        ACCESSTOKEN_LOG_WARN(LABEL, "Do open data base first!");
128        return GENERAL_ERROR;
129    }
130    int32_t result = 0;
131    char* errorMessage = nullptr;
132    int32_t ret = sqlite3_exec(db_, "ROLLBACK;", nullptr, nullptr, &errorMessage);
133    if (ret != SQLITE_OK) {
134        ACCESSTOKEN_LOG_ERROR(LABEL, "Failed, errorMsg: %{public}s", errorMessage);
135        result = GENERAL_ERROR;
136    }
137    sqlite3_free(errorMessage);
138    return result;
139}
140
141Statement SqliteHelper::Prepare(const std::string& sql) const
142{
143    return Statement(db_, sql);
144}
145
146int32_t SqliteHelper::ExecuteSql(const std::string& sql) const
147{
148    if (db_ == nullptr) {
149        ACCESSTOKEN_LOG_WARN(LABEL, "Do open data base first!");
150        return GENERAL_ERROR;
151    }
152    char* errorMessage = nullptr;
153    int32_t result = 0;
154    int32_t res = sqlite3_exec(db_, sql.c_str(), nullptr, nullptr, &errorMessage);
155    if (res != SQLITE_OK) {
156        ACCESSTOKEN_LOG_ERROR(LABEL, "Failed, errorMsg: %{public}s", errorMessage);
157        result = GENERAL_ERROR;
158    }
159    sqlite3_free(errorMessage);
160    return result;
161}
162
163int32_t SqliteHelper::GetVersion() const __attribute__((no_sanitize("cfi")))
164{
165    if (db_ == nullptr) {
166        ACCESSTOKEN_LOG_WARN(LABEL, "Do open data base first!");
167        return GENERAL_ERROR;
168    }
169    auto statement = Prepare(PRAGMA_VERSION_COMMAND);
170    int32_t version = 0;
171    while (statement.Step() == Statement::State::ROW) {
172        version = statement.GetColumnInt(0);
173    }
174    ACCESSTOKEN_LOG_INFO(LABEL, "Version: %{public}d", version);
175    return version;
176}
177
178void SqliteHelper::SetVersion() const
179{
180    if (db_ == nullptr) {
181        ACCESSTOKEN_LOG_WARN(LABEL, "Do open data base first!");
182        return;
183    }
184    auto statement = Prepare(PRAGMA_VERSION_COMMAND + " = " + std::to_string(currentVersion_));
185    statement.Step();
186}
187
188std::string SqliteHelper::SpitError() const
189{
190    if (db_ == nullptr) {
191        ACCESSTOKEN_LOG_WARN(LABEL, "Do open data base first!");
192        return "";
193    }
194    return sqlite3_errmsg(db_);
195}
196} // namespace AccessToken
197} // namespace Security
198} // namespace OHOS
199