Quellcode durchsuchen

refactor(core): 五个比较条件合一为 CmpCondition (B2)

Eq/Gt/Ge/Lt/Le 五个结构体 (逐字重复) → CmpCondition{Field,Value,Type,Op};
构造函数 core.Eq/Gt/Ge/Lt/Le 签名不变, 调用方零改动; classifyLeaf 五分支
塌缩为一; table_compare 五个 wrapper 合并为 compareOp —— Ge/Le 一次比较
+符号判断, 消除"等于"分支的二次堆解引用/二次编码。净 -120 行。
yl32940 vor 3 Wochen
Ursprung
Commit
be26dc0529
5 geänderte Dateien mit 72 neuen und 202 gelöschten Zeilen
  1. 36 156
      DB/core/condition_types.go
  2. 11 16
      DB/core/query_plan.go
  3. 23 28
      DB/core/table_compare.go
  4. 1 1
      DB/core/types/codec.go
  5. 1 1
      DB/core/types/types.go

+ 36 - 156
DB/core/condition_types.go

@@ -31,7 +31,23 @@ func (c AndCondition) Match(row []byte, table *Table) bool {
 	return true
 }
 
-type EqCondition struct {
+// CmpOp 比较算子: CmpCondition 的一次单行比较取什么方向。
+// Eq/Gt/Ge/Lt/Le 五个构造函数各填一个值; 索引规划 (classifyLeaf) 与行级过滤
+// (CmpCondition.Match → compareOp) 都按它分派。
+type CmpOp byte
+
+const (
+	OpEq CmpOp = iota // =
+	OpGt              // >
+	OpGe              // >=
+	OpLt              // <
+	OpLe              // <=
+)
+
+// CmpCondition 五种单值比较 (=, >, >=, <, <=) 的统一实现。
+// 原 EqCondition/GtCondition/GeCondition/LtCondition/LeCondition 五个导出类型
+// 已合并为本类型; Eq/Gt/Ge/Lt/Le 构造函数签名不变, 仍是构造条件的唯一入口。
+type CmpCondition struct {
 	Field string
 	Value interface{}
 
@@ -39,6 +55,9 @@ type EqCondition struct {
 	// (见 Table.compareColTypeFor)。它只剩"值怎么从 interface{} 取出"的提示作用,
 	// 传任何值结果都一样; 保留仅为兼容既有调用方, 新代码传 0 即可。
 	Type types.FieldType
+
+	// Op 比较方向, 由 Eq/Gt/Ge/Lt/Le 构造函数填入。
+	Op CmpOp
 }
 
 // Eq 构造 "field = value" 条件。
@@ -48,98 +67,38 @@ type EqCondition struct {
 // 上面刻意不写成 "Deprecated:" 段落 —— Go 只能标记整个标识符废弃, 写了会把本函数
 // 一起判为废弃, 而本函数还要继续用。
 func Eq(field string, value interface{}, typ types.FieldType) Condition {
-	return EqCondition{Field: field, Value: value, Type: typ}
+	return CmpCondition{Field: field, Value: value, Type: typ, Op: OpEq}
 }
 
-func (c EqCondition) Fields() []string {
-	return []string{c.Field}
-}
-
-func (c EqCondition) Match(row []byte, table *Table) bool {
-	fieldIdx := table.findFieldIndex(c.Field)
-	if fieldIdx < 0 {
-		return false
-	}
-	// 比较类型与列级参数一律取列的实际定义 (声明类型已废弃), 与索引路径编键同源。
-	ct := table.compareColTypeFor(fieldIdx)
-	// 定位列并把行内【实际长度】的字节补齐到档位宽度 (v9 行是变长的, 条件值仍按档位编码)。
-	seg, ok := table.rowFieldForCompare(row, fieldIdx, ct)
-	if !ok {
-		return false
-	}
-	return table.compareValue(seg, ct, c.Value)
-}
-
-type GeCondition struct {
-	Field string
-	Value interface{}
-
-	// Deprecated: 已不参与比较 —— 行级过滤与索引路径都按【列的实际存储类型】编字节
-	// (见 Table.compareColTypeFor)。它只剩"值怎么从 interface{} 取出"的提示作用,
-	// 传任何值结果都一样; 保留仅为兼容既有调用方, 新代码传 0 即可。
-	Type types.FieldType
+// Gt 构造 "field > value" 条件。第三个参数 typ 的废弃说明见 Eq。
+func Gt(field string, value interface{}, typ types.FieldType) Condition {
+	return CmpCondition{Field: field, Value: value, Type: typ, Op: OpGt}
 }
 
-// Ge 构造 "field >= value" 条件。
-//
-// 第三个参数 typ 已废弃: 比较一律按列的实际存储类型进行 (见 Table.compareColTypeFor),
-// 它取任何值结果都一样, 传 types.FieldType(0) 即可。
-// 上面刻意不写成 "Deprecated:" 段落 —— Go 只能标记整个标识符废弃, 写了会把本函数
-// 一起判为废弃, 而本函数还要继续用。
+// Ge 构造 "field >= value" 条件。第三个参数 typ 的废弃说明见 Eq。
 func Ge(field string, value interface{}, typ types.FieldType) Condition {
-	return GeCondition{Field: field, Value: value, Type: typ}
-}
-
-func (c GeCondition) Fields() []string {
-	return []string{c.Field}
+	return CmpCondition{Field: field, Value: value, Type: typ, Op: OpGe}
 }
 
-func (c GeCondition) Match(row []byte, table *Table) bool {
-	fieldIdx := table.findFieldIndex(c.Field)
-	if fieldIdx < 0 {
-		return false
-	}
-
-	// 比较类型与列级参数一律取列的实际定义 (声明类型已废弃), 与索引路径编键同源。
-	ct := table.compareColTypeFor(fieldIdx)
-	// 定位列并把行内【实际长度】的字节补齐到档位宽度 (v9 行是变长的, 条件值仍按档位编码)。
-	seg, ok := table.rowFieldForCompare(row, fieldIdx, ct)
-	if !ok {
-		return false
-	}
-	return table.compareGreaterOrEqual(seg, ct, c.Value)
-}
-
-type GtCondition struct {
-	Field string
-	Value interface{}
-
-	// Deprecated: 已不参与比较 —— 行级过滤与索引路径都按【列的实际存储类型】编字节
-	// (见 Table.compareColTypeFor)。它只剩"值怎么从 interface{} 取出"的提示作用,
-	// 传任何值结果都一样; 保留仅为兼容既有调用方, 新代码传 0 即可。
-	Type types.FieldType
+// Lt 构造 "field < value" 条件。第三个参数 typ 的废弃说明见 Eq。
+func Lt(field string, value interface{}, typ types.FieldType) Condition {
+	return CmpCondition{Field: field, Value: value, Type: typ, Op: OpLt}
 }
 
-// Gt 构造 "field > value" 条件。
-//
-// 第三个参数 typ 已废弃: 比较一律按列的实际存储类型进行 (见 Table.compareColTypeFor),
-// 它取任何值结果都一样, 传 types.FieldType(0) 即可。
-// 上面刻意不写成 "Deprecated:" 段落 —— Go 只能标记整个标识符废弃, 写了会把本函数
-// 一起判为废弃, 而本函数还要继续用。
-func Gt(field string, value interface{}, typ types.FieldType) Condition {
-	return GtCondition{Field: field, Value: value, Type: typ}
+// Le 构造 "field <= value" 条件。第三个参数 typ 的废弃说明见 Eq。
+func Le(field string, value interface{}, typ types.FieldType) Condition {
+	return CmpCondition{Field: field, Value: value, Type: typ, Op: OpLe}
 }
 
-func (c GtCondition) Fields() []string {
+func (c CmpCondition) Fields() []string {
 	return []string{c.Field}
 }
 
-func (c GtCondition) Match(row []byte, table *Table) bool {
+func (c CmpCondition) Match(row []byte, table *Table) bool {
 	fieldIdx := table.findFieldIndex(c.Field)
 	if fieldIdx < 0 {
 		return false
 	}
-
 	// 比较类型与列级参数一律取列的实际定义 (声明类型已废弃), 与索引路径编键同源。
 	ct := table.compareColTypeFor(fieldIdx)
 	// 定位列并把行内【实际长度】的字节补齐到档位宽度 (v9 行是变长的, 条件值仍按档位编码)。
@@ -147,7 +106,7 @@ func (c GtCondition) Match(row []byte, table *Table) bool {
 	if !ok {
 		return false
 	}
-	return table.compareGreater(seg, ct, c.Value)
+	return table.compareOp(seg, ct, c.Value, c.Op)
 }
 
 type InCondition struct {
@@ -188,52 +147,13 @@ func (c InCondition) Match(row []byte, table *Table) bool {
 		return false
 	}
 	for _, value := range c.Values {
-		if table.compareValue(seg, ct, value) {
+		if table.compareOp(seg, ct, value, OpEq) {
 			return true
 		}
 	}
 	return false
 }
 
-type LeCondition struct {
-	Field string
-	Value interface{}
-
-	// Deprecated: 已不参与比较 —— 行级过滤与索引路径都按【列的实际存储类型】编字节
-	// (见 Table.compareColTypeFor)。它只剩"值怎么从 interface{} 取出"的提示作用,
-	// 传任何值结果都一样; 保留仅为兼容既有调用方, 新代码传 0 即可。
-	Type types.FieldType
-}
-
-// Le 构造 "field <= value" 条件。
-//
-// 第三个参数 typ 已废弃: 比较一律按列的实际存储类型进行 (见 Table.compareColTypeFor),
-// 它取任何值结果都一样, 传 types.FieldType(0) 即可。
-// 上面刻意不写成 "Deprecated:" 段落 —— Go 只能标记整个标识符废弃, 写了会把本函数
-// 一起判为废弃, 而本函数还要继续用。
-func Le(field string, value interface{}, typ types.FieldType) Condition {
-	return LeCondition{Field: field, Value: value, Type: typ}
-}
-
-func (c LeCondition) Fields() []string {
-	return []string{c.Field}
-}
-
-func (c LeCondition) Match(row []byte, table *Table) bool {
-	fieldIdx := table.findFieldIndex(c.Field)
-	if fieldIdx < 0 {
-		return false
-	}
-	// 比较类型与列级参数一律取列的实际定义 (声明类型已废弃), 与索引路径编键同源。
-	ct := table.compareColTypeFor(fieldIdx)
-	// 定位列并把行内【实际长度】的字节补齐到档位宽度 (v9 行是变长的, 条件值仍按档位编码)。
-	seg, ok := table.rowFieldForCompare(row, fieldIdx, ct)
-	if !ok {
-		return false
-	}
-	return table.compareLessOrEqual(seg, ct, c.Value)
-}
-
 type LikeCondition struct {
 	Field         string
 	Pattern       string
@@ -325,46 +245,6 @@ func likeMatch(str, pattern string) bool {
 	return true
 }
 
-type LtCondition struct {
-	Field string
-	Value interface{}
-
-	// Deprecated: 已不参与比较 —— 行级过滤与索引路径都按【列的实际存储类型】编字节
-	// (见 Table.compareColTypeFor)。它只剩"值怎么从 interface{} 取出"的提示作用,
-	// 传任何值结果都一样; 保留仅为兼容既有调用方, 新代码传 0 即可。
-	Type types.FieldType
-}
-
-// Lt 构造 "field < value" 条件。
-//
-// 第三个参数 typ 已废弃: 比较一律按列的实际存储类型进行 (见 Table.compareColTypeFor),
-// 它取任何值结果都一样, 传 types.FieldType(0) 即可。
-// 上面刻意不写成 "Deprecated:" 段落 —— Go 只能标记整个标识符废弃, 写了会把本函数
-// 一起判为废弃, 而本函数还要继续用。
-func Lt(field string, value interface{}, typ types.FieldType) Condition {
-	return LtCondition{Field: field, Value: value, Type: typ}
-}
-
-func (c LtCondition) Fields() []string {
-	return []string{c.Field}
-}
-
-func (c LtCondition) Match(row []byte, table *Table) bool {
-	fieldIdx := table.findFieldIndex(c.Field)
-	if fieldIdx < 0 {
-		return false
-	}
-
-	// 比较类型与列级参数一律取列的实际定义 (声明类型已废弃), 与索引路径编键同源。
-	ct := table.compareColTypeFor(fieldIdx)
-	// 定位列并把行内【实际长度】的字节补齐到档位宽度 (v9 行是变长的, 条件值仍按档位编码)。
-	seg, ok := table.rowFieldForCompare(row, fieldIdx, ct)
-	if !ok {
-		return false
-	}
-	return table.compareLess(seg, ct, c.Value)
-}
-
 type NotCondition struct {
 	Cond Condition
 }

+ 11 - 16
DB/core/query_plan.go

@@ -91,15 +91,17 @@ type indexKeyLayout struct {
 	total   int
 }
 
-// cmpKind 叶子条件能为键分量提供的边界方向
+// cmpKind 叶子条件能为键分量提供的边界方向。
+// 取值与 CmpOp 一一对应 (cmpEq == cmpKind(OpEq), ...), classifyLeaf 因此可以
+// 直接 cmpKind(t.Op) 转换, 不必再写映射表。
 type cmpKind int
 
 const (
-	cmpEq cmpKind = iota
-	cmpGe
-	cmpGt
-	cmpLe
-	cmpLt
+	cmpEq = cmpKind(OpEq)
+	cmpGe = cmpKind(OpGe)
+	cmpGt = cmpKind(OpGt)
+	cmpLe = cmpKind(OpLe)
+	cmpLt = cmpKind(OpLt)
 )
 
 // keyConstraint 某个键分量上的合并边界 (多个 AND 子条件取交集)
@@ -247,16 +249,9 @@ func (q *Query) locateComponent(fieldName string, meta *IndexMeta) int {
 // comp < 0 表示与该索引无关 (字段不在索引里, 或该条件类型给不出可比边界)。
 func (q *Query) classifyLeaf(c Condition, meta *IndexMeta) (comp int, value interface{}, kind cmpKind) {
 	switch t := c.(type) {
-	case EqCondition:
-		return q.locateComponent(t.Field, meta), t.Value, cmpEq
-	case GeCondition:
-		return q.locateComponent(t.Field, meta), t.Value, cmpGe
-	case GtCondition:
-		return q.locateComponent(t.Field, meta), t.Value, cmpGt
-	case LeCondition:
-		return q.locateComponent(t.Field, meta), t.Value, cmpLe
-	case LtCondition:
-		return q.locateComponent(t.Field, meta), t.Value, cmpLt
+	case CmpCondition:
+		// cmpKind 与 CmpOp 取值一一对应 (见 cmpKind 常量块), 直接转换。
+		return q.locateComponent(t.Field, meta), t.Value, cmpKind(t.Op)
 	default:
 		return -1, nil, cmpEq
 	}

+ 23 - 28
DB/core/table_compare.go

@@ -15,11 +15,11 @@ const maxCompareInlineSize = types.MaxInlineColumnWidth
 
 // ======================== 行内字段比较 ========================
 //
-// 三个比较函数都经由 compareFieldBytes 实现, 也就是"把条件值编码成与行内同源的字节,
-// 再按字节比较"。这样做的理由:
+// 行级比较统一经由 compareOp → compareFieldBytes 实现, 也就是"把条件值编码成与行内
+// 同源的字节, 再按字节比较"。这样做的理由:
 //   - 行内字节与索引键字节本就同源 (都出自 types.EncodeValue), 于是行级过滤与索引
 //     范围扫描必然给出同一个结论, 不会出现"索引选出 5 行、过滤后剩 0 行"的自相矛盾;
-//   - 新增类型不必再回来改这三个函数 (以前每个类型要在这里各写 3 遍, 漏改一处就是静默漏行)。
+//   - 新增类型不必再回来改这一个函数 (以前每个类型要在这里各写 3 遍, 漏改一处就是静默漏行)。
 //
 // 与旧的"解码成 Go 值再比"相比, 唯一的行为差异是 NaN: 按字节比时 NaN == NaN 成立,
 // 而 float 的 == 不成立。索引键本来就是按字节比的, 统一到字节序反而消除了
@@ -225,30 +225,25 @@ func (table *Table) compareColTypeFor(fieldIdx int) colType {
 	return colType{table.Field[fieldIdx].FieldType, table.Field[fieldIdx].Param()}
 }
 
-// compareValue 比较字段值是否相等 (seg 由 rowFieldForCompare 定位并补齐)
-func (table *Table) compareValue(seg []byte, ct colType, value interface{}) bool {
+// compareOp 按算子 op 比较行内字段与给定值 (seg 由 rowFieldForCompare 定位并补齐)。
+// 只做一次 compareFieldBytes 再按符号判断 —— Ge/Le 因此不必像旧实现那样为"等于"分支
+// 再比一遍 (动态列每多比一次就要多解一次堆引用、把条件值再编一次码)。
+func (table *Table) compareOp(seg []byte, ct colType, value interface{}, op CmpOp) bool {
 	c, ok := table.compareFieldBytes(seg, ct, value)
-	return ok && c == 0
-}
-
-// compareGreater 比较字段值是否大于给定值
-func (table *Table) compareGreater(seg []byte, ct colType, value interface{}) bool {
-	c, ok := table.compareFieldBytes(seg, ct, value)
-	return ok && c > 0
-}
-
-// compareLess 比较字段值是否小于给定值
-func (table *Table) compareLess(seg []byte, ct colType, value interface{}) bool {
-	c, ok := table.compareFieldBytes(seg, ct, value)
-	return ok && c < 0
-}
-
-// compareGreaterOrEqual 比较字段值是否大于等于给定值
-func (table *Table) compareGreaterOrEqual(seg []byte, ct colType, value interface{}) bool {
-	return table.compareGreater(seg, ct, value) || table.compareValue(seg, ct, value)
-}
-
-// compareLessOrEqual 比较字段值是否小于等于给定值
-func (table *Table) compareLessOrEqual(seg []byte, ct colType, value interface{}) bool {
-	return table.compareLess(seg, ct, value) || table.compareValue(seg, ct, value)
+	if !ok {
+		return false
+	}
+	switch op {
+	case OpEq:
+		return c == 0
+	case OpGt:
+		return c > 0
+	case OpGe:
+		return c >= 0
+	case OpLt:
+		return c < 0
+	case OpLe:
+		return c <= 0
+	}
+	return false
 }

+ 1 - 1
DB/core/types/codec.go

@@ -17,7 +17,7 @@ import (
 // 【为什么必须收敛到一处】行内字节与索引键字节是同一份编码 —— 索引的 B+ 树直接按
 // 字节序比较, 所以"字节序 == 逻辑序"这条不变式只要有一处走样, 范围扫描就会静默漏行。
 // 以前每个类型要在 10 个 switch 里各写一遍 (writeFieldValue / readFieldValue /
-// encodeIndexKey / compareValue / compareGreater / compareLess / compareFieldValues /
+// encodeIndexKey / compareOp / compareFieldValues /
 // ORM 的 normalizeValue / zeroValue / migrate 的 coerce...), 靠人肉对齐;
 // 现在新增类型只需要: 常量 + 注册表 + 本文件的一个 case。
 //

+ 1 - 1
DB/core/types/types.go

@@ -612,7 +612,7 @@ func (f FieldInfo) Width() uint16 { return FieldSize(f.FieldType, f.Param()) }
 
 // ColumnScale 返回某字段的列级 scale (Decimal 的小数位数; 其余类型为 0)。
 //
-// 查询侧 (compareValue / encodeIndexKey) 需要它才能把条件值编码成与行内完全一致的
+// 查询侧 (compareOp / encodeIndexKey) 需要它才能把条件值编码成与行内完全一致的
 // 字节; 未提供时按 Decimal 的"值自带 scale"处理会漏掉"列级 scale 不一致"这种情况。
 func ColumnScale(f *FieldInfo) byte {
 	if f == nil {