Skip to content

Commit e5ab79c

Browse files
committed
fix(protocol): handle missing types in protocolBufferFromValue
- Add uint, uint8, uint16, uint32, uint64 to the integer type switch so unsigned integers are serialized instead of silently dropped - Fix float32 panic in protocolBufferFromFloat caused by unconditional float64 type assertion - Dereference pointer types (*int, *string, etc.) via reflect, mapping nil pointers to NULL and non-nil pointers to their underlying value - Add unit tests for all supported types and a mixed-array regression test that reproduces the original buffer count mismatch
1 parent a3dbd27 commit e5ab79c

File tree

2 files changed

+126
-2
lines changed

2 files changed

+126
-2
lines changed

chunk.go

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"fmt"
2323
"io"
2424
"net"
25+
"reflect"
2526
"strconv"
2627
"strings"
2728
"time"
@@ -225,7 +226,7 @@ func protocolBufferFromValue(v interface{}) [][]byte {
225226
switch v := v.(type) {
226227
case nil:
227228
return protocolBufferFromNull()
228-
case int, int8, int16, int32, int64:
229+
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
229230
return protocolBufferFromInt(v)
230231
case float32, float64:
231232
return protocolBufferFromFloat(v)
@@ -234,6 +235,13 @@ func protocolBufferFromValue(v interface{}) [][]byte {
234235
case []byte:
235236
return protocolBufferFromBytes(v)
236237
default:
238+
rv := reflect.ValueOf(v)
239+
if rv.Kind() == reflect.Ptr {
240+
if rv.IsNil() {
241+
return protocolBufferFromNull()
242+
}
243+
return protocolBufferFromValue(rv.Elem().Interface())
244+
}
237245
return make([][]byte, 0)
238246
}
239247
}
@@ -255,7 +263,14 @@ func protocolBufferFromInt(v interface{}) [][]byte {
255263
}
256264

257265
func protocolBufferFromFloat(v interface{}) [][]byte {
258-
return [][]byte{[]byte(fmt.Sprintf("%c%s ", CMD_FLOAT, strconv.FormatFloat(v.(float64), 'f', -1, 64)))}
266+
var f float64
267+
switch v := v.(type) {
268+
case float32:
269+
f = float64(v)
270+
case float64:
271+
f = v
272+
}
273+
return [][]byte{[]byte(fmt.Sprintf("%c%s ", CMD_FLOAT, strconv.FormatFloat(f, 'f', -1, 64)))}
259274
}
260275

261276
// func protocolBufferFromFloat(v interface{}) [][]byte {

chunk_internal_test.go

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package sqlitecloud
2+
3+
import (
4+
"testing"
5+
)
6+
7+
func TestProtocolBufferFromValue(t *testing.T) {
8+
tests := []struct {
9+
name string
10+
value interface{}
11+
wantLen int // expected number of []byte buffers returned
12+
wantType byte
13+
}{
14+
// Basic types
15+
{"nil", nil, 1, CMD_NULL},
16+
{"string", "hello", 1, CMD_ZEROSTRING},
17+
{"int", int(42), 1, CMD_INT},
18+
{"int8", int8(8), 1, CMD_INT},
19+
{"int16", int16(16), 1, CMD_INT},
20+
{"int32", int32(32), 1, CMD_INT},
21+
{"int64", int64(64), 1, CMD_INT},
22+
{"float32", float32(3.14), 1, CMD_FLOAT},
23+
{"float64", float64(2.71), 1, CMD_FLOAT},
24+
{"[]byte", []byte("blob"), 2, CMD_BLOB}, // header + data
25+
26+
// Unsigned integers
27+
{"uint", uint(1), 1, CMD_INT},
28+
{"uint8", uint8(1), 1, CMD_INT},
29+
{"uint16", uint16(1), 1, CMD_INT},
30+
{"uint32", uint32(1), 1, CMD_INT},
31+
{"uint64", uint64(1), 1, CMD_INT},
32+
33+
// Pointer types (dereferenced)
34+
{"*int", intPtr(42), 1, CMD_INT},
35+
{"*string", strPtr("hello"), 1, CMD_ZEROSTRING},
36+
{"*int nil", (*int)(nil), 1, CMD_NULL},
37+
{"*string nil", (*string)(nil), 1, CMD_NULL},
38+
39+
// Unsupported types still return empty buffers
40+
{"bool", true, 0, 0},
41+
}
42+
43+
for _, tt := range tests {
44+
t.Run(tt.name, func(t *testing.T) {
45+
buffers := protocolBufferFromValue(tt.value)
46+
if len(buffers) != tt.wantLen {
47+
t.Errorf("protocolBufferFromValue(%T(%v)): got %d buffers, want %d", tt.value, tt.value, len(buffers), tt.wantLen)
48+
}
49+
if tt.wantLen > 0 && len(buffers) > 0 {
50+
if buffers[0][0] != tt.wantType {
51+
t.Errorf("protocolBufferFromValue(%T(%v)): got type %c, want %c", tt.value, tt.value, buffers[0][0], tt.wantType)
52+
}
53+
}
54+
})
55+
}
56+
}
57+
58+
func TestProtocolBufferFromValueMixedArray(t *testing.T) {
59+
// Simulates the loop in sendArray: builds buffers from a mixed values slice
60+
// and checks that the number of buffer groups matches the number of values.
61+
pInt := intPtr(99)
62+
values := []interface{}{
63+
"hello", // string -> 1 buffer
64+
int(42), // int -> 1 buffer
65+
nil, // nil -> 1 buffer
66+
pInt, // *int -> 1 buffer (dereferenced to int)
67+
float64(3), // float64 -> 1 buffer
68+
uint(7), // uint -> 1 buffer
69+
[]byte("x"), // []byte -> 2 buffers (header+data)
70+
}
71+
72+
// Count how many values produce at least one buffer
73+
buffersPerValue := make([]int, len(values))
74+
totalBuffers := 0
75+
missingValues := 0
76+
77+
for i, v := range values {
78+
bufs := protocolBufferFromValue(v)
79+
buffersPerValue[i] = len(bufs)
80+
totalBuffers += len(bufs)
81+
if len(bufs) == 0 {
82+
missingValues++
83+
t.Errorf("value[%d] (%T = %v) produced 0 buffers — will be silently dropped", i, v, v)
84+
}
85+
}
86+
87+
if missingValues > 0 {
88+
t.Errorf("%d out of %d values produced no buffers and will be missing from the protocol message", missingValues, len(values))
89+
}
90+
91+
// Reproduce the exact loop from sendArray
92+
buffers := [][]byte{}
93+
for _, v := range values {
94+
buffers = append(buffers, protocolBufferFromValue(v)...)
95+
}
96+
97+
t.Logf("values count: %d, total buffers: %d, buffers per value: %v", len(values), len(buffers), buffersPerValue)
98+
99+
// Every value must produce at least 1 buffer ([]byte produces 2)
100+
expectedMinBuffers := len(values)
101+
if len(buffers) < expectedMinBuffers {
102+
t.Errorf("buffers array has %d elements, expected at least %d (one per value). %d values were silently dropped.",
103+
len(buffers), expectedMinBuffers, missingValues)
104+
}
105+
}
106+
107+
// helpers
108+
func intPtr(v int) *int { return &v }
109+
func strPtr(v string) *string { return &v }

0 commit comments

Comments
 (0)