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

freeeve / tinykvs / 21187883726

20 Jan 2026 09:28PM UTC coverage: 71.004% (+0.03%) from 70.979%
21187883726

push

github

freeeve
fix(scripts): improve shell script best practices

- Use [[ ]] instead of [ ] for conditional tests
- Rename local variables to lower_case convention
- Add explicit return statements to functions

5757 of 8108 relevant lines covered (71.0%)

403816.13 hits per line

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

93.43
/cmd/tinykvs/shell_write.go
1
package main
2

3
import (
4
        "encoding/json"
5
        "fmt"
6
        "strings"
7

8
        "github.com/blastrain/vitess-sqlparser/sqlparser"
9
        "github.com/freeeve/tinykvs"
10
)
11

12
const msgErrFmt = "Error: %v\n"
13

14
func (s *Shell) handleInsert(stmt *sqlparser.Insert) {
268✔
15
        rows, ok := stmt.Rows.(sqlparser.Values)
268✔
16
        if !ok || len(rows) == 0 {
268✔
17
                fmt.Println("Error: Invalid INSERT syntax")
×
18
                return
×
19
        }
×
20

21
        inserted := 0
268✔
22
        for _, row := range rows {
538✔
23
                if s.insertRow(row) {
534✔
24
                        inserted++
264✔
25
                }
264✔
26
        }
27

28
        fmt.Printf("INSERT %d\n", inserted)
268✔
29
}
30

31
// insertRow processes a single INSERT row and returns true on success.
32
func (s *Shell) insertRow(row sqlparser.ValTuple) bool {
270✔
33
        if len(row) < 2 {
271✔
34
                fmt.Println("Error: INSERT requires (key, value)")
1✔
35
                return false
1✔
36
        }
1✔
37

38
        key := extractValue(row[0])
269✔
39
        if key == "" {
270✔
40
                fmt.Println("Error: key cannot be empty")
1✔
41
                return false
1✔
42
        }
1✔
43

44
        value, hexBytes, isHex := extractValueAndType(row[1])
268✔
45
        return s.storeInsertValue(key, value, hexBytes, isHex)
268✔
46
}
47

48
// storeInsertValue stores a value, trying msgpack, JSON, bytes, or string in order.
49
func (s *Shell) storeInsertValue(key, value string, hexBytes []byte, isHex bool) bool {
268✔
50
        keyBytes := []byte(key)
268✔
51

268✔
52
        // Try msgpack first if it's hex data that looks like a msgpack map
268✔
53
        if isHex && isMsgpackMap(hexBytes) {
274✔
54
                if ok := s.tryStoreMsgpack(keyBytes, hexBytes); ok {
7✔
55
                        return true
1✔
56
                }
1✔
57
                // Fall through to store as bytes if msgpack decode fails
58
        }
59

60
        // Detect JSON and store as record
61
        if ok := s.tryStoreJSON(keyBytes, value); ok {
388✔
62
                return true
121✔
63
        }
121✔
64

65
        // Store hex data as bytes, strings as strings
66
        return s.storeRawValue(keyBytes, value, hexBytes, isHex)
146✔
67
}
68

69
// tryStoreMsgpack attempts to decode and store msgpack data.
70
func (s *Shell) tryStoreMsgpack(key, hexBytes []byte) bool {
6✔
71
        record, err := tinykvs.DecodeMsgpack(hexBytes)
6✔
72
        if err != nil {
11✔
73
                return false
5✔
74
        }
5✔
75
        if err := s.store.PutMap(key, record); err != nil {
1✔
76
                fmt.Printf(msgErrFmt, err)
×
77
                return false
×
78
        }
×
79
        return true
1✔
80
}
81

82
// tryStoreJSON attempts to parse and store JSON data.
83
func (s *Shell) tryStoreJSON(key []byte, value string) bool {
267✔
84
        if !strings.HasPrefix(value, "{") || !strings.HasSuffix(value, "}") {
411✔
85
                return false
144✔
86
        }
144✔
87
        var record map[string]any
123✔
88
        if err := json.Unmarshal([]byte(value), &record); err != nil {
124✔
89
                return false
1✔
90
        }
1✔
91
        if err := s.store.PutMap(key, record); err != nil {
123✔
92
                fmt.Printf(msgErrFmt, err)
1✔
93
                return false
1✔
94
        }
1✔
95
        return true
121✔
96
}
97

98
// storeRawValue stores a value as either bytes or string.
99
func (s *Shell) storeRawValue(key []byte, value string, hexBytes []byte, isHex bool) bool {
146✔
100
        var err error
146✔
101
        if isHex {
157✔
102
                err = s.store.PutBytes(key, hexBytes)
11✔
103
        } else {
146✔
104
                err = s.store.PutString(key, value)
135✔
105
        }
135✔
106
        if err != nil {
150✔
107
                fmt.Printf(msgErrFmt, err)
4✔
108
                return false
4✔
109
        }
4✔
110
        return true
142✔
111
}
112

113
func (s *Shell) handleUpdate(stmt *sqlparser.Update) {
6✔
114
        // Extract key from WHERE
6✔
115
        var keyEquals string
6✔
116
        if stmt.Where != nil {
11✔
117
                var dummy1, dummy2, dummy3 string
5✔
118
                var dummyFilters []*valueFilter
5✔
119
                s.parseWhere(stmt.Where.Expr, &keyEquals, &dummy1, &dummy2, &dummy3, &dummyFilters)
5✔
120
        }
5✔
121

122
        if keyEquals == "" {
7✔
123
                fmt.Println("Error: UPDATE requires WHERE k = 'value'")
1✔
124
                return
1✔
125
        }
1✔
126

127
        // Extract new value from SET
128
        var newValue string
5✔
129
        for _, expr := range stmt.Exprs {
10✔
130
                if strings.ToLower(expr.Name.Name.String()) == "v" {
9✔
131
                        newValue = extractValue(expr.Expr)
4✔
132
                        break
4✔
133
                }
134
        }
135

136
        // Detect JSON and store as record (same logic as INSERT)
137
        if strings.HasPrefix(newValue, "{") && strings.HasSuffix(newValue, "}") {
6✔
138
                var record map[string]any
1✔
139
                if err := json.Unmarshal([]byte(newValue), &record); err == nil {
2✔
140
                        if err := s.store.PutMap([]byte(keyEquals), record); err != nil {
1✔
141
                                fmt.Printf(msgErrFmt, err)
×
142
                                return
×
143
                        }
×
144
                        fmt.Println("UPDATE 1")
1✔
145
                        return
1✔
146
                }
147
        }
148

149
        if err := s.store.PutString([]byte(keyEquals), newValue); err != nil {
5✔
150
                fmt.Printf(msgErrFmt, err)
1✔
151
                return
1✔
152
        }
1✔
153

154
        fmt.Println("UPDATE 1")
3✔
155
}
156

157
func (s *Shell) handleDelete(stmt *sqlparser.Delete) {
13✔
158
        var keyEquals string
13✔
159
        var keyPrefix string
13✔
160
        var keyStart, keyEnd string
13✔
161
        var dummyFilters []*valueFilter
13✔
162

13✔
163
        if stmt.Where != nil {
25✔
164
                s.parseWhere(stmt.Where.Expr, &keyEquals, &keyPrefix, &keyStart, &keyEnd, &dummyFilters)
12✔
165
        }
12✔
166

167
        if keyEquals != "" {
17✔
168
                // Delete single key
4✔
169
                if err := s.store.Delete([]byte(keyEquals)); err != nil {
5✔
170
                        fmt.Printf(msgErrFmt, err)
1✔
171
                        return
1✔
172
                }
1✔
173
                fmt.Println("DELETE 1")
3✔
174
        } else if keyPrefix != "" {
13✔
175
                // Delete by prefix
4✔
176
                deleted, err := s.store.DeletePrefix([]byte(keyPrefix))
4✔
177
                if err != nil {
5✔
178
                        fmt.Printf(msgErrFmt, err)
1✔
179
                        return
1✔
180
                }
1✔
181
                fmt.Printf("DELETE %d\n", deleted)
3✔
182
        } else if keyStart != "" && keyEnd != "" {
9✔
183
                // Delete range
4✔
184
                deleted, err := s.store.DeleteRange([]byte(keyStart), []byte(keyEnd))
4✔
185
                if err != nil {
5✔
186
                        fmt.Printf(msgErrFmt, err)
1✔
187
                        return
1✔
188
                }
1✔
189
                fmt.Printf("DELETE %d\n", deleted)
3✔
190
        } else {
1✔
191
                fmt.Println("Error: DELETE requires WHERE clause")
1✔
192
        }
1✔
193
}
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