• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

freeeve / tinykvs / 21101309417

17 Jan 2026 09:42PM UTC coverage: 68.627% (-4.3%) from 72.975%
21101309417

push

github

freeeve
feat: add ORDER BY support and box-style table formatting

New features:
- ORDER BY support in SQL queries (ASC/DESC, multiple columns)
- Box-style table formatting with Unicode borders
- GitHub Actions CI for multi-platform releases (linux/darwin/windows × amd64/arm64)
- Build script with ldflags for version embedding

Improvements:
- Split shell.go into logical modules (shell_select.go, shell_write.go,
  shell_csv.go, shell_sql.go, shell_sort.go)
- Version string no longer shows redundant commit hash
- Reorganized tests into corresponding test files
- Added batch_parallel.go for parallel batch operations
- Test coverage improvements

975 of 1677 new or added lines in 11 files covered. (58.14%)

8 existing lines in 3 files now uncovered.

5180 of 7548 relevant lines covered (68.63%)

430934.64 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

6.52
/cmd/tinykvs/shell_sort.go
1
package main
2

3
import (
4
        "sort"
5
        "strconv"
6
        "strings"
7
)
8

9
// SortOrder represents a single ORDER BY column
10
type SortOrder struct {
11
        Field      string // "k" for key, or field name like "ttl", "v.name"
12
        Descending bool
13
}
14

15
// SortableRows wraps rows for sorting with ORDER BY
16
type SortableRows struct {
17
        Headers []string
18
        Rows    [][]string
19
        Orders  []SortOrder
20
}
21

NEW
22
func (s *SortableRows) Len() int {
×
NEW
23
        return len(s.Rows)
×
NEW
24
}
×
25

NEW
26
func (s *SortableRows) Swap(i, j int) {
×
NEW
27
        s.Rows[i], s.Rows[j] = s.Rows[j], s.Rows[i]
×
NEW
28
}
×
29

NEW
30
func (s *SortableRows) Less(i, j int) bool {
×
NEW
31
        for _, order := range s.Orders {
×
NEW
32
                colIdx := s.findColumn(order.Field)
×
NEW
33
                if colIdx < 0 {
×
NEW
34
                        continue
×
35
                }
36

NEW
37
                valI := s.Rows[i][colIdx]
×
NEW
38
                valJ := s.Rows[j][colIdx]
×
NEW
39

×
NEW
40
                cmp := compareValues(valI, valJ)
×
NEW
41
                if cmp == 0 {
×
NEW
42
                        continue
×
43
                }
44

NEW
45
                if order.Descending {
×
NEW
46
                        return cmp > 0
×
NEW
47
                }
×
NEW
48
                return cmp < 0
×
49
        }
NEW
50
        return false
×
51
}
52

NEW
53
func (s *SortableRows) findColumn(field string) int {
×
NEW
54
        // Normalize field name
×
NEW
55
        field = strings.TrimPrefix(field, "v.")
×
NEW
56
        field = strings.ToLower(field)
×
NEW
57

×
NEW
58
        for i, h := range s.Headers {
×
NEW
59
                if strings.ToLower(h) == field {
×
NEW
60
                        return i
×
NEW
61
                }
×
62
        }
NEW
63
        return -1
×
64
}
65

66
// compareValues compares two string values, attempting numeric comparison first
NEW
67
func compareValues(a, b string) int {
×
NEW
68
        // Try numeric comparison first
×
NEW
69
        numA, errA := strconv.ParseFloat(a, 64)
×
NEW
70
        numB, errB := strconv.ParseFloat(b, 64)
×
NEW
71
        if errA == nil && errB == nil {
×
NEW
72
                if numA < numB {
×
NEW
73
                        return -1
×
NEW
74
                } else if numA > numB {
×
NEW
75
                        return 1
×
NEW
76
                }
×
NEW
77
                return 0
×
78
        }
79

80
        // Fall back to string comparison
NEW
81
        if a < b {
×
NEW
82
                return -1
×
NEW
83
        } else if a > b {
×
NEW
84
                return 1
×
NEW
85
        }
×
NEW
86
        return 0
×
87
}
88

89
// SortRows sorts rows according to the ORDER BY clauses
NEW
90
func SortRows(headers []string, rows [][]string, orders []SortOrder) {
×
NEW
91
        if len(orders) == 0 || len(rows) == 0 {
×
NEW
92
                return
×
NEW
93
        }
×
94

NEW
95
        sortable := &SortableRows{
×
NEW
96
                Headers: headers,
×
NEW
97
                Rows:    rows,
×
NEW
98
                Orders:  orders,
×
NEW
99
        }
×
NEW
100
        sort.Stable(sortable)
×
101
}
102

103
// ParseOrderBy extracts ORDER BY clauses from SQL
104
// Returns the modified SQL (with ORDER BY removed) and the sort orders
105
func ParseOrderBy(sql string) (string, []SortOrder) {
292✔
106
        lower := strings.ToLower(sql)
292✔
107
        idx := strings.LastIndex(lower, " order by ")
292✔
108
        if idx == -1 {
584✔
109
                return sql, nil
292✔
110
        }
292✔
111

NEW
112
        orderPart := sql[idx+len(" order by "):]
×
NEW
113
        sql = sql[:idx]
×
NEW
114

×
NEW
115
        // Remove any trailing LIMIT clause from orderPart
×
NEW
116
        limitIdx := strings.Index(strings.ToLower(orderPart), " limit ")
×
NEW
117
        if limitIdx != -1 {
×
NEW
118
                // Put LIMIT back on the main SQL
×
NEW
119
                sql = sql + orderPart[limitIdx:]
×
NEW
120
                orderPart = orderPart[:limitIdx]
×
NEW
121
        }
×
122

123
        // Parse order columns
NEW
124
        var orders []SortOrder
×
NEW
125
        parts := strings.Split(orderPart, ",")
×
NEW
126
        for _, part := range parts {
×
NEW
127
                part = strings.TrimSpace(part)
×
NEW
128
                if part == "" {
×
NEW
129
                        continue
×
130
                }
131

NEW
132
                order := SortOrder{}
×
NEW
133
                tokens := strings.Fields(part)
×
NEW
134
                if len(tokens) >= 1 {
×
NEW
135
                        order.Field = tokens[0]
×
NEW
136
                }
×
NEW
137
                if len(tokens) >= 2 {
×
NEW
138
                        if strings.ToUpper(tokens[1]) == "DESC" {
×
NEW
139
                                order.Descending = true
×
NEW
140
                        }
×
141
                }
NEW
142
                orders = append(orders, order)
×
143
        }
144

NEW
145
        return sql, orders
×
146
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc