// ORDER BY and pagination for materialized projection rows: a // decorate-sort-undecorate that evaluates each row's key vector once, // with a typed fast path (numeric/entity key columns pack into // order-preserving words so the comparator is pure word compares) and a // bounded top-k selection under LIMIT. Split from project.go, which holds // the streaming projection sink.
package exec import ( ) // sortRowsByOrder orders outs by proj.OrderBy, decorate-sort-undecorate: each // row's key vector is evaluated once up front, so the comparator does no // per-comparison evaluation or allocation (an ORDER BY key would otherwise be // re-evaluated O(rows log rows) times). matchedAt supplies each row's matched // prefix (nil when no key needs it) and base is that prefix's width, so a // non-projected key expression reads a reused combined-row buffer under an // invariant column scope. func sortRowsByOrder( *eval.Ctx, *plan.ProjPlan, map[string]int, func(int) []value.Value, int, [][]value.Value) [][]value.Value { := len(.OrderBy) := make([]int, ) for := range .OrderBy { [] = plan.OrderColIndex(.OrderBy[].Expr, .Columns, .Returns) } := make(map[string]int, len()+len(.Columns)) maps.Copy(, ) for , := range .Columns { [] = + } := make([]value.Value, len()*) var []value.Value for := range { := false for := range .OrderBy { if [] >= 0 { [*+] = [][[]] continue } if ! { = append([:0], ()...) = append(, []...) = true } [*+] = eval.Eval(, .OrderBy[].Expr, , ) } } := make([]int, len()) for := range { [] = } // Typed key columns: a key column homogeneous over OrderCmp's numeric // tier (Int/Float) or over one entity kind compares through its // primitive encoding -- the identical kernel (value.TotalOrderF64 / // id order), skipping the boxed rank dispatch per comparison. Any // other mix keeps value.OrderCmp. const ( = uint8(iota) ) := make([]uint8, ) var []float64 var []uint64 if len() > 0 { for := range { , , := true, true, true for := range { switch [*+].Kind() { case value.KindInt, value.KindFloat: , = false, false case value.KindNode: , = false, false case value.KindRel: , = false, false default: , , = false, false, false } if ! && ! && ! { break } } switch { case : [] = if == nil { = make([]float64, len()*) } for := range { [*+] = value.OrderNumF64([*+]) } case || : [] = if == nil { = make([]uint64, len()*) } for := range { var uint64 if , := [*+].AsNode(); { = uint64(uint32()) } else if , := [*+].AsRel(); { = uint64(uint32()) } [*+] = } } } } // The index tiebreak makes cmp a total order, so an unstable generic // sort (no reflection-based swapping) reproduces stable-sort output // exactly -- and a total order is what lets the bounded selection // below equal sort-then-truncate. := func(, int) int { , := *, * for := range .OrderBy { var int switch [] { case : = value.TotalOrderF64([+], [+]) case : , := [+], [+] switch { case < : = -1 case > : = 1 } default: = value.OrderCmp([+], [+]) } if .OrderBy[].Desc { = - } if != 0 { return } } return - } // When every key column is typed, the key vector packs into // order-preserving uint64 words -- numeric through the IEEE-754 // totalOrder monotone encoding (the same relation TotalOrderF64 // computes per comparison, applied once per key instead), entity ids // directly, descending baked in by complement -- so the comparator is // pure word compares. Result order is identical to the typed // comparator above by monotonicity of the encodings. := len() > 0 for := range { if [] == { = false break } } if { := make([]uint64, len()*) for := range { := .OrderBy[].Desc for := range { var uint64 if [] == { = packSortWordF64([*+]) } else { = [*+] } if { = ^ } [*+] = } } = func(, int) int { , := *, * for := 0; < ; ++ { , := [+], [+] if != { if < { return -1 } return 1 } } return - } } // Under ORDER BY + LIMIT, pagination consumes only the leading // skip+limit rows: select those with a bounded heap (one comparison // per rejected row) instead of sorting everything. if := orderBound(); >= 0 && < len() { = topKIdx(, , ) } slices.SortFunc(, ) := make([][]value.Value, len()) for , := range { [] = [] } return } // packSortWordF64 is the IEEE-754 totalOrder monotone uint64 encoding: // unsigned comparison of the encoded words equals value.TotalOrderF64 on // the raw floats (negatives complement fully; non-negatives set the sign // bit), so a packed key column sorts through plain word compares. func packSortWordF64( float64) uint64 { := math.Float64bits() if &(1<<63) != 0 { return ^ } return | 1<<63 } // orderBound is the row count pagination can consume after an ordered // projection (skip+limit), or -1 when unbounded. func orderBound( *plan.ProjPlan) int { if .Limit == nil { return -1 } := *.Limit if .Skip != nil { += *.Skip } if > uint64(^uint(0)>>1) { return -1 } return int() } // topKIdx selects the k smallest elements of idx under the total order // cmp, order among them unspecified (the caller sorts the survivors). A // size-k max-heap over the prefix; every further candidate is rejected // with a single comparison against the current k-th unless it improves // the set. Equal to sort-then-truncate because cmp is total. func topKIdx( []int, int, func(, int) int) []int { if == 0 { return [:0] } := [:] for := /2 - 1; >= 0; -- { siftDownIdx(, , ) } for , := range [:] { if (, [0]) < 0 { [0] = siftDownIdx(, 0, ) } } return } // siftDownIdx restores the max-heap property below i. func siftDownIdx( []int, int, func(, int) int) { for { := 2* + 1 if >= len() { return } := if := + 1; < len() && ([], []) > 0 { = } if ([], []) <= 0 { return } [], [] = [], [] = } } // paginate applies OFFSET/SKIP then LIMIT. func paginate( [][]value.Value, , *uint64) [][]value.Value { if != nil { := int(*) if >= len() { return nil } = [:] } if != nil && uint64(len()) > * { = [:*] } return }