mirror of https://github.com/jackc/pgx.git
Add timestamp support
But not to NullTime because of text vs binary encoding difficulties. You really should never use timestamp anyway.scan-io
parent
c108378973
commit
ed2b3b3b49
4
conn.go
4
conn.go
|
@ -438,7 +438,7 @@ func (c *Conn) sendPreparedQuery(ps *PreparedStatement, arguments ...interface{}
|
||||||
switch oid {
|
switch oid {
|
||||||
case BoolOid, ByteaOid, Int2Oid, Int4Oid, Int8Oid, Float4Oid, Float8Oid, TimestampTzOid:
|
case BoolOid, ByteaOid, Int2Oid, Int4Oid, Int8Oid, Float4Oid, Float8Oid, TimestampTzOid:
|
||||||
wbuf.WriteInt16(BinaryFormatCode)
|
wbuf.WriteInt16(BinaryFormatCode)
|
||||||
case TextOid, VarcharOid, DateOid:
|
case TextOid, VarcharOid, DateOid, TimestampOid:
|
||||||
wbuf.WriteInt16(TextFormatCode)
|
wbuf.WriteInt16(TextFormatCode)
|
||||||
default:
|
default:
|
||||||
return SerializationError(fmt.Sprintf("Parameter %d oid %d is not a core type and argument type %T does not implement TextEncoder or BinaryEncoder", i, oid, arg))
|
return SerializationError(fmt.Sprintf("Parameter %d oid %d is not a core type and argument type %T does not implement TextEncoder or BinaryEncoder", i, oid, arg))
|
||||||
|
@ -488,6 +488,8 @@ func (c *Conn) sendPreparedQuery(ps *PreparedStatement, arguments ...interface{}
|
||||||
err = encodeDate(wbuf, arguments[i])
|
err = encodeDate(wbuf, arguments[i])
|
||||||
case TimestampTzOid:
|
case TimestampTzOid:
|
||||||
err = encodeTimestampTz(wbuf, arguments[i])
|
err = encodeTimestampTz(wbuf, arguments[i])
|
||||||
|
case TimestampOid:
|
||||||
|
err = encodeTimestamp(wbuf, arguments[i])
|
||||||
default:
|
default:
|
||||||
return SerializationError(fmt.Sprintf("%T is not a core type and it does not implement TextEncoder or BinaryEncoder", arg))
|
return SerializationError(fmt.Sprintf("%T is not a core type and it does not implement TextEncoder or BinaryEncoder", arg))
|
||||||
}
|
}
|
||||||
|
|
13
query.go
13
query.go
|
@ -199,10 +199,17 @@ func (rows *Rows) Scan(dest ...interface{}) (err error) {
|
||||||
case *float64:
|
case *float64:
|
||||||
*d = decodeFloat8(rows, fd, size)
|
*d = decodeFloat8(rows, fd, size)
|
||||||
case *time.Time:
|
case *time.Time:
|
||||||
if fd.DataType == DateOid {
|
switch fd.DataType {
|
||||||
|
case DateOid:
|
||||||
*d = decodeDate(rows, fd, size)
|
*d = decodeDate(rows, fd, size)
|
||||||
} else {
|
case TimestampTzOid:
|
||||||
*d = decodeTimestampTz(rows, fd, size)
|
*d = decodeTimestampTz(rows, fd, size)
|
||||||
|
case TimestampOid:
|
||||||
|
*d = decodeTimestamp(rows, fd, size)
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("Can't convert OID %v to time.Time", fd.DataType)
|
||||||
|
rows.Fatal(err)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
case Scanner:
|
case Scanner:
|
||||||
|
@ -254,6 +261,8 @@ func (rows *Rows) Values() ([]interface{}, error) {
|
||||||
values = append(values, decodeDate(rows, fd, size))
|
values = append(values, decodeDate(rows, fd, size))
|
||||||
case TimestampTzOid:
|
case TimestampTzOid:
|
||||||
values = append(values, decodeTimestampTz(rows, fd, size))
|
values = append(values, decodeTimestampTz(rows, fd, size))
|
||||||
|
case TimestampOid:
|
||||||
|
values = append(values, decodeTimestamp(rows, fd, size))
|
||||||
default:
|
default:
|
||||||
// if it is not an intrinsic type then return the text
|
// if it is not an intrinsic type then return the text
|
||||||
switch fd.FormatCode {
|
switch fd.FormatCode {
|
||||||
|
|
|
@ -387,6 +387,7 @@ func TestQueryRowCoreTypes(t *testing.T) {
|
||||||
{"select $1::float8", []interface{}{float64(1.23)}, []interface{}{&actual.f64}, allTypes{f64: 1.23}},
|
{"select $1::float8", []interface{}{float64(1.23)}, []interface{}{&actual.f64}, allTypes{f64: 1.23}},
|
||||||
{"select $1::bool", []interface{}{true}, []interface{}{&actual.b}, allTypes{b: true}},
|
{"select $1::bool", []interface{}{true}, []interface{}{&actual.b}, allTypes{b: true}},
|
||||||
{"select $1::timestamptz", []interface{}{time.Unix(123, 5000)}, []interface{}{&actual.t}, allTypes{t: time.Unix(123, 5000)}},
|
{"select $1::timestamptz", []interface{}{time.Unix(123, 5000)}, []interface{}{&actual.t}, allTypes{t: time.Unix(123, 5000)}},
|
||||||
|
{"select $1::timestamp", []interface{}{time.Date(2010, 1, 2, 3, 4, 5, 0, time.Local)}, []interface{}{&actual.t}, allTypes{t: time.Date(2010, 1, 2, 3, 4, 5, 0, time.Local)}},
|
||||||
{"select $1::date", []interface{}{time.Date(1987, 1, 2, 0, 0, 0, 0, time.Local)}, []interface{}{&actual.t}, allTypes{t: time.Date(1987, 1, 2, 0, 0, 0, 0, time.Local)}},
|
{"select $1::date", []interface{}{time.Date(1987, 1, 2, 0, 0, 0, 0, time.Local)}, []interface{}{&actual.t}, allTypes{t: time.Date(1987, 1, 2, 0, 0, 0, 0, time.Local)}},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
44
values.go
44
values.go
|
@ -22,6 +22,7 @@ const (
|
||||||
Float8Oid = 701
|
Float8Oid = 701
|
||||||
VarcharOid = 1043
|
VarcharOid = 1043
|
||||||
DateOid = 1082
|
DateOid = 1082
|
||||||
|
TimestampOid = 1114
|
||||||
TimestampTzOid = 1184
|
TimestampTzOid = 1184
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -366,10 +367,15 @@ type NullTime struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NullTime) Scan(rows *Rows, fd *FieldDescription, size int32) error {
|
func (n *NullTime) Scan(rows *Rows, fd *FieldDescription, size int32) error {
|
||||||
|
if fd.DataType != TimestampTzOid {
|
||||||
|
return SerializationError(fmt.Sprintf("NullTime.EncodeBinary cannot encode into OID %d", fd.DataType))
|
||||||
|
}
|
||||||
|
|
||||||
if size == -1 {
|
if size == -1 {
|
||||||
n.Time, n.Valid = time.Time{}, false
|
n.Time, n.Valid = time.Time{}, false
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
n.Valid = true
|
n.Valid = true
|
||||||
n.Time = decodeTimestampTz(rows, fd, size)
|
n.Time = decodeTimestampTz(rows, fd, size)
|
||||||
|
|
||||||
|
@ -962,3 +968,41 @@ func encodeTimestampTz(w *WriteBuf, value interface{}) error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func decodeTimestamp(rows *Rows, fd *FieldDescription, size int32) time.Time {
|
||||||
|
var zeroTime time.Time
|
||||||
|
|
||||||
|
if fd.DataType != TimestampOid {
|
||||||
|
rows.Fatal(ProtocolError(fmt.Sprintf("Expected type oid %v but received type oid %v", TimestampOid, fd.DataType)))
|
||||||
|
return zeroTime
|
||||||
|
}
|
||||||
|
|
||||||
|
switch fd.FormatCode {
|
||||||
|
case TextFormatCode:
|
||||||
|
s := rows.mr.ReadString(size)
|
||||||
|
t, err := time.ParseInLocation("2006-01-02 15:04:05.999999", s, time.Local)
|
||||||
|
if err != nil {
|
||||||
|
rows.Fatal(ProtocolError(fmt.Sprintf("Can't decode timestamp: %v - %v", err, s)))
|
||||||
|
return zeroTime
|
||||||
|
}
|
||||||
|
return t
|
||||||
|
case BinaryFormatCode:
|
||||||
|
rows.Fatal(ProtocolError("Can't decode binary timestamp"))
|
||||||
|
return zeroTime
|
||||||
|
default:
|
||||||
|
rows.Fatal(ProtocolError(fmt.Sprintf("Unknown field description format code: %v", fd.FormatCode)))
|
||||||
|
return zeroTime
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeTimestamp(w *WriteBuf, value interface{}) error {
|
||||||
|
t, ok := value.(time.Time)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("Expected time.Time, received %T", value)
|
||||||
|
}
|
||||||
|
|
||||||
|
s := t.Format("2006-01-02 15:04:05.999999")
|
||||||
|
return encodeText(w, s)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue