1 /*
2 * Copyright (c) 2025 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 #include <gtest/gtest.h>
16 #include <sys/stat.h>
17 #include <sys/types.h>
18
19 #include <cstdio>
20 #include <string>
21 #include <unistd.h>
22
23 #include "common.h"
24 #include "sqlite3sym.h"
25
26 using namespace testing::ext;
27 using namespace UnitTest::SQLiteTest;
28
29 #define TEST_DIR "./sqlitebinlogtest"
30 #define TEST_DB (TEST_DIR "/test.db")
31 #define TEST_BACKUP_DB (TEST_DIR "/test_bak.db")
32 #define TEST_DATA_COUNT 1000
33 #define TEST_DATA_REAL 16.1
34
UtSqliteLogPrint(const void * data,int err,const char * msg)35 static void UtSqliteLogPrint(const void *data, int err, const char *msg)
36 {
37 std::cout << "SqliteBinlogTest SQLite xLog err:" << err << ", msg:" << msg << std::endl;
38 }
39
UtPresetDb(const char * dbFile)40 static void UtPresetDb(const char *dbFile)
41 {
42 /**
43 * @tc.steps: step1. Prepare compressed db
44 * @tc.expected: step1. Execute successfully
45 */
46 sqlite3 *db = NULL;
47 std::string dbFileUri = "file:";
48 dbFileUri += dbFile;
49 EXPECT_EQ(sqlite3_open(dbFileUri.c_str(), &db), SQLITE_OK);
50 /**
51 * @tc.steps: step2. Create table and fill it, make db size big enough
52 * @tc.expected: step2. Execute successfully
53 */
54 static const char *UT_DDL_CREATE_TABLE = "CREATE TABLE salary("
55 "entryId INTEGER PRIMARY KEY,"
56 "entryName Text,"
57 "salary REAL,"
58 "class INTEGER);";
59 EXPECT_EQ(sqlite3_exec(db, UT_DDL_CREATE_TABLE, NULL, NULL, NULL), SQLITE_OK);
60 sqlite3_close(db);
61 }
62
UtEnableBinlog(sqlite3 * db)63 static void UtEnableBinlog(sqlite3 *db)
64 {
65 Sqlite3BinlogConfig cfg = {
66 .mode = Sqlite3BinlogMode::ROW,
67 .fullCallbackThreshold = 2,
68 .maxFileSize = 1024 * 1024 * 4,
69 .xErrorCallback = nullptr,
70 .xLogFullCallback = nullptr,
71 .callbackCtx = nullptr,
72 };
73 EXPECT_EQ(sqlite3_db_config(db, SQLITE_DBCONFIG_ENABLE_BINLOG, &cfg), SQLITE_OK);
74 }
75
UtQueryResult(void * data,int argc,char ** argv,char ** azColName)76 static int UtQueryResult(void *data, int argc, char **argv, char **azColName)
77 {
78 int count = *static_cast<int *>(data);
79 *static_cast<int *>(data) = count + 1;
80 // 2 means 2 fields, entryId, entryName
81 EXPECT_EQ(argc, 2);
82 return SQLITE_OK;
83 }
84
UtGetRecordCount(sqlite3 * db)85 static int UtGetRecordCount(sqlite3 *db)
86 {
87 int count = 0;
88 EXPECT_EQ(sqlite3_exec(db, "SELECT entryId, entryName FROM salary;", UtQueryResult, &count, nullptr), SQLITE_OK);
89 return count;
90 }
91
92 class SqliteBinlogTest : public testing::Test {
93 public:
94 static void SetUpTestCase(void);
95 static void TearDownTestCase(void);
96 void SetUp();
97 void TearDown();
98 };
99
SetUpTestCase(void)100 void SqliteBinlogTest::SetUpTestCase(void)
101 {
102 Common::RemoveDir(TEST_DIR);
103 Common::MakeDir(TEST_DIR);
104 }
105
TearDownTestCase(void)106 void SqliteBinlogTest::TearDownTestCase(void)
107 {
108 }
109
SetUp(void)110 void SqliteBinlogTest::SetUp(void)
111 {
112 std::string command = "rm -rf ";
113 command += TEST_DIR "/*";
114 system(command.c_str());
115 sqlite3_config(SQLITE_CONFIG_LOG, &UtSqliteLogPrint, NULL);
116 UtPresetDb(TEST_DB);
117 UtPresetDb(TEST_BACKUP_DB);
118 }
119
TearDown(void)120 void SqliteBinlogTest::TearDown(void)
121 {
122 sqlite3_config(SQLITE_CONFIG_LOG, NULL, NULL);
123 }
124
125 /**
126 * @tc.name: BinlogReplayTest001
127 * @tc.desc: Test replay sql with test value that has single quote
128 * @tc.type: FUNC
129 */
130 HWTEST_F(SqliteBinlogTest, BinlogReplayTest001, TestSize.Level0)
131 {
132 /**
133 * @tc.steps: step1. open db and set binlog
134 * @tc.expected: step1. ok
135 */
136 sqlite3 *db = NULL;
137 EXPECT_EQ(sqlite3_open_v2(TEST_DB, &db,
138 SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX, nullptr), SQLITE_OK);
139 ASSERT_NE(db, nullptr);
140 UtEnableBinlog(db);
141 /**
142 * @tc.steps: step2. Insert records with single quotes
143 * @tc.expected: step2. Execute successfully
144 */
145 static const char *UT_SQL_INSERT_DATA =
146 "INSERT INTO salary(entryId, entryName, salary, class) VALUES(?,?,?,?);";
147 sqlite3_stmt *insertStmt = NULL;
148 EXPECT_EQ(sqlite3_prepare_v2(db, UT_SQL_INSERT_DATA, -1, &insertStmt, NULL), SQLITE_OK);
149 for (int i = 0; i < TEST_DATA_COUNT; i++) {
150 // bind parameters, 1, 2, 3, 4 are sequence number of fields
151 sqlite3_bind_int(insertStmt, 1, i + 1);
152 sqlite3_bind_text(insertStmt, 2, "'salary-entry-name'", -1, SQLITE_STATIC);
153 sqlite3_bind_double(insertStmt, 3, TEST_DATA_REAL + i);
154 sqlite3_bind_int(insertStmt, 4, i + 1);
155 EXPECT_EQ(sqlite3_step(insertStmt), SQLITE_DONE);
156 sqlite3_reset(insertStmt);
157 }
158 sqlite3_finalize(insertStmt);
159 /**
160 * @tc.steps: step3. binlog replay to write into backup db
161 * @tc.expected: step3. Return SQLITE_OK
162 */
163 sqlite3 *backupDb = NULL;
164 EXPECT_EQ(sqlite3_open_v2(TEST_BACKUP_DB, &backupDb,
165 SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX, nullptr), SQLITE_OK);
166 ASSERT_NE(backupDb, nullptr);
167 EXPECT_EQ(sqlite3_replay_binlog(db, backupDb), SQLITE_OK);
168 /**
169 * @tc.steps: step4. check db count
170 * @tc.expected: step4. both db have TEST_DATA_COUNT
171 */
172 EXPECT_EQ(UtGetRecordCount(db), TEST_DATA_COUNT);
173 EXPECT_EQ(UtGetRecordCount(backupDb), TEST_DATA_COUNT);
174 sqlite3_close_v2(db);
175 sqlite3_close_v2(backupDb);
176 }
177