Przeglądaj źródła

feat: 查询引擎完整实现 — 流式Scan/索引匹配/反序遍历/聚合/增删改查

核心功能:
- Query.Find() 索引自动匹配 + 全表扫描 fallback
- 流式 Scan(callback) 替代全量收集, Count/Sum/Max/Min 零内存开销
- 5 种索引 RangeScan 支持 reverse 反序遍历
- Deleter.Exec() / Updater.Exec() 基于 Query 实现

Bug 修复:
- PrimaryHandler DataPage 泄漏 (FreePage 未调用)
- 二级 B+ 树不允许重复键 → 允许同 key 多条目
- BTreePKRangeScan 返回整页行 → FindRowByIndexKey 精确匹配
- GetDataPageIDs 返回重复 dpID → 去重
- StrDataPage 硬编码 strKeyLen=16 → 动态 entryLen-8

测试:
- stress2_demo 30/30 通过
- query_demo 21/21 通过
- bench 20 万行基准测试完整验证
yl32940 1 miesiąc temu
rodzic
commit
c07952c5eb

+ 42 - 2
DB/deleter.go

@@ -1,5 +1,11 @@
 package DB
 
+import (
+	"TsDB2/DB/page"
+	"TsDB2/DB/util"
+	"encoding/binary"
+)
+
 // Deleter 删除操作对象
 type Deleter struct {
 	table *Table
@@ -19,6 +25,40 @@ func (d *Deleter) Where(cond Condition) *Deleter {
 
 // Exec 执行删除
 func (d *Deleter) Exec() (int64, error) {
-	// 实现删除逻辑
-	return 0, nil
+	// 扫描匹配行, 收集 pkID
+	q := d.table.Query().Where(d.where)
+	results, err := q.Find()
+	if err != nil {
+		return 0, err
+	}
+	// 找到主键索引名
+	var pkIdxName string
+	for i := range d.table.indexMeta.metas {
+		m := &d.table.indexMeta.metas[i]
+		if m.Flag&0x80 != 0 {
+			continue
+		}
+		if m.IndexType == page.IndexTypePrimary || m.IndexType == page.IndexTypeTime {
+			pkIdxName = util.BytesToString(m.IndexName[:])
+			break
+		}
+	}
+	if pkIdxName == "" {
+		return 0, nil
+	}
+	var count int64
+	for _, row := range results {
+		// 找主键字段值作为 pkID
+		for _, fv := range row {
+			key := make([]byte, 8)
+			if v, ok := fv.Value.(uint64); ok {
+				binary.BigEndian.PutUint64(key, v)
+				if err := d.table.DeleteRow(pkIdxName, key); err == nil {
+					count++
+				}
+				break
+			}
+		}
+	}
+	return count, nil
 }

+ 95 - 41
DB/index/btree/primary.go

@@ -226,59 +226,108 @@ func BTreeFindRightmostLeaf(rootPID uint32, handle page.HandleDb) uint32 {
 
 // BTreePKRangeScan 涓婚敭 B+ 鏍戣寖鍥存壂鎻?
 func BTreePKRangeScan(rootPID uint32, handle page.HandleDb,
-	startKey, endKey []byte, startInc, endInc bool,
+	startKey, endKey []byte, startInc, endInc bool, reverse bool,
 	callback func(row []byte) bool) error {
 	if rootPID == 0 {
 		return nil
 	}
-	cur := rootPID
-	for {
-		n := btLoad(handle, cur)
-		if n == nil {
-			return nil
-		}
-		if n.isLeaf() {
-			break
-		}
-		cur = btRoute(n, startKey)
-	}
-	visited := make(map[uint32]bool)
-	for cur != 0 {
-		n := btLoad(handle, cur)
-		if n == nil {
-			break
+	if !reverse {
+		// 正向: 路由到 startKey 所在叶, 沿 NextPage 遍历
+		cur := rootPID
+		for {
+			n := btLoad(handle, cur)
+			if n == nil {
+				return nil
+			}
+			if n.isLeaf() {
+				break
+			}
+			cur = btRoute(n, startKey)
 		}
-		for i := 0; i < int(n.info.Count); i++ {
-			entry := btEntry(n, i)
-			key := btKey(entry)
-			if endKey != nil {
-				cmp := btCmp(key, endKey)
-				if cmp > 0 || (cmp == 0 && !endInc) {
-					return nil
-				}
+		for cur != 0 {
+			n := btLoad(handle, cur)
+			if n == nil {
+				break
 			}
-			if startKey != nil {
-				cmp := btCmp(key, startKey)
-				if cmp < 0 || (cmp == 0 && !startInc) {
+			for i := 0; i < int(n.info.Count); i++ {
+				entry := btEntry(n, i)
+				key := btKey(entry)
+				if endKey != nil {
+					cmp := btCmp(key, endKey)
+					if cmp > 0 || (cmp == 0 && !endInc) {
+						return nil
+					}
+				}
+				if startKey != nil {
+					cmp := btCmp(key, startKey)
+					if cmp < 0 || (cmp == 0 && !startInc) {
+						continue
+					}
+				}
+				dpID := btPID(entry)
+				dp, err := datapage.Load(handle, dpID)
+				if err != nil {
 					continue
 				}
+				// 每个 B+ 树条目精确匹配一行 (FindRowByIndexKey 按索引键过滤)
+				if row, ok := dp.FindRowByIndexKey(key, 8); ok {
+					if !callback(row) {
+						return nil
+					}
+				}
 			}
-			dpID := btPID(entry)
-			if visited[dpID] {
-				continue
+			cur = n.info.Hdr.NextPage
+		}
+	} else {
+		// 反向: 路由到 endKey 所在叶(或最右叶), 沿 PrePage 遍历
+		cur := rootPID
+		if endKey != nil {
+			for {
+				n := btLoad(handle, cur)
+				if n == nil {
+					return nil
+				}
+				if n.isLeaf() {
+					break
+				}
+				cur = btRoute(n, endKey)
 			}
-			visited[dpID] = true
-			dp, err := datapage.Load(handle, dpID)
-			if err != nil {
-				continue
+		} else {
+			cur = BTreeFindRightmostLeaf(rootPID, handle)
+		}
+		for cur != 0 {
+			n := btLoad(handle, cur)
+			if n == nil {
+				break
 			}
-			for _, row := range dp.GetAllRows() {
-				if !callback(row) {
-					return nil
+			for i := int(n.info.Count) - 1; i >= 0; i-- {
+				entry := btEntry(n, i)
+				key := btKey(entry)
+				if startKey != nil {
+					cmp := btCmp(key, startKey)
+					if cmp < 0 || (cmp == 0 && !startInc) {
+						return nil
+					}
+				}
+				if endKey != nil {
+					cmp := btCmp(key, endKey)
+					if cmp > 0 || (cmp == 0 && !endInc) {
+						continue
+					}
+				}
+				dpID := btPID(entry)
+				dp, err := datapage.Load(handle, dpID)
+				if err != nil {
+					continue
+				}
+				if row, ok := dp.FindRowByIndexKey(key, 8); ok {
+					if !callback(row) {
+						return nil
+					}
 				}
 			}
+			cur = n.info.Hdr.PrePage
 		}
-		cur = n.info.Hdr.NextPage
 	}
 	return nil
 }
@@ -699,12 +748,13 @@ func FindSpaceInLeaf(handle page.HandleDb, rootPID uint32, rowSize uint32, key [
 	}
 }
 
-// GetDataPageIDs 返回所有 DataPage ID (按叶节点顺序)
+// GetDataPageIDs 返回所有 DataPage ID (去重, 用于全表扫描)
 func GetDataPageIDs(handle page.HandleDb, rootPID uint32) []uint32 {
 	if rootPID == 0 {
 		return nil
 	}
 	var dpIDs []uint32
+	seen := make(map[uint32]bool)
 	cur := rootPID
 	for {
 		n := btLoad(handle, cur)
@@ -722,7 +772,11 @@ func GetDataPageIDs(handle page.HandleDb, rootPID uint32) []uint32 {
 			break
 		}
 		for i := 0; i < int(n.info.Count); i++ {
-			dpIDs = append(dpIDs, btPID(btEntry(n, i)))
+			dpID := btPID(btEntry(n, i))
+			if !seen[dpID] {
+				seen[dpID] = true
+				dpIDs = append(dpIDs, dpID)
+			}
 		}
 		cur = n.info.Hdr.NextPage
 	}

+ 90 - 34
DB/index/btree/secondary.go

@@ -171,49 +171,105 @@ func SBTSearch(rootPID uint32, key []byte, handle page.HandleDb) (uint64, bool)
 // ======================== 鑼冨洿鎵弿 ========================
 
 func SBTRangeScan(rootPID uint32, handle page.HandleDb,
-	startKey, endKey []byte, startInc, endInc bool,
+	startKey, endKey []byte, startInc, endInc bool, reverse bool,
 	callback func(row []byte) bool) error {
 	if rootPID == 0 {
 		return nil
 	}
-	cur := rootPID
-	for {
-		n := sbtLoad(handle, cur)
-		if n == nil {
-			return nil
-		}
-		if n.isLeaf() {
-			break
+	if !reverse {
+		cur := rootPID
+		for {
+			n := sbtLoad(handle, cur)
+			if n == nil {
+				return nil
+			}
+			if n.isLeaf() {
+				break
+			}
+			cur = sbtRoute(n, startKey)
 		}
-		cur = sbtRoute(n, startKey)
-	}
-	for cur != 0 {
-		n := sbtLoad(handle, cur)
-		if n == nil {
-			break
+		for cur != 0 {
+			n := sbtLoad(handle, cur)
+			if n == nil {
+				break
+			}
+			el := int(n.info.EntryLen)
+			for i := 0; i < int(n.info.Count); i++ {
+				entry := sbtEntry(n, i)
+				key := entry[:el-8]
+				if endKey != nil {
+					cmp := sbtCmp(key, endKey)
+					if cmp > 0 || (cmp == 0 && !endInc) {
+						return nil
+					}
+				}
+				if startKey != nil {
+					cmp := sbtCmp(key, startKey)
+					if cmp < 0 || (cmp == 0 && !startInc) {
+						continue
+					}
+				}
+				pkID := entry[el-8:]
+				if !callback(pkID) {
+					return nil
+				}
+			}
+			cur = n.info.Hdr.NextPage
 		}
-		el := int(n.info.EntryLen)
-		for i := 0; i < int(n.info.Count); i++ {
-			entry := sbtEntry(n, i)
-			key := entry[:el-8]
-			if endKey != nil {
-				cmp := sbtCmp(key, endKey)
-				if cmp > 0 || (cmp == 0 && !endInc) {
+	} else {
+		// 反向: 路由到 endKey 或最右叶, 沿 PrePage 遍历
+		cur := rootPID
+		if endKey != nil {
+			for {
+				n := sbtLoad(handle, cur)
+				if n == nil {
 					return nil
 				}
+				if n.isLeaf() {
+					break
+				}
+				cur = sbtRoute(n, endKey)
 			}
-			if startKey != nil {
-				cmp := sbtCmp(key, startKey)
-				if cmp < 0 || (cmp == 0 && !startInc) {
-					continue
+		} else {
+			for {
+				n := sbtLoad(handle, cur)
+				if n == nil {
+					return nil
+				}
+				if n.isLeaf() {
+					break
 				}
+				cur = sbtChildPID(sbtEntry(n, int(n.info.Count)-1))
 			}
-			pkID := entry[el-8:]
-			if !callback(pkID) {
-				return nil
+		}
+		for cur != 0 {
+			n := sbtLoad(handle, cur)
+			if n == nil {
+				break
 			}
+			el := int(n.info.EntryLen)
+			for i := int(n.info.Count) - 1; i >= 0; i-- {
+				entry := sbtEntry(n, i)
+				key := entry[:el-8]
+				if startKey != nil {
+					cmp := sbtCmp(key, startKey)
+					if cmp < 0 || (cmp == 0 && !startInc) {
+						return nil
+					}
+				}
+				if endKey != nil {
+					cmp := sbtCmp(key, endKey)
+					if cmp > 0 || (cmp == 0 && !endInc) {
+						continue
+					}
+				}
+				pkID := entry[el-8:]
+				if !callback(pkID) {
+					return nil
+				}
+			}
+			cur = n.info.Hdr.PrePage
 		}
-		cur = n.info.Hdr.NextPage
 	}
 	return nil
 }
@@ -237,11 +293,11 @@ func SBTInsert(rootPID uint32, entry []byte, handle page.HandleDb) (uint32, erro
 		}
 		if n.isLeaf() {
 			key := entry[:el-8]
-			_, found := sbtLeafFind(n, key)
-			if found {
-				return rootPID, page.ErrDuplicateKey
-			}
 			ip := sbtLeafPos(n, key)
+			// 二级索引允许重复键, 插入到相同 key 的条目之后
+			for ip < int(n.info.Count) && sbtCmp(sbtEntry(n, ip)[:el-8], key) == 0 {
+				ip++
+			}
 			if int(n.info.Hdr.UseSize)+el <= btSplitThreshold() {
 				sbtInsEntry(n, entry, ip)
 				sbtSave(handle, n)

+ 2 - 2
DB/index/btree_handler.go

@@ -74,7 +74,7 @@ func (h *BTreeHandler) ReadDataPageRows(handle page.HandleDb, dpID uint32) [][]b
 }
 
 func (h *BTreeHandler) RangeScan(handle page.HandleDb, entryPage uint32,
-	startKey, endKey []byte, startInclusive, endInclusive bool,
+	startKey, endKey []byte, startInclusive, endInclusive bool, reverse bool,
 	callback func(row []byte) bool) error {
-	return btree.SBTRangeScan(entryPage, handle, startKey, endKey, startInclusive, endInclusive, callback)
+	return btree.SBTRangeScan(entryPage, handle, startKey, endKey, startInclusive, endInclusive, reverse, callback)
 }

+ 55 - 18
DB/index/compose_handler.go

@@ -174,11 +174,17 @@ func (h *ComposeChainHandler) ReadDataPageRows(handle page.HandleDb, dpID uint32
 // 鏉$洰鏍煎紡: [key(keyLen) + pkID(8B BE)]
 // 鍥炶皟鏀跺埌 8B BigEndian pkID
 func (h *ComposeChainHandler) RangeScan(handle page.HandleDb, entryPage uint32,
-	startKey, endKey []byte, startInclusive, endInclusive bool,
+	startKey, endKey []byte, startInclusive, endInclusive bool, reverse bool,
 	callback func(row []byte) bool) error {
 	if entryPage == 0 {
 		return nil
 	}
+	type pageData struct {
+		entries []byte
+		el      int
+		count   int
+	}
+	var pages []pageData
 	curPageID := entryPage
 	for curPageID != 0 {
 		buf := make([]byte, page.IndexPageSize)
@@ -188,28 +194,59 @@ func (h *ComposeChainHandler) RangeScan(handle page.HandleDb, entryPage uint32,
 		hdr := (*page.IndexHead)(unsafe.Pointer(&buf[0]))
 		el := int(hdr.EntryLen)
 		count := int(hdr.Count)
-		entries := buf[page.IndexHeadSize : page.IndexHeadSize+count*el]
-		for i := 0; i < count; i++ {
-			entry := entries[i*el : (i+1)*el]
-			key := entry[:el-8]
-			if endKey != nil {
-				cmp := composeCmp(key, endKey)
-				if cmp > 0 || (cmp == 0 && !endInclusive) {
-					continue
+		entries := make([]byte, count*el)
+		copy(entries, buf[page.IndexHeadSize:page.IndexHeadSize+count*el])
+		pages = append(pages, pageData{entries: entries, el: el, count: count})
+		curPageID = hdr.Hdr.NextPage
+	}
+	if !reverse {
+		for p := 0; p < len(pages); p++ {
+			pg := &pages[p]
+			for i := 0; i < pg.count; i++ {
+				entry := pg.entries[i*pg.el : (i+1)*pg.el]
+				key := entry[:pg.el-8]
+				if endKey != nil {
+					cmp := composeCmp(key, endKey)
+					if cmp > 0 || (cmp == 0 && !endInclusive) {
+						return nil
+					}
 				}
-			}
-			if startKey != nil {
-				cmp := composeCmp(key, startKey)
-				if cmp < 0 || (cmp == 0 && !startInclusive) {
-					continue
+				if startKey != nil {
+					cmp := composeCmp(key, startKey)
+					if cmp < 0 || (cmp == 0 && !startInclusive) {
+						continue
+					}
+				}
+				pkID := entry[pg.el-8:]
+				if !callback(pkID) {
+					return nil
 				}
 			}
-			pkID := entry[el-8:]
-			if !callback(pkID) {
-				return nil
+		}
+	} else {
+		for p := len(pages) - 1; p >= 0; p-- {
+			pg := &pages[p]
+			for i := pg.count - 1; i >= 0; i-- {
+				entry := pg.entries[i*pg.el : (i+1)*pg.el]
+				key := entry[:pg.el-8]
+				if startKey != nil {
+					cmp := composeCmp(key, startKey)
+					if cmp < 0 || (cmp == 0 && !startInclusive) {
+						return nil
+					}
+				}
+				if endKey != nil {
+					cmp := composeCmp(key, endKey)
+					if cmp > 0 || (cmp == 0 && !endInclusive) {
+						continue
+					}
+				}
+				pkID := entry[pg.el-8:]
+				if !callback(pkID) {
+					return nil
+				}
 			}
 		}
-		curPageID = hdr.Hdr.NextPage
 	}
 	return nil
 }

+ 9 - 0
DB/index/datapage/datapage.go

@@ -342,6 +342,15 @@ func (dp *DataPage) GetAllRows() [][]byte {
 	return result
 }
 
+// GetAllRowsReversed 返回所有未删除行,顺序反转(最后一行在前)
+func (dp *DataPage) GetAllRowsReversed() [][]byte {
+	rows := dp.GetAllRows()
+	for i, j := 0, len(rows)-1; i < j; i, j = i+1, j-1 {
+		rows[i], rows[j] = rows[j], rows[i]
+	}
+	return rows
+}
+
 // ======================== 内部辅助 ========================
 
 // nextRow 解析下一行: 返回 (数据长度, 是否已删除, 数据起始偏移, 是否有效)

+ 3 - 1
DB/index/ientry.go

@@ -91,12 +91,14 @@ type IndexHandler interface {
 	// RangeScan 范围扫描
 	// startKey/endKey: 范围边界 (nil 表示无下界/上界)
 	// startInclusive/endInclusive: 是否包含边界
+	// reverse: true 时从右向左反向遍历
 	// callback: 每行回调, 返回 false 停止
-	// 主键/时序索引: row = [pkID(8B LE) + rowData]
+	// 主键/时序索引: row = [pkID(8B BE) + rowData]
 	// 二级索引: row = 8B BigEndian pkID
 	RangeScan(handle page.HandleDb, entryPage uint32,
 		startKey, endKey []byte,
 		startInclusive, endInclusive bool,
+		reverse bool,
 		callback func(row []byte) bool) error
 }
 

+ 8 - 2
DB/index/primary_handler.go

@@ -133,6 +133,9 @@ func (h *PrimaryHandler) Delete(handle page.HandleDb, entryPage *uint32, indexKe
 					if newRoot != rootPID {
 						*entryPage = newRoot
 					}
+					if handle.FreePage != nil {
+						handle.FreePage(dpID)
+					}
 				} else {
 					dp.Save()
 				}
@@ -164,6 +167,9 @@ func (h *PrimaryHandler) DeleteByPK(handle page.HandleDb, entryPage *uint32, pkI
 					if newRoot != rootPID {
 						*entryPage = newRoot
 					}
+					if handle.FreePage != nil {
+						handle.FreePage(dpID)
+					}
 				} else {
 					dp.Save()
 				}
@@ -212,7 +218,7 @@ func (h *PrimaryHandler) ReadDataPageRows(handle page.HandleDb, dpID uint32) [][
 }
 
 func (h *PrimaryHandler) RangeScan(handle page.HandleDb, entryPage uint32,
-	startKey, endKey []byte, startInclusive, endInclusive bool,
+	startKey, endKey []byte, startInclusive, endInclusive bool, reverse bool,
 	callback func(row []byte) bool) error {
-	return btree.BTreePKRangeScan(entryPage, handle, startKey, endKey, startInclusive, endInclusive, callback)
+	return btree.BTreePKRangeScan(entryPage, handle, startKey, endKey, startInclusive, endInclusive, reverse, callback)
 }

+ 57 - 18
DB/index/str_handler.go

@@ -159,11 +159,18 @@ func (h *StrChainHandler) ReadDataPageRows(handle page.HandleDb, dpID uint32) []
 // 鏉$洰鏍煎紡: [key(keyLen) + pkID(8B BE)]
 // 鍥炶皟鏀跺埌 8B BigEndian pkID
 func (h *StrChainHandler) RangeScan(handle page.HandleDb, entryPage uint32,
-	startKey, endKey []byte, startInclusive, endInclusive bool,
+	startKey, endKey []byte, startInclusive, endInclusive bool, reverse bool,
 	callback func(row []byte) bool) error {
 	if entryPage == 0 {
 		return nil
 	}
+	// 收集所有页条目
+	type pageData struct {
+		entries []byte
+		el      int
+		count   int
+	}
+	var pages []pageData
 	curPageID := entryPage
 	for curPageID != 0 {
 		buf := make([]byte, page.IndexPageSize)
@@ -173,28 +180,60 @@ func (h *StrChainHandler) RangeScan(handle page.HandleDb, entryPage uint32,
 		hdr := (*page.IndexHead)(unsafe.Pointer(&buf[0]))
 		el := int(hdr.EntryLen)
 		count := int(hdr.Count)
-		entries := buf[page.IndexHeadSize : page.IndexHeadSize+count*el]
-		for i := 0; i < count; i++ {
-			entry := entries[i*el : (i+1)*el]
-			key := entry[:el-8]
-			if endKey != nil {
-				cmp := strCmp(key, endKey)
-				if cmp > 0 || (cmp == 0 && !endInclusive) {
-					continue
+		entries := make([]byte, count*el)
+		copy(entries, buf[page.IndexHeadSize:page.IndexHeadSize+count*el])
+		pages = append(pages, pageData{entries: entries, el: el, count: count})
+		curPageID = hdr.Hdr.NextPage
+	}
+	// 遍历
+	if !reverse {
+		for p := 0; p < len(pages); p++ {
+			pg := &pages[p]
+			for i := 0; i < pg.count; i++ {
+				entry := pg.entries[i*pg.el : (i+1)*pg.el]
+				key := entry[:pg.el-8]
+				if endKey != nil {
+					cmp := strCmp(key, endKey)
+					if cmp > 0 || (cmp == 0 && !endInclusive) {
+						return nil
+					}
 				}
-			}
-			if startKey != nil {
-				cmp := strCmp(key, startKey)
-				if cmp < 0 || (cmp == 0 && !startInclusive) {
-					continue
+				if startKey != nil {
+					cmp := strCmp(key, startKey)
+					if cmp < 0 || (cmp == 0 && !startInclusive) {
+						continue
+					}
+				}
+				pkID := entry[pg.el-8:]
+				if !callback(pkID) {
+					return nil
 				}
 			}
-			pkID := entry[el-8:]
-			if !callback(pkID) {
-				return nil
+		}
+	} else {
+		for p := len(pages) - 1; p >= 0; p-- {
+			pg := &pages[p]
+			for i := pg.count - 1; i >= 0; i-- {
+				entry := pg.entries[i*pg.el : (i+1)*pg.el]
+				key := entry[:pg.el-8]
+				if startKey != nil {
+					cmp := strCmp(key, startKey)
+					if cmp < 0 || (cmp == 0 && !startInclusive) {
+						return nil
+					}
+				}
+				if endKey != nil {
+					cmp := strCmp(key, endKey)
+					if cmp > 0 || (cmp == 0 && !endInclusive) {
+						continue
+					}
+				}
+				pkID := entry[pg.el-8:]
+				if !callback(pkID) {
+					return nil
+				}
 			}
 		}
-		curPageID = hdr.Hdr.NextPage
 	}
 	return nil
 }

+ 15 - 13
DB/index/strdatapage/strdatapage.go

@@ -9,11 +9,10 @@ import (
 )
 
 // StrDataPage 字符串索引操作器
-// 不再管理独立的 DataPage,直接在 IndexPage 条目中存储 [strKey(16B) + PK_ID(8B)]
+// 不再管理独立的 DataPage,直接在 IndexPage 条目中存储 [strKey(N) + PK_ID(8B)]
 // Search 返回 PK_ID 的 8 字节编码,Table 层再通过 B+ 树解析实际行数据
 type StrDataPage struct{}
 
-const strKeyLen = 16
 const pkIDLen = 8
 
 // countBackOffset Count 字段距离 entries 起始位置的向后偏移
@@ -23,18 +22,19 @@ var countBackOffset = int(unsafe.Sizeof(page.PageHdr{})) + 14 - (int(unsafe.Size
 
 // ======================== IDateEntry 接口实现 ========================
 
-// Insert 在 IndexPage 条目中插入 [strKey(16B) + PK_ID(8B)]
+// Insert 在 IndexPage 条目中插入 [strKey(N) + PK_ID(8B)]
 // 注意:调用方(insertWithIndex)负责更新 Count 和保存页
 func (s *StrDataPage) Insert(handle page.HandleDb, indexEntries []byte, entryLen int, count int, key []byte, pkID uint64, rowData []byte) error {
 	n := count
+	keyLen := entryLen - pkIDLen // 实际索引键长度
 
 	// 二分查找插入位置(按 strKey 字典序)
 	pos := n
 	left, right := 0, n-1
 	for left <= right {
 		mid := (left + right) / 2
-		entryKey := indexEntries[mid*entryLen : mid*entryLen+strKeyLen]
-		if bytes.Compare(entryKey, key[:strKeyLen]) >= 0 {
+		entryKey := indexEntries[mid*entryLen : mid*entryLen+keyLen]
+		if bytes.Compare(entryKey, key[:keyLen]) >= 0 {
 			pos = mid
 			right = mid - 1
 		} else {
@@ -47,10 +47,10 @@ func (s *StrDataPage) Insert(handle page.HandleDb, indexEntries []byte, entryLen
 		copy(indexEntries[(pos+1)*entryLen:], indexEntries[pos*entryLen:n*entryLen])
 	}
 
-	// 写入新条目 [strKey(16B) + PK_ID(8B)]
+	// 写入新条目 [strKey(keyLen) + PK_ID(8B)]
 	entry := indexEntries[pos*entryLen : (pos+1)*entryLen]
-	copy(entry[:strKeyLen], key[:strKeyLen])
-	binary.BigEndian.PutUint64(entry[strKeyLen:], pkID)
+	copy(entry[:keyLen], key[:keyLen])
+	binary.BigEndian.PutUint64(entry[keyLen:], pkID)
 
 	return nil
 }
@@ -61,14 +61,15 @@ func (s *StrDataPage) Search(handle page.HandleDb, indexEntries []byte, entryLen
 	if n == 0 {
 		return nil, false
 	}
+	keyLen := entryLen - pkIDLen
 
 	left, right := 0, n-1
 	for left <= right {
 		mid := (left + right) / 2
-		entryKey := indexEntries[mid*entryLen : mid*entryLen+strKeyLen]
-		cmp := bytes.Compare(entryKey, key[:strKeyLen])
+		entryKey := indexEntries[mid*entryLen : mid*entryLen+keyLen]
+		cmp := bytes.Compare(entryKey, key[:keyLen])
 		if cmp == 0 {
-			pkIDBytes := indexEntries[mid*entryLen+strKeyLen : mid*entryLen+strKeyLen+pkIDLen]
+			pkIDBytes := indexEntries[mid*entryLen+keyLen : mid*entryLen+keyLen+pkIDLen]
 			result := make([]byte, pkIDLen)
 			copy(result, pkIDBytes)
 			return result, true
@@ -84,14 +85,15 @@ func (s *StrDataPage) Search(handle page.HandleDb, indexEntries []byte, entryLen
 // Delete 从 IndexPage 中删除条目(前移后续条目)
 func (s *StrDataPage) Delete(handle page.HandleDb, indexEntries []byte, entryLen int, key []byte) error {
 	n := len(indexEntries) / entryLen
+	keyLen := entryLen - pkIDLen
 
 	// 查找目标条目
 	pos := -1
 	left, right := 0, n-1
 	for left <= right {
 		mid := (left + right) / 2
-		entryKey := indexEntries[mid*entryLen : mid*entryLen+strKeyLen]
-		cmp := bytes.Compare(entryKey, key[:strKeyLen])
+		entryKey := indexEntries[mid*entryLen : mid*entryLen+keyLen]
+		cmp := bytes.Compare(entryKey, key[:keyLen])
 		if cmp == 0 {
 			pos = mid
 			break

+ 93 - 27
DB/index/time_handler.go

@@ -393,12 +393,18 @@ func (h *TimeIndexHandler) UpdateDataPageByPK(handle page.HandleDb, dpID uint32,
 // startKey/endKey: BigEndian 缂栫爜鐨勬椂闂存埑
 // 閬嶅巻 IndexPage 鏉$洰閾? 瀵规椂闂磋寖鍥翠笌鏌ヨ鑼冨洿閲嶅彔鐨勬潯鐩姞杞?DataPage 骞跺洖璋冩瘡琛?
 func (h *TimeIndexHandler) RangeScan(handle page.HandleDb, entryPage uint32,
-	startKey, endKey []byte, startInclusive, endInclusive bool,
+	startKey, endKey []byte, startInclusive, endInclusive bool, reverse bool,
 	callback func(row []byte) bool) error {
 	if entryPage == 0 {
 		return nil
 	}
-	visited := make(map[uint32]bool)
+	// 收集所有索引页数据
+	type pageInfo struct {
+		entries  []byte
+		entryLen int
+		count    int
+	}
+	var pages []pageInfo
 	curPageID := entryPage
 	for curPageID != 0 {
 		buf := make([]byte, page.IndexPageSize)
@@ -408,12 +414,42 @@ func (h *TimeIndexHandler) RangeScan(handle page.HandleDb, entryPage uint32,
 		hdr := (*page.IndexHead)(unsafe.Pointer(&buf[0]))
 		entryLen := int(hdr.EntryLen)
 		count := int(hdr.Count)
-		entries := buf[page.IndexHeadSize : page.IndexHeadSize+count*entryLen]
-		for i := 0; i < count; i++ {
-			off := i * entryLen
-			beginTime := entries[off : off+8]
-			lastTime := entries[off+8 : off+16]
-			dpID := binary.BigEndian.Uint32(entries[off+entryLen-4:])
+		entries := make([]byte, count*entryLen)
+		copy(entries, buf[page.IndexHeadSize:page.IndexHeadSize+count*entryLen])
+		pages = append(pages, pageInfo{entries: entries, entryLen: entryLen, count: count})
+		curPageID = hdr.Hdr.NextPage
+	}
+
+	// 确定遍历顺序
+	pageStart, pageEnd, pageStep := 0, len(pages)-1, 1
+	if reverse {
+		pageStart, pageEnd, pageStep = len(pages)-1, 0, -1
+	}
+
+	visited := make(map[uint32]bool)
+	for p := pageStart; ; p += pageStep {
+		if pageStep > 0 && p > pageEnd {
+			break
+		}
+		if pageStep < 0 && p < pageEnd {
+			break
+		}
+		pg := &pages[p]
+		entryStart, entryEnd, entryStep := 0, pg.count-1, 1
+		if reverse {
+			entryStart, entryEnd, entryStep = pg.count-1, 0, -1
+		}
+		for i := entryStart; ; i += entryStep {
+			if entryStep > 0 && i > entryEnd {
+				break
+			}
+			if entryStep < 0 && i < entryEnd {
+				break
+			}
+			off := i * pg.entryLen
+			beginTime := pg.entries[off : off+8]
+			lastTime := pg.entries[off+8 : off+16]
+			dpID := binary.BigEndian.Uint32(pg.entries[off+pg.entryLen-4:])
 			if dpID == 0 || visited[dpID] {
 				continue
 			}
@@ -439,30 +475,60 @@ func (h *TimeIndexHandler) RangeScan(handle page.HandleDb, entryPage uint32,
 			dpEnd := dpHdr.UseSize
 			const dpHdrSz = uint32(unsafe.Sizeof(page.PageHdr{})) + 4
 			const deletedFl = uint32(0x80000000)
-			dpOff := dpHdrSz
-			for j := uint32(0); j < dpCount; j++ {
-				if dpOff+4 > dpEnd {
-					break
-				}
-				rawLen := binary.LittleEndian.Uint32(dpBuf[dpOff:])
-				if rawLen&deletedFl != 0 {
-					dpOff += 4 + (rawLen & ^deletedFl)
-					continue
+
+			if !reverse {
+				dpOff := dpHdrSz
+				for j := uint32(0); j < dpCount; j++ {
+					if dpOff+4 > dpEnd {
+						break
+					}
+					rawLen := binary.LittleEndian.Uint32(dpBuf[dpOff:])
+					if rawLen&deletedFl != 0 {
+						dpOff += 4 + (rawLen & ^deletedFl)
+						continue
+					}
+					rowLen := rawLen
+					dpOff += 4
+					if dpOff+rowLen > dpEnd {
+						break
+					}
+					row := make([]byte, rowLen)
+					copy(row, dpBuf[dpOff:dpOff+rowLen])
+					dpOff += rowLen
+					if !callback(row) {
+						return nil
+					}
 				}
-				rowLen := rawLen
-				dpOff += 4
-				if dpOff+rowLen > dpEnd {
-					break
+			} else {
+				// 反向: 先收集所有行再反转
+				var rows [][]byte
+				dpOff := dpHdrSz
+				for j := uint32(0); j < dpCount; j++ {
+					if dpOff+4 > dpEnd {
+						break
+					}
+					rawLen := binary.LittleEndian.Uint32(dpBuf[dpOff:])
+					if rawLen&deletedFl != 0 {
+						dpOff += 4 + (rawLen & ^deletedFl)
+						continue
+					}
+					rowLen := rawLen
+					dpOff += 4
+					if dpOff+rowLen > dpEnd {
+						break
+					}
+					row := make([]byte, rowLen)
+					copy(row, dpBuf[dpOff:dpOff+rowLen])
+					dpOff += rowLen
+					rows = append(rows, row)
 				}
-				row := make([]byte, rowLen)
-				copy(row, dpBuf[dpOff:dpOff+rowLen])
-				dpOff += rowLen
-				if !callback(row) {
-					return nil
+				for k := len(rows) - 1; k >= 0; k-- {
+					if !callback(rows[k]) {
+						return nil
+					}
 				}
 			}
 		}
-		curPageID = hdr.Hdr.NextPage
 	}
 	return nil
 }

+ 528 - 50
DB/query.go

@@ -1,8 +1,12 @@
 package DB
 
 import (
+	"TsDB2/DB/index"
 	"TsDB2/DB/page"
 	"TsDB2/DB/types"
+	"bytes"
+	"encoding/binary"
+	"sort"
 )
 
 // Query 查询操作对象
@@ -22,26 +26,30 @@ type SortField struct {
 	Descending bool
 }
 
-// Query 返回查询操作对象
-func (table *Table) Query() *Query {
-	return &Query{
-		table: table,
-	}
+// scanPlan 索引扫描计划
+type scanPlan struct {
+	meta           *IndexMeta
+	handler        index.IndexHandler
+	startKey       []byte
+	endKey         []byte
+	startInclusive bool
+	endInclusive   bool
+	isPrimary      bool      // 主键/时序索引=true
+	remainingCond  Condition // 需要行级过滤的剩余条件
 }
 
-// Select 指定查询字段
-func (q *Query) Select(cols ...string) *Query {
-	q.selectCols = cols
-	return q
+// Query 返回查询操作对象
+func (table *Table) Query() *Query {
+	return &Query{table: table}
 }
 
-// Where 设置查询条件
-func (q *Query) Where(cond Condition) *Query {
-	q.whereCond = cond
-	return q
-}
+func (q *Query) Select(cols ...string) *Query  { q.selectCols = cols; return q }
+func (q *Query) Where(cond Condition) *Query   { q.whereCond = cond; return q }
+func (q *Query) Limit(n int) *Query            { q.limit = n; return q }
+func (q *Query) Offset(n int) *Query           { q.offset = n; return q }
+func (q *Query) GroupBy(cols ...string) *Query { q.groupBys = cols; return q }
+func (q *Query) Having(cond Condition) *Query  { q.havingCond = cond; return q }
 
-// OrderBy 设置排序
 func (q *Query) OrderBy(field string, descending ...bool) *Query {
 	desc := false
 	if len(descending) > 0 {
@@ -51,69 +59,539 @@ func (q *Query) OrderBy(field string, descending ...bool) *Query {
 	return q
 }
 
-// Limit 设置限制数量
-func (q *Query) Limit(n int) *Query {
-	q.limit = n
-	return q
+// ======================== 索引键编码 ========================
+
+// encodeIndexKey 将条件值编码为索引键字节 (BigEndian, 与 writeFieldValue 一致)
+func encodeIndexKey(value interface{}, ft types.FieldType) []byte {
+	size := types.GetSize(ft)
+	buf := make([]byte, size)
+	switch ft {
+	case types.FieldType_1byte:
+		buf[0] = value.(byte)
+	case types.FieldType_2byte:
+		binary.BigEndian.PutUint16(buf, uint16(value.(int16)))
+	case types.FieldType_4byte:
+		binary.BigEndian.PutUint32(buf, uint32(value.(int32)))
+	case types.FieldType_8byte:
+		binary.BigEndian.PutUint64(buf, value.(uint64))
+	default:
+		// 字符串类型
+		b := value.([]byte)
+		copy(buf, b)
+	}
+	return buf
 }
 
-// Offset 设置偏移量
-func (q *Query) Offset(n int) *Query {
-	q.offset = n
-	return q
+// ======================== 索引匹配 ========================
+
+// fieldNameFromMeta 获取索引第一个字段的名称
+func (q *Query) fieldNameFromMeta(meta *IndexMeta) string {
+	fieldIdx := int(meta.FieldIDs[0])
+	if fieldIdx < len(q.table.Field) {
+		return string(q.table.Field[fieldIdx].FieldName)
+	}
+	return ""
 }
 
-// GroupBy 设置分组
-func (q *Query) GroupBy(cols ...string) *Query {
-	q.groupBys = cols
-	return q
+// fieldTypeFromMeta 获取索引第一个字段的类型
+func (q *Query) fieldTypeFromMeta(meta *IndexMeta) types.FieldType {
+	fieldIdx := int(meta.FieldIDs[0])
+	if fieldIdx < len(q.table.Field) {
+		return q.table.Field[fieldIdx].FieldType
+	}
+	return 0
 }
 
-// Having 设置分组过滤条件
-func (q *Query) Having(cond Condition) *Query {
-	q.havingCond = cond
-	return q
+// condToRange 尝试从条件中提取扫描范围, 返回 (startKey, endKey, startInc, endInc, ok)
+func (q *Query) condToRange(cond Condition, fieldName string, ft types.FieldType) ([]byte, []byte, bool, bool, Condition) {
+	switch c := cond.(type) {
+	case EqCondition:
+		if c.Field == fieldName {
+			key := encodeIndexKey(c.Value, ft)
+			return key, key, true, true, nil
+		}
+	case GeCondition:
+		if c.Field == fieldName {
+			return encodeIndexKey(c.Value, ft), nil, true, false, nil
+		}
+	case GtCondition:
+		if c.Field == fieldName {
+			return encodeIndexKey(c.Value, ft), nil, false, false, nil
+		}
+	case LeCondition:
+		if c.Field == fieldName {
+			return nil, encodeIndexKey(c.Value, ft), false, true, nil
+		}
+	case LtCondition:
+		if c.Field == fieldName {
+			return nil, encodeIndexKey(c.Value, ft), false, false, nil
+		}
+	case AndCondition:
+		// 找第一个可索引的子条件, 其余作为 remainingCond
+		var used int = -1
+		var sk, ek []byte
+		var si, ei, found bool
+		for i, sub := range c.Conditions {
+			s, e, sinc, einc, rem2 := q.condToRange(sub, fieldName, ft)
+			if rem2 == nil || (s != nil || e != nil) {
+				sk, ek, si, ei = s, e, sinc, einc
+				used = i
+				found = true
+				break
+			}
+		}
+		if found {
+			// 剩余条件 = 除 used 外的所有子条件
+			var remaining []Condition
+			for i, sub := range c.Conditions {
+				if i != used {
+					remaining = append(remaining, sub)
+				}
+			}
+			var rem Condition
+			if len(remaining) == 1 {
+				rem = remaining[0]
+			} else if len(remaining) > 1 {
+				rem = And(remaining...)
+			}
+			return sk, ek, si, ei, rem
+		}
+	}
+	return nil, nil, false, false, cond
+}
+
+// buildScanPlan 分析 WHERE 条件, 选择最优索引
+func (q *Query) buildScanPlan() *scanPlan {
+	if q.whereCond == nil {
+		return nil
+	}
+	if q.table.indexMeta == nil {
+		return nil
+	}
+
+	var bestPlan *scanPlan
+
+	for i := range q.table.indexMeta.metas {
+		m := &q.table.indexMeta.metas[i]
+		if m.Flag&0x80 != 0 {
+			continue
+		}
+		isPrimary := m.IndexType == page.IndexTypePrimary || m.IndexType == page.IndexTypeTime
+		fieldName := q.fieldNameFromMeta(m)
+		ft := q.fieldTypeFromMeta(m)
+		if fieldName == "" {
+			continue
+		}
+
+		sk, ek, si, ei, rem := q.condToRange(q.whereCond, fieldName, ft)
+		if sk == nil && ek == nil {
+			continue // 此索引不匹配
+		}
+
+		handler, ok := index.Compose.GetHandler(m.IndexType)
+		if !ok {
+			continue
+		}
+
+		plan := &scanPlan{
+			meta: m, handler: handler,
+			startKey: sk, endKey: ek,
+			startInclusive: si, endInclusive: ei,
+			isPrimary: isPrimary, remainingCond: rem,
+		}
+		// 优先选主键/时序索引 (避免回查)
+		if isPrimary || bestPlan == nil {
+			bestPlan = plan
+		}
+		if isPrimary {
+			break // 主键索引最优, 不再找
+		}
+	}
+	return bestPlan
 }
 
+// ======================== 核心查询 ========================
+
 // Find 执行查询,返回多行
 func (q *Query) Find() ([][]types.FieldValue, error) {
-	// 实现查询逻辑
-	return nil, nil
+	var results [][]types.FieldValue
+	err := q.scan(func(row []types.FieldValue) bool {
+		results = append(results, row)
+		return true
+	})
+	if err != nil {
+		return nil, err
+	}
+	// 投影
+	if len(q.selectCols) > 0 {
+		results = q.project(results)
+	}
+	return results, nil
 }
 
-// FindOne 执行查询,返回单行
+// Scan 流式扫描查询, 回调函数逐行处理数据, 返回 false 提前终止
+// 适用于不需要全量收集结果的场景, 内存友好
+func (q *Query) Scan(callback func(row []types.FieldValue) bool) error {
+	return q.scan(callback)
+}
+
+// scan 内部流式扫描核心方法
+// 自动处理: 索引选择 → 排序 → OFFSET → LIMIT → 回调
+func (q *Query) scan(callback func(row []types.FieldValue) bool) error {
+	plan := q.buildScanPlan()
+
+	// 判断是否需要内存排序 (OrderBy 字段与索引不一致时)
+	needSort := false
+	if len(q.orderBys) > 0 {
+		if plan == nil {
+			needSort = true
+		} else {
+			idxField := q.fieldNameFromMeta(plan.meta)
+			if !(len(q.orderBys) == 1 && q.orderBys[0].Field == idxField) {
+				needSort = true
+			}
+		}
+	}
+
+	// 需要排序时, 必须先收集全部再排序, 无法流式 LIMIT
+	if needSort {
+		var all [][]types.FieldValue
+		err := q.executeScan(func(row []types.FieldValue) bool {
+			all = append(all, row)
+			return true
+		}, plan)
+		if err != nil {
+			return err
+		}
+		q.sortResults(all)
+		// OFFSET
+		if q.offset > 0 {
+			if q.offset >= len(all) {
+				return nil
+			}
+			all = all[q.offset:]
+		}
+		// LIMIT
+		if q.limit > 0 && q.limit < len(all) {
+			all = all[:q.limit]
+		}
+		for _, row := range all {
+			if !callback(row) {
+				return nil
+			}
+		}
+		return nil
+	}
+
+	// 不需要排序: 直接流式传递 callback, 支持 LIMIT 提前终止
+	seen := 0
+	return q.executeScan(func(row []types.FieldValue) bool {
+		if seen < q.offset {
+			seen++
+			return true // 跳过 OFFSET
+		}
+		seen++
+		if !callback(row) {
+			return false // 用户要求停止
+		}
+		// LIMIT 检查 (0 表示无限制)
+		if q.limit > 0 && seen-q.offset >= q.limit {
+			return false // 已满足, 提前终止扫描
+		}
+		return true
+	}, plan)
+}
+
+// executeScan 执行索引扫描或全表扫描, 将反序列化后的行传递给回调
+func (q *Query) executeScan(callback func(row []types.FieldValue) bool, plan *scanPlan) error {
+	if plan != nil {
+		return q.indexScan(plan, callback)
+	}
+	return q.fullTableScan(callback)
+}
+
+// indexScan 使用索引扫描 (流式回调模式)
+func (q *Query) indexScan(plan *scanPlan, callback func(row []types.FieldValue) bool) error {
+	// 判断 OrderBy 是否需要反向
+	reverse := false
+	if len(q.orderBys) == 1 {
+		idxField := q.fieldNameFromMeta(plan.meta)
+		if q.orderBys[0].Field == idxField && q.orderBys[0].Descending {
+			reverse = true
+		}
+	}
+
+	if plan.isPrimary {
+		// 主键/时序索引: RangeScan 直接返回 storeRow = [pkID(8B) + rowData]
+		return plan.handler.RangeScan(q.table.handle, plan.meta.EntryPage,
+			plan.startKey, plan.endKey, plan.startInclusive, plan.endInclusive, reverse,
+			func(row []byte) bool {
+				if len(row) < 8 {
+					return true
+				}
+				rowData := row[8:]
+				if plan.remainingCond != nil {
+					if !plan.remainingCond.Match(rowData, q.table) {
+						return true
+					}
+				}
+				return callback(q.table.deserializeRow(rowData))
+			})
+	}
+
+	// 二级索引: RangeScan 返回 pkID(8B BE), 需回查主键
+	return plan.handler.RangeScan(q.table.handle, plan.meta.EntryPage,
+		plan.startKey, plan.endKey, plan.startInclusive, plan.endInclusive, reverse,
+		func(row []byte) bool {
+			if len(row) < 8 {
+				return true
+			}
+			pkID := binary.BigEndian.Uint64(row[:8])
+			storeRow, found := q.table.indexMeta.ResolveByPK(pkID, q.table.Field)
+			if !found {
+				return true
+			}
+			rowData := storeRow[8:]
+			if plan.remainingCond != nil {
+				if !plan.remainingCond.Match(rowData, q.table) {
+					return true
+				}
+			}
+			return callback(q.table.deserializeRow(rowData))
+		})
+}
+
+// fullTableScan 全表扫描 (流式回调模式)
+func (q *Query) fullTableScan(callback func(row []types.FieldValue) bool) error {
+	// 找主键/时序索引
+	var primMeta *IndexMeta
+	var primHandler index.IndexHandler
+	for i := range q.table.indexMeta.metas {
+		m := &q.table.indexMeta.metas[i]
+		if m.Flag&0x80 != 0 {
+			continue
+		}
+		if m.IndexType == page.IndexTypePrimary || m.IndexType == page.IndexTypeTime {
+			primMeta = m
+			primHandler, _ = index.Compose.GetHandler(m.IndexType)
+			break
+		}
+	}
+	if primMeta == nil || primHandler == nil {
+		return nil
+	}
+
+	dpIDs := primHandler.GetDataPageIDs(q.table.handle, primMeta.EntryPage)
+	for _, dpID := range dpIDs {
+		rows := primHandler.ReadDataPageRows(q.table.handle, dpID)
+		for _, storeRow := range rows {
+			if len(storeRow) < 8 {
+				continue
+			}
+			rowData := storeRow[8:]
+			if q.whereCond != nil {
+				if !q.whereCond.Match(rowData, q.table) {
+					continue
+				}
+			}
+			if !callback(q.table.deserializeRow(rowData)) {
+				return nil // 回调要求提前终止
+			}
+		}
+	}
+	return nil
+}
+
+// ======================== 排序 ========================
+
+func (q *Query) sortResults(results [][]types.FieldValue) {
+	sort.SliceStable(results, func(i, j int) bool {
+		for _, ob := range q.orderBys {
+			fi := q.table.findFieldIndex(ob.Field)
+			if fi < 0 {
+				continue
+			}
+			vi := findFieldValue(results[i], ob.Field)
+			vj := findFieldValue(results[j], ob.Field)
+			if vi == nil || vj == nil {
+				continue
+			}
+			cmp := compareFieldValues(vi, vj)
+			if cmp == 0 {
+				continue
+			}
+			if ob.Descending {
+				return cmp > 0
+			}
+			return cmp < 0
+		}
+		return false
+	})
+}
+
+func findFieldValue(row []types.FieldValue, field string) interface{} {
+	for _, fv := range row {
+		if string(fv.FieldName) == field {
+			return fv.Value
+		}
+	}
+	return nil
+}
+
+func compareFieldValues(a, b interface{}) int {
+	switch va := a.(type) {
+	case uint64:
+		vb := b.(uint64)
+		if va < vb {
+			return -1
+		}
+		if va > vb {
+			return 1
+		}
+		return 0
+	case int16:
+		vb := a.(int16)
+		vbb := b.(int16)
+		if vb < vbb {
+			return -1
+		}
+		if vb > vbb {
+			return 1
+		}
+		return 0
+	case int32:
+		vb := b.(int32)
+		if va < vb {
+			return -1
+		}
+		if va > vb {
+			return 1
+		}
+		return 0
+	case byte:
+		vb := b.(byte)
+		if va < vb {
+			return -1
+		}
+		if va > vb {
+			return 1
+		}
+		return 0
+	case []byte:
+		vb := b.([]byte)
+		return bytes.Compare(va, vb)
+	}
+	return 0
+}
+
+// ======================== 投影 ========================
+
+func (q *Query) project(results [][]types.FieldValue) [][]types.FieldValue {
+	colSet := make(map[string]bool)
+	for _, c := range q.selectCols {
+		colSet[c] = true
+	}
+	for i, row := range results {
+		var filtered []types.FieldValue
+		for _, fv := range row {
+			if colSet[string(fv.FieldName)] {
+				filtered = append(filtered, fv)
+			}
+		}
+		results[i] = filtered
+	}
+	return results
+}
+
+// ======================== 流式聚合 ========================
+
+// FindOne 流式查询,返回单行 (命中即停,不遍历全表)
 func (q *Query) FindOne() ([]types.FieldValue, error) {
-	q.limit = 1
-	result, err := q.Find()
+	var result []types.FieldValue
+	found := false
+	err := q.scan(func(row []types.FieldValue) bool {
+		result = row
+		found = true
+		return false // 找到第一行即停止
+	})
 	if err != nil {
 		return nil, err
 	}
-	if len(result) == 0 {
+	if !found {
 		return nil, page.ErrNotFound
 	}
-	return result[0], nil
+	return result, nil
 }
 
-// Count 执行计数
+// Count 流式计数 (不收集行,扫描过程中直接累加)
 func (q *Query) Count() (int64, error) {
-	// 实现计数逻辑
-	return 0, nil
+	var count int64
+	err := q.scan(func(row []types.FieldValue) bool {
+		count++
+		return true
+	})
+	return count, err
 }
 
-// Sum 执行求和
+// Sum 流式求和 (不收集行,扫描过程中直接累加)
 func (q *Query) Sum(col string) (float64, error) {
-	// 实现求和逻辑
-	return 0, nil
+	var sum float64
+	err := q.scan(func(row []types.FieldValue) bool {
+		for _, fv := range row {
+			if string(fv.FieldName) == col {
+				sum += toFloat64(fv.Value)
+				break
+			}
+		}
+		return true
+	})
+	return sum, err
 }
 
-// Max 执行求最大值
+// Max 流式求最大值 (不收集行,扫描过程中维护当前最大值)
 func (q *Query) Max(col string) (interface{}, error) {
-	// 实现求最大值逻辑
-	return nil, nil
+	var maxVal interface{}
+	err := q.scan(func(row []types.FieldValue) bool {
+		for _, fv := range row {
+			if string(fv.FieldName) == col {
+				if maxVal == nil || compareFieldValues(fv.Value, maxVal) > 0 {
+					maxVal = fv.Value
+				}
+				break
+			}
+		}
+		return true
+	})
+	return maxVal, err
 }
 
-// Min 执行求最小值
+// Min 流式求最小值 (不收集行,扫描过程中维护当前最小值)
 func (q *Query) Min(col string) (interface{}, error) {
-	// 实现求最小值逻辑
-	return nil, nil
+	var minVal interface{}
+	err := q.scan(func(row []types.FieldValue) bool {
+		for _, fv := range row {
+			if string(fv.FieldName) == col {
+				if minVal == nil || compareFieldValues(fv.Value, minVal) < 0 {
+					minVal = fv.Value
+				}
+				break
+			}
+		}
+		return true
+	})
+	return minVal, err
+}
+
+func toFloat64(v interface{}) float64 {
+	switch val := v.(type) {
+	case uint64:
+		return float64(val)
+	case int16:
+		return float64(val)
+	case int32:
+		return float64(val)
+	case byte:
+		return float64(val)
+	}
+	return 0
 }

+ 51 - 2
DB/updater.go

@@ -1,5 +1,12 @@
 package DB
 
+import (
+	"TsDB2/DB/page"
+	"TsDB2/DB/types"
+	"TsDB2/DB/util"
+	"encoding/binary"
+)
+
 // Updater 更新操作对象
 type Updater struct {
 	table   *Table
@@ -29,6 +36,48 @@ func (u *Updater) Where(cond Condition) *Updater {
 
 // Exec 执行更新
 func (u *Updater) Exec() (int64, error) {
-	// 实现更新逻辑
-	return 0, nil
+	// 扫描匹配行
+	q := u.table.Query().Where(u.where)
+	results, err := q.Find()
+	if err != nil {
+		return 0, err
+	}
+	// 找主键索引名
+	var pkIdxName string
+	for i := range u.table.indexMeta.metas {
+		m := &u.table.indexMeta.metas[i]
+		if m.Flag&0x80 != 0 {
+			continue
+		}
+		if m.IndexType == page.IndexTypePrimary || m.IndexType == page.IndexTypeTime {
+			pkIdxName = util.BytesToString(m.IndexName[:])
+			break
+		}
+	}
+	if pkIdxName == "" {
+		return 0, nil
+	}
+	var count int64
+	for _, row := range results {
+		// 找主键字段 (第一个 uint64 字段作为 pkID)
+		for _, fv := range row {
+			if v, ok := fv.Value.(uint64); ok {
+				key := make([]byte, 8)
+				binary.BigEndian.PutUint64(key, v)
+				// 构建新行值
+				var values []types.FieldValue
+				for field, val := range u.updates {
+					values = append(values, types.FieldValue{
+						FieldName: []byte(field),
+						Value:     val,
+					})
+				}
+				if err := u.table.UpdateRow(pkIdxName, key, values); err == nil {
+					count++
+				}
+				break
+			}
+		}
+	}
+	return count, nil
 }

+ 525 - 0
demo/bench/main.go

@@ -0,0 +1,525 @@
+package main
+
+import (
+	"TsDB2/DB"
+	"TsDB2/DB/page"
+	"TsDB2/DB/types"
+	"fmt"
+	"os"
+	"runtime"
+	"sort"
+	"strings"
+	"time"
+)
+
+const N = 200_000
+
+// ======================== 计时工具 ========================
+
+type benchResult struct {
+	name     string
+	count    int64
+	duration time.Duration
+	detail   string
+}
+
+var results []benchResult
+
+func section(title string) {
+	fmt.Printf("\n%s\n%s\n", title, strings.Repeat("=", len(title)))
+}
+
+func timed(name string, fn func() (int64, string)) {
+	t0 := time.Now()
+	cnt, detail := fn()
+	d := time.Since(t0)
+	results = append(results, benchResult{name, cnt, d, detail})
+	qps := float64(cnt) / d.Seconds()
+	if d.Seconds() < 0.001 {
+		fmt.Printf("  %-52s %12d  %10v  %12s  %s\n", name, cnt, d, "N/A", detail)
+	} else {
+		fmt.Printf("  %-52s %12d  %10v  %10.0f/s  %s\n", name, cnt, d, qps, detail)
+	}
+}
+
+func findUint64(row []types.FieldValue, field string) uint64 {
+	for _, fv := range row {
+		if string(fv.FieldName) == field {
+			if v, ok := fv.Value.(uint64); ok {
+				return v
+			}
+		}
+	}
+	return 0
+}
+
+func findString(row []types.FieldValue, field string) string {
+	for _, fv := range row {
+		if string(fv.FieldName) == field {
+			if v, ok := fv.Value.([]byte); ok {
+				return strings.TrimRight(string(v), "\x00")
+			}
+		}
+	}
+	return ""
+}
+
+// ======================== 主函数 ========================
+
+func main() {
+	runtime.GOMAXPROCS(runtime.NumCPU())
+	dbName := fmt.Sprintf("bench_%d.db", time.Now().UnixNano())
+	fmt.Printf("TsDB2 Benchmark — %s rows\nDatabase: %s\nCPUs: %d\n",
+		formatNum(N), dbName, runtime.NumCPU())
+
+	os.RemoveAll(dbName)
+	db, err := DB.Open(dbName)
+	if err != nil {
+		fmt.Println("Open error:", err)
+		return
+	}
+	defer db.Close()
+
+	// ==================== 建表 ====================
+	section("1. 建表 + 建索引")
+	table, err := db.CreateTable("bench", []types.FieldInfo{
+		{types.FieldType_8byte, []byte("id")},    // 0: 主键
+		{types.FieldType_8byte, []byte("age")},   // 1: 二级 B+ 树
+		{types.FieldType_4byte, []byte("score")}, // 2: 无索引
+		{types.FieldType_32byte, []byte("name")}, // 3: 字符串索引
+	})
+	if err != nil {
+		fmt.Println("CreateTable error:", err)
+		return
+	}
+	timed("创建表", func() (int64, string) { return 1, "4 fields" })
+
+	_, err = table.CreateIndex("pk_id", page.IndexTypePrimary, []byte{0})
+	if err != nil {
+		fmt.Println("CreateIndex pk error:", err)
+		return
+	}
+	_, err = table.CreateIndex("idx_age", page.IndexTypeBTree, []byte{1})
+	if err != nil {
+		fmt.Println("CreateIndex age error:", err)
+		return
+	}
+	_, err = table.CreateIndex("idx_name", page.IndexTypeStr, []byte{3})
+	if err != nil {
+		fmt.Println("CreateIndex name error:", err)
+		return
+	}
+	timed("创建 3 个索引 (Primary + BTree + Str)", func() (int64, string) { return 3, "" })
+
+	// ==================== 批量插入 ====================
+	section("2. 批量插入 (增)")
+	t0 := time.Now()
+	insertOK := int64(0)
+	reportInterval := N / 10
+	if reportInterval < 100000 {
+		reportInterval = 100000
+	}
+	for i := 0; i < N; i++ {
+		nameBytes := make([]byte, 32)
+		copy(nameBytes, fmt.Sprintf("U%d", i))
+		_, err := table.InsertRow([]types.FieldValue{
+			{FieldName: []byte("id"), Value: uint64(i + 1)},
+			{FieldName: []byte("age"), Value: uint64(18 + i%80)},  // 18-97
+			{FieldName: []byte("score"), Value: int32(i % 1000)},  // 0-999
+			{FieldName: []byte("name"), Value: nameBytes},
+		})
+		if err == nil {
+			insertOK++
+		}
+		if (i+1)%reportInterval == 0 {
+			elapsed := time.Since(t0)
+			qps := float64(insertOK) / elapsed.Seconds()
+			fmt.Printf("  进度: %s / %s  (%.0f rows/s)  已耗时 %v\n",
+				formatNum(int(insertOK)), formatNum(N), qps, elapsed.Round(time.Millisecond))
+		}
+	}
+	insertDur := time.Since(t0)
+	results = append(results, benchResult{"批量插入", insertOK, insertDur,
+		fmt.Sprintf("%.0f rows/s", float64(insertOK)/insertDur.Seconds())})
+	fmt.Printf("  插入完成: %s rows, 耗时 %v, 吞吐 %.0f rows/s\n",
+		formatNum(int(insertOK)), insertDur.Round(time.Millisecond),
+		float64(insertOK)/insertDur.Seconds())
+
+	// ==================== 一级索引查询 (主键) ====================
+	section("3. 一级索引查询 (主键 B+ 树)")
+
+	timed("PK 精确匹配 Eq(id=N/2)", func() (int64, string) {
+		rows, _ := table.Query().Where(DB.Eq("id", uint64(N/2), types.FieldType_8byte)).Find()
+		if len(rows) == 0 { return 0, "not found" }
+		return int64(len(rows)), fmt.Sprintf("id=%d", findUint64(rows[0], "id"))
+	})
+
+	timed("PK 范围查询 id>=N*0.5 AND id<N*0.5+100", func() (int64, string) {
+		base := uint64(N / 2)
+		rows, _ := table.Query().Where(DB.And(
+			DB.Ge("id", base, types.FieldType_8byte),
+			DB.Lt("id", base+100, types.FieldType_8byte),
+		)).Find()
+		return int64(len(rows)), ""
+	})
+
+	timed("PK 范围查询 id>=N*0.5 AND id<=N*0.5+100", func() (int64, string) {
+		base := uint64(N / 2)
+		rows, _ := table.Query().Where(DB.And(
+			DB.Ge("id", base, types.FieldType_8byte),
+			DB.Le("id", base+100, types.FieldType_8byte),
+		)).Find()
+		return int64(len(rows)), ""
+	})
+
+	timed("PK Gt(id>N-10)", func() (int64, string) {
+		rows, _ := table.Query().Where(DB.Gt("id", uint64(N-10), types.FieldType_8byte)).Find()
+		return int64(len(rows)), ""
+	})
+
+	timed("PK Le(id<=10)", func() (int64, string) {
+		rows, _ := table.Query().Where(DB.Le("id", uint64(10), types.FieldType_8byte)).Find()
+		return int64(len(rows)), ""
+	})
+
+	timed("PK FindOne(id=N/3)", func() (int64, string) {
+		row, err := table.Query().Where(DB.Eq("id", uint64(N/3), types.FieldType_8byte)).FindOne()
+		if err != nil || row == nil {
+			return 0, "not found"
+		}
+		return 1, fmt.Sprintf("age=%d", findUint64(row, "age"))
+	})
+
+	timed("PK ORDER BY id ASC LIMIT 100", func() (int64, string) {
+		rows, _ := table.Query().OrderBy("id").Limit(100).Find()
+		return int64(len(rows)), fmt.Sprintf("first_id=%d", findUint64(rows[0], "id"))
+	})
+
+	timed("PK ORDER BY id DESC LIMIT 100", func() (int64, string) {
+		rows, _ := table.Query().OrderBy("id", true).Limit(100).Find()
+		return int64(len(rows)), fmt.Sprintf("first_id=%d", findUint64(rows[0], "id"))
+	})
+
+	timed("PK COUNT(*)", func() (int64, string) {
+		cnt, _ := table.Query().Count()
+		return cnt, ""
+	})
+
+	timed("PK SUM(age)", func() (int64, string) {
+		sum, _ := table.Query().Sum("age")
+		return int64(sum), fmt.Sprintf("%.0f", sum)
+	})
+
+	timed("PK MAX(score) / MIN(score)", func() (int64, string) {
+		maxV, _ := table.Query().Max("score")
+		minV, _ := table.Query().Min("score")
+		return 2, fmt.Sprintf("max=%v min=%v", maxV, minV)
+	})
+
+	// ==================== 二级索引查询 ====================
+	section("4. 二级索引查询 (B+ 树 idx_age)")
+
+	timed("SEC 精确匹配 Eq(age=50)", func() (int64, string) {
+		rows, _ := table.Query().Where(DB.Eq("age", uint64(50), types.FieldType_8byte)).Find()
+		return int64(len(rows)), ""
+	})
+
+	timed("SEC 范围 age>=30 AND age<40", func() (int64, string) {
+		rows, _ := table.Query().Where(DB.And(
+			DB.Ge("age", uint64(30), types.FieldType_8byte),
+			DB.Lt("age", uint64(40), types.FieldType_8byte),
+		)).Find()
+		return int64(len(rows)), ""
+	})
+
+	timed("SEC 范围 age>=80 AND age<=97", func() (int64, string) {
+		rows, _ := table.Query().Where(DB.And(
+			DB.Ge("age", uint64(80), types.FieldType_8byte),
+			DB.Le("age", uint64(97), types.FieldType_8byte),
+		)).Find()
+		return int64(len(rows)), ""
+	})
+
+	timed("SEC Gt(age>90)", func() (int64, string) {
+		rows, _ := table.Query().Where(DB.Gt("age", uint64(90), types.FieldType_8byte)).Find()
+		return int64(len(rows)), ""
+	})
+
+	timed("SEC Le(age<=20)", func() (int64, string) {
+		rows, _ := table.Query().Where(DB.Le("age", uint64(20), types.FieldType_8byte)).Find()
+		return int64(len(rows)), ""
+	})
+
+	timed("SEC ORDER BY age ASC LIMIT 50", func() (int64, string) {
+		rows, _ := table.Query().OrderBy("age").Limit(50).Find()
+		return int64(len(rows)), fmt.Sprintf("first_age=%d", findUint64(rows[0], "age"))
+	})
+
+	timed("SEC ORDER BY age DESC LIMIT 50", func() (int64, string) {
+		rows, _ := table.Query().OrderBy("age", true).Limit(50).Find()
+		return int64(len(rows)), fmt.Sprintf("first_age=%d", findUint64(rows[0], "age"))
+	})
+
+	timed("SEC COUNT(age>=50)", func() (int64, string) {
+		cnt, _ := table.Query().Where(DB.Ge("age", uint64(50), types.FieldType_8byte)).Count()
+		return cnt, ""
+	})
+
+	timed("SEC SUM(score) WHERE age BETWEEN 25 AND 35", func() (int64, string) {
+		sum, _ := table.Query().Where(DB.And(
+			DB.Ge("age", uint64(25), types.FieldType_8byte),
+			DB.Le("age", uint64(35), types.FieldType_8byte),
+		)).Sum("score")
+		return int64(sum), fmt.Sprintf("%.0f", sum)
+	})
+
+	// ==================== 字符串索引查询 ====================
+	section("5. 字符串索引查询 (idx_name)")
+
+	timed("STR 精确匹配 Eq(name=last)", func() (int64, string) {
+		nk := make([]byte, 32)
+		copy(nk, fmt.Sprintf("U%d", N-1))
+		rows, _ := table.Query().Where(DB.Eq("name", nk, types.FieldType_32byte)).Find()
+		return int64(len(rows)), ""
+	})
+
+	timed("STR 范围 name>='U100' AND name<'U200'", func() (int64, string) {
+		sk := make([]byte, 32)
+		copy(sk, "U100")
+		ek := make([]byte, 32)
+		copy(ek, "U200")
+		rows, _ := table.Query().Where(DB.And(
+			DB.Ge("name", sk, types.FieldType_32byte),
+			DB.Lt("name", ek, types.FieldType_32byte),
+		)).Find()
+		return int64(len(rows)), ""
+	})
+
+	// ==================== 全表扫描 + 条件 ====================
+	section("6. 全表扫描 + 复杂条件 (score 无索引)")
+
+	timed("全表: score>900", func() (int64, string) {
+		rows, _ := table.Query().Where(DB.Gt("score", int32(900), types.FieldType_4byte)).Find()
+		return int64(len(rows)), ""
+	})
+
+	timed("全表: score>=500 AND score<=600", func() (int64, string) {
+		rows, _ := table.Query().Where(DB.And(
+			DB.Ge("score", int32(500), types.FieldType_4byte),
+			DB.Le("score", int32(600), types.FieldType_4byte),
+		)).Find()
+		return int64(len(rows)), ""
+	})
+
+	timed("全表: In(score, [1,2,3,999])", func() (int64, string) {
+		rows, _ := table.Query().Where(DB.In("score", []interface{}{
+			int32(1), int32(2), int32(3), int32(999),
+		}, types.FieldType_4byte)).Find()
+		return int64(len(rows)), ""
+	})
+
+	timed("全表: Not(Eq(age, 50))", func() (int64, string) {
+		cnt, _ := table.Query().Where(DB.Not(DB.Eq("age", uint64(50), types.FieldType_8byte))).Count()
+		return cnt, ""
+	})
+
+	timed("全表: Like(name, 'U100%')", func() (int64, string) {
+		rows, _ := table.Query().Where(DB.Like("name", "U100%")).Find()
+		return int64(len(rows)), ""
+	})
+
+	timed("全表: Or(Eq(age,18), Eq(age,97))", func() (int64, string) {
+		rows, _ := table.Query().Where(DB.Or(
+			DB.Eq("age", uint64(18), types.FieldType_8byte),
+			DB.Eq("age", uint64(97), types.FieldType_8byte),
+		)).Find()
+		return int64(len(rows)), ""
+	})
+
+	// ==================== 排序测试 ====================
+	section("7. 排序测试")
+
+	timed("ORDER BY age ASC (内存排序)", func() (int64, string) {
+		rows, _ := table.Query().Where(DB.Ge("score", int32(990), types.FieldType_4byte)).
+			OrderBy("age").Find()
+		ok := sort.SliceIsSorted(rows, func(i, j int) bool {
+			return findUint64(rows[i], "age") < findUint64(rows[j], "age")
+		})
+		return int64(len(rows)), fmt.Sprintf("sorted=%v", ok)
+	})
+
+	timed("ORDER BY age DESC (内存排序)", func() (int64, string) {
+		rows, _ := table.Query().Where(DB.Ge("score", int32(990), types.FieldType_4byte)).
+			OrderBy("age", true).Find()
+		ok := sort.SliceIsSorted(rows, func(i, j int) bool {
+			return findUint64(rows[i], "age") > findUint64(rows[j], "age")
+		})
+		return int64(len(rows)), fmt.Sprintf("sorted=%v", ok)
+	})
+
+	timed("ORDER BY score ASC, age DESC (多字段)", func() (int64, string) {
+		rows, _ := table.Query().Where(DB.And(
+			DB.Ge("score", int32(995), types.FieldType_4byte),
+			DB.Le("score", int32(999), types.FieldType_4byte),
+		)).OrderBy("score").OrderBy("age", true).Find()
+		return int64(len(rows)), ""
+	})
+
+	// ==================== 分组模拟 ====================
+	section("8. 分组统计 (Scan 流式)")
+	timed("GROUP BY age → COUNT(*) (流式 Scan)", func() (int64, string) {
+		groups := make(map[uint64]int64)
+		table.Query().Scan(func(row []types.FieldValue) bool {
+			age := findUint64(row, "age")
+			groups[age]++
+			return true
+		})
+		return int64(len(groups)), fmt.Sprintf("%d groups", len(groups))
+	})
+
+	timed("GROUP BY age → AVG(score) (流式 Scan)", func() (int64, string) {
+		type agg struct{ sum float64; cnt int64 }
+		groups := make(map[uint64]*agg)
+		table.Query().Scan(func(row []types.FieldValue) bool {
+			age := findUint64(row, "age")
+			score := findUint64(row, "score")
+			if groups[age] == nil {
+				groups[age] = &agg{}
+			}
+			groups[age].sum += float64(score)
+			groups[age].cnt++
+			return true
+		})
+		return int64(len(groups)), fmt.Sprintf("%d groups", len(groups))
+	})
+
+	// ==================== 更新 ====================
+	section("9. 批量更新 (改)")
+
+	timed("UPDATE score=0 WHERE id>=1 AND id<=N/10", func() (int64, string) {
+		cnt, err := table.Update().Set("score", int32(0)).Where(DB.And(
+			DB.Ge("id", uint64(1), types.FieldType_8byte),
+			DB.Le("id", uint64(N/10), types.FieldType_8byte),
+		)).Exec()
+		if err != nil {
+			return 0, err.Error()
+		}
+		return cnt, ""
+	})
+
+	// 验证更新
+	timed("验证: score=0 的行数", func() (int64, string) {
+		cnt, _ := table.Query().Where(DB.Eq("score", int32(0), types.FieldType_4byte)).Count()
+		return cnt, ""
+	})
+
+	timed("UPDATE age=99 WHERE age=18 (二级索引条件)", func() (int64, string) {
+		cnt, err := table.Update().Set("age", uint64(99)).Where(
+			DB.Eq("age", uint64(18), types.FieldType_8byte),
+		).Exec()
+		if err != nil {
+			return 0, err.Error()
+		}
+		return cnt, ""
+	})
+
+	// ==================== 删除 ====================
+	section("10. 批量删除 (删)")
+
+	timed("DELETE WHERE id>N*0.9", func() (int64, string) {
+		cnt, err := table.Delete().Where(
+			DB.Gt("id", uint64(float64(N)*0.9), types.FieldType_8byte),
+		).Exec()
+		if err != nil {
+			return 0, err.Error()
+		}
+		return cnt, ""
+	})
+
+	timed("验证: 删除后总行数", func() (int64, string) {
+		cnt, _ := table.Query().Count()
+		return cnt, ""
+	})
+
+	timed("DELETE WHERE age=97 (二级索引条件)", func() (int64, string) {
+		cnt, err := table.Delete().Where(
+			DB.Eq("age", uint64(97), types.FieldType_8byte),
+		).Exec()
+		if err != nil {
+			return 0, err.Error()
+		}
+		return cnt, ""
+	})
+
+	timed("最终: 总行数", func() (int64, string) {
+		cnt, _ := table.Query().Count()
+		return cnt, ""
+	})
+
+	// ==================== Offset + Limit ====================
+	section("11. OFFSET + LIMIT 分页")
+
+	timed("PK 分页: OFFSET N/2 LIMIT 10", func() (int64, string) {
+		rows, _ := table.Query().OrderBy("id").Offset(N/2).Limit(10).Find()
+		if len(rows) == 0 { return 0, "empty" }
+		return int64(len(rows)), fmt.Sprintf("first_id=%d", findUint64(rows[0], "id"))
+	})
+
+	timed("SEC 分页: OFFSET N/10 LIMIT 20", func() (int64, string) {
+		rows, _ := table.Query().OrderBy("age").Offset(N/10).Limit(20).Find()
+		if len(rows) == 0 { return 0, "empty" }
+		return int64(len(rows)), fmt.Sprintf("first_age=%d", findUint64(rows[0], "age"))
+	})
+
+	// ==================== 汇总报告 ====================
+	section("性能汇总报告")
+	fmt.Printf("\n  %-52s %12s %12s %12s  %s\n", "操作", "行数", "耗时", "吞吐(rows/s)", "备注")
+	fmt.Printf("  %s\n", strings.Repeat("-", 120))
+	totalTime := time.Duration(0)
+	for _, r := range results {
+		qps := "N/A"
+		if r.duration.Seconds() >= 0.001 {
+			qps = fmt.Sprintf("%.0f", float64(r.count)/r.duration.Seconds())
+		}
+		fmt.Printf("  %-52s %12s %12v %12s  %s\n",
+			r.name, formatNum(int(r.count)), r.duration.Round(time.Millisecond), qps, r.detail)
+		totalTime += r.duration
+	}
+	fmt.Printf("\n  总耗时: %v\n", totalTime.Round(time.Millisecond))
+
+	// 文件信息
+	if fi, err := os.Stat(dbName); err == nil {
+		fmt.Printf("  数据库文件大小: %s\n", formatBytes(fi.Size()))
+	}
+	fmt.Println("\n=== 基准测试完成 ===")
+}
+
+// ======================== 工具函数 ========================
+
+func formatNum(n int) string {
+	if n < 0 {
+		return "-" + formatNum(-n)
+	}
+	s := fmt.Sprintf("%d", n)
+	var result []byte
+	for i, c := range s {
+		if i > 0 && (len(s)-i)%3 == 0 {
+			result = append(result, ',')
+		}
+		result = append(result, byte(c))
+	}
+	return string(result)
+}
+
+func formatBytes(b int64) string {
+	const unit = 1024
+	if b < unit {
+		return fmt.Sprintf("%d B", b)
+	}
+	div, exp := int64(unit), 0
+	for n := b / unit; n >= unit; n /= unit {
+		div *= unit
+		exp++
+	}
+	return fmt.Sprintf("%.1f %cB", float64(b)/float64(div), "KMGTPE"[exp])
+}

+ 215 - 0
demo/query/query_demo.go

@@ -0,0 +1,215 @@
+package main
+
+import (
+	"TsDB2/DB"
+	"TsDB2/DB/page"
+	"TsDB2/DB/types"
+	"fmt"
+	"time"
+)
+
+func main() {
+	dbName := fmt.Sprintf("query_test_%d.db", time.Now().UnixNano())
+	db, err := DB.Open(dbName)
+	if err != nil {
+		fmt.Println("Open error:", err)
+		return
+	}
+	defer db.Close()
+
+	// ===== 创建表 =====
+	table, err := db.CreateTable("users", []types.FieldInfo{
+		{types.FieldType_8byte, []byte("id")},    // field 0
+		{types.FieldType_8byte, []byte("age")},   // field 1
+		{types.FieldType_8byte, []byte("score")}, // field 2
+		{types.FieldType_32byte, []byte("name")}, // field 3
+	})
+	if err != nil {
+		fmt.Println("CreateTable error:", err)
+		return
+	}
+
+	// ===== 创建索引 =====
+	_, err = table.CreateIndex("pk_id", page.IndexTypePrimary, []byte{0})
+	if err != nil {
+		fmt.Println("CreateIndex pk_id error:", err)
+		return
+	}
+	_, err = table.CreateIndex("idx_age", page.IndexTypeBTree, []byte{1})
+	if err != nil {
+		fmt.Println("CreateIndex idx_age error:", err)
+		return
+	}
+	_, err = table.CreateIndex("idx_name", page.IndexTypeStr, []byte{3})
+	if err != nil {
+		fmt.Println("CreateIndex idx_name error:", err)
+		return
+	}
+
+	// ===== 插入 100 行 =====
+	// id=i+1, age=18+i%50, score=50+i, name=user_XXX
+	for i := 0; i < 100; i++ {
+		nameBytes := make([]byte, 32)
+		copy(nameBytes, fmt.Sprintf("user_%03d", i))
+		_, err := table.InsertRow([]types.FieldValue{
+			{FieldName: []byte("id"), Value: uint64(i + 1)},
+			{FieldName: []byte("age"), Value: uint64(18 + i%50)},
+			{FieldName: []byte("score"), Value: uint64(50 + i)},
+			{FieldName: []byte("name"), Value: nameBytes},
+		})
+		if err != nil {
+			fmt.Printf("Insert error at i=%d id=%d: %v\n", i, i+1, err)
+			return
+		}
+	}
+	fmt.Println("插入 100 行完成")
+
+	pass, fail := 0, 0
+	check := func(name string, ok bool, detail string) {
+		if ok {
+			pass++
+			fmt.Printf("  [PASS] %s\n", name)
+		} else {
+			fail++
+			fmt.Printf("  [FAIL] %s %s\n", name, detail)
+		}
+	}
+
+	// ===== 1. 精确匹配 (命中主键索引) =====
+	fmt.Println("\n--- 1. 精确匹配 (主键索引) ---")
+	rows, err := table.Query().Where(DB.Eq("id", uint64(5), types.FieldType_8byte)).Find()
+	check("Eq(id=5) → 1 row", err == nil && len(rows) == 1, fmt.Sprintf("err=%v n=%d", err, len(rows)))
+	if len(rows) > 0 {
+		ageVal := findUint64(rows[0], "age")
+		check("  age=22", ageVal == 22, fmt.Sprintf("got %d", ageVal))
+	}
+
+	// ===== 2. 范围查询 (命中二级 B+ 树索引) =====
+	fmt.Println("\n--- 2. 范围查询 (二级 B+ 树索引) ---")
+	// age=18+i%50, age in [30,35) → i%50 in {12,13,14,15,16} → 10 rows
+	rows, err = table.Query().
+		Where(DB.And(
+			DB.Ge("age", uint64(30), types.FieldType_8byte),
+			DB.Lt("age", uint64(35), types.FieldType_8byte),
+		)).Find()
+	check("age>=30 AND age<35 → 10 rows", err == nil && len(rows) == 10, fmt.Sprintf("err=%v n=%d", err, len(rows)))
+
+	// ===== 3. 全表扫描 (无条件) =====
+	fmt.Println("\n--- 3. 全表扫描 ---")
+	rows, err = table.Query().Find()
+	check("无条件全表 → 100 rows", err == nil && len(rows) == 100, fmt.Sprintf("err=%v n=%d", err, len(rows)))
+
+	// ===== 4. 全表扫描 + WHERE (score 无索引) =====
+	fmt.Println("\n--- 4. 全表扫描 + WHERE (score 无索引) ---")
+	// score=50+i, score>140 → i=91..99 → 9 rows
+	rows, err = table.Query().Where(DB.Gt("score", uint64(140), types.FieldType_8byte)).Find()
+	check("score>140 → 9 rows", err == nil && len(rows) == 9, fmt.Sprintf("err=%v n=%d", err, len(rows)))
+
+	// ===== 5. OrderBy 降序 + Limit =====
+	fmt.Println("\n--- 5. OrderBy 降序 + Limit ---")
+	rows, err = table.Query().
+		Where(DB.Ge("age", uint64(60), types.FieldType_8byte)).
+		OrderBy("age", true).
+		Limit(3).Find()
+	check("age>=60 DESC LIMIT 3", err == nil && len(rows) == 3, fmt.Sprintf("err=%v n=%d", err, len(rows)))
+	if len(rows) > 0 {
+		check("  首行 age=67", findUint64(rows[0], "age") == 67, fmt.Sprintf("got %d", findUint64(rows[0], "age")))
+	}
+
+	// ===== 6. Offset =====
+	fmt.Println("\n--- 6. Offset ---")
+	rows, err = table.Query().OrderBy("id").Offset(95).Find()
+	check("OFFSET 95 → 5 rows", err == nil && len(rows) == 5, fmt.Sprintf("err=%v n=%d", err, len(rows)))
+
+	// ===== 7. 流式 Count (不收集行) =====
+	fmt.Println("\n--- 7. 流式 Count ---")
+	cnt, err := table.Query().Where(DB.Ge("age", uint64(18), types.FieldType_8byte)).Count()
+	check("COUNT(age>=18) = 100", err == nil && cnt == 100, fmt.Sprintf("err=%v cnt=%d", err, cnt))
+
+	// ===== 8. 流式 Sum (不收集行) =====
+	fmt.Println("\n--- 8. 流式 Sum ---")
+	// sum(score) = sum(50+i, i=0..99) = 50*100 + 99*100/2 = 5000+4950 = 9950
+	sum, err := table.Query().Sum("score")
+	check("SUM(score) = 9950", err == nil && sum == 9950, fmt.Sprintf("err=%v sum=%.0f", err, sum))
+
+	// ===== 9. 流式 Max/Min (不收集行) =====
+	fmt.Println("\n--- 9. 流式 Max/Min ---")
+	maxVal, err := table.Query().Max("score")
+	check("MAX(score) = 149", err == nil && maxVal.(uint64) == 149, fmt.Sprintf("err=%v val=%v", err, maxVal))
+	minVal, err := table.Query().Min("score")
+	check("MIN(score) = 50", err == nil && minVal.(uint64) == 50, fmt.Sprintf("err=%v val=%v", err, minVal))
+
+	// ===== 10. 字符串索引查询 =====
+	fmt.Println("\n--- 10. 字符串索引查询 ---")
+	nameKey := make([]byte, 32)
+	copy(nameKey, "user_050")
+	rows, err = table.Query().Where(DB.Eq("name", nameKey, types.FieldType_32byte)).Find()
+	check("Eq(name=user_050) → 1 row", err == nil && len(rows) == 1, fmt.Sprintf("err=%v n=%d", err, len(rows)))
+
+	// ===== 11. FindOne (流式,命中即停) =====
+	fmt.Println("\n--- 11. FindOne (流式命中即停) ---")
+	row, err := table.Query().Where(DB.Eq("id", uint64(42), types.FieldType_8byte)).FindOne()
+	check("FindOne(id=42)", err == nil && row != nil, fmt.Sprintf("err=%v", err))
+	if row != nil {
+		check("  score=91", findUint64(row, "score") == 91, fmt.Sprintf("got %d", findUint64(row, "score")))
+	}
+
+	// ===== 12. 反向遍历 (OrderBy DESC 命中索引) =====
+	fmt.Println("\n--- 12. 反向遍历 (OrderBy DESC 命中索引) ---")
+	rows, err = table.Query().
+		Where(DB.And(
+			DB.Ge("age", uint64(20), types.FieldType_8byte),
+			DB.Le("age", uint64(25), types.FieldType_8byte),
+		)).
+		OrderBy("age", true).
+		Find()
+	check("age 20-25 DESC → 12 rows", err == nil && len(rows) == 12, fmt.Sprintf("err=%v n=%d", err, len(rows)))
+	if len(rows) >= 2 {
+		a1 := findUint64(rows[0], "age")
+		a2 := findUint64(rows[1], "age")
+		check("  降序验证 a1>=a2", a1 >= a2, fmt.Sprintf("%d < %d", a1, a2))
+	}
+
+	// ===== 13. 流式 Scan 回调 =====
+	fmt.Println("\n--- 13. 流式 Scan 回调 ---")
+	scanCount := 0
+	var scanFirstID uint64
+	err = table.Query().
+		Where(DB.Ge("id", uint64(10), types.FieldType_8byte)).
+		OrderBy("id").
+		Scan(func(r []types.FieldValue) bool {
+			if scanCount == 0 {
+				scanFirstID = findUint64(r, "id")
+			}
+			scanCount++
+			return scanCount < 5 // 只取 5 行, 提前终止
+		})
+	check("Scan 回调 err=nil", err == nil, fmt.Sprintf("err=%v", err))
+	check("  回调收到 5 行", scanCount == 5, fmt.Sprintf("got %d", scanCount))
+	check("  首行 id=10", scanFirstID == 10, fmt.Sprintf("got %d", scanFirstID))
+
+	// ===== 14. 流式 Count + Limit =====
+	fmt.Println("\n--- 14. 流式 Count + Limit ---")
+	cnt, err = table.Query().Limit(10).Count()
+	check("LIMIT 10 COUNT = 10", err == nil && cnt == 10, fmt.Sprintf("err=%v cnt=%d", err, cnt))
+
+	// ===== 汇总 =====
+	fmt.Println("\n============================================================")
+	fmt.Printf("测试完成: 通过 %d, 失败 %d\n", pass, fail)
+	if fail > 0 {
+		fmt.Println("!!! 存在失败的测试 !!!")
+	} else {
+		fmt.Println("=== 全部通过 ===")
+	}
+}
+
+func findUint64(row []types.FieldValue, field string) uint64 {
+	for _, fv := range row {
+		if string(fv.FieldName) == field {
+			if v, ok := fv.Value.(uint64); ok {
+				return v
+			}
+		}
+	}
+	return 0
+}