func FuzzParseTimestamp(data []byte) int { _, err := pq.ParseTimestamp(nil, string(data)) if err != nil { return 0 } return 1 }
// parseTs parses timestamps in any of the formats that Postgres accepts over // the wire protocol. // // Postgres is lenient in what it accepts as a timestamp, so we must also be // lenient. As new drivers are used with CockroachDB and formats are found that // we don't support but Postgres does, add them here. Then create an integration // test for the driver and add a case to TestParseTs. func parseTs(str string) (time.Time, error) { // RFC3339Nano is sent by github.com/lib/pq (go). if ts, err := time.Parse(time.RFC3339Nano, str); err == nil { return ts, nil } // pq.ParseTimestamp parses the timestamp format that both Postgres and // CockroachDB send in responses, so this allows roundtripping of the encoded // timestamps that we send. return pq.ParseTimestamp(nil, str) }
// 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: dd := &parser.DDecimal{} if _, ok := dd.SetString(string(b)); !ok { return nil, fmt.Errorf("could not parse string %q as decimal", b) } d = dd default: return d, fmt.Errorf("unsupported numeric format code: %d", code) } case oid.T_text, oid.T_varchar: switch code { case formatText: d = parser.DString(b) default: return d, fmt.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.DBytes(result) } else { return d, fmt.Errorf("unsupported bytea encoding: %q", b) } case formatBinary: d = parser.DBytes(b) default: return d, fmt.Errorf("unsupported bytea format code: %d", code) } case oid.T_timestamp: switch code { case formatText: ts, err := pq.ParseTimestamp(nil, string(b)) if err != nil { return d, fmt.Errorf("could not parse string %q as timestamp", b) } d = parser.DTimestamp{Time: ts} case formatBinary: return d, fmt.Errorf("unsupported timestamp format code: %d", code) } case oid.T_date: switch code { case formatText: ts, err := pq.ParseTimestamp(nil, string(b)) if err != nil { return d, fmt.Errorf("could not parse string %q as date", b) } daysSinceEpoch := ts.Unix() / secondsInDay d = parser.DDate(daysSinceEpoch) case formatBinary: return d, fmt.Errorf("unsupported date format code: %d", code) } default: return d, fmt.Errorf("unsupported OID: %v", id) } return d, nil }