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
16 #include <gtest/gtest.h>
17
18 #include <chrono>
19 #include <iostream>
20 #include <sstream>
21
22 #include "log_print.h"
23 #ifndef USE_SQLITE_SYMBOLS
24 #include "sqlite3.h"
25 #else
26 #include "sqlite3sym.h"
27 #endif
28
29 using namespace std;
30 using namespace testing::ext;
31 using namespace DistributedDB;
32
33 class SqliteAdapterTest : public testing::Test {
34 public:
35 static void SetUpTestCase(void);
36 static void TearDownTestCase(void);
37 void SetUp();
38 void TearDown();
39 };
40
SetUpTestCase(void)41 void SqliteAdapterTest::SetUpTestCase(void)
42 {}
43
TearDownTestCase(void)44 void SqliteAdapterTest::TearDownTestCase(void)
45 {}
46
SetUp()47 void SqliteAdapterTest::SetUp()
48 {}
49
TearDown()50 void SqliteAdapterTest::TearDown()
51 {}
52
53 using Clock = std::chrono::system_clock;
54
55 static bool g_needSkip = false;
56
HandleRc(sqlite3 * db,int rc)57 static void HandleRc(sqlite3 *db, int rc)
58 {
59 if (rc != SQLITE_OK) {
60 LOGE("Failed!!!!!!!!!! rc: %d", rc);
61 g_needSkip = true;
62 }
63 }
64 static sqlite3 *g_sqliteDb = nullptr;
65 static char *g_errMsg = nullptr;
66
Callback(void * data,int argc,char ** argv,char ** colName)67 static int Callback(void *data, int argc, char **argv, char **colName)
68 {
69 for (int i = 0; i < argc; i++) {
70 LOGD("%s = %s\n", colName[i], argv[i] ? argv[i] : "nullptr");
71 }
72 return 0; // 返回 0 表示继续执行查询
73 }
74
QueryCallback(void * data,int argc,char ** argv,char ** colName)75 static int QueryCallback(void *data, int argc, char **argv, char **colName)
76 {
77 if (argc != 1) {
78 return 0;
79 }
80 auto expectCount = reinterpret_cast<int64_t>(data);
81 int base = 10;
82 EXPECT_EQ(expectCount, strtol(argv[0], nullptr, base));
83 return 0;
84 }
85
SQLTest(const char * sql)86 static void SQLTest(const char *sql)
87 {
88 if (g_needSkip) {
89 // 蓝区没有so导致失败,直接跳过测试
90 return;
91 }
92 int errCode = sqlite3_exec(g_sqliteDb, sql, Callback, nullptr, &g_errMsg);
93 if (errCode != SQLITE_OK) {
94 if (g_errMsg != nullptr) {
95 LOGE("SQL error: %s\n", g_errMsg);
96 sqlite3_free(g_errMsg);
97 g_errMsg = nullptr;
98 ASSERT_TRUE(false);
99 }
100 }
101 }
102
103 static int g_rank = 0;
104 static const char *g_dbPath = "test.db";
105
106 /**
107 * @tc.name: SqliteAdapterTest001
108 * @tc.desc: Get blob size over limit
109 * @tc.type: FUNC
110 * @tc.require:
111 * @tc.author: wanghaishuo
112 */
113 HWTEST_F(SqliteAdapterTest, SqliteAdapterTest001, TestSize.Level0)
114 {
115 // Save any error messages
116 char *zErrMsg = nullptr;
117
118 // Save the connection result
119 int rc = sqlite3_open_v2(g_dbPath, &g_sqliteDb, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, nullptr);
120 HandleRc(g_sqliteDb, rc);
121
122 auto before = Clock::now();
123
124 rc = sqlite3_db_config(g_sqliteDb, SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, 1, nullptr);
125 HandleRc(g_sqliteDb, rc);
126
127 rc = sqlite3_load_extension(g_sqliteDb, "libcustomtokenizer.z.so", nullptr, nullptr);
128 HandleRc(g_sqliteDb, rc);
129
130 // create fts table
131 string sql = "CREATE VIRTUAL TABLE example USING fts5(name, content, tokenize = 'customtokenizer')";
132 rc = sqlite3_exec(g_sqliteDb, sql.c_str(), Callback, 0, &zErrMsg);
133 HandleRc(g_sqliteDb, rc);
134
135 const char *SQLINSERT1 =
136 "INSERT INTO example(name, content) VALUES('文档1', '这是一个测试文档,用于测试中文文本的分词和索引。');";
137 SQLTest(SQLINSERT1);
138 const char *SQLINSERT2 =
139 "INSERT INTO example(name, content) VALUES('文档2', '我们将使用这个示例来演示如何在SQLite中进行全文搜索。');";
140 SQLTest(SQLINSERT2);
141 const char *SQLINSERT3 =
142 "INSERT INTO example(name, content) VALUES('文档3', 'ICU分词器能够很好地处理中文文本的分词和分析。');";
143 SQLTest(SQLINSERT3);
144 const char *SQLINSERT4 = "INSERT INTO example(name, content) VALUES('uksdhf', '');";
145 SQLTest(SQLINSERT4);
146 const char *SQLINSERT5 = "INSERT INTO example(name, content) VALUES('', '');";
147 SQLTest(SQLINSERT5);
148 const char *SQLQUERY1 = "SELECT * FROM example WHERE example MATCH '分* OR SQLite';";
149 SQLTest(SQLQUERY1);
150
151 const char *SQLQUERY2 = "SELECT * FROM example WHERE example MATCH '分词' ORDER BY rank;";
152 SQLTest(SQLQUERY2);
153
154 const char *SQLQUERY3 = "SELECT * FROM example WHERE name MATCH 'uksdhf';";
155 SQLTest(SQLQUERY3);
156
157 const char *SQLDROP = "DROP TABLE IF EXISTS example;";
158 SQLTest(SQLDROP);
159
160 // Close the connection
161 sqlite3_close(g_sqliteDb);
162 }
163
164 /**
165 * @tc.name: SqliteAdapterTest002
166 * @tc.desc: Test cut for short words
167 * @tc.type: FUNC
168 * @tc.require:
169 * @tc.author: liaoyonghuang
170 */
171 HWTEST_F(SqliteAdapterTest, SqliteAdapterTest002, TestSize.Level0)
172 {
173 /**
174 * @tc.steps: step1. prepare db
175 * @tc.expected: step1. OK.
176 */
177 // Save any error messages
178 char *zErrMsg = nullptr;
179
180 // Save the connection result
181 int rc = sqlite3_open_v2(g_dbPath, &g_sqliteDb, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, nullptr);
182 HandleRc(g_sqliteDb, rc);
183
184 rc = sqlite3_db_config(g_sqliteDb, SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, 1, nullptr);
185 HandleRc(g_sqliteDb, rc);
186
187 rc = sqlite3_load_extension(g_sqliteDb, "libcustomtokenizer.z.so", nullptr, nullptr);
188 HandleRc(g_sqliteDb, rc);
189 /**
190 * @tc.steps: step2. create table
191 * @tc.expected: step2. OK.
192 */
193 string sql = "CREATE VIRTUAL TABLE example USING fts5(content, tokenize = 'customtokenizer cut_mode short_words')";
194 rc = sqlite3_exec(g_sqliteDb, sql.c_str(), Callback, 0, &zErrMsg);
195 HandleRc(g_sqliteDb, rc);
196 /**
197 * @tc.steps: step3. insert records
198 * @tc.expected: step3. OK.
199 */
200 std::vector<std::string> records = {
201 "电子邮件",
202 "这是一封电子邮件",
203 "这是一封关于少数民族的电子邮件",
204 "华中师范大学是一所位于武汉市的全日制综合性师范大学",
205 "中华人民共和国",
206 "武汉市长江大桥Wuhan Yangtze River Bridge是武汉市最长的桥"
207 };
208 for (const auto &record : records) {
209 std::string insertSql = "insert into example values('" + record + "');";
210 SQLTest(insertSql.c_str());
211 }
212 /**
213 * @tc.steps: step4. test cut for short words
214 * @tc.expected: step4. OK.
215 */
216 std::vector<std::pair<std::string, int>> expectResult = {
217 {"电子", 3}, {"邮件", 3}, {"电子邮件", 3}, {"少数", 1}, {"民族", 1}, {"少数民族", 1}, {"华中", 1},
218 {"中师", 1}, {"师范", 1}, {"共和", 1}, {"共和国", 1}, {"人民共和国", 0}, {"Yangtze", 1}, {"Wuhan", 1},
219 {"市长", 0}
220 };
221 // 蓝区没有so导致失败,直接跳过测试
222 if (!g_needSkip) {
223 for (const auto &[word, expectMatchNum] : expectResult) {
224 std::string querySql = "SELECT count(*) FROM example WHERE content MATCH '" + word + "';";
225 EXPECT_EQ(sqlite3_exec(g_sqliteDb, querySql.c_str(), QueryCallback,
226 reinterpret_cast<void*>(expectMatchNum), nullptr), SQLITE_OK);
227 }
228 }
229
230 const char *SQLDROP = "DROP TABLE IF EXISTS example;";
231 SQLTest(SQLDROP);
232 EXPECT_EQ(sqlite3_close(g_sqliteDb), SQLITE_OK);
233 }
234
235 /**
236 * @tc.name: SqliteAdapterTest003
237 * @tc.desc: Test invalid args
238 * @tc.type: FUNC
239 * @tc.require:
240 * @tc.author: liaoyonghuang
241 */
242 HWTEST_F(SqliteAdapterTest, SqliteAdapterTest003, TestSize.Level0)
243 {
244 /**
245 * @tc.steps: step1. prepare db
246 * @tc.expected: step1. OK.
247 */
248 // Save any error messages
249 char *zErrMsg = nullptr;
250
251 // Save the connection result
252 int rc = sqlite3_open_v2(g_dbPath, &g_sqliteDb, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, nullptr);
253 HandleRc(g_sqliteDb, rc);
254
255 rc = sqlite3_db_config(g_sqliteDb, SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, 1, nullptr);
256 HandleRc(g_sqliteDb, rc);
257
258 rc = sqlite3_load_extension(g_sqliteDb, "libcustomtokenizer.z.so", nullptr, nullptr);
259 HandleRc(g_sqliteDb, rc);
260 /**
261 * @tc.steps: step2. create table which invalid args
262 * @tc.expected: step2. return SQLITE_ERROR.
263 */
264 string sql = "CREATE VIRTUAL TABLE example USING fts5(content, tokenize = 'customtokenizer cut_mode')";
265 rc = sqlite3_exec(g_sqliteDb, sql.c_str(), Callback, 0, &zErrMsg);
266 EXPECT_EQ(SQLITE_ERROR, rc);
267
268 sql = "CREATE VIRTUAL TABLE example USING fts5(content, tokenize = 'customtokenizer cut_mode xxx xxx')";
269 rc = sqlite3_exec(g_sqliteDb, sql.c_str(), Callback, 0, &zErrMsg);
270 EXPECT_EQ(SQLITE_ERROR, rc);
271
272 sql = "CREATE VIRTUAL TABLE example USING fts5(content, tokenize = 'customtokenizer cut_mode xxx')";
273 rc = sqlite3_exec(g_sqliteDb, sql.c_str(), Callback, 0, &zErrMsg);
274 EXPECT_EQ(SQLITE_ERROR, rc);
275
276 sql = "CREATE VIRTUAL TABLE example USING fts5(content, tokenize = 'customtokenizer xxx short_words')";
277 rc = sqlite3_exec(g_sqliteDb, sql.c_str(), Callback, 0, &zErrMsg);
278 EXPECT_EQ(SQLITE_ERROR, rc);
279
280 const char *SQLDROP = "DROP TABLE IF EXISTS example;";
281 SQLTest(SQLDROP);
282 EXPECT_EQ(sqlite3_close(g_sqliteDb), SQLITE_OK);
283 }
284
285 /**
286 * @tc.name: SqliteAdapterTest004
287 * @tc.desc: Test cut_mode default
288 * @tc.type: FUNC
289 * @tc.require:
290 * @tc.author: liaoyonghuang
291 */
292 HWTEST_F(SqliteAdapterTest, SqliteAdapterTest004, TestSize.Level0)
293 {
294 /**
295 * @tc.steps: step1. prepare db
296 * @tc.expected: step1. OK.
297 */
298 // Save any error messages
299 char *zErrMsg = nullptr;
300
301 // Save the connection result
302 int rc = sqlite3_open_v2(g_dbPath, &g_sqliteDb, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, nullptr);
303 HandleRc(g_sqliteDb, rc);
304
305 rc = sqlite3_db_config(g_sqliteDb, SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, 1, nullptr);
306 HandleRc(g_sqliteDb, rc);
307
308 rc = sqlite3_load_extension(g_sqliteDb, "libcustomtokenizer.z.so", nullptr, nullptr);
309 HandleRc(g_sqliteDb, rc);
310 /**
311 * @tc.steps: step2. create table
312 * @tc.expected: step2. OK.
313 */
314 string sql = "CREATE VIRTUAL TABLE example USING fts5(content, tokenize = 'customtokenizer cut_mode default')";
315 rc = sqlite3_exec(g_sqliteDb, sql.c_str(), Callback, 0, &zErrMsg);
316 HandleRc(g_sqliteDb, rc);
317 /**
318 * @tc.steps: step3. insert records
319 * @tc.expected: step3. OK.
320 */
321 std::vector<std::string> records = {
322 "电子邮件",
323 "这是一封电子邮件",
324 "这是一封关于少数民族的电子邮件"
325 };
326 for (const auto &record : records) {
327 std::string insertSql = "insert into example values('" + record + "');";
328 SQLTest(insertSql.c_str());
329 }
330 /**
331 * @tc.steps: step4. test cut for short words
332 * @tc.expected: step4. OK.
333 */
334 std::vector<std::pair<std::string, int>> expectResult = {
335 {"电子", 0}, {"邮件", 0}, {"电子邮件", 3}, {"少数", 0}, {"民族", 0}, {"少数民族", 1}
336 };
337 // 蓝区没有so导致失败,直接跳过测试
338 if (!g_needSkip) {
339 for (const auto &[word, expectMatchNum] : expectResult) {
340 std::string querySql = "SELECT count(*) FROM example WHERE content MATCH '" + word + "';";
341 EXPECT_EQ(sqlite3_exec(g_sqliteDb, querySql.c_str(), QueryCallback,
342 reinterpret_cast<void*>(expectMatchNum), nullptr), SQLITE_OK);
343 }
344 }
345
346 const char *SQLDROP = "DROP TABLE IF EXISTS example;";
347 SQLTest(SQLDROP);
348 EXPECT_EQ(sqlite3_close(g_sqliteDb), SQLITE_OK);
349 }