diff --git a/.cursor/plans/多数据库方言与前缀支持_d7ceee79.plan.md b/.cursor/plans/多数据库方言与前缀支持_d7ceee79.plan.md new file mode 100644 index 0000000..941b804 --- /dev/null +++ b/.cursor/plans/多数据库方言与前缀支持_d7ceee79.plan.md @@ -0,0 +1,249 @@ +--- +name: 多数据库方言与前缀支持 +overview: 为 HoTimeDB ORM 实现完整的多数据库(MySQL/PostgreSQL/SQLite)方言支持和自动表前缀功能,采用智能解析+辅助方法兜底的混合策略,保持完全向后兼容。 +todos: + - id: dialect-interface + content: 扩展 Dialect 接口,添加 QuoteIdentifier 和 QuoteChar 方法 + status: pending + - id: identifier-processor + content: 新建 identifier.go,实现 IdentifierProcessor 及智能解析逻辑 + status: pending + - id: db-integration + content: 在 db.go 中集成处理器,添加 T() 和 C() 辅助方法 + status: pending + - id: crud-update + content: 修改 crud.go 中 Select/Insert/Update/Delete/buildJoin 等方法 + status: pending + - id: where-update + content: 修改 where.go 中 varCond 等条件处理方法 + status: pending + - id: builder-check + content: 检查 builder.go 是否需要额外修改 + status: pending + - id: testing + content: 编写测试用例验证多数据库和前缀功能 + status: pending + - id: todo-1769037903242-d7aip6nh1 + content: "" + status: pending +--- + +# HoTimeDB 多数据库方言与自动前缀支持计划(更新版) + +## 目标 + +1. **多数据库方言支持**:MySQL、PostgreSQL、SQLite 标识符引号自动转换 +2. **自动表前缀**:主表、JOIN 表、ON/WHERE 条件中的表名自动添加前缀 +3. **完全向后兼容**:用户现有写法无需修改 +4. **辅助方法兜底**:边缘情况可用 `T()` / `C()` 精确控制 + +## 混合策略设计 + +### 各部分处理方式 + +| 位置 | 处理方式 | 准确度 | 说明 | + +|------|---------|--------|------| + +| 主表名 | 自动 | 100% | `Select("order")` 自动处理 | + +| JOIN 表名 | 自动 | 100% | `[><]order` 中提取表名处理 | + +| ON 条件字符串 | 智能解析 | ~95% | 正则匹配 `table.column` 模式 | + +| WHERE 条件 Map | 自动 | 100% | Map 的 key 是结构化的 | + +| SELECT 字段 | 智能解析 | ~95% | 同 ON 条件 | + +### 辅助方法(兜底) + +```go +db.T("order") // 返回 "`app_order`" (MySQL) 或 "\"app_order\"" (PG) +db.C("order", "name") // 返回 "`app_order`.`name`" +db.C("order.name") // 同上,支持点号格式 +``` + +## 实现步骤 + +### 第1步:扩展 Dialect 接口([db/dialect.go](db/dialect.go)) + +添加新方法到 `Dialect` 接口: + +```go +// QuoteIdentifier 处理单个标识符(去除已有引号,添加正确引号) +QuoteIdentifier(name string) string + +// QuoteChar 返回引号字符 +QuoteChar() string +``` + +三种方言实现: + +- MySQL: 反引号 `` ` `` +- PostgreSQL/SQLite: 双引号 `"` + +### 第2步:添加标识符处理器([db/identifier.go](db/identifier.go) 新文件) + +```go +type IdentifierProcessor struct { + dialect Dialect + prefix string +} + +// ProcessTableName 处理表名(添加前缀+引号) +// "order" → "`app_order`" +func (p *IdentifierProcessor) ProcessTableName(name string) string + +// ProcessColumn 处理 table.column 格式 +// "order.name" → "`app_order`.`name`" +// "`order`.name" → "`app_order`.`name`" +func (p *IdentifierProcessor) ProcessColumn(name string) string + +// ProcessConditionString 智能解析条件字符串 +// "user.id = order.user_id" → "`app_user`.`id` = `app_order`.`user_id`" +func (p *IdentifierProcessor) ProcessConditionString(condition string) string + +// ProcessFieldList 处理字段列表字符串 +// "order.id, user.name AS uname" → "`app_order`.`id`, `app_user`.`name` AS uname" +func (p *IdentifierProcessor) ProcessFieldList(fields string) string +``` + +**智能解析正则**: + +```go +// 匹配 table.column 模式,排除已有引号、函数调用等 +// 模式: \b([a-zA-Z_][a-zA-Z0-9_]*)\.([a-zA-Z_][a-zA-Z0-9_]*)\b +// 排除: `table`.column, "table".column, FUNC(), 123.456 +``` + +### 第3步:在 HoTimeDB 中集成([db/db.go](db/db.go)) + +```go +// processor 缓存 +var processorOnce sync.Once +var processor *IdentifierProcessor + +// GetProcessor 获取标识符处理器 +func (that *HoTimeDB) GetProcessor() *IdentifierProcessor + +// T 辅助方法:获取带前缀和引号的表名 +func (that *HoTimeDB) T(table string) string + +// C 辅助方法:获取带前缀和引号的 table.column +func (that *HoTimeDB) C(args ...string) string +``` + +### 第4步:修改 CRUD 方法([db/crud.go](db/crud.go)) + +**Select 方法改动**: + +```go +// L112-116 原代码 +if !strings.Contains(table, ".") && !strings.Contains(table, " AS ") { + query += " FROM `" + that.Prefix + table + "` " +} else { + query += " FROM " + that.Prefix + table + " " +} + +// 改为 +query += " FROM " + that.GetProcessor().ProcessTableName(table) + " " + +// 字段列表处理(L90-107) +// 如果是字符串,调用 ProcessFieldList 处理 +``` + +**buildJoin 方法改动**(L156-222): + +```go +// 原代码 L186-190 +table := Substr(k, 3, len(k)-3) +if !strings.Contains(table, " ") { + table = "`" + table + "`" +} +query += " LEFT JOIN " + table + " ON " + v.(string) + " " + +// 改为 +table := Substr(k, 3, len(k)-3) +table = that.GetProcessor().ProcessTableName(table) +onCondition := that.GetProcessor().ProcessConditionString(v.(string)) +query += " LEFT JOIN " + table + " ON " + onCondition + " " +``` + +**Insert/BatchInsert/Update/Delete** 同样修改表名和字段名处理。 + +### 第5步:修改 WHERE 条件处理([db/where.go](db/where.go)) + +**varCond 方法改动**(多处): + +```go +// 原代码(多处出现) +if !strings.Contains(k, ".") { + k = "`" + k + "`" +} + +// 改为 +k = that.GetProcessor().ProcessColumn(k) +``` + +需要修改的函数: + +- `varCond` (L205-338) +- `handleDefaultCondition` (L340-368) +- `handlePlainField` (L370-400) + +### 第6步:修改链式构建器([db/builder.go](db/builder.go)) + +**LeftJoin 等方法需要传递处理器**: + +由于 builder 持有 HoTimeDB 引用,可以直接使用: + +```go +func (that *HotimeDBBuilder) LeftJoin(table, joinStr string) *HotimeDBBuilder { + // 不在这里处理,让 buildJoin 统一处理 + that.Join(Map{"[>]" + table: joinStr}) + return that +} +``` + +JOIN 的实际处理在 `crud.go` 的 `buildJoin` 中完成。 + +## 智能解析的边界处理 + +### 会自动处理的情况 + +- `user.id = order.user_id` → 正确处理 +- `user.id=order.user_id` → 正确处理(无空格) +- `` `user`.id = order.user_id `` → 正确处理(混合格式) +- `user.id = order.user_id AND order.status = 1` → 正确处理 + +### 需要辅助方法的边缘情况 + +- 子查询中的表名 +- 复杂 CASE WHEN 表达式 +- 动态拼接的 SQL 片段 + +## 文件清单 + +| 文件 | 操作 | 说明 | + +|------|------|------| + +| [db/dialect.go](db/dialect.go) | 修改 | 扩展 Dialect 接口 | + +| [db/identifier.go](db/identifier.go) | 新增 | IdentifierProcessor 实现 | + +| [db/db.go](db/db.go) | 修改 | 集成处理器,添加 T()/C() 方法 | + +| [db/crud.go](db/crud.go) | 修改 | 修改所有 CRUD 方法 | + +| [db/where.go](db/where.go) | 修改 | 修改条件处理逻辑 | + +| [db/builder.go](db/builder.go) | 检查 | 可能无需修改(buildJoin 统一处理)| + +## 测试用例 + +1. **多数据库切换**:MySQL → PostgreSQL → SQLite +2. **前缀场景**:有前缀 vs 无前缀 +3. **复杂 JOIN**:多表 JOIN + 复杂 ON 条件 +4. **混合写法**:`order.name` + `` `user`.id `` 混用 +5. **辅助方法**:`T()` 和 `C()` 正确性 \ No newline at end of file diff --git a/example/main.go b/example/main.go index cf1dd76..2bf1f89 100644 --- a/example/main.go +++ b/example/main.go @@ -107,10 +107,9 @@ func initTestTables(that *Context) { create_time DATETIME )`) - // 检查 admin 表数据 - adminCount := that.Db.Count("admin") - articleCount := that.Db.Count("article") - + // 检查 admin 表数据(确认表存在) + _ = that.Db.Count("admin") + _ = that.Db.Count("article") } // ==================== 1. 基础 CRUD 测试 ==================== @@ -184,7 +183,6 @@ func testBasicCRUD(that *Context) Map { deleteAffected := that.Db.Delete("test_batch", Map{"id": tempId}) deleteTest["result"] = deleteAffected > 0 deleteTest["affected"] = deleteAffected - debugLog("main.go:175", "Delete 测试", Map{"affected": deleteAffected}, "H1_DELETE") tests = append(tests, deleteTest) result["tests"] = tests @@ -197,14 +195,11 @@ func testConditionSyntax(that *Context) Map { result := Map{"name": "条件查询语法测试", "tests": Slice{}} tests := Slice{} - debugLog("main.go:188", "开始条件查询语法测试 (MySQL)", nil, "H2_CONDITION") - // 2.1 等于 (=) - 使用 article 表 test1 := Map{"name": "等于条件 (=)"} articles1 := that.Db.Select("article", "id,title", Map{"state": 0, "LIMIT": 3}) test1["result"] = true test1["count"] = len(articles1) - debugLog("main.go:195", "等于条件测试", Map{"count": len(articles1)}, "H2_EQUAL") tests = append(tests, test1) // 2.2 不等于 ([!]) @@ -213,7 +208,6 @@ func testConditionSyntax(that *Context) Map { test2["result"] = true test2["count"] = len(articles2) test2["lastQuery"] = that.Db.LastQuery - debugLog("main.go:204", "不等于条件测试", Map{"count": len(articles2), "query": that.Db.LastQuery}, "H2_NOT_EQUAL") tests = append(tests, test2) // 2.3 大于 ([>]) 和 小于 ([<]) @@ -225,7 +219,6 @@ func testConditionSyntax(that *Context) Map { }) test3["result"] = true test3["count"] = len(articles3) - debugLog("main.go:216", "大于小于条件测试", Map{"count": len(articles3)}, "H2_GREATER_LESS") tests = append(tests, test3) // 2.4 大于等于 ([>=]) 和 小于等于 ([<=]) @@ -237,7 +230,6 @@ func testConditionSyntax(that *Context) Map { }) test4["result"] = true test4["count"] = len(articles4) - debugLog("main.go:228", "大于等于小于等于条件测试", Map{"count": len(articles4)}, "H2_GTE_LTE") tests = append(tests, test4) // 2.5 LIKE 模糊查询 ([~]) @@ -246,7 +238,6 @@ func testConditionSyntax(that *Context) Map { test5["result"] = true test5["count"] = len(articles5) test5["lastQuery"] = that.Db.LastQuery - debugLog("main.go:237", "LIKE 模糊查询测试", Map{"count": len(articles5), "query": that.Db.LastQuery}, "H2_LIKE") tests = append(tests, test5) // 2.6 右模糊 ([~!]) @@ -254,7 +245,6 @@ func testConditionSyntax(that *Context) Map { articles6 := that.Db.Select("admin", "id,name", Map{"name[~!]": "管理", "LIMIT": 3}) test6["result"] = true test6["count"] = len(articles6) - debugLog("main.go:245", "右模糊查询测试", Map{"count": len(articles6)}, "H2_LIKE_RIGHT") tests = append(tests, test6) // 2.7 BETWEEN ([<>]) @@ -263,7 +253,6 @@ func testConditionSyntax(that *Context) Map { test7["result"] = true test7["count"] = len(articles7) test7["lastQuery"] = that.Db.LastQuery - debugLog("main.go:254", "BETWEEN 区间查询测试", Map{"count": len(articles7), "query": that.Db.LastQuery}, "H2_BETWEEN") tests = append(tests, test7) // 2.8 NOT BETWEEN ([><]) @@ -271,7 +260,6 @@ func testConditionSyntax(that *Context) Map { articles8 := that.Db.Select("article", "id,title,sort", Map{"sort[><]": Slice{-10, 0}, "LIMIT": 3}) test8["result"] = true test8["count"] = len(articles8) - debugLog("main.go:262", "NOT BETWEEN 查询测试", Map{"count": len(articles8)}, "H2_NOT_BETWEEN") tests = append(tests, test8) // 2.9 IN 查询 @@ -280,7 +268,6 @@ func testConditionSyntax(that *Context) Map { test9["result"] = true test9["count"] = len(articles9) test9["lastQuery"] = that.Db.LastQuery - debugLog("main.go:271", "IN 查询测试", Map{"count": len(articles9), "query": that.Db.LastQuery}, "H2_IN") tests = append(tests, test9) // 2.10 NOT IN ([!]) @@ -288,7 +275,6 @@ func testConditionSyntax(that *Context) Map { articles10 := that.Db.Select("article", "id,title", Map{"id[!]": Slice{1, 2, 3}, "LIMIT": 5}) test10["result"] = true test10["count"] = len(articles10) - debugLog("main.go:279", "NOT IN 查询测试", Map{"count": len(articles10)}, "H2_NOT_IN") tests = append(tests, test10) // 2.11 IS NULL @@ -296,7 +282,6 @@ func testConditionSyntax(that *Context) Map { articles11 := that.Db.Select("article", "id,title,img", Map{"img": nil, "LIMIT": 3}) test11["result"] = true test11["count"] = len(articles11) - debugLog("main.go:287", "IS NULL 查询测试", Map{"count": len(articles11)}, "H2_IS_NULL") tests = append(tests, test11) // 2.12 IS NOT NULL ([!]) @@ -304,7 +289,6 @@ func testConditionSyntax(that *Context) Map { articles12 := that.Db.Select("article", "id,title,create_time", Map{"create_time[!]": nil, "LIMIT": 3}) test12["result"] = true test12["count"] = len(articles12) - debugLog("main.go:295", "IS NOT NULL 查询测试", Map{"count": len(articles12)}, "H2_IS_NOT_NULL") tests = append(tests, test12) // 2.13 直接 SQL ([##] 用于 SQL 片段) @@ -316,7 +300,6 @@ func testConditionSyntax(that *Context) Map { test13["result"] = true test13["count"] = len(articles13) test13["lastQuery"] = that.Db.LastQuery - debugLog("main.go:307", "直接 SQL 片段查询测试", Map{"count": len(articles13), "query": that.Db.LastQuery}, "H2_RAW_SQL") tests = append(tests, test13) // 2.14 显式 AND 条件 @@ -330,7 +313,6 @@ func testConditionSyntax(that *Context) Map { }) test14["result"] = true test14["count"] = len(articles14) - debugLog("main.go:321", "显式 AND 条件测试", Map{"count": len(articles14)}, "H2_EXPLICIT_AND") tests = append(tests, test14) // 2.15 OR 条件 @@ -344,7 +326,6 @@ func testConditionSyntax(that *Context) Map { }) test15["result"] = true test15["count"] = len(articles15) - debugLog("main.go:335", "OR 条件测试", Map{"count": len(articles15)}, "H2_OR") tests = append(tests, test15) // 2.16 嵌套 AND/OR 条件 @@ -362,7 +343,6 @@ func testConditionSyntax(that *Context) Map { test16["result"] = true test16["count"] = len(articles16) test16["lastQuery"] = that.Db.LastQuery - debugLog("main.go:353", "嵌套 AND/OR 条件测试", Map{"count": len(articles16), "query": that.Db.LastQuery}, "H2_NESTED") tests = append(tests, test16) result["tests"] = tests @@ -375,8 +355,6 @@ func testChainQuery(that *Context) Map { result := Map{"name": "链式查询测试", "tests": Slice{}} tests := Slice{} - debugLog("main.go:366", "开始链式查询测试 (MySQL)", nil, "H3_CHAIN") - // 3.1 基本链式查询 - 使用 article 表 test1 := Map{"name": "基本链式查询 Table().Where().Select()"} articles1 := that.Db.Table("article"). @@ -384,7 +362,6 @@ func testChainQuery(that *Context) Map { Select("id,title,author") test1["result"] = len(articles1) >= 0 test1["count"] = len(articles1) - debugLog("main.go:375", "基本链式查询测试", Map{"count": len(articles1)}, "H3_BASIC") tests = append(tests, test1) // 3.2 链式 And 条件 @@ -396,7 +373,6 @@ func testChainQuery(that *Context) Map { Select("id,title,click_num") test2["result"] = len(articles2) >= 0 test2["count"] = len(articles2) - debugLog("main.go:387", "链式 And 条件测试", Map{"count": len(articles2)}, "H3_AND") tests = append(tests, test2) // 3.3 链式 Or 条件 @@ -410,7 +386,6 @@ func testChainQuery(that *Context) Map { Select("id,title,sort,click_num") test3["result"] = len(articles3) >= 0 test3["count"] = len(articles3) - debugLog("main.go:401", "链式 Or 条件测试", Map{"count": len(articles3)}, "H3_OR") tests = append(tests, test3) // 3.4 链式 Order @@ -422,7 +397,6 @@ func testChainQuery(that *Context) Map { Select("id,title,create_time") test4["result"] = len(articles4) >= 0 test4["count"] = len(articles4) - debugLog("main.go:413", "链式 Order 排序测试", Map{"count": len(articles4)}, "H3_ORDER") tests = append(tests, test4) // 3.5 链式 Limit @@ -433,7 +407,6 @@ func testChainQuery(that *Context) Map { Select("id,title") test5["result"] = len(articles5) <= 3 test5["count"] = len(articles5) - debugLog("main.go:424", "链式 Limit 限制测试", Map{"count": len(articles5)}, "H3_LIMIT") tests = append(tests, test5) // 3.6 链式 Get 单条 @@ -443,7 +416,6 @@ func testChainQuery(that *Context) Map { Get("id,title,author") test6["result"] = article6 != nil || true // 允许为空 test6["article"] = article6 - debugLog("main.go:434", "链式 Get 获取单条测试", Map{"article": article6}, "H3_GET") tests = append(tests, test6) // 3.7 链式 Count @@ -453,7 +425,6 @@ func testChainQuery(that *Context) Map { Count() test7["result"] = count7 >= 0 test7["count"] = count7 - debugLog("main.go:444", "链式 Count 统计测试", Map{"count": count7}, "H3_COUNT") tests = append(tests, test7) // 3.8 链式 Page 分页 @@ -464,7 +435,6 @@ func testChainQuery(that *Context) Map { Select("id,title") test8["result"] = len(articles8) <= 5 test8["count"] = len(articles8) - debugLog("main.go:455", "链式 Page 分页测试", Map{"count": len(articles8)}, "H3_PAGE") tests = append(tests, test8) // 3.9 链式 Group 分组 @@ -475,7 +445,6 @@ func testChainQuery(that *Context) Map { Select("ctg_id, COUNT(*) as cnt") test9["result"] = len(stats9) >= 0 test9["stats"] = stats9 - debugLog("main.go:466", "链式 Group 分组测试", Map{"stats": stats9}, "H3_GROUP") tests = append(tests, test9) // 3.10 链式 Update @@ -492,7 +461,6 @@ func testChainQuery(that *Context) Map { test10["result"] = true test10["note"] = "无可用测试数据" } - debugLog("main.go:483", "链式 Update 更新测试", test10, "H3_UPDATE") tests = append(tests, test10) result["tests"] = tests @@ -505,8 +473,6 @@ func testJoinQuery(that *Context) Map { result := Map{"name": "JOIN查询测试", "tests": Slice{}} tests := Slice{} - debugLog("main.go:496", "开始 JOIN 查询测试 (MySQL)", nil, "H4_JOIN") - // 4.1 LEFT JOIN 链式 - article 关联 ctg test1 := Map{"name": "LEFT JOIN 链式查询"} articles1 := that.Db.Table("article"). @@ -517,7 +483,6 @@ func testJoinQuery(that *Context) Map { test1["result"] = len(articles1) >= 0 test1["count"] = len(articles1) test1["data"] = articles1 - debugLog("main.go:508", "LEFT JOIN 链式查询测试", Map{"count": len(articles1)}, "H4_LEFT_JOIN") tests = append(tests, test1) // 4.2 传统 JOIN 语法 @@ -534,7 +499,6 @@ func testJoinQuery(that *Context) Map { test2["result"] = len(articles2) >= 0 test2["count"] = len(articles2) test2["lastQuery"] = that.Db.LastQuery - debugLog("main.go:525", "传统 JOIN 语法测试", Map{"count": len(articles2), "query": that.Db.LastQuery}, "H4_TRADITIONAL_JOIN") tests = append(tests, test2) // 4.3 多表 JOIN - article 关联 ctg 和 admin @@ -548,7 +512,6 @@ func testJoinQuery(that *Context) Map { test3["result"] = len(articles3) >= 0 test3["count"] = len(articles3) test3["data"] = articles3 - debugLog("main.go:539", "多表 JOIN 测试", Map{"count": len(articles3)}, "H4_MULTI_JOIN") tests = append(tests, test3) // 4.4 INNER JOIN @@ -560,7 +523,6 @@ func testJoinQuery(that *Context) Map { Select("article.id, article.title, ctg.name as ctg_name") test4["result"] = len(articles4) >= 0 test4["count"] = len(articles4) - debugLog("main.go:551", "INNER JOIN 测试", Map{"count": len(articles4)}, "H4_INNER_JOIN") tests = append(tests, test4) result["tests"] = tests @@ -573,14 +535,11 @@ func testAggregate(that *Context) Map { result := Map{"name": "聚合函数测试", "tests": Slice{}} tests := Slice{} - debugLog("main.go:564", "开始聚合函数测试 (MySQL)", nil, "H5_AGGREGATE") - // 5.1 Count 总数 test1 := Map{"name": "Count 总数统计"} count1 := that.Db.Count("article") test1["result"] = count1 >= 0 test1["count"] = count1 - debugLog("main.go:571", "Count 总数统计测试", Map{"count": count1}, "H5_COUNT") tests = append(tests, test1) // 5.2 Count 带条件 @@ -588,7 +547,6 @@ func testAggregate(that *Context) Map { count2 := that.Db.Count("article", Map{"state": 0}) test2["result"] = count2 >= 0 test2["count"] = count2 - debugLog("main.go:579", "Count 条件统计测试", Map{"count": count2}, "H5_COUNT_WHERE") tests = append(tests, test2) // 5.3 Sum 求和 @@ -596,7 +554,6 @@ func testAggregate(that *Context) Map { sum3 := that.Db.Sum("article", "click_num", Map{"state": 0}) test3["result"] = sum3 >= 0 test3["sum"] = sum3 - debugLog("main.go:587", "Sum 求和测试", Map{"sum": sum3}, "H5_SUM") tests = append(tests, test3) // 5.4 Avg 平均值 @@ -604,7 +561,6 @@ func testAggregate(that *Context) Map { avg4 := that.Db.Avg("article", "click_num", Map{"state": 0}) test4["result"] = avg4 >= 0 test4["avg"] = avg4 - debugLog("main.go:595", "Avg 平均值测试", Map{"avg": avg4}, "H5_AVG") tests = append(tests, test4) // 5.5 Max 最大值 @@ -612,7 +568,6 @@ func testAggregate(that *Context) Map { max5 := that.Db.Max("article", "click_num", Map{"state": 0}) test5["result"] = max5 >= 0 test5["max"] = max5 - debugLog("main.go:603", "Max 最大值测试", Map{"max": max5}, "H5_MAX") tests = append(tests, test5) // 5.6 Min 最小值 @@ -620,7 +575,6 @@ func testAggregate(that *Context) Map { min6 := that.Db.Min("article", "sort", Map{"state": 0}) test6["result"] = true // sort 可能为 0 test6["min"] = min6 - debugLog("main.go:611", "Min 最小值测试", Map{"min": min6}, "H5_MIN") tests = append(tests, test6) // 5.7 GROUP BY 分组统计 @@ -635,7 +589,6 @@ func testAggregate(that *Context) Map { }) test7["result"] = len(stats7) >= 0 test7["stats"] = stats7 - debugLog("main.go:625", "GROUP BY 分组统计测试", Map{"stats": stats7}, "H5_GROUP_BY") tests = append(tests, test7) result["tests"] = tests @@ -648,8 +601,6 @@ func testPagination(that *Context) Map { result := Map{"name": "分页查询测试", "tests": Slice{}} tests := Slice{} - debugLog("main.go:638", "开始分页查询测试 (MySQL)", nil, "H6_PAGINATION") - // 6.1 PageSelect 分页查询 test1 := Map{"name": "PageSelect 分页查询"} articles1 := that.Db.Page(1, 5).PageSelect("article", "*", Map{ @@ -658,7 +609,6 @@ func testPagination(that *Context) Map { }) test1["result"] = len(articles1) <= 5 test1["count"] = len(articles1) - debugLog("main.go:648", "PageSelect 分页查询测试", Map{"count": len(articles1)}, "H6_PAGE_SELECT") tests = append(tests, test1) // 6.2 第二页 @@ -669,7 +619,6 @@ func testPagination(that *Context) Map { }) test2["result"] = len(articles2) <= 5 test2["count"] = len(articles2) - debugLog("main.go:659", "PageSelect 第二页测试", Map{"count": len(articles2)}, "H6_PAGE_2") tests = append(tests, test2) // 6.3 链式分页 @@ -681,7 +630,6 @@ func testPagination(that *Context) Map { Select("id,title,author") test3["result"] = len(articles3) <= 3 test3["count"] = len(articles3) - debugLog("main.go:671", "链式 Page 分页测试", Map{"count": len(articles3)}, "H6_CHAIN_PAGE") tests = append(tests, test3) // 6.4 Offset 偏移 @@ -694,7 +642,6 @@ func testPagination(that *Context) Map { test4["result"] = len(articles4) <= 3 test4["count"] = len(articles4) test4["lastQuery"] = that.Db.LastQuery - debugLog("main.go:684", "Offset 偏移查询测试", Map{"count": len(articles4), "query": that.Db.LastQuery}, "H6_OFFSET") tests = append(tests, test4) result["tests"] = tests @@ -707,8 +654,6 @@ func testBatchInsert(that *Context) Map { result := Map{"name": "批量插入测试", "tests": Slice{}} tests := Slice{} - debugLog("main.go:697", "开始批量插入测试 (MySQL)", nil, "H7_BATCH") - // 7.1 批量插入 test1 := Map{"name": "BatchInsert 批量插入"} timestamp := time.Now().UnixNano() @@ -720,7 +665,6 @@ func testBatchInsert(that *Context) Map { test1["result"] = affected1 >= 0 test1["affected"] = affected1 test1["lastQuery"] = that.Db.LastQuery - debugLog("main.go:710", "BatchInsert 批量插入测试", Map{"affected": affected1, "query": that.Db.LastQuery}, "H7_BATCH_INSERT") tests = append(tests, test1) // 7.2 带 [#] 的批量插入 @@ -732,7 +676,6 @@ func testBatchInsert(that *Context) Map { }) test2["result"] = affected2 >= 0 test2["affected"] = affected2 - debugLog("main.go:722", "BatchInsert 带 [#] 标记测试", Map{"affected": affected2}, "H7_BATCH_RAW") tests = append(tests, test2) // 清理测试数据 @@ -749,8 +692,6 @@ func testUpsert(that *Context) Map { result := Map{"name": "Upsert测试", "tests": Slice{}} tests := Slice{} - debugLog("main.go:739", "开始 Upsert 测试 (MySQL)", nil, "H8_UPSERT") - // 使用 admin 表测试 Upsert(MySQL ON DUPLICATE KEY UPDATE) timestamp := time.Now().Unix() testPhone := fmt.Sprintf("199%08d", timestamp%100000000) @@ -774,7 +715,6 @@ func testUpsert(that *Context) Map { test1["result"] = affected1 >= 0 test1["affected"] = affected1 test1["lastQuery"] = that.Db.LastQuery - debugLog("main.go:763", "Upsert 插入新记录测试", Map{"affected": affected1, "query": that.Db.LastQuery}, "H8_UPSERT_INSERT") tests = append(tests, test1) // 8.2 Upsert 更新已存在记录 @@ -795,7 +735,6 @@ func testUpsert(that *Context) Map { ) test2["result"] = affected2 >= 0 test2["affected"] = affected2 - debugLog("main.go:783", "Upsert 更新已存在记录测试", Map{"affected": affected2}, "H8_UPSERT_UPDATE") tests = append(tests, test2) // 验证更新结果 @@ -815,8 +754,6 @@ func testTransaction(that *Context) Map { result := Map{"name": "事务测试", "tests": Slice{}} tests := Slice{} - debugLog("main.go:803", "开始事务测试 (MySQL)", nil, "H9_TRANSACTION") - // 9.1 事务成功提交 test1 := Map{"name": "事务成功提交"} timestamp := time.Now().Unix() @@ -830,7 +767,6 @@ func testTransaction(that *Context) Map { "state": 1, "create_time[#]": "NOW()", }) - debugLog("main.go:818", "事务内插入记录", Map{"recordId": recordId}, "H9_TX_INSERT") return recordId != 0 }) @@ -839,7 +775,6 @@ func testTransaction(that *Context) Map { // 验证数据是否存在 checkRecord := that.Db.Get("test_batch", "*", Map{"name": testName1}) test1["recordExists"] = checkRecord != nil - debugLog("main.go:831", "事务成功提交测试", Map{"success": success1, "recordExists": checkRecord != nil}, "H9_TX_SUCCESS") tests = append(tests, test1) // 9.2 事务回滚 @@ -848,13 +783,12 @@ func testTransaction(that *Context) Map { success2 := that.Db.Action(func(tx HoTimeDB) bool { // 插入记录 - recordId := tx.Insert("test_batch", Map{ + _ = tx.Insert("test_batch", Map{ "name": testName2, "title": "事务回滚测试", "state": 1, "create_time[#]": "NOW()", }) - debugLog("main.go:846", "事务内插入(将回滚)", Map{"recordId": recordId}, "H9_TX_ROLLBACK_INSERT") // 返回 false 触发回滚 return false @@ -864,7 +798,6 @@ func testTransaction(that *Context) Map { // 验证数据是否不存在(已回滚) checkRecord2 := that.Db.Get("test_batch", "*", Map{"name": testName2}) test2["recordRolledBack"] = checkRecord2 == nil - debugLog("main.go:856", "事务回滚测试", Map{"success": success2, "rolledBack": checkRecord2 == nil}, "H9_TX_ROLLBACK") tests = append(tests, test2) // 清理测试数据 @@ -880,14 +813,11 @@ func testRawSQL(that *Context) Map { result := Map{"name": "原生SQL测试", "tests": Slice{}} tests := Slice{} - debugLog("main.go:872", "开始原生 SQL 测试 (MySQL)", nil, "H10_RAW_SQL") - // 10.1 Query 查询 - 使用真实的 article 表 test1 := Map{"name": "Query 原生查询"} articles1 := that.Db.Query("SELECT id, title, author FROM `article` WHERE state = ? LIMIT ?", 0, 5) test1["result"] = len(articles1) >= 0 test1["count"] = len(articles1) - debugLog("main.go:879", "Query 原生查询测试", Map{"count": len(articles1)}, "H10_QUERY") tests = append(tests, test1) // 10.2 Exec 执行 - 使用 article 表 @@ -908,23 +838,12 @@ func testRawSQL(that *Context) Map { test2["result"] = true test2["note"] = "无可用测试数据" } - 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 @@ -932,17 +851,7 @@ func testRawSQL(that *Context) Map { // 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 @@ -951,17 +860,7 @@ func testRawSQL(that *Context) Map { // 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 @@ -970,16 +869,7 @@ func testRawSQL(that *Context) Map { // 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 @@ -987,16 +877,7 @@ func testRawSQL(that *Context) Map { // 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 @@ -1004,16 +885,7 @@ func testRawSQL(that *Context) Map { // 额外测试: 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 @@ -1021,16 +893,7 @@ func testRawSQL(that *Context) Map { // 额外测试: 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