* 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 <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <algorithm>
#include <ctime>
#include <sstream>
#include <string>
#include <vector>
#include "abs_predicates.h"
#include "rdb_test_common.h"
#include "rdb_errno.h"
#include "rdb_helper.h"
#include "rdb_open_callback.h"
#include "rdb_predicates.h"
using namespace testing::ext;
using namespace OHOS::NativeRdb;
class RdbStorePredicateJoinTest : public testing::Test {
public:
static void SetUpTestCase(void);
static void TearDownTestCase(void);
void SetUp();
void TearDown();
void GenerateAllTables();
void InsertUserDates();
void InsertBookDates();
int ResultSize(std::shared_ptr<ResultSet> &resultSet);
static const std::string DATABASE_NAME;
static std::shared_ptr<RdbStore> store;
};
const std::string RdbStorePredicateJoinTest::DATABASE_NAME = RDB_TEST_PATH + "predicates_join_test.db";
std::shared_ptr<RdbStore> RdbStorePredicateJoinTest::store = nullptr;
const std::string CREATE_TABLE_USER_SQL = "CREATE TABLE IF NOT EXISTS user "
"(userId INTEGER PRIMARY KEY AUTOINCREMENT, firstName TEXT, lastName TEXT,"
"age INTEGER , balance REAL NOT NULL)";
const std::string CREATE_TABLE_BOOK_SQL = "CREATE TABLE IF NOT EXISTS book "
"(id INTEGER PRIMARY KEY AUTOINCREMENT,name TEXT, userId INTEGER,"
"FOREIGN KEY (userId) REFERENCES user (userId) "
"ON UPDATE NO ACTION ON DELETE CASCADE)";
class PredicateJoinTestOpenCallback : public RdbOpenCallback {
public:
int OnCreate(RdbStore &store) override;
int OnUpgrade(RdbStore &store, int oldVersion, int newVersion) override;
};
int PredicateJoinTestOpenCallback::OnCreate(RdbStore &store)
{
return E_OK;
}
int PredicateJoinTestOpenCallback::OnUpgrade(RdbStore &store, int oldVersion, int newVersion)
{
return E_OK;
}
void RdbStorePredicateJoinTest::SetUpTestCase(void)
{
}
void RdbStorePredicateJoinTest::TearDownTestCase(void)
{
}
void RdbStorePredicateJoinTest::SetUp(void)
{
int errCode = E_OK;
RdbStoreConfig config(RdbStorePredicateJoinTest::DATABASE_NAME);
PredicateJoinTestOpenCallback helper;
RdbStorePredicateJoinTest::store = RdbHelper::GetRdbStore(config, 1, helper, errCode);
EXPECT_NE(RdbStorePredicateJoinTest::store, nullptr);
RdbStorePredicateJoinTest::GenerateAllTables();
}
void RdbStorePredicateJoinTest::TearDown(void)
{
RdbHelper::DeleteRdbStore(RdbStorePredicateJoinTest::DATABASE_NAME);
}
void RdbStorePredicateJoinTest::GenerateAllTables()
{
RdbStorePredicateJoinTest::store->ExecuteSql(CREATE_TABLE_USER_SQL);
RdbStorePredicateJoinTest::InsertUserDates();
RdbStorePredicateJoinTest::store->ExecuteSql(CREATE_TABLE_BOOK_SQL);
RdbStorePredicateJoinTest::InsertBookDates();
}
void RdbStorePredicateJoinTest::InsertUserDates()
{
int64_t id;
ValuesBucket values;
values.PutInt("userId", 1);
values.PutString("firstName", std::string("Zhang"));
values.PutString("lastName", std::string("San"));
values.PutInt("age", 29);
values.PutDouble("balance", 100.51);
store->Insert(id, "user", values);
values.Clear();
values.PutInt("userId", 2);
values.PutString("firstName", std::string("Li"));
values.PutString("lastName", std::string("Si"));
values.PutInt("age", 30);
values.PutDouble("balance", 200.51);
store->Insert(id, "user", values);
values.Clear();
values.PutInt("userId", 3);
values.PutString("firstName", std::string("Wang"));
values.PutString("lastName", std::string("Wu"));
values.PutInt("age", 30);
values.PutDouble("balance", 300.51);
store->Insert(id, "user", values);
values.Clear();
values.PutInt("userId", 4);
values.PutString("firstName", std::string("Sun"));
values.PutString("lastName", std::string("Liu"));
values.PutInt("age", 31);
values.PutDouble("balance", 400.51);
store->Insert(id, "user", values);
values.Clear();
values.PutInt("userId", 5);
values.PutString("firstName", std::string("Ma"));
values.PutString("lastName", std::string("Qi"));
values.PutInt("age", 32);
values.PutDouble("balance", 500.51);
store->Insert(id, "user", values);
}
void RdbStorePredicateJoinTest::InsertBookDates()
{
int64_t id;
ValuesBucket values;
values.PutInt("id", 1);
values.PutString("name", std::string("SanGuo"));
values.PutInt("userId", 1);
store->Insert(id, "book", values);
values.Clear();
values.PutInt("id", 2);
values.PutString("name", std::string("XiYouJi"));
values.PutInt("userId", 2);
store->Insert(id, "book", values);
values.Clear();
values.PutInt("id", 3);
values.PutString("name", std::string("ShuiHuZhuan"));
values.PutInt("userId", 3);
store->Insert(id, "book", values);
}
int RdbStorePredicateJoinTest::ResultSize(std::shared_ptr<ResultSet> &resultSet)
{
if (resultSet->GoToFirstRow() != E_OK) {
return 0;
}
int count = 1;
while (resultSet->GoToNextRow() == E_OK) {
count++;
}
return count;
}
* @tc.name: RdbStore_CrossJoin_001
* @tc.desc: Normal testCase of RdbPredicates for CrossJoin
* @tc.type: FUNC
*/
HWTEST_F(RdbStorePredicateJoinTest, RdbStore_CrossJoin_001, TestSize.Level1)
{
RdbPredicates predicates("user");
std::vector<std::string> clauses;
clauses.push_back("user.userId = book.userId");
predicates.CrossJoin("book")->On(clauses);
std::vector<std::string> joinTypes;
joinTypes.push_back("CROSS JOIN");
EXPECT_EQ(joinTypes, predicates.GetJoinTypes());
EXPECT_EQ("book", predicates.GetJoinTableNames()[0]);
EXPECT_EQ("ON(user.userId = book.userId)", predicates.GetJoinConditions()[0]);
EXPECT_EQ("CROSS JOIN book ON(user.userId = book.userId)", predicates.GetJoinClause());
std::vector<std::string> columns;
std::shared_ptr<ResultSet> allDataTypes = RdbStorePredicateJoinTest::store->Query(predicates, columns);
EXPECT_EQ(3, ResultSize(allDataTypes));
EXPECT_EQ(E_OK, allDataTypes->GoToFirstRow());
int userId;
EXPECT_EQ(E_OK, allDataTypes->GetInt(0, userId));
EXPECT_EQ(1, userId);
std::string firstName;
EXPECT_EQ(E_OK, allDataTypes->GetString(1, firstName));
EXPECT_EQ("Zhang", firstName);
std::string lastName;
EXPECT_EQ(E_OK, allDataTypes->GetString(2, lastName));
EXPECT_EQ("San", lastName);
int age;
EXPECT_EQ(E_OK, allDataTypes->GetInt(3, age));
EXPECT_EQ(29, age);
double balance;
EXPECT_EQ(E_OK, allDataTypes->GetDouble(4, balance));
EXPECT_EQ(100.51, balance);
int id;
EXPECT_EQ(E_OK, allDataTypes->GetInt(5, id));
EXPECT_EQ(1, id);
std::string name;
EXPECT_EQ(E_OK, allDataTypes->GetString(6, name));
EXPECT_EQ("SanGuo", name);
int userId_1;
EXPECT_EQ(E_OK, allDataTypes->GetInt(7, userId_1));
EXPECT_EQ(1, userId_1);
}
* @tc.name: RdbStore_InnerJoin_002
* @tc.desc: Normal testCase of RdbPredicates for InnerJoin
* @tc.type: FUNC
*/
HWTEST_F(RdbStorePredicateJoinTest, RdbStore_InnerJoin_002, TestSize.Level1)
{
RdbPredicates predicates("user");
std::vector<std::string> clauses;
clauses.push_back("user.userId = book.userId");
predicates.InnerJoin("book")->On(clauses)->EqualTo("book.name", "SanGuo");
std::vector<std::string> joinTypes;
joinTypes.push_back("INNER JOIN");
EXPECT_EQ(joinTypes, predicates.GetJoinTypes());
EXPECT_EQ("ON(user.userId = book.userId)", predicates.GetJoinConditions()[0]);
std::vector<std::string> columns;
std::shared_ptr<ResultSet> allDataTypes = RdbStorePredicateJoinTest::store->Query(predicates, columns);
EXPECT_EQ(1, ResultSize(allDataTypes));
EXPECT_EQ(E_OK, allDataTypes->GoToFirstRow());
int userId;
EXPECT_EQ(E_OK, allDataTypes->GetInt(0, userId));
EXPECT_EQ(1, userId);
std::string firstName;
EXPECT_EQ(E_OK, allDataTypes->GetString(1, firstName));
EXPECT_EQ("Zhang", firstName);
std::string lastName;
EXPECT_EQ(E_OK, allDataTypes->GetString(2, lastName));
EXPECT_EQ("San", lastName);
int age;
EXPECT_EQ(E_OK, allDataTypes->GetInt(3, age));
EXPECT_EQ(29, age);
double balance;
EXPECT_EQ(E_OK, allDataTypes->GetDouble(4, balance));
EXPECT_EQ(100.51, balance);
int id;
EXPECT_EQ(E_OK, allDataTypes->GetInt(5, id));
EXPECT_EQ(1, id);
std::string name;
EXPECT_EQ(E_OK, allDataTypes->GetString(6, name));
EXPECT_EQ("SanGuo", name);
int userId_1;
EXPECT_EQ(E_OK, allDataTypes->GetInt(7, userId_1));
EXPECT_EQ(1, userId_1);
}
* @tc.name: RdbStore_LeftOuterJoin_003
* @tc.desc: Normal testCase of RdbPredicates for LeftOuterJoin
* @tc.type: FUNC
*/
HWTEST_F(RdbStorePredicateJoinTest, RdbStore_LeftOuterJoin_003, TestSize.Level1)
{
RdbPredicates predicates("user");
std::vector<std::string> fields;
fields.push_back("userId");
predicates.LeftOuterJoin("book")->Using(fields)->EqualTo("name", "SanGuo");
std::vector<std::string> joinTypes;
joinTypes.push_back("LEFT OUTER JOIN");
EXPECT_EQ(joinTypes, predicates.GetJoinTypes());
EXPECT_EQ("USING(userId)", predicates.GetJoinConditions()[0]);
std::vector<std::string> columns;
std::shared_ptr<ResultSet> allDataTypes = RdbStorePredicateJoinTest::store->Query(predicates, columns);
EXPECT_EQ(1, ResultSize(allDataTypes));
EXPECT_EQ(E_OK, allDataTypes->GoToFirstRow());
int userId;
EXPECT_EQ(E_OK, allDataTypes->GetInt(0, userId));
EXPECT_EQ(1, userId);
std::string firstName;
EXPECT_EQ(E_OK, allDataTypes->GetString(1, firstName));
EXPECT_EQ("Zhang", firstName);
std::string lastName;
EXPECT_EQ(E_OK, allDataTypes->GetString(2, lastName));
EXPECT_EQ("San", lastName);
int age;
EXPECT_EQ(E_OK, allDataTypes->GetInt(3, age));
EXPECT_EQ(29, age);
double balance;
EXPECT_EQ(E_OK, allDataTypes->GetDouble(4, balance));
EXPECT_EQ(100.51, balance);
int id;
EXPECT_EQ(E_OK, allDataTypes->GetInt(5, id));
EXPECT_EQ(1, id);
std::string name;
EXPECT_EQ(E_OK, allDataTypes->GetString(6, name));
EXPECT_EQ("SanGuo", name);
}
* @tc.name: RdbStore_LeftOuterJoin_004
* @tc.desc: Normal testCase of RdbPredicates for LeftOuterJoin
* @tc.type: FUNC
*/
HWTEST_F(RdbStorePredicateJoinTest, RdbStore_LeftOuterJoin_004, TestSize.Level1)
{
RdbPredicates predicates("user");
std::vector<std::string> clauses;
clauses.push_back("user.userId = book.userId");
std::vector<std::string> joinTypes;
joinTypes.push_back("LEFT OUTER JOIN");
predicates.LeftOuterJoin("book")->On(clauses);
EXPECT_EQ(joinTypes, predicates.GetJoinTypes());
EXPECT_EQ("ON(user.userId = book.userId)", predicates.GetJoinConditions()[0]);
std::vector<std::string> columns;
std::shared_ptr<ResultSet> allDataTypes = RdbStorePredicateJoinTest::store->Query(predicates, columns);
EXPECT_EQ(5, ResultSize(allDataTypes));
}
* @tc.name: RdbStore_LeftOuterJoin_005
* @tc.desc: Abnormal testCase of RdbPredicates for LeftOuterJoin, if tableName is ""
* @tc.type: FUNC
*/
HWTEST_F(RdbStorePredicateJoinTest, RdbStore_LeftOuterJoin_005, TestSize.Level1)
{
RdbPredicates predicates("user");
std::vector<std::string> clauses;
clauses.push_back("user.userId = book.userId");
std::vector<std::string> joinTypes;
predicates.LeftOuterJoin("")->On(clauses);
EXPECT_EQ(joinTypes, predicates.GetJoinTypes());
EXPECT_EQ(joinTypes, predicates.GetJoinConditions());
std::vector<std::string> columns;
std::shared_ptr<ResultSet> allDataTypes = RdbStorePredicateJoinTest::store->Query(predicates, columns);
EXPECT_EQ(5, ResultSize(allDataTypes));
allDataTypes->Close();
}
* @tc.name: RdbStore_LeftOuterJoin_006
* @tc.desc: Abnormal testCase of RdbPredicates for LeftOuterJoin, if the join condition is []
* @tc.type: FUNC
*/
HWTEST_F(RdbStorePredicateJoinTest, RdbStore_LeftOuterJoin_006, TestSize.Level1)
{
RdbPredicates predicates("user");
std::vector<std::string> clauses;
std::vector<std::string> joinTypes;
joinTypes.push_back("LEFT OUTER JOIN");
predicates.LeftOuterJoin("book")->On(clauses);
EXPECT_EQ(joinTypes, predicates.GetJoinTypes());
EXPECT_EQ(clauses, predicates.GetJoinConditions());
}
* @tc.name: RdbStore_LeftOuterJoin_007
* @tc.desc: Abnormal testCase of RdbPredicates for LeftOuterJoin, if fields is []
* @tc.type: FUNC
*/
HWTEST_F(RdbStorePredicateJoinTest, RdbStore_LeftOuterJoin_007, TestSize.Level1)
{
RdbPredicates predicates("user");
std::vector<std::string> fields;
predicates.LeftOuterJoin("book")->Using(fields)->EqualTo("name", "SanGuo");
std::vector<std::string> joinTypes;
joinTypes.push_back("LEFT OUTER JOIN");
EXPECT_EQ(joinTypes, predicates.GetJoinTypes());
EXPECT_EQ(fields, predicates.GetJoinConditions());
}
* @tc.name: RdbStore_LeftOuterJoin_008
* @tc.desc: Abnormal testCase of RdbPredicates for LeftOuterJoin, if tableName is ""
* @tc.type: FUNC
*/
HWTEST_F(RdbStorePredicateJoinTest, RdbStore_LeftOuterJoin_008, TestSize.Level1)
{
RdbPredicates predicates("user");
std::vector<std::string> fields;
fields.push_back("userId");
predicates.LeftOuterJoin("")->Using(fields)->EqualTo("name", "SanGuo");
std::vector<std::string> joinTypes;
EXPECT_EQ(joinTypes, predicates.GetJoinTypes());
EXPECT_EQ(joinTypes, predicates.GetJoinConditions());
std::vector<std::string> columns;
std::shared_ptr<ResultSet> allDataTypes = RdbStorePredicateJoinTest::store->Query(predicates, columns);
EXPECT_EQ(0, ResultSize(allDataTypes));
allDataTypes->Close();
}
* @tc.name: RdbStore_LeftOuterJoin_009
* @tc.desc: Abnormal testCase of RdbPredicates for LeftOuterJoin, if join count rather than 1
* @tc.type: FUNC
*/
HWTEST_F(RdbStorePredicateJoinTest, RdbStore_LeftOuterJoin_009, TestSize.Level1)
{
RdbPredicates predicates("user");
std::vector<std::string> fields;
fields.push_back("userId");
predicates.LeftOuterJoin("book")->LeftOuterJoin("book");
EXPECT_EQ(2, predicates.GetJoinCount());
predicates.Using(fields)->EqualTo("name", "SanGuo");
std::vector<std::string> joinTypes{ "LEFT OUTER JOIN", "LEFT OUTER JOIN" };
EXPECT_EQ(joinTypes, predicates.GetJoinTypes());
EXPECT_EQ(0, predicates.GetJoinCount());
std::vector<std::string> columns;
std::shared_ptr<ResultSet> allDataTypes = RdbStorePredicateJoinTest::store->Query(predicates, columns);
EXPECT_NE(allDataTypes, nullptr);
allDataTypes->Close();
}