Skip to content

Commit bb06b09

Browse files
authored
feat(client): replace long256 string with big.Int (#8)
1 parent 576423f commit bb06b09

File tree

3 files changed

+82
-18
lines changed

3 files changed

+82
-18
lines changed

sender.go

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -325,15 +325,28 @@ func (s *LineSender) Int64Column(name string, val int64) *LineSender {
325325

326326
// Long256Column adds a 256-bit unsigned integer (long256) column
327327
// value to the ILP message.
328-
//
329-
// val should contain a hex encoded 256-bit unsigned integer value
330-
// with "0x" prefix and "i" suffix. Any attempt to set a string which is
331-
// not a hexadecimal will not be parsed and rejected by the database.
332-
//
328+
//
329+
// Only non-negative numbers that fit into 256-bit unsigned integer are
330+
// supported and any other input value would lead to an error.
331+
//
333332
// Column name cannot contain any of the following characters:
334333
// '\n', '\r', '?', '.', ',', ”', '"', '\\', '/', ':', ')', '(', '+',
335334
// '-', '*' '%%', '~', or a non-printable char.
336-
func (s *LineSender) Long256Column(name, val string) *LineSender {
335+
func (s *LineSender) Long256Column(name string, val *big.Int) *LineSender {
336+
if val.Sign() < 0 {
337+
if s.lastErr != nil {
338+
return s
339+
}
340+
s.lastErr = fmt.Errorf("long256 cannot be negative: %s", val.String())
341+
return s
342+
}
343+
if val.BitLen() > 256 {
344+
if s.lastErr != nil {
345+
return s
346+
}
347+
s.lastErr = fmt.Errorf("long256 cannot be larger than 256-bit: %v", val.BitLen())
348+
return s
349+
}
337350
if !s.prepareForField(name) {
338351
return s
339352
}
@@ -342,7 +355,10 @@ func (s *LineSender) Long256Column(name, val string) *LineSender {
342355
return s
343356
}
344357
s.buf.WriteByte('=')
345-
s.lastErr = s.writeStrValue(val, false)
358+
s.buf.WriteByte('0')
359+
s.buf.WriteByte('x')
360+
s.buf.WriteBigInt(val)
361+
s.buf.WriteByte('i')
346362
if s.lastErr != nil {
347363
return s
348364
}
@@ -828,3 +844,10 @@ func (b *buffer) WriteFloat(f float64) {
828844
s := strconv.AppendFloat(a[0:0], f, 'G', -1, 64)
829845
b.Write(s)
830846
}
847+
848+
func (b *buffer) WriteBigInt(i *big.Int) {
849+
// We need up to 64 bytes to fit an unsigned 256-bit number.
850+
var a [64]byte
851+
s := i.Append(a[0:0], 16)
852+
b.Write(s)
853+
}

sender_integration_test.go

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
"encoding/json"
3030
"fmt"
3131
"io/ioutil"
32+
"math/big"
3233
"net/http"
3334
"net/url"
3435
"path/filepath"
@@ -231,12 +232,13 @@ func TestE2EValidWrites(t *testing.T) {
231232
"all column types",
232233
testTable,
233234
func(s *qdb.LineSender) error {
235+
val, _ := big.NewInt(0).SetString("123a4", 16)
234236
err := s.
235237
Table(testTable).
236238
Symbol("sym_col", "test_ilp1").
237239
Float64Column("double_col", 12.2).
238240
Int64Column("long_col", 12).
239-
Long256Column("long256_col", "0x123a4i").
241+
Long256Column("long256_col", val).
240242
StringColumn("str_col", "foobar").
241243
BoolColumn("bool_col", true).
242244
TimestampColumn("timestamp_col", 42).
@@ -245,12 +247,13 @@ func TestE2EValidWrites(t *testing.T) {
245247
return err
246248
}
247249

250+
val, _ = big.NewInt(0).SetString("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16)
248251
return s.
249252
Table(testTable).
250253
Symbol("sym_col", "test_ilp2").
251254
Float64Column("double_col", 11.2).
252255
Int64Column("long_col", 11).
253-
Long256Column("long256_col", "0x123a3i").
256+
Long256Column("long256_col", val).
254257
StringColumn("str_col", "barbaz").
255258
BoolColumn("bool_col", false).
256259
TimestampColumn("timestamp_col", 43).
@@ -269,7 +272,7 @@ func TestE2EValidWrites(t *testing.T) {
269272
},
270273
Dataset: [][]interface{}{
271274
{"test_ilp1", float64(12.2), float64(12), "0x0123a4", "foobar", true, "1970-01-01T00:00:00.000042Z", "1970-01-01T00:00:00.000001Z"},
272-
{"test_ilp2", float64(11.2), float64(11), "0x0123a3", "barbaz", false, "1970-01-01T00:00:00.000043Z", "1970-01-01T00:00:00.000002Z"},
275+
{"test_ilp2", float64(11.2), float64(11), "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "barbaz", false, "1970-01-01T00:00:00.000043Z", "1970-01-01T00:00:00.000002Z"},
273276
},
274277
Count: 2,
275278
},
@@ -340,9 +343,10 @@ func TestE2EValidWrites(t *testing.T) {
340343
"single column long256",
341344
testTable,
342345
func(s *qdb.LineSender) error {
346+
val, _ := big.NewInt(0).SetString("7fffffffffffffff", 16)
343347
return s.
344348
Table(testTable).
345-
Long256Column("foobar", "0x123a4i").
349+
Long256Column("foobar", val).
346350
At(ctx, 42000)
347351
},
348352
tableData{
@@ -351,7 +355,7 @@ func TestE2EValidWrites(t *testing.T) {
351355
{"timestamp", "TIMESTAMP"},
352356
},
353357
Dataset: [][]interface{}{
354-
{"0x0123a4", "1970-01-01T00:00:00.000042Z"},
358+
{"0x7fffffffffffffff", "1970-01-01T00:00:00.000042Z"},
355359
},
356360
Count: 1,
357361
},

sender_test.go

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import (
3232
"io/ioutil"
3333
"log"
3434
"math"
35+
"math/big"
3536
"net"
3637
"reflect"
3738
"strconv"
@@ -192,11 +193,11 @@ func TestLong256Column(t *testing.T) {
192193
val string
193194
expected string
194195
}{
195-
{"random bits ", "0x0123a4i", "0x0123a4"},
196-
{"8bit max", "0xffffffffi", "0xffffffff"},
197-
{"16bit max", "0xffffffffffffffffi", "0xffffffffffffffff"},
198-
{"32bit max", "0xffffffffffffffffffffffffffffffffi", "0xffffffffffffffffffffffffffffffff"},
199-
{"64bit long", "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffi", "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"},
196+
{"zero", "0", "0x0"},
197+
{"one", "1", "0x1"},
198+
{"32-bit max", strconv.FormatInt(math.MaxInt32, 16), "0x7fffffff"},
199+
{"64-bit random", strconv.FormatInt(7423093023234231, 16), "0x1a5f4386c8d8b7"},
200+
{"256-bit max", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"},
200201
}
201202

202203
for _, tc := range testCases {
@@ -207,7 +208,8 @@ func TestLong256Column(t *testing.T) {
207208
sender, err := qdb.NewLineSender(ctx, qdb.WithAddress(srv.addr))
208209
assert.NoError(t, err)
209210

210-
err = sender.Table(testTable).Long256Column("a_col", tc.val).AtNow(ctx)
211+
newVal, _ := big.NewInt(0).SetString(tc.val, 16)
212+
err = sender.Table(testTable).Long256Column("a_col", newVal).AtNow(ctx)
211213
assert.NoError(t, err)
212214

213215
err = sender.Flush(ctx)
@@ -424,6 +426,41 @@ func TestErrorOnNegativeTimestamp(t *testing.T) {
424426
assert.Empty(t, sender.Messages())
425427
}
426428

429+
func TestErrorOnNegativeLong256(t *testing.T) {
430+
ctx := context.Background()
431+
432+
srv, err := newTestServer(readAndDiscard)
433+
assert.NoError(t, err)
434+
defer srv.close()
435+
436+
sender, err := qdb.NewLineSender(ctx, qdb.WithAddress(srv.addr))
437+
assert.NoError(t, err)
438+
defer sender.Close()
439+
440+
err = sender.Table(testTable).Long256Column("long256_col", big.NewInt(-42)).AtNow(ctx)
441+
442+
assert.ErrorContains(t, err, "long256 cannot be negative: -42")
443+
assert.Empty(t, sender.Messages())
444+
}
445+
446+
func TestErrorOnLargerLong256(t *testing.T) {
447+
ctx := context.Background()
448+
449+
srv, err := newTestServer(readAndDiscard)
450+
assert.NoError(t, err)
451+
defer srv.close()
452+
453+
sender, err := qdb.NewLineSender(ctx, qdb.WithAddress(srv.addr))
454+
assert.NoError(t, err)
455+
defer sender.Close()
456+
457+
bigVal, _ := big.NewInt(0).SetString("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16)
458+
err = sender.Table(testTable).Long256Column("long256_col", bigVal).AtNow(ctx)
459+
460+
assert.ErrorContains(t, err, "long256 cannot be larger than 256-bit: 260")
461+
assert.Empty(t, sender.Messages())
462+
}
463+
427464
func TestErrorOnSymbolCallAfterColumn(t *testing.T) {
428465
ctx := context.Background()
429466

0 commit comments

Comments
 (0)