func benchmarkWriteDate(b *testing.B, format formatCode) { d, err := parser.ParseDDate("2010-09-28", time.UTC) if err != nil { b.Fatal(err) } benchmarkWriteType(b, d, format) }
func TestBinaryDate(t *testing.T) { defer leaktest.AfterTest(t)() testBinaryDatumType(t, "date", func(val string) parser.Datum { d, err := parser.ParseDDate(val, time.UTC) if err != nil { t.Fatal(err) } return d }) }
// 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.MakeDBool(parser.DBool(v)) case formatBinary: switch b[0] { case 0: d = parser.MakeDBool(false) case 1: d = parser.MakeDBool(true) default: return d, errors.Errorf("unsupported binary bool: %q", b) } default: return d, errors.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.NewDInt(parser.DInt(i)) case formatBinary: if len(b) < 2 { return d, errors.Errorf("int2 requires 2 bytes for binary format") } i := int16(binary.BigEndian.Uint16(b)) d = parser.NewDInt(parser.DInt(i)) default: return d, errors.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.NewDInt(parser.DInt(i)) case formatBinary: if len(b) < 4 { return d, errors.Errorf("int4 requires 4 bytes for binary format") } i := int32(binary.BigEndian.Uint32(b)) d = parser.NewDInt(parser.DInt(i)) default: return d, errors.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.NewDInt(parser.DInt(i)) case formatBinary: if len(b) < 8 { return d, errors.Errorf("int8 requires 8 bytes for binary format") } i := int64(binary.BigEndian.Uint64(b)) d = parser.NewDInt(parser.DInt(i)) default: return d, errors.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.NewDFloat(parser.DFloat(f)) case formatBinary: if len(b) < 4 { return d, errors.Errorf("float4 requires 4 bytes for binary format") } f := math.Float32frombits(binary.BigEndian.Uint32(b)) d = parser.NewDFloat(parser.DFloat(f)) default: return d, errors.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.NewDFloat(parser.DFloat(f)) case formatBinary: if len(b) < 8 { return d, errors.Errorf("float8 requires 8 bytes for binary format") } f := math.Float64frombits(binary.BigEndian.Uint64(b)) d = parser.NewDFloat(parser.DFloat(f)) default: return d, errors.Errorf("unsupported float8 format code: %d", code) } case oid.T_numeric: switch code { case formatText: dd := &parser.DDecimal{} if _, ok := dd.SetString(string(b)); !ok { return nil, errors.Errorf("could not parse string %q as decimal", b) } d = dd case formatBinary: r := bytes.NewReader(b) alloc := struct { pgNum pgNumeric i16 int16 dd parser.DDecimal }{} for _, ptr := range []interface{}{ &alloc.pgNum.ndigits, &alloc.pgNum.weight, &alloc.pgNum.sign, &alloc.pgNum.dscale, } { if err := binary.Read(r, binary.BigEndian, ptr); err != nil { return d, err } } if alloc.pgNum.ndigits > 0 { decDigits := make([]byte, 0, alloc.pgNum.ndigits*pgDecDigits) nextDigit := func() error { if err := binary.Read(r, binary.BigEndian, &alloc.i16); err != nil { return err } numZeroes := pgDecDigits for i16 := alloc.i16; i16 > 0; i16 /= 10 { numZeroes-- } for ; numZeroes > 0; numZeroes-- { decDigits = append(decDigits, '0') } return nil } for i := int16(0); i < alloc.pgNum.ndigits-1; i++ { if err := nextDigit(); err != nil { return d, err } if alloc.i16 > 0 { decDigits = strconv.AppendUint(decDigits, uint64(alloc.i16), 10) } } // The last digit may contain padding, which we need to deal with. if err := nextDigit(); err != nil { return d, err } dscale := (alloc.pgNum.ndigits - (alloc.pgNum.weight + 1)) * pgDecDigits if overScale := dscale - alloc.pgNum.dscale; overScale > 0 { dscale -= overScale for i := int16(0); i < overScale; i++ { alloc.i16 /= 10 } } decDigits = strconv.AppendUint(decDigits, uint64(alloc.i16), 10) decString := string(decDigits) if _, ok := alloc.dd.UnscaledBig().SetString(decString, 10); !ok { return nil, errors.Errorf("could not parse string %q as decimal", decString) } alloc.dd.SetScale(inf.Scale(dscale)) } switch alloc.pgNum.sign { case pgNumericPos: case pgNumericNeg: alloc.dd.Neg(&alloc.dd.Dec) default: return d, errors.Errorf("unsupported numeric sign: %d", alloc.pgNum.sign) } d = &alloc.dd default: return d, errors.Errorf("unsupported numeric format code: %d", code) } case oid.T_text, oid.T_varchar: switch code { case formatText, formatBinary: d = parser.NewDString(string(b)) default: return d, errors.Errorf("unsupported text format code: %d", code) } case oid.T_bytea: switch code { case formatText: // http://www.postgresql.org/docs/current/static/datatype-binary.html#AEN5667 // Code cribbed from github.com/lib/pq. // We only support hex encoding. if len(b) >= 2 && bytes.Equal(b[:2], []byte("\\x")) { b = b[2:] // trim off leading "\\x" result := make([]byte, hex.DecodedLen(len(b))) _, err := hex.Decode(result, b) if err != nil { return d, err } d = parser.NewDBytes(parser.DBytes(result)) } else { return d, errors.Errorf("unsupported bytea encoding: %q", b) } case formatBinary: d = parser.NewDBytes(parser.DBytes(b)) default: return d, errors.Errorf("unsupported bytea format code: %d", code) } case oid.T_timestamp: switch code { case formatText: ts, err := parseTs(string(b)) if err != nil { return d, errors.Errorf("could not parse string %q as timestamp", b) } d = parser.MakeDTimestamp(ts, time.Microsecond) case formatBinary: if len(b) < 8 { return d, errors.Errorf("timestamp requires 8 bytes for binary format") } i := int64(binary.BigEndian.Uint64(b)) d = parser.MakeDTimestamp(pgBinaryToTime(i), time.Microsecond) default: return d, errors.Errorf("unsupported timestamp format code: %d", code) } case oid.T_timestamptz: switch code { case formatText: ts, err := parseTs(string(b)) if err != nil { return d, errors.Errorf("could not parse string %q as timestamp", b) } d = parser.MakeDTimestampTZ(ts, time.Microsecond) case formatBinary: if len(b) < 8 { return d, errors.Errorf("timestamptz requires 8 bytes for binary format") } i := int64(binary.BigEndian.Uint64(b)) d = parser.MakeDTimestampTZ(pgBinaryToTime(i), time.Microsecond) default: return d, errors.Errorf("unsupported timestamptz format code: %d", code) } case oid.T_date: switch code { case formatText: ts, err := parseTs(string(b)) if err != nil { res, err := parser.ParseDDate(string(b), time.UTC) if err != nil { return d, errors.Errorf("could not parse string %q as date", b) } d = res } else { daysSinceEpoch := ts.Unix() / secondsInDay d = parser.NewDDate(parser.DDate(daysSinceEpoch)) } case formatBinary: if len(b) < 4 { return d, errors.Errorf("date requires 4 bytes for binary format") } i := int32(binary.BigEndian.Uint32(b)) d = pgBinaryToDate(i) default: return d, errors.Errorf("unsupported date format code: %d", code) } case oid.T_interval: switch code { case formatText: d, err := parser.ParseDInterval(string(b)) if err != nil { return d, errors.Errorf("could not parse string %q as interval", b) } return d, nil default: return d, errors.Errorf("unsupported interval format code: %d", code) } default: return d, errors.Errorf("unsupported OID: %v", id) } return d, nil }