Skip to content

Commit

Permalink
Create an arbitrary-precision decimal encoding scheme
Browse files Browse the repository at this point in the history
The encoding is based on sqlite4's key encoding:
http://sqlite.org/src4/doc/trunk/www/key_encoding.wiki
  • Loading branch information
nvanbenschoten committed Jan 25, 2016
1 parent 59669be commit c660695
Show file tree
Hide file tree
Showing 21 changed files with 711 additions and 201 deletions.
2 changes: 1 addition & 1 deletion GLOCKFILE
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ github.com/cockroachdb/c-lz4 c40aaae2fc50293eb8750b34632bc3efe813e23f
github.com/cockroachdb/c-protobuf 4feb192131ea08dfbd7253a00868ad69cbb61b81
github.com/cockroachdb/c-rocksdb b7fb7bddcb55be35eacdf67e9e2c931083ce02c4
github.com/cockroachdb/c-snappy 5c6d0932e0adaffce4bfca7bdf2ac37f79952ccf
github.com/cockroachdb/decimal 0fab2d0647ef3cdf007d2c3fa1f56efebfb365c9
github.com/cockroachdb/yacc 443154b1852a8702b07d675da6cd97cd9177a316
github.com/codahale/hdrhistogram 954f16e8b9ef0e5d5189456aa4c1202758e04f17
github.com/coreos/etcd 8199147cf859882f625523446c28ae2e53b2432f
Expand All @@ -45,7 +46,6 @@ github.com/peterh/liner d5e5aeeb67ca5aeeddeb0b6c3af05421ff63a0b6
github.com/rcrowley/go-metrics 51425a2415d21afadfd55cd93432c0bc69e9598d
github.com/robfig/glock cb3c3ec56de988289cab7bbd284eddc04dfee6c9
github.com/samalba/dockerclient 548a5825c12336eb640190d7eb253e155fc286ad
github.com/shopspring/decimal af95951b3f91e0748623df28c8756ed65f805161
github.com/spf13/cobra 9c9300901990faada0c5fb3b5730f452585c7c2b
github.com/spf13/pflag 7f60f83a2c81bc3c3c0d5297f61ddfa68da9d3b7
github.com/tebeka/go2xunit fff0d031ea2b81ed06c367293219e4bc005235d0
Expand Down
6 changes: 3 additions & 3 deletions client/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ import (
"time"

"github.com/cockroachdb/cockroach/roachpb"
"github.com/cockroachdb/decimal"
"github.com/gogo/protobuf/proto"
"github.com/shopspring/decimal"
)

// TODO(pmattis): The methods in this file needs tests.
Expand Down Expand Up @@ -67,8 +67,8 @@ func marshalValue(v interface{}) (roachpb.Value, error) {
return r, nil

case decimal.Decimal:
r.SetDecimal(t)
return r, nil
err := r.SetDecimal(t)
return r, err

case roachpb.Key:
r.SetBytes([]byte(t))
Expand Down
58 changes: 5 additions & 53 deletions keys/printer.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import (
"fmt"
"strconv"
"strings"
"time"

"github.com/cockroachdb/cockroach/roachpb"
"github.com/cockroachdb/cockroach/util/encoding"
Expand Down Expand Up @@ -204,61 +203,14 @@ func print(key roachpb.Key) string {

func decodeKeyPrint(key roachpb.Key) string {
var buf bytes.Buffer
for k := 0; len(key) > 0; k++ {
var err error
switch encoding.PeekType(key) {
case encoding.Null:
key, _ = encoding.DecodeIfNull(key)
fmt.Fprintf(&buf, "/NULL")
case encoding.NotNull:
key, _ = encoding.DecodeIfNotNull(key)
fmt.Fprintf(&buf, "/#")
case encoding.Int:
var i int64
key, i, err = encoding.DecodeVarintAscending(key)
if err == nil {
fmt.Fprintf(&buf, "/%d", i)
}
case encoding.Float:
var f float64
key, f, err = encoding.DecodeFloatAscending(key, nil)
if err == nil {
fmt.Fprintf(&buf, "/%f", f)
}
case encoding.Bytes:
var s string
key, s, err = encoding.DecodeStringAscending(key, nil)
if err == nil {
fmt.Fprintf(&buf, "/%q", s)
}
case encoding.BytesDesc:
var s string
key, s, err = encoding.DecodeStringDescending(key, nil)
if err == nil {
fmt.Fprintf(&buf, "/%q", s)
}
case encoding.Time:
var t time.Time
key, t, err = encoding.DecodeTimeAscending(key)
if err == nil {
fmt.Fprintf(&buf, "/%s", t.UTC().Format(time.UnixDate))
}
case encoding.TimeDesc:
var t time.Time
key, t, err = encoding.DecodeTimeDescending(key)
if err == nil {
fmt.Fprintf(&buf, "/%s", t.UTC().Format(time.UnixDate))
}
default:
// This shouldn't ever happen, but if it does let the loop exit.
fmt.Fprintf(&buf, "/%q", []byte(key))
key = nil
}

for len(key) > 0 {
k, s, err := encoding.PrettyPrintValue(key)
if err != nil {
fmt.Fprintf(&buf, "/<%v>", err)
continue
} else {
fmt.Fprintf(&buf, "/%s", s)
}
key = k
}
return buf.String()
}
Expand Down
14 changes: 14 additions & 0 deletions keys/printer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@
package keys

import (
"math"
"testing"
"time"

"github.com/cockroachdb/cockroach/roachpb"
"github.com/cockroachdb/cockroach/util/encoding"
"github.com/cockroachdb/cockroach/util/leaktest"
"github.com/cockroachdb/decimal"
)

func TestPrettyPrint(t *testing.T) {
Expand Down Expand Up @@ -75,6 +77,12 @@ func TestPrettyPrint(t *testing.T) {
{MakeKey(MakeTablePrefix(42),
roachpb.RKey(encoding.EncodeFloatDescending(nil, float64(-233.221112)))),
"/Table/42/233.221112"},
{MakeKey(MakeTablePrefix(42),
roachpb.RKey(encoding.EncodeFloatAscending(nil, math.Inf(1)))),
"/Table/42/+Inf"},
{MakeKey(MakeTablePrefix(42),
roachpb.RKey(encoding.EncodeFloatAscending(nil, math.NaN()))),
"/Table/42/NaN"},
{MakeKey(MakeTablePrefix(42),
roachpb.RKey(encoding.EncodeVarintAscending(nil, 1222)),
roachpb.RKey(encoding.EncodeStringAscending(nil, "handsome man"))),
Expand Down Expand Up @@ -104,6 +112,12 @@ func TestPrettyPrint(t *testing.T) {
{MakeKey(MakeTablePrefix(42),
roachpb.RKey(encoding.EncodeTimeDescending(nil, tm))),
"/Table/42/Sat Mar 7 11:06:39 UTC 2015"},
{MakeKey(MakeTablePrefix(42),
roachpb.RKey(encoding.EncodeDecimalAscending(nil, decimal.New(1234, -2)))),
"/Table/42/12.34"},
{MakeKey(MakeTablePrefix(42),
roachpb.RKey(encoding.EncodeDecimalDescending(nil, decimal.New(1234, -2)))),
"/Table/42/-12.34"},

// others
{MakeKey([]byte("")), "/Min"},
Expand Down
35 changes: 27 additions & 8 deletions roachpb/data.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"hash"
"hash/crc32"
"math"
"math/big"
"math/rand"
"sort"
"strconv"
Expand All @@ -33,8 +34,8 @@ import (
"github.com/cockroachdb/cockroach/util"
"github.com/cockroachdb/cockroach/util/encoding"
"github.com/cockroachdb/cockroach/util/uuid"
"github.com/cockroachdb/decimal"
"github.com/gogo/protobuf/proto"
"github.com/shopspring/decimal"
)

const (
Expand Down Expand Up @@ -421,17 +422,26 @@ func (v *Value) SetProto(msg proto.Message) error {
// SetTime encodes the specified time value into the bytes field of the
// receiver, sets the tag and clears the checksum.
func (v *Value) SetTime(t time.Time) {
v.RawBytes = make([]byte, headerSize, 16)
v.RawBytes = encoding.EncodeTimeAscending(v.RawBytes[:headerSize], t)
const encodingSizeOverestimate = 11
v.RawBytes = make([]byte, headerSize, headerSize+encodingSizeOverestimate)
v.RawBytes = encoding.EncodeTimeAscending(v.RawBytes, t)
v.setTag(ValueType_TIME)
}

// SetDecimal encodes the specified decimal value into the bytes field of
// the receiver, sets the tag and clears the checksum.
func (v *Value) SetDecimal(d decimal.Decimal) {
v.RawBytes = make([]byte, headerSize, 16)
v.RawBytes = encoding.EncodeDecimalAscending(v.RawBytes[:headerSize], d)
func (v *Value) SetDecimal(d decimal.Decimal) error {
// TODO(nvanbenschoten) Deal with exponent normalization.
bb, err := d.BigInt().GobEncode()
if err != nil {
return fmt.Errorf("failed to Gob encode decimal's big.Int: %v", err)
}
v.RawBytes = make([]byte, headerSize+binary.MaxVarintLen64+len(bb))
n := binary.PutVarint(v.RawBytes[headerSize:], int64(d.Exponent()))
copy(v.RawBytes[headerSize+n:], bb)
v.RawBytes = v.RawBytes[:headerSize+n+len(bb)]
v.setTag(ValueType_DECIMAL)
return nil
}

// GetBytes returns the bytes field of the receiver. If the tag is not
Expand Down Expand Up @@ -507,8 +517,17 @@ func (v Value) GetDecimal() (decimal.Decimal, error) {
if tag := v.GetTag(); tag != ValueType_DECIMAL {
return decimal.Decimal{}, fmt.Errorf("value type is not %s: %s", ValueType_DECIMAL, tag)
}
_, d, err := encoding.DecodeDecimalAscending(v.dataBytes())
return d, err
data := v.dataBytes()
i, n := binary.Varint(data)
if n <= 0 {
return decimal.Decimal{}, fmt.Errorf("int64 varint decoding failed: %d", n)
}
bi := new(big.Int)
err := bi.GobDecode(data[n:])
if err != nil {
return decimal.Decimal{}, err
}
return decimal.NewFromBigInt(bi, int32(i)), nil
}

// GetTimeseries decodes an InternalTimeSeriesData value from the bytes
Expand Down
32 changes: 26 additions & 6 deletions roachpb/data_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (

"github.com/cockroachdb/cockroach/util"
"github.com/cockroachdb/cockroach/util/uuid"
"github.com/cockroachdb/decimal"
)

// TestKeyNext tests that the method for creating lexicographic
Expand Down Expand Up @@ -297,14 +298,30 @@ func TestSetGetChecked(t *testing.T) {
t.Fatal(err)
}

v.SetFloat(1.1)
if _, err := v.GetFloat(); err != nil {
f := 1.1
v.SetFloat(f)
if r, err := v.GetFloat(); err != nil {
t.Fatal(err)
} else if f != r {
t.Errorf("set %f on a value and extracted it, expected %f back, but got %f", f, f, r)
}

v.SetInt(1)
if _, err := v.GetInt(); err != nil {
i := int64(1)
v.SetInt(i)
if r, err := v.GetInt(); err != nil {
t.Fatal(err)
} else if i != r {
t.Errorf("set %d on a value and extracted it, expected %d back, but got %d", i, i, r)
}

d := decimal.New(11, -1)
if err := v.SetDecimal(d); err != nil {
t.Fatal(err)
}
if r, err := v.GetDecimal(); err != nil {
t.Fatal(err)
} else if !d.Equals(r) {
t.Errorf("set %s on a value and extracted it, expected %s back, but got %s", d, d, r)
}

if err := v.SetProto(&Value{}); err != nil {
Expand All @@ -324,9 +341,12 @@ func TestSetGetChecked(t *testing.T) {
t.Fatal(err)
}

v.SetTime(time.Time{})
if _, err := v.GetTime(); err != nil {
ti := time.Time{}
v.SetTime(ti)
if r, err := v.GetTime(); err != nil {
t.Fatal(err)
} else if !ti.Equal(r) {
t.Errorf("set %s on a value and extracted it, expected %s back, but got %s", ti, ti, r)
}
}

Expand Down
2 changes: 1 addition & 1 deletion sql/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import (
"time"

"github.com/gogo/protobuf/proto"
"github.com/shopspring/decimal"

"github.com/cockroachdb/cockroach/client"
"github.com/cockroachdb/cockroach/config"
Expand All @@ -38,6 +37,7 @@ import (
"github.com/cockroachdb/cockroach/util/metric"
"github.com/cockroachdb/cockroach/util/retry"
"github.com/cockroachdb/cockroach/util/stop"
"github.com/cockroachdb/decimal"
)

var testingWaitForMetadata bool
Expand Down
2 changes: 1 addition & 1 deletion sql/group.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import (
"github.com/cockroachdb/cockroach/util"
"github.com/cockroachdb/cockroach/util/encoding"
"github.com/cockroachdb/cockroach/util/log"
"github.com/shopspring/decimal"
"github.com/cockroachdb/decimal"
)

var aggregates = map[string]func() aggregateImpl{
Expand Down
Loading

0 comments on commit c660695

Please sign in to comment.