* Copyright (c) 2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <gtest/gtest.h>
#include <random>
#include <string>
#include "rdb_test_common.h"
#include "rdb_errno.h"
#include "rdb_helper.h"
#include "rdb_open_callback.h"
#include "sqlite_global_config.h"
using namespace testing::ext;
using namespace OHOS::NativeRdb;
class RdbWalLimitTest : public testing::Test {
public:
static void SetUpTestCase(void);
static void TearDownTestCase(void);
void SetUp();
void TearDown();
static std::vector<uint8_t> CreateRandomData(int32_t len);
static void MakeWalReachLimit();
static void MakeWalNoReachLimit();
static void MakeWalIncrease();
static void KeepReadConnection();
static ValuesBucket MakeValueBucket(const int &id);
static const std::string DATABASE_NAME;
static std::shared_ptr<RdbStore> store;
static std::shared_ptr<ResultSet> resultSet;
};
const std::string RdbWalLimitTest::DATABASE_NAME = RDB_TEST_PATH + "walLimit_test.db";
std::shared_ptr<RdbStore> RdbWalLimitTest::store = nullptr;
std::shared_ptr<ResultSet> RdbWalLimitTest::resultSet = nullptr;
std::vector<uint8_t> blobValue = RdbWalLimitTest::CreateRandomData(1 * 1024 * 1024);
class RdbWalLimitCallback : public RdbOpenCallback {
public:
int OnCreate(RdbStore &store) override;
int OnUpgrade(RdbStore &store, int oldVersion, int newVersion) override;
static const std::string CREATE_TABLE_TEST;
};
const std::string RdbWalLimitCallback::CREATE_TABLE_TEST(
"CREATE TABLE IF NOT EXISTS test "
"(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, age INTEGER, salary REAL, blobType BLOB)");
int RdbWalLimitCallback::OnCreate(RdbStore &store)
{
return store.ExecuteSql(CREATE_TABLE_TEST);
}
int RdbWalLimitCallback::OnUpgrade(RdbStore &store, int oldVersion, int newVersion)
{
return E_OK;
}
void RdbWalLimitTest::SetUpTestCase(void)
{
}
void RdbWalLimitTest::TearDownTestCase(void)
{
}
void RdbWalLimitTest::SetUp(void)
{
int errCode = E_OK;
RdbHelper::DeleteRdbStore(DATABASE_NAME);
RdbStoreConfig config(DATABASE_NAME);
RdbWalLimitCallback helper;
store = RdbHelper::GetRdbStore(config, 1, helper, errCode);
EXPECT_NE(store, nullptr);
EXPECT_EQ(errCode, E_OK);
}
void RdbWalLimitTest::TearDown(void)
{
if (resultSet != nullptr) {
EXPECT_EQ(resultSet->Close(), E_OK);
resultSet = nullptr;
}
store = nullptr;
RdbHelper::DeleteRdbStore(DATABASE_NAME);
}
std::vector<uint8_t> RdbWalLimitTest::CreateRandomData(int32_t len)
{
std::random_device randomDevice;
std::uniform_int_distribution<int> distribution(0, std::numeric_limits<uint8_t>::max());
std::vector<uint8_t> value(len);
for (int32_t i = 0; i < len; i++) {
value[i] = static_cast<uint8_t>(distribution(randomDevice));
}
return value;
}
void RdbWalLimitTest::KeepReadConnection()
{
int64_t id;
ValuesBucket values;
values.PutInt("id", 1);
values.PutString("name", std::string("lisi"));
values.PutInt("age", 18);
values.PutDouble("salary", 200.8);
values.PutBlob("blobType", std::vector<uint8_t>{ 1, 2, 3 });
EXPECT_EQ(store->Insert(id, "test", values), E_OK);
resultSet = store->QueryByStep("SELECT * FROM test");
EXPECT_NE(resultSet, nullptr);
int count = 0;
EXPECT_EQ(resultSet->GetRowCount(count), E_OK);
EXPECT_EQ(count, 1);
EXPECT_EQ(resultSet->GoToFirstRow(), E_OK);
}
void RdbWalLimitTest::MakeWalIncrease()
{
int64_t id;
ValuesBucket values;
for (int i = 1; i < 199; i++) {
values.Clear();
values.PutInt("id", i);
values.PutString("name", std::string("lisi"));
values.PutInt("age", 18);
values.PutDouble("salary", 200.8);
values.PutBlob("blobType", blobValue);
EXPECT_EQ(store->Insert(id, "test", values), E_OK);
}
}
void RdbWalLimitTest::MakeWalReachLimit()
{
int64_t id;
int i = 1;
int ret = E_OK;
ValuesBucket values;
while (ret == E_OK) {
i++;
values.Clear();
values.PutInt("id", i);
values.PutString("name", std::string("lisi"));
values.PutInt("age", 18);
values.PutDouble("salary", 200.8);
values.PutBlob("blobType", blobValue);
ret = store->Insert(id, "test", values);
}
}
void RdbWalLimitTest::MakeWalNoReachLimit()
{
int64_t id;
ValuesBucket values;
for (int i = 2; i < 20; i++) {
values.Clear();
values.PutInt("id", i);
values.PutString("name", std::string("lisi"));
values.PutInt("age", 18);
values.PutDouble("salary", 200.8);
values.PutBlob("blobType", blobValue);
EXPECT_EQ(store->Insert(id, "test", values), E_OK);
}
}
ValuesBucket RdbWalLimitTest::MakeValueBucket(const int &id)
{
ValuesBucket values;
values.PutInt("id", id);
values.PutString("name", std::string("lisi"));
values.PutInt("age", 18);
values.PutDouble("salary", 200.8);
values.PutBlob("blobType", std::vector<uint8_t>{ 1, 2, 3 });
return values;
}
* @tc.name: RdbStore_WalOverLimit_001
* @tc.desc: Without reading data or conducting transactions, if data is continuously written,
* the WAL size will not exceed the default limit.
* @tc.type: FUNC
*/
HWTEST_F(RdbWalLimitTest, RdbStore_WalOverLimit_001, TestSize.Level1)
{
MakeWalIncrease();
int64_t id;
ValuesBucket values = MakeValueBucket(199);
values.PutBlob("blobType", blobValue);
EXPECT_EQ(store->Insert(id, "test", values), E_OK);
std::vector<ValuesBucket> valuesBuckets;
for (int i = 200; i < 210; i++) {
valuesBuckets.push_back(RdbWalLimitTest::MakeValueBucket(i));
}
int64_t insertNum = 0;
EXPECT_EQ(store->BatchInsert(insertNum, "test", valuesBuckets), E_OK);
EXPECT_EQ(store->ExecuteSql("DELETE FROM test"), E_OK);
}
* @tc.name: RdbStore_WalOverLimit_002
* @tc.desc: Before the wal file exceeds the limit, both read and write can be executed normally.
* @tc.type: FUNC
*/
HWTEST_F(RdbWalLimitTest, RdbStore_WalOverLimit_002, TestSize.Level1)
{
KeepReadConnection();
MakeWalNoReachLimit();
ValuesBucket values = MakeValueBucket(20);
std::vector<ValuesBucket> valuesBuckets;
for (int i = 21; i < 30; i++) {
valuesBuckets.push_back(MakeValueBucket(i));
}
int64_t insertNum = 0;
EXPECT_EQ(store->BatchInsert(insertNum, "test", valuesBuckets), E_OK);
EXPECT_EQ(store->ExecuteSql("DELETE FROM test"), E_OK);
}
* @tc.name: RdbStore_WalOverLimit_003
* @tc.desc: During transactions, the size of the wal file may exceed the limit.
* @tc.type: FUNC
*/
HWTEST_F(RdbWalLimitTest, RdbStore_WalOverLimit_003, TestSize.Level3)
{
ValuesBucket values = MakeValueBucket(200);
int64_t id;
store->BeginTransaction();
MakeWalReachLimit();
EXPECT_EQ(store->Insert(id, "test", values), E_WAL_SIZE_OVER_LIMIT);
int errCode = store->Commit();
EXPECT_EQ(errCode, E_OK);
}
* @tc.name: RdbStore_WalOverLimit_003
* @tc.desc: During transactions, the size of the wal file may exceed the limit.
* @tc.type: FUNC
*/
HWTEST_F(RdbWalLimitTest, RdbStore_WalOverLimit_004, TestSize.Level3)
{
ValuesBucket values = MakeValueBucket(200);
int64_t id;
store->BeginTransaction();
MakeWalReachLimit();
EXPECT_EQ(store->Insert(id, "test", values), E_WAL_SIZE_OVER_LIMIT);
int errCode = store->RollBack();
EXPECT_EQ(errCode, E_OK);
}