feat(db): 实现数据库查询中的数组参数展开和空数组处理
- 在 Get 方法中添加无参数时的默认字段和 LIMIT 1 处理 - 实现 expandArrayPlaceholder 方法,自动展开 IN (?) 和 NOT IN (?) 中的数组参数 - 为空数组的 IN 条件生成 1=0 永假条件,NOT IN 生成 1=1 永真条件 - 在 queryWithRetry 和 execWithRetry 中集成数组占位符预处理 - 修复 where.go 中空切片条件的处理逻辑 - 添加完整的 IN/NOT IN 数组查询测试用例 - 更新 .gitignore 规则格式
This commit is contained in:
+125
-45
@@ -1,9 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
. "code.hoteas.com/golang/hotime"
|
||||
@@ -11,33 +9,7 @@ import (
|
||||
. "code.hoteas.com/golang/hotime/db"
|
||||
)
|
||||
|
||||
// 调试日志文件路径
|
||||
const debugLogPath = `d:\work\hotimev1\.cursor\debug.log`
|
||||
|
||||
// debugLog 写入调试日志
|
||||
func debugLog(location, message string, data interface{}, hypothesisId string) {
|
||||
// #region agent log
|
||||
logEntry := Map{
|
||||
"location": location,
|
||||
"message": message,
|
||||
"data": data,
|
||||
"timestamp": time.Now().UnixMilli(),
|
||||
"sessionId": "debug-session",
|
||||
"runId": "hotimedb-test-run",
|
||||
"hypothesisId": hypothesisId,
|
||||
}
|
||||
jsonBytes, _ := json.Marshal(logEntry)
|
||||
f, err := os.OpenFile(debugLogPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||
if err == nil {
|
||||
f.WriteString(string(jsonBytes) + "\n")
|
||||
f.Close()
|
||||
}
|
||||
// #endregion
|
||||
}
|
||||
|
||||
func main() {
|
||||
debugLog("main.go:35", "开始 HoTimeDB 全功能测试", nil, "START")
|
||||
|
||||
appIns := Init("config/config.json")
|
||||
appIns.SetConnectListener(func(that *Context) (isFinished bool) {
|
||||
return isFinished
|
||||
@@ -49,7 +21,6 @@ func main() {
|
||||
// 测试入口 - 运行所有测试
|
||||
"all": func(that *Context) {
|
||||
results := Map{}
|
||||
debugLog("main.go:48", "开始所有测试", nil, "ALL_TESTS")
|
||||
|
||||
// 初始化测试表
|
||||
initTestTables(that)
|
||||
@@ -84,16 +55,13 @@ func main() {
|
||||
// 10. 原生 SQL 测试
|
||||
results["10_raw_sql"] = testRawSQL(that)
|
||||
|
||||
debugLog("main.go:80", "所有测试完成", results, "ALL_TESTS_DONE")
|
||||
that.Display(0, results)
|
||||
},
|
||||
|
||||
// 查询数据库表结构
|
||||
"tables": func(that *Context) {
|
||||
debugLog("main.go:tables", "查询数据库表结构", nil, "TABLES")
|
||||
// 查询所有表
|
||||
tables := that.Db.Query("SHOW TABLES")
|
||||
debugLog("main.go:tables", "表列表", tables, "TABLES")
|
||||
that.Display(0, Map{"tables": tables})
|
||||
},
|
||||
|
||||
@@ -108,7 +76,6 @@ func main() {
|
||||
columns := that.Db.Query("DESCRIBE " + tableName)
|
||||
// 查询表数据(前10条)
|
||||
data := that.Db.Select(tableName, Map{"LIMIT": 10})
|
||||
debugLog("main.go:describe", "表结构", Map{"table": tableName, "columns": columns, "data": data}, "DESCRIBE")
|
||||
that.Display(0, Map{"table": tableName, "columns": columns, "sample_data": data})
|
||||
},
|
||||
|
||||
@@ -144,11 +111,6 @@ func initTestTables(that *Context) {
|
||||
adminCount := that.Db.Count("admin")
|
||||
articleCount := that.Db.Count("article")
|
||||
|
||||
debugLog("main.go:init", "MySQL数据库初始化检查完成", Map{
|
||||
"adminCount": adminCount,
|
||||
"articleCount": articleCount,
|
||||
"dbType": "MySQL",
|
||||
}, "INIT")
|
||||
}
|
||||
|
||||
// ==================== 1. 基础 CRUD 测试 ====================
|
||||
@@ -156,8 +118,6 @@ func testBasicCRUD(that *Context) Map {
|
||||
result := Map{"name": "基础CRUD测试", "tests": Slice{}}
|
||||
tests := Slice{}
|
||||
|
||||
debugLog("main.go:103", "开始基础 CRUD 测试 (MySQL)", nil, "H1_CRUD")
|
||||
|
||||
// 1.1 Insert 测试 - 使用 admin 表
|
||||
insertTest := Map{"name": "Insert 插入测试 (admin表)"}
|
||||
adminId := that.Db.Insert("admin", Map{
|
||||
@@ -173,7 +133,6 @@ func testBasicCRUD(that *Context) Map {
|
||||
insertTest["result"] = adminId > 0
|
||||
insertTest["adminId"] = adminId
|
||||
insertTest["lastQuery"] = that.Db.LastQuery
|
||||
debugLog("main.go:118", "Insert 测试", Map{"adminId": adminId, "success": adminId > 0, "query": that.Db.LastQuery}, "H1_INSERT")
|
||||
tests = append(tests, insertTest)
|
||||
|
||||
// 1.2 Get 测试
|
||||
@@ -181,7 +140,6 @@ func testBasicCRUD(that *Context) Map {
|
||||
admin := that.Db.Get("admin", "*", Map{"id": adminId})
|
||||
getTest["result"] = admin != nil && admin.GetInt64("id") == adminId
|
||||
getTest["admin"] = admin
|
||||
debugLog("main.go:126", "Get 测试", Map{"admin": admin, "success": admin != nil}, "H1_GET")
|
||||
tests = append(tests, getTest)
|
||||
|
||||
// 1.3 Select 测试 - 单条件
|
||||
@@ -189,7 +147,6 @@ func testBasicCRUD(that *Context) Map {
|
||||
admins1 := that.Db.Select("admin", "*", Map{"state": 1, "LIMIT": 5})
|
||||
selectTest1["result"] = len(admins1) >= 0 // 可能表中没有数据
|
||||
selectTest1["count"] = len(admins1)
|
||||
debugLog("main.go:134", "Select 单条件测试", Map{"count": len(admins1)}, "H1_SELECT1")
|
||||
tests = append(tests, selectTest1)
|
||||
|
||||
// 1.4 Select 测试 - 多条件(自动 AND)
|
||||
@@ -203,7 +160,6 @@ func testBasicCRUD(that *Context) Map {
|
||||
selectTest2["result"] = true // 只要不报错就算成功
|
||||
selectTest2["count"] = len(admins2)
|
||||
selectTest2["lastQuery"] = that.Db.LastQuery
|
||||
debugLog("main.go:149", "Select 多条件自动AND测试", Map{"count": len(admins2), "query": that.Db.LastQuery}, "H1_SELECT2")
|
||||
tests = append(tests, selectTest2)
|
||||
|
||||
// 1.5 Update 测试
|
||||
@@ -214,7 +170,6 @@ func testBasicCRUD(that *Context) Map {
|
||||
}, Map{"id": adminId})
|
||||
updateTest["result"] = affected > 0
|
||||
updateTest["affected"] = affected
|
||||
debugLog("main.go:160", "Update 测试", Map{"affected": affected}, "H1_UPDATE")
|
||||
tests = append(tests, updateTest)
|
||||
|
||||
// 1.6 Delete 测试 - 使用 test_batch 表
|
||||
@@ -956,6 +911,131 @@ func testRawSQL(that *Context) Map {
|
||||
debugLog("main.go:899", "Exec 原生执行测试", test2, "H10_EXEC")
|
||||
tests = append(tests, test2)
|
||||
|
||||
// ==================== IN/NOT IN 数组测试 ====================
|
||||
// H1: IN (?) 配合非空数组能正确展开
|
||||
test3 := Map{"name": "H1: IN (?) 非空数组展开"}
|
||||
// #region agent log
|
||||
debugLog("main.go:test3", "H1测试开始: IN非空数组", Map{"ids": []int{1, 2, 3, 4, 5}}, "H1")
|
||||
// #endregion
|
||||
articles3 := that.Db.Query("SELECT id, title FROM `article` WHERE id IN (?) LIMIT 10", []int{1, 2, 3, 4, 5})
|
||||
// #region agent log
|
||||
debugLog("main.go:test3", "H1测试结果: IN非空数组", Map{
|
||||
"count": len(articles3),
|
||||
"lastQuery": that.Db.LastQuery,
|
||||
"data": articles3,
|
||||
}, "H1")
|
||||
// #endregion
|
||||
test3["result"] = len(articles3) >= 0
|
||||
test3["count"] = len(articles3)
|
||||
test3["lastQuery"] = that.Db.LastQuery
|
||||
tests = append(tests, test3)
|
||||
|
||||
// H2: IN (?) 配合空数组替换为 1=0
|
||||
test4 := Map{"name": "H2: IN (?) 空数组替换为1=0"}
|
||||
// #region agent log
|
||||
debugLog("main.go:test4", "H2测试开始: IN空数组", Map{"ids": []int{}}, "H2")
|
||||
// #endregion
|
||||
articles4 := that.Db.Query("SELECT id, title FROM `article` WHERE id IN (?) LIMIT 10", []int{})
|
||||
// #region agent log
|
||||
debugLog("main.go:test4", "H2测试结果: IN空数组", Map{
|
||||
"count": len(articles4),
|
||||
"lastQuery": that.Db.LastQuery,
|
||||
"expected": "应包含1=0,返回0条记录",
|
||||
}, "H2")
|
||||
// #endregion
|
||||
test4["result"] = len(articles4) == 0 // 空数组的IN应该返回0条
|
||||
test4["count"] = len(articles4)
|
||||
test4["lastQuery"] = that.Db.LastQuery
|
||||
test4["expected"] = "count=0, SQL应包含1=0"
|
||||
tests = append(tests, test4)
|
||||
|
||||
// H3: NOT IN (?) 配合空数组替换为 1=1
|
||||
test5 := Map{"name": "H3: NOT IN (?) 空数组替换为1=1"}
|
||||
// #region agent log
|
||||
debugLog("main.go:test5", "H3测试开始: NOT IN空数组", Map{"ids": []int{}}, "H3")
|
||||
// #endregion
|
||||
articles5 := that.Db.Query("SELECT id, title FROM `article` WHERE id NOT IN (?) LIMIT 10", []int{})
|
||||
// #region agent log
|
||||
debugLog("main.go:test5", "H3测试结果: NOT IN空数组", Map{
|
||||
"count": len(articles5),
|
||||
"lastQuery": that.Db.LastQuery,
|
||||
"expected": "应包含1=1,返回所有记录(限制10条)",
|
||||
}, "H3")
|
||||
// #endregion
|
||||
test5["result"] = len(articles5) > 0 // NOT IN空数组应该返回记录
|
||||
test5["count"] = len(articles5)
|
||||
test5["lastQuery"] = that.Db.LastQuery
|
||||
test5["expected"] = "count>0, SQL应包含1=1"
|
||||
tests = append(tests, test5)
|
||||
|
||||
// H4: NOT IN (?) 配合非空数组正常展开
|
||||
test6 := Map{"name": "H4: NOT IN (?) 非空数组展开"}
|
||||
// #region agent log
|
||||
debugLog("main.go:test6", "H4测试开始: NOT IN非空数组", Map{"ids": []int{1, 2, 3}}, "H4")
|
||||
// #endregion
|
||||
articles6 := that.Db.Query("SELECT id, title FROM `article` WHERE id NOT IN (?) LIMIT 10", []int{1, 2, 3})
|
||||
// #region agent log
|
||||
debugLog("main.go:test6", "H4测试结果: NOT IN非空数组", Map{
|
||||
"count": len(articles6),
|
||||
"lastQuery": that.Db.LastQuery,
|
||||
}, "H4")
|
||||
// #endregion
|
||||
test6["result"] = len(articles6) >= 0
|
||||
test6["count"] = len(articles6)
|
||||
test6["lastQuery"] = that.Db.LastQuery
|
||||
tests = append(tests, test6)
|
||||
|
||||
// H5: 普通 ? 占位符保持原有行为
|
||||
test7 := Map{"name": "H5: 普通?占位符不受影响"}
|
||||
// #region agent log
|
||||
debugLog("main.go:test7", "H5测试开始: 普通占位符", Map{"state": 0, "limit": 5}, "H5")
|
||||
// #endregion
|
||||
articles7 := that.Db.Query("SELECT id, title FROM `article` WHERE state = ? LIMIT ?", 0, 5)
|
||||
// #region agent log
|
||||
debugLog("main.go:test7", "H5测试结果: 普通占位符", Map{
|
||||
"count": len(articles7),
|
||||
"lastQuery": that.Db.LastQuery,
|
||||
}, "H5")
|
||||
// #endregion
|
||||
test7["result"] = len(articles7) >= 0
|
||||
test7["count"] = len(articles7)
|
||||
test7["lastQuery"] = that.Db.LastQuery
|
||||
tests = append(tests, test7)
|
||||
|
||||
// 额外测试: Select ORM方法的空数组处理
|
||||
test8 := Map{"name": "ORM Select: IN空数组"}
|
||||
// #region agent log
|
||||
debugLog("main.go:test8", "ORM测试开始: Select IN空数组", nil, "H2_ORM")
|
||||
// #endregion
|
||||
articles8 := that.Db.Select("article", "id,title", Map{"id": []int{}, "LIMIT": 10})
|
||||
// #region agent log
|
||||
debugLog("main.go:test8", "ORM测试结果: Select IN空数组", Map{
|
||||
"count": len(articles8),
|
||||
"lastQuery": that.Db.LastQuery,
|
||||
}, "H2_ORM")
|
||||
// #endregion
|
||||
test8["result"] = len(articles8) == 0
|
||||
test8["count"] = len(articles8)
|
||||
test8["lastQuery"] = that.Db.LastQuery
|
||||
tests = append(tests, test8)
|
||||
|
||||
// 额外测试: Select ORM方法的NOT IN空数组处理
|
||||
test9 := Map{"name": "ORM Select: NOT IN空数组"}
|
||||
// #region agent log
|
||||
debugLog("main.go:test9", "ORM测试开始: Select NOT IN空数组", nil, "H3_ORM")
|
||||
// #endregion
|
||||
articles9 := that.Db.Select("article", "id,title", Map{"id[!]": []int{}, "LIMIT": 10})
|
||||
// #region agent log
|
||||
debugLog("main.go:test9", "ORM测试结果: Select NOT IN空数组", Map{
|
||||
"count": len(articles9),
|
||||
"lastQuery": that.Db.LastQuery,
|
||||
}, "H3_ORM")
|
||||
// #endregion
|
||||
test9["result"] = len(articles9) > 0 // NOT IN 空数组应返回记录
|
||||
test9["count"] = len(articles9)
|
||||
test9["lastQuery"] = that.Db.LastQuery
|
||||
tests = append(tests, test9)
|
||||
|
||||
result["tests"] = tests
|
||||
result["success"] = true
|
||||
return result
|
||||
|
||||
Reference in New Issue
Block a user