1 /*
2  * Copyright (c) 2023 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 <gtest/gtest.h>
17 
18 #include <random>
19 #include <string>
20 
21 #include "common.h"
22 #include "rdb_errno.h"
23 #include "rdb_helper.h"
24 #include "rdb_open_callback.h"
25 #include "sqlite_global_config.h"
26 
27 using namespace testing::ext;
28 using namespace OHOS::NativeRdb;
29 
30 class RdbWalLimitTest : public testing::Test {
31 public:
32     static void SetUpTestCase(void);
33     static void TearDownTestCase(void);
34     void SetUp();
35     void TearDown();
36 
37     static std::vector<uint8_t> CreateRandomData(int32_t len);
38     static void MakeWalReachLimit();
39     static void MakeWalNoReachLimit();
40     static void MakeWalIncrease();
41     static void KeepReadConnection();
42     static ValuesBucket MakeValueBucket(const int &id);
43 
44     static const std::string DATABASE_NAME;
45     static std::shared_ptr<RdbStore> store;
46     static std::shared_ptr<ResultSet> resultSet;
47 };
48 
49 const std::string RdbWalLimitTest::DATABASE_NAME = RDB_TEST_PATH + "walLimit_test.db";
50 std::shared_ptr<RdbStore> RdbWalLimitTest::store = nullptr;
51 std::shared_ptr<ResultSet> RdbWalLimitTest::resultSet = nullptr;
52 
53 // create 1M data
54 std::vector<uint8_t> blobValue = RdbWalLimitTest::CreateRandomData(1 * 1024 * 1024);
55 
56 class RdbWalLimitCallback : public RdbOpenCallback {
57 public:
58     int OnCreate(RdbStore &store) override;
59     int OnUpgrade(RdbStore &store, int oldVersion, int newVersion) override;
60     static const std::string CREATE_TABLE_TEST;
61 };
62 
63 const std::string RdbWalLimitCallback::CREATE_TABLE_TEST(
64     "CREATE TABLE IF NOT EXISTS test "
65     "(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, age INTEGER, salary REAL, blobType BLOB)");
66 
OnCreate(RdbStore & store)67 int RdbWalLimitCallback::OnCreate(RdbStore &store)
68 {
69     return store.ExecuteSql(CREATE_TABLE_TEST);
70 }
71 
OnUpgrade(RdbStore & store,int oldVersion,int newVersion)72 int RdbWalLimitCallback::OnUpgrade(RdbStore &store, int oldVersion, int newVersion)
73 {
74     return E_OK;
75 }
76 
SetUpTestCase(void)77 void RdbWalLimitTest::SetUpTestCase(void)
78 {
79 }
80 
TearDownTestCase(void)81 void RdbWalLimitTest::TearDownTestCase(void)
82 {
83 }
84 
SetUp(void)85 void RdbWalLimitTest::SetUp(void)
86 {
87     int errCode = E_OK;
88     RdbHelper::DeleteRdbStore(DATABASE_NAME);
89     RdbStoreConfig config(DATABASE_NAME);
90     RdbWalLimitCallback helper;
91     store = RdbHelper::GetRdbStore(config, 1, helper, errCode);
92     EXPECT_NE(store, nullptr);
93     EXPECT_EQ(errCode, E_OK);
94 }
95 
TearDown(void)96 void RdbWalLimitTest::TearDown(void)
97 {
98     if (resultSet != nullptr) {
99         EXPECT_EQ(resultSet->Close(), E_OK);
100         resultSet = nullptr;
101     }
102 
103     store = nullptr;
104     RdbHelper::DeleteRdbStore(DATABASE_NAME);
105 }
106 
CreateRandomData(int32_t len)107 std::vector<uint8_t> RdbWalLimitTest::CreateRandomData(int32_t len)
108 {
109     std::random_device randomDevice;
110     std::uniform_int_distribution<int> distribution(0, std::numeric_limits<uint8_t>::max());
111     std::vector<uint8_t> value(len);
112     for (int32_t i = 0; i < len; i++) {
113         value[i] = static_cast<uint8_t>(distribution(randomDevice));
114     }
115     return value;
116 }
117 
KeepReadConnection()118 void RdbWalLimitTest::KeepReadConnection()
119 {
120     int64_t id;
121     ValuesBucket values;
122     values.PutInt("id", 1);
123     values.PutString("name", std::string("lisi"));
124     values.PutInt("age", 18);
125     values.PutDouble("salary", 200.8);
126     values.PutBlob("blobType", std::vector<uint8_t>{ 1, 2, 3 });
127     EXPECT_EQ(store->Insert(id, "test", values), E_OK);
128 
129     resultSet = store->QueryByStep("SELECT * FROM test");
130     EXPECT_NE(resultSet, nullptr);
131 
132     int count = 0;
133     EXPECT_EQ(resultSet->GetRowCount(count), E_OK);
134     EXPECT_EQ(count, 1);
135 
136     EXPECT_EQ(resultSet->GoToFirstRow(), E_OK);
137 }
138 
MakeWalIncrease()139 void RdbWalLimitTest::MakeWalIncrease()
140 {
141     int64_t id;
142     ValuesBucket values;
143     for (int i = 1; i < 199; i++) {
144         values.Clear();
145         values.PutInt("id", i);
146         values.PutString("name", std::string("lisi"));
147         values.PutInt("age", 18);
148         values.PutDouble("salary", 200.8);
149         values.PutBlob("blobType", blobValue);
150         EXPECT_EQ(store->Insert(id, "test", values), E_OK);
151     }
152 }
153 
MakeWalReachLimit()154 void RdbWalLimitTest::MakeWalReachLimit()
155 {
156     int64_t id;
157     int i = 1;
158     int ret = E_OK;
159     ValuesBucket values;
160     while (ret == E_OK) {
161         i++;
162         values.Clear();
163         values.PutInt("id", i);
164         values.PutString("name", std::string("lisi"));
165         values.PutInt("age", 18);
166         values.PutDouble("salary", 200.8);
167         values.PutBlob("blobType", blobValue);
168         ret = store->Insert(id, "test", values);
169     }
170 }
171 
MakeWalNoReachLimit()172 void RdbWalLimitTest::MakeWalNoReachLimit()
173 {
174     int64_t id;
175     ValuesBucket values;
176     for (int i = 2; i < 20; i++) {
177         values.Clear();
178         values.PutInt("id", i);
179         values.PutString("name", std::string("lisi"));
180         values.PutInt("age", 18);
181         values.PutDouble("salary", 200.8);
182         values.PutBlob("blobType", blobValue);
183         EXPECT_EQ(store->Insert(id, "test", values), E_OK);
184     }
185 }
186 
MakeValueBucket(const int & id)187 ValuesBucket RdbWalLimitTest::MakeValueBucket(const int &id)
188 {
189     ValuesBucket values;
190     values.PutInt("id", id);
191     values.PutString("name", std::string("lisi"));
192     values.PutInt("age", 18);
193     values.PutDouble("salary", 200.8);
194     values.PutBlob("blobType", std::vector<uint8_t>{ 1, 2, 3 });
195     return values;
196 }
197 
198 /**
199  * @tc.name: RdbStore_WalOverLimit_001
200  * @tc.desc: Without reading data or conducting transactions, if data is continuously written,
201  * the WAL size will not exceed the default limit.
202  * @tc.type: FUNC
203  * @tc.acquire: AR000HR0G5
204  */
205 HWTEST_F(RdbWalLimitTest, RdbStore_WalOverLimit_001, TestSize.Level1)
206 {
207     MakeWalIncrease();
208 
209     int64_t id;
210 
211     ValuesBucket values = MakeValueBucket(199);
212     values.PutBlob("blobType", blobValue);
213     EXPECT_EQ(store->Insert(id, "test", values), E_OK);
214 
215     std::vector<ValuesBucket> valuesBuckets;
216     for (int i = 200; i < 210; i++) {
217         valuesBuckets.push_back(RdbWalLimitTest::MakeValueBucket(i));
218     }
219 
220     int64_t insertNum = 0;
221     EXPECT_EQ(store->BatchInsert(insertNum, "test", valuesBuckets), E_OK);
222 
223     EXPECT_EQ(store->ExecuteSql("DELETE FROM test"), E_OK);
224 }
225 
226 /**
227  * @tc.name: RdbStore_WalOverLimit_002
228  * @tc.desc: Before the wal file exceeds the limit, both read and write can be executed normally.
229  * @tc.type: FUNC
230  * @tc.acquire: AR000HR0G5
231  */
232 HWTEST_F(RdbWalLimitTest, RdbStore_WalOverLimit_002, TestSize.Level1)
233 {
234     KeepReadConnection();
235     MakeWalNoReachLimit();
236     ValuesBucket values = MakeValueBucket(20);
237 
238     std::vector<ValuesBucket> valuesBuckets;
239     for (int i = 21; i < 30; i++) {
240         valuesBuckets.push_back(MakeValueBucket(i));
241     }
242 
243     int64_t insertNum = 0;
244     EXPECT_EQ(store->BatchInsert(insertNum, "test", valuesBuckets), E_OK);
245 
246     EXPECT_EQ(store->ExecuteSql("DELETE FROM test"), E_OK);
247 }
248 
249 /**
250  * @tc.name: RdbStore_WalOverLimit_003
251  * @tc.desc: During transactions, the size of the wal file may exceed the limit.
252  * @tc.type: FUNC
253  * @tc.acquire: AR000HR0G5
254  */
255 HWTEST_F(RdbWalLimitTest, RdbStore_WalOverLimit_003, TestSize.Level3)
256 {
257     ValuesBucket values = MakeValueBucket(200);
258     int64_t id;
259     store->BeginTransaction();
260     MakeWalReachLimit();
261     EXPECT_EQ(store->Insert(id, "test", values), E_WAL_SIZE_OVER_LIMIT);
262     int errCode = store->Commit();
263     EXPECT_EQ(errCode, E_OK);
264 }
265 
266 /**
267  * @tc.name: RdbStore_WalOverLimit_003
268  * @tc.desc: During transactions, the size of the wal file may exceed the limit.
269  * @tc.type: FUNC
270  * @tc.acquire: AR000HR0G5
271  */
272 HWTEST_F(RdbWalLimitTest, RdbStore_WalOverLimit_004, TestSize.Level3)
273 {
274     ValuesBucket values = MakeValueBucket(200);
275     int64_t id;
276     store->BeginTransaction();
277     MakeWalReachLimit();
278     EXPECT_EQ(store->Insert(id, "test", values), E_WAL_SIZE_OVER_LIMIT);
279     int errCode = store->RollBack();
280     EXPECT_EQ(errCode, E_OK);
281 }