query.go 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. package core
  2. import (
  3. "slices"
  4. "git.ycl.pub/yl32940/TsDB2/DB/core/index"
  5. "git.ycl.pub/yl32940/TsDB2/DB/core/types"
  6. )
  7. // Query 查询操作对象
  8. type Query struct {
  9. table *Table
  10. selectCols []string // SELECT 字段列表
  11. whereCond Condition // WHERE 条件
  12. orderBys []SortField // ORDER BY
  13. limit int // LIMIT
  14. offset int // OFFSET
  15. // wantMask 投影掩码 (master 下标 → 是否解码), selectCols 与 orderBys 的并集;
  16. // 排序键从解码后的行里取, ORDER BY 列必须保留在掩码中。nil = 不过滤 (全列)。
  17. wantMask []bool
  18. wantBuilt bool
  19. }
  20. type SortField struct {
  21. Field string
  22. Descending bool
  23. }
  24. // keyRange 索引扫描的单个键区间。
  25. type keyRange struct {
  26. startKey []byte
  27. endKey []byte
  28. startInclusive bool
  29. endInclusive bool
  30. }
  31. // scanPlan 索引扫描计划。
  32. // ranges 通常为单区间; WHERE ... IN (...) 落在可用分量上时展开成多区间
  33. // (每个值一个精确区间), 空切片表示"结果必为空" (如 IN 空集合)。
  34. type scanPlan struct {
  35. meta *IndexMeta
  36. handler index.IndexHandler
  37. ranges []keyRange
  38. rowStore bool // 该索引是否承载行数据 (主键/时序=true)
  39. remainingCond Condition // 需要行级过滤的剩余条件
  40. }
  41. // Query 返回查询操作对象
  42. func (table *Table) Query() *Query {
  43. return &Query{table: table}
  44. }
  45. func (q *Query) Select(cols ...string) *Query {
  46. q.selectCols = cols
  47. q.wantBuilt = false // 投影掩码随 selectCols 失效
  48. return q
  49. }
  50. func (q *Query) Where(cond Condition) *Query { q.whereCond = cond; return q }
  51. func (q *Query) Limit(n int) *Query { q.limit = n; return q }
  52. func (q *Query) Offset(n int) *Query { q.offset = n; return q }
  53. func (q *Query) OrderBy(field string, descending ...bool) *Query {
  54. desc := false
  55. if len(descending) > 0 {
  56. desc = descending[0]
  57. }
  58. q.orderBys = append(q.orderBys, SortField{Field: field, Descending: desc})
  59. q.wantBuilt = false // 投影掩码随 orderBys 失效
  60. return q
  61. }
  62. // rowWithPK 把结果行与它的 pkID 绑在一起 —— 排序/翻页会重排结果集, pkID 必须
  63. // 跟着行一起移动, 否则回调会把 A 行的 pkID 配到 B 行的数据上。
  64. type rowWithPK struct {
  65. pkID uint64
  66. row []types.FieldValue
  67. }
  68. // ======================== 核心查询 ========================
  69. // Find 执行查询,返回多行
  70. func (q *Query) Find() ([][]types.FieldValue, error) {
  71. var results [][]types.FieldValue
  72. err := q.scan(func(row []types.FieldValue) bool {
  73. results = append(results, row)
  74. return true
  75. })
  76. if err != nil {
  77. return nil, err
  78. }
  79. // 投影
  80. if len(q.selectCols) > 0 {
  81. results = q.project(results)
  82. }
  83. return results, nil
  84. }
  85. // Scan 流式扫描查询, 回调函数逐行处理数据, 返回 false 提前终止
  86. // 适用于不需要全量收集结果的场景, 内存友好
  87. func (q *Query) Scan(callback func(row []types.FieldValue) bool) error {
  88. return q.scan(callback)
  89. }
  90. // scan 内部流式扫描核心方法
  91. // 自动处理: 索引选择 → 排序 → OFFSET → LIMIT → 回调
  92. func (q *Query) scan(callback func(row []types.FieldValue) bool) error {
  93. return q.scanWithPKID(func(_ uint64, row []types.FieldValue) bool {
  94. return callback(row)
  95. })
  96. }
  97. // ScanWithPKID 流式扫描, 回调同时拿到行主键号 pkID 与列值。
  98. //
  99. // 迁移/导出工具必须用这个而不是 Scan: 行数据里不含 pkID (它是行的 8 字节前缀,
  100. // 不是字段), 只拿字段值就没法还原"这一行的主键号是多少" —— 导出到 SQL 时要么
  101. // 丢掉真实主键, 要么被迫用某个列值冒充 (列值可能是 0、可能被改过)。
  102. func (q *Query) ScanWithPKID(callback func(pkID uint64, row []types.FieldValue) bool) error {
  103. return q.scanWithPKID(callback)
  104. }
  105. // scanWithPKID 与 scan 完全一致, 但额外把每行的真实 pkID 交给回调。
  106. //
  107. // 为什么需要: 行数据里不含 pkID (它是行的 8 字节前缀, 不是字段), 普通 Scan 回调
  108. // 只拿得到字段值。删除/更新必须按真实 pkID 定位行, 旧实现只能退而求其次地
  109. // "取第一个 uint64 类型的字段值当 pkID" —— 主键列不是行内第一个 8 字节列时,
  110. // 它删掉的是另一个主键指向的行, 匹配上的行反而一条都没动, 而且不报错。
  111. func (q *Query) scanWithPKID(callback func(pkID uint64, row []types.FieldValue) bool) error {
  112. defer q.table.db.lockRead()() // 读锁: 纯查询可并发; 被 Deleter.Exec 调用时 (外层已持写锁) 同 goroutine 跳过
  113. plan := q.buildScanPlan()
  114. // 判断是否需要内存排序 (OrderBy 字段与索引不一致时)
  115. needSort := false
  116. if len(q.orderBys) > 0 {
  117. if plan == nil {
  118. needSort = true
  119. } else {
  120. idxField := q.fieldNameFromMeta(plan.meta)
  121. // 免排序只适用于单区间: 多区间 (IN 展开) 的输出是"各区间有序、
  122. // 全局无序"的拼接, 必须交给下面的排序管线。
  123. if !(len(q.orderBys) == 1 && q.orderBys[0].Field == idxField && len(plan.ranges) == 1) {
  124. needSort = true
  125. }
  126. }
  127. }
  128. // 需要排序时, 必须先收集全部再排序, 无法流式 LIMIT
  129. if needSort {
  130. specs := q.buildOrderSpecs()
  131. // ORDER BY + LIMIT: 流式 top-k, 只保留 limit+offset 行的小顶堆,
  132. // 内存 O(k) 且免全量排序 (旧实现物化全部结果再整体排序)。
  133. if q.limit > 0 && len(specs) > 0 {
  134. return q.topKScan(callback, plan, specs)
  135. }
  136. var all []rowWithPK
  137. err := q.executeScan(func(pkID uint64, row []types.FieldValue) bool {
  138. all = append(all, rowWithPK{pkID, row})
  139. return true
  140. }, plan, 0)
  141. if err != nil {
  142. return err
  143. }
  144. // 排序必须连 pkID 一起搬: 只排 []FieldValue 会让回调拿到错位的 pkID。
  145. // decoration: 每行只按名提取一次排序键, 键必须随行进数组 —— 排序会重排
  146. // 切片元素, 平行键数组会因此错位 (索引指向交换后的别的行)。
  147. // 各行列集合可能因 NULL 跳过而不同, 键按名取、缺失用 nil 占位。
  148. if len(specs) > 0 {
  149. dec := make([]struct {
  150. item rowWithPK
  151. keys []interface{}
  152. }, len(all))
  153. for i := range all {
  154. k := make([]interface{}, len(specs))
  155. for j, s := range specs {
  156. k[j], _ = lookupFieldValue(all[i].row, []byte(s.field))
  157. }
  158. dec[i] = struct {
  159. item rowWithPK
  160. keys []interface{}
  161. }{all[i], k}
  162. }
  163. slices.SortStableFunc(dec, func(a, b struct {
  164. item rowWithPK
  165. keys []interface{}
  166. }) int {
  167. return compareKeys(a.keys, b.keys, specs)
  168. })
  169. all = all[:0]
  170. for i := range dec {
  171. all = append(all, dec[i].item)
  172. }
  173. }
  174. // OFFSET
  175. if q.offset > 0 {
  176. if q.offset >= len(all) {
  177. return nil
  178. }
  179. all = all[q.offset:]
  180. }
  181. // LIMIT
  182. if q.limit > 0 && q.limit < len(all) {
  183. all = all[:q.limit]
  184. }
  185. for _, item := range all {
  186. if !callback(item.pkID, item.row) {
  187. return nil
  188. }
  189. }
  190. return nil
  191. }
  192. // 不需要排序: 直接流式传递 callback, 支持 LIMIT 提前终止。
  193. // 回表预算 = LIMIT+OFFSET: 达到即收工 (OFFSET 行也要交付给下面的跳行逻辑)。
  194. budget := 0
  195. if q.limit > 0 {
  196. budget = q.limit + q.offset
  197. }
  198. seen := 0
  199. return q.executeScan(func(pkID uint64, row []types.FieldValue) bool {
  200. if seen < q.offset {
  201. seen++
  202. return true // 跳过 OFFSET
  203. }
  204. seen++
  205. if !callback(pkID, row) {
  206. return false // 用户要求停止
  207. }
  208. // LIMIT 检查 (0 表示无限制)
  209. if q.limit > 0 && seen-q.offset >= q.limit {
  210. return false // 已满足, 提前终止扫描
  211. }
  212. return true
  213. }, plan, budget)
  214. }
  215. // deserialize 行解码入口: 无投影走全列解码; 有投影只解码选中列 (含 ORDER BY 列,
  216. // 排序键要从解码后的行里取)。掩码按 master 下标, 随查询构建器惰性构建一次。
  217. func (q *Query) deserialize(rowData []byte) []types.FieldValue {
  218. if len(q.selectCols) == 0 {
  219. return q.table.deserializeRow(rowData)
  220. }
  221. if !q.wantBuilt {
  222. mask := make([]bool, len(q.table.Field))
  223. mark := func(name string) {
  224. if i := q.table.findFieldIndex(name); i >= 0 {
  225. mask[i] = true
  226. }
  227. }
  228. for _, c := range q.selectCols {
  229. mark(c)
  230. }
  231. for _, ob := range q.orderBys {
  232. mark(ob.Field)
  233. }
  234. q.wantMask = mask
  235. q.wantBuilt = true
  236. }
  237. return q.table.deserializeRowSelect(rowData, q.wantMask)
  238. }
  239. // ======================== 投影 ========================
  240. func (q *Query) project(results [][]types.FieldValue) [][]types.FieldValue {
  241. colSet := make(map[string]bool)
  242. for _, c := range q.selectCols {
  243. colSet[c] = true
  244. }
  245. for i, row := range results {
  246. var filtered []types.FieldValue
  247. for _, fv := range row {
  248. if colSet[string(fv.FieldName)] {
  249. filtered = append(filtered, fv)
  250. }
  251. }
  252. results[i] = filtered
  253. }
  254. return results
  255. }