func datumFromProto(d driver.Datum) parser.Datum { arg := d.Payload if arg == nil { return parser.DNull } switch t := arg.(type) { case *driver.Datum_BoolVal: return parser.DBool(t.BoolVal) case *driver.Datum_IntVal: return parser.DInt(t.IntVal) case *driver.Datum_FloatVal: return parser.DFloat(t.FloatVal) case *driver.Datum_DecimalVal: dec, err := decimal.NewFromString(t.DecimalVal) if err != nil { panic(fmt.Sprintf("could not parse decimal: %v", err)) } return parser.DDecimal{Decimal: dec} case *driver.Datum_BytesVal: return parser.DBytes(t.BytesVal) case *driver.Datum_StringVal: return parser.DString(t.StringVal) case *driver.Datum_DateVal: return parser.DDate(t.DateVal) case *driver.Datum_TimeVal: return parser.DTimestamp{Time: t.TimeVal.GoTime()} case *driver.Datum_IntervalVal: return parser.DInterval{Duration: time.Duration(t.IntervalVal)} default: panic(fmt.Sprintf("unexpected type %T", t)) } }
func TestDecimalMandE(t *testing.T) { testCases := []struct { Value string E int M []byte }{ {"1.0", 1, []byte{0x02}}, {"10.0", 1, []byte{0x14}}, {"99.0", 1, []byte{0xc6}}, {"99.01", 1, []byte{0xc7, 0x02}}, {"99.0001", 1, []byte{0xc7, 0x01, 0x02}}, {"100.0", 2, []byte{0x02}}, {"100.01", 2, []byte{0x03, 0x01, 0x02}}, {"100.1", 2, []byte{0x03, 0x01, 0x14}}, {"1234", 2, []byte{0x19, 0x44}}, {"9999", 2, []byte{0xc7, 0xc6}}, {"9999.000001", 2, []byte{0xc7, 0xc7, 0x01, 0x01, 0x02}}, {"9999.000009", 2, []byte{0xc7, 0xc7, 0x01, 0x01, 0x12}}, {"9999.00001", 2, []byte{0xc7, 0xc7, 0x01, 0x01, 0x14}}, {"9999.00009", 2, []byte{0xc7, 0xc7, 0x01, 0x01, 0xb4}}, {"9999.000099", 2, []byte{0xc7, 0xc7, 0x01, 0x01, 0xc6}}, {"9999.0001", 2, []byte{0xc7, 0xc7, 0x01, 0x02}}, {"9999.001", 2, []byte{0xc7, 0xc7, 0x01, 0x14}}, {"9999.01", 2, []byte{0xc7, 0xc7, 0x02}}, {"9999.1", 2, []byte{0xc7, 0xc7, 0x14}}, {"10000", 3, []byte{0x02}}, {"10001", 3, []byte{0x03, 0x01, 0x02}}, {"12345", 3, []byte{0x03, 0x2f, 0x5a}}, {"123450", 3, []byte{0x19, 0x45, 0x64}}, {"1234.5", 2, []byte{0x19, 0x45, 0x64}}, {"12.345", 1, []byte{0x19, 0x45, 0x64}}, {"0.123", 0, []byte{0x19, 0x3c}}, {"0.0123", 0, []byte{0x03, 0x2e}}, {"0.00123", -1, []byte{0x19, 0x3c}}, {"1e-307", -153, []byte{0x14}}, {"1e308", 155, []byte{0x2}}, {"9223372036854775807", 10, []byte{0x13, 0x2d, 0x43, 0x91, 0x07, 0x89, 0x6d, 0x9b, 0x75, 0x0e}}, } for _, c := range testCases { d, err := decimal.NewFromString(c.Value) if err != nil { t.Fatalf("could not parse decimal: %v", err) } if e, m := decimalMandE(d, nil); e != c.E || !bytes.Equal(m, c.M) { t.Errorf("unexpected mismatch in E/M for %v. expected E=%v | M=[% x], got E=%v | M=[% x]", c.Value, c.E, c.M, e, m) } } }
// decodeOidDatum decodes bytes with specified Oid and format code into // a datum. func decodeOidDatum(id oid.Oid, code formatCode, b []byte) (parser.Datum, error) { var d parser.Datum switch id { case oid.T_bool: switch code { case formatText: v, err := strconv.ParseBool(string(b)) if err != nil { return d, err } d = parser.DBool(v) default: return d, fmt.Errorf("unsupported bool format code: %d", code) } case oid.T_int2: switch code { case formatText: i, err := strconv.ParseInt(string(b), 10, 64) if err != nil { return d, err } d = parser.DInt(i) case formatBinary: var i int16 err := binary.Read(bytes.NewReader(b), binary.BigEndian, &i) if err != nil { return d, err } d = parser.DInt(i) default: return d, fmt.Errorf("unsupported int2 format code: %d", code) } case oid.T_int4: switch code { case formatText: i, err := strconv.ParseInt(string(b), 10, 64) if err != nil { return d, err } d = parser.DInt(i) case formatBinary: var i int32 err := binary.Read(bytes.NewReader(b), binary.BigEndian, &i) if err != nil { return d, err } d = parser.DInt(i) default: return d, fmt.Errorf("unsupported int4 format code: %d", code) } case oid.T_int8: switch code { case formatText: i, err := strconv.ParseInt(string(b), 10, 64) if err != nil { return d, err } d = parser.DInt(i) case formatBinary: var i int64 err := binary.Read(bytes.NewReader(b), binary.BigEndian, &i) if err != nil { return d, err } d = parser.DInt(i) default: return d, fmt.Errorf("unsupported int8 format code: %d", code) } case oid.T_float4: switch code { case formatText: f, err := strconv.ParseFloat(string(b), 64) if err != nil { return d, err } d = parser.DFloat(f) case formatBinary: var f float32 err := binary.Read(bytes.NewReader(b), binary.BigEndian, &f) if err != nil { return d, err } d = parser.DFloat(f) default: return d, fmt.Errorf("unsupported float4 format code: %d", code) } case oid.T_float8: switch code { case formatText: f, err := strconv.ParseFloat(string(b), 64) if err != nil { return d, err } d = parser.DFloat(f) case formatBinary: var f float64 err := binary.Read(bytes.NewReader(b), binary.BigEndian, &f) if err != nil { return d, err } d = parser.DFloat(f) default: return d, fmt.Errorf("unsupported float8 format code: %d", code) } case oid.T_numeric: switch code { case formatText: dec, err := decimal.NewFromString(string(b)) if err != nil { return d, err } d = parser.DDecimal{Decimal: dec} default: return d, fmt.Errorf("unsupported numeric format code: %d", code) } case oid.T_text: switch code { case formatText: d = parser.DString(b) default: return d, fmt.Errorf("unsupported text format code: %d", code) } // TODO(mjibson): implement date/time types default: return d, fmt.Errorf("unsupported OID: %v", id) } return d, nil }