• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }