mirror of https://github.com/jackc/pgx.git
Add Status to pgtype.Date
parent
984500455c
commit
7bf783ae20
|
@ -0,0 +1,55 @@
|
|||
package pgx_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func BenchmarkPgtypeInt4ParseBinary(b *testing.B) {
|
||||
conn := mustConnect(b, *defaultConnConfig)
|
||||
defer closeConn(b, conn)
|
||||
|
||||
_, err := conn.Prepare("selectBinary", "select n::int4 from generate_series(1, 100) n")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
var n int32
|
||||
|
||||
rows, err := conn.Query("selectBinary")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
err := rows.Scan(&n)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
if rows.Err() != nil {
|
||||
b.Fatal(rows.Err())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkPgtypeInt4EncodeBinary(b *testing.B) {
|
||||
conn := mustConnect(b, *defaultConnConfig)
|
||||
defer closeConn(b, conn)
|
||||
|
||||
_, err := conn.Prepare("encodeBinary", "select $1::int4, $2::int4, $3::int4, $4::int4, $5::int4, $6::int4, $7::int4")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
rows, err := conn.Query("encodeBinary", int32(i), int32(i), int32(i), int32(i), int32(i), int32(i), int32(i))
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
rows.Close()
|
||||
}
|
||||
}
|
|
@ -9,14 +9,9 @@ import (
|
|||
)
|
||||
|
||||
type Date struct {
|
||||
// time.Time is embedded to hide internal implementation. Possibly do date
|
||||
// implementation at some point rather than simply delegating to time.Time.
|
||||
// Also TODO handling Infinity and -Infinity
|
||||
t time.Time
|
||||
}
|
||||
|
||||
func NewDate(year int, month time.Month, day int) *Date {
|
||||
return &Date{t: time.Date(year, month, day, 0, 0, 0, 0, time.Local)}
|
||||
// TODO handling Infinity and -Infinity
|
||||
Time time.Time
|
||||
Status Status
|
||||
}
|
||||
|
||||
func (d *Date) ConvertFrom(src interface{}) error {
|
||||
|
@ -24,7 +19,7 @@ func (d *Date) ConvertFrom(src interface{}) error {
|
|||
case Date:
|
||||
*d = value
|
||||
case time.Time:
|
||||
*d = Date{t: value}
|
||||
*d = Date{Time: value}
|
||||
default:
|
||||
if originalSrc, ok := underlyingTimeType(src); ok {
|
||||
return d.ConvertFrom(originalSrc)
|
||||
|
@ -46,7 +41,8 @@ func (d *Date) DecodeText(r io.Reader) error {
|
|||
}
|
||||
|
||||
if size == -1 {
|
||||
return fmt.Errorf("invalid length for date: %v", size)
|
||||
*d = Date{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
buf := make([]byte, int(size))
|
||||
|
@ -55,11 +51,13 @@ func (d *Date) DecodeText(r io.Reader) error {
|
|||
return err
|
||||
}
|
||||
|
||||
d.t, err = time.ParseInLocation("2006-01-02", string(buf), time.Local)
|
||||
t, err := time.ParseInLocation("2006-01-02", string(buf), time.UTC)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*d = Date{Time: t}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -69,6 +67,11 @@ func (d *Date) DecodeBinary(r io.Reader) error {
|
|||
return err
|
||||
}
|
||||
|
||||
if size == -1 {
|
||||
*d = Date{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
if size != 4 {
|
||||
return fmt.Errorf("invalid length for date: %v", size)
|
||||
}
|
||||
|
@ -78,28 +81,38 @@ func (d *Date) DecodeBinary(r io.Reader) error {
|
|||
return err
|
||||
}
|
||||
|
||||
d.t = time.Date(2000, 1, int(1+dayOffset), 0, 0, 0, 0, time.Local)
|
||||
t := time.Date(2000, 1, int(1+dayOffset), 0, 0, 0, 0, time.UTC)
|
||||
|
||||
*d = Date{Time: t}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d Date) EncodeText(w io.Writer) error {
|
||||
if done, err := encodeNotPresent(w, d.Status); done {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err := pgio.WriteInt32(w, 10)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
_, err = w.Write([]byte(d.t.Format("2006-01-02")))
|
||||
_, err = w.Write([]byte(d.Time.Format("2006-01-02")))
|
||||
return err
|
||||
}
|
||||
|
||||
func (d Date) EncodeBinary(w io.Writer) error {
|
||||
if done, err := encodeNotPresent(w, d.Status); done {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err := pgio.WriteInt32(w, 4)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tUnix := time.Date(d.t.Year(), d.t.Month(), d.t.Day(), 0, 0, 0, 0, time.UTC).Unix()
|
||||
tUnix := time.Date(d.Time.Year(), d.Time.Month(), d.Time.Day(), 0, 0, 0, 0, time.UTC).Unix()
|
||||
dateEpoch := time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC).Unix()
|
||||
|
||||
secSinceDateEpoch := tUnix - dateEpoch
|
||||
|
@ -108,7 +121,3 @@ func (d Date) EncodeBinary(w io.Writer) error {
|
|||
_, err = pgio.WriteInt32(w, int32(daysSinceDateEpoch))
|
||||
return err
|
||||
}
|
||||
|
||||
func (d Date) Time() time.Time {
|
||||
return d.t
|
||||
}
|
||||
|
|
|
@ -1,90 +1,22 @@
|
|||
package pgtype_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx"
|
||||
"github.com/jackc/pgx/pgio"
|
||||
"github.com/jackc/pgx/pgtype"
|
||||
)
|
||||
|
||||
func TestDateTranscode(t *testing.T) {
|
||||
conn := mustConnectPgx(t)
|
||||
defer mustClose(t, conn)
|
||||
|
||||
ps, err := conn.Prepare("test", "select $1::date")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
result *pgtype.Date
|
||||
}{
|
||||
{result: pgtype.NewDate(1900, 1, 1)},
|
||||
{result: pgtype.NewDate(1970, 1, 1)},
|
||||
{result: pgtype.NewDate(1999, 12, 31)},
|
||||
{result: pgtype.NewDate(2000, 1, 1)},
|
||||
{result: pgtype.NewDate(2000, 1, 2)},
|
||||
{result: pgtype.NewDate(2200, 1, 1)},
|
||||
}
|
||||
|
||||
ps.FieldDescriptions[0].FormatCode = pgx.TextFormatCode
|
||||
for i, tt := range tests {
|
||||
inputBuf := &bytes.Buffer{}
|
||||
err = tt.result.EncodeText(inputBuf)
|
||||
if err != nil {
|
||||
t.Errorf("TextFormat %d: %v", i, err)
|
||||
}
|
||||
|
||||
var s string
|
||||
err := conn.QueryRow("test", string(inputBuf.Bytes()[4:])).Scan(&s)
|
||||
if err != nil {
|
||||
t.Errorf("TextFormat %d: %v", i, err)
|
||||
}
|
||||
|
||||
outputBuf := &bytes.Buffer{}
|
||||
pgio.WriteInt32(outputBuf, int32(len(s)))
|
||||
outputBuf.WriteString(s)
|
||||
var r pgtype.Date
|
||||
err = r.DecodeText(outputBuf)
|
||||
if err != nil {
|
||||
t.Errorf("TextFormat %d: %v", i, err)
|
||||
}
|
||||
|
||||
if r != *tt.result {
|
||||
t.Errorf("TextFormat %d: expected %v, got %v", i, *tt.result, r)
|
||||
}
|
||||
}
|
||||
|
||||
ps.FieldDescriptions[0].FormatCode = pgx.BinaryFormatCode
|
||||
for i, tt := range tests {
|
||||
inputBuf := &bytes.Buffer{}
|
||||
err = tt.result.EncodeBinary(inputBuf)
|
||||
if err != nil {
|
||||
t.Errorf("BinaryFormat %d: %v", i, err)
|
||||
}
|
||||
|
||||
var buf []byte
|
||||
err := conn.QueryRow("test", inputBuf.Bytes()[4:]).Scan(&buf)
|
||||
if err != nil {
|
||||
t.Errorf("BinaryFormat %d: %v", i, err)
|
||||
}
|
||||
|
||||
outputBuf := &bytes.Buffer{}
|
||||
pgio.WriteInt32(outputBuf, int32(len(buf)))
|
||||
outputBuf.Write(buf)
|
||||
var r pgtype.Date
|
||||
err = r.DecodeBinary(outputBuf)
|
||||
if err != nil {
|
||||
t.Errorf("BinaryFormat %d: %v", i, err)
|
||||
}
|
||||
|
||||
if r != *tt.result {
|
||||
t.Errorf("BinaryFormat %d: expected %v, got %v", i, tt.result, r)
|
||||
}
|
||||
}
|
||||
testSuccessfulTranscode(t, "date", []interface{}{
|
||||
pgtype.Date{Time: time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present},
|
||||
pgtype.Date{Time: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present},
|
||||
pgtype.Date{Time: time.Date(1999, 12, 31, 0, 0, 0, 0, time.UTC), Status: pgtype.Present},
|
||||
pgtype.Date{Time: time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present},
|
||||
pgtype.Date{Time: time.Date(2000, 1, 2, 0, 0, 0, 0, time.UTC), Status: pgtype.Present},
|
||||
pgtype.Date{Time: time.Date(2200, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present},
|
||||
pgtype.Date{Status: pgtype.Null},
|
||||
})
|
||||
}
|
||||
|
||||
func TestDateConvertFrom(t *testing.T) {
|
||||
|
@ -94,13 +26,13 @@ func TestDateConvertFrom(t *testing.T) {
|
|||
source interface{}
|
||||
result *pgtype.Date
|
||||
}{
|
||||
{source: time.Date(1900, 1, 1, 0, 0, 0, 0, time.Local), result: pgtype.NewDate(1900, 1, 1)},
|
||||
{source: time.Date(1970, 1, 1, 0, 0, 0, 0, time.Local), result: pgtype.NewDate(1970, 1, 1)},
|
||||
{source: time.Date(1970, 1, 1, 0, 0, 0, 0, time.Local), result: pgtype.NewDate(1999, 12, 31)},
|
||||
{source: time.Date(1970, 1, 1, 0, 0, 0, 0, time.Local), result: pgtype.NewDate(2000, 1, 1)},
|
||||
{source: time.Date(1970, 1, 1, 0, 0, 0, 0, time.Local), result: pgtype.NewDate(2000, 1, 2)},
|
||||
{source: time.Date(1970, 1, 1, 0, 0, 0, 0, time.Local), result: pgtype.NewDate(2200, 1, 1)},
|
||||
{source: _time(time.Date(1970, 1, 1, 0, 0, 0, 0, time.Local)), result: pgtype.NewDate(1970, 1, 1)},
|
||||
{source: time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC), result: &pgtype.Date{Time: time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC)}},
|
||||
{source: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC), result: &pgtype.Date{Time: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC)}},
|
||||
{source: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC), result: &pgtype.Date{Time: time.Date(1999, 12, 31, 0, 0, 0, 0, time.UTC)}},
|
||||
{source: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC), result: &pgtype.Date{Time: time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)}},
|
||||
{source: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC), result: &pgtype.Date{Time: time.Date(2000, 1, 2, 0, 0, 0, 0, time.UTC)}},
|
||||
{source: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC), result: &pgtype.Date{Time: time.Date(2200, 1, 1, 0, 0, 0, 0, time.UTC)}},
|
||||
{source: _time(time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC)), result: &pgtype.Date{Time: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC)}},
|
||||
}
|
||||
|
||||
for i, tt := range successfulTests {
|
||||
|
|
12
values.go
12
values.go
|
@ -1996,11 +1996,6 @@ func encodeJSONB(w *WriteBuf, oid OID, value interface{}) error {
|
|||
}
|
||||
|
||||
func decodeDate(vr *ValueReader) time.Time {
|
||||
if vr.Len() == -1 {
|
||||
vr.Fatal(ProtocolError("Cannot decode null into time.Time"))
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
if vr.Type().DataType != DateOID {
|
||||
vr.Fatal(ProtocolError(fmt.Sprintf("Cannot decode oid %v into time.Time", vr.Type().DataType)))
|
||||
return time.Time{}
|
||||
|
@ -2025,7 +2020,12 @@ func decodeDate(vr *ValueReader) time.Time {
|
|||
return time.Time{}
|
||||
}
|
||||
|
||||
return d.Time()
|
||||
if d.Status == pgtype.Null {
|
||||
vr.Fatal(ProtocolError("Cannot decode null into int16"))
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
return d.Time
|
||||
}
|
||||
|
||||
func encodeTime(w *WriteBuf, oid OID, value time.Time) error {
|
||||
|
|
Loading…
Reference in New Issue