Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pgwire: only show TZ minutes offset if non-zero #65063

Merged
merged 2 commits into from
May 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 11 additions & 6 deletions pkg/cli/cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -287,8 +287,9 @@ func Example_sql_format() {
c.RunWithArgs([]string{"sql", "-e", "create database t; create table t.times (bare timestamp, withtz timestamptz)"})
c.RunWithArgs([]string{"sql", "-e", "insert into t.times values ('2016-01-25 10:10:10', '2016-01-25 10:10:10-05:00')"})
c.RunWithArgs([]string{"sql", "-e", "select bare from t.times; select withtz from t.times"})
c.RunWithArgs([]string{"sql", "-e", "select '2021-03-20'::date; select '01:01'::time; select '01:01'::timetz"})
c.RunWithArgs([]string{"sql", "-e", "select (1/3.0)::real; select (1/3.0)::double precision"})
c.RunWithArgs([]string{"sql", "-e",
"select '2021-03-20'::date; select '01:01'::time; select '01:01'::timetz; select '01:01+02:02'::timetz"})
c.RunWithArgs([]string{"sql", "-e", "select (1/3.0)::real; select (1/3.0)::double precision; select '-inf'::float8"})

// Output:
// sql -e create database t; create table t.times (bare timestamp, withtz timestamptz)
Expand All @@ -299,19 +300,23 @@ func Example_sql_format() {
// bare
// 2016-01-25 10:10:10
// withtz
// 2016-01-25 15:10:10+00:00:00
// sql -e select '2021-03-20'::date; select '01:01'::time; select '01:01'::timetz
// 2016-01-25 15:10:10+00
// sql -e select '2021-03-20'::date; select '01:01'::time; select '01:01'::timetz; select '01:01+02:02'::timetz
// date
// 2021-03-20
// time
// 01:01:00
// timetz
// 01:01:00+00:00:00
// sql -e select (1/3.0)::real; select (1/3.0)::double precision
// 01:01:00+00
// timetz
// 01:01:00+02:02
// sql -e select (1/3.0)::real; select (1/3.0)::double precision; select '-inf'::float8
// float4
// 0.33333334
// float8
// 0.3333333333333333
// float8
// -Infinity
}

func Example_sql_column_labels() {
Expand Down
15 changes: 14 additions & 1 deletion pkg/cli/sql_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"database/sql/driver"
"fmt"
"io"
"math"
"net/url"
"reflect"
"strconv"
Expand Down Expand Up @@ -1134,6 +1135,11 @@ func formatVal(
if colType == "FLOAT4" {
width = 32
}
if math.IsInf(t, 1) {
return "Infinity"
} else if math.IsInf(t, -1) {
return "-Infinity"
}
return strconv.FormatFloat(t, 'g', -1, width)

case string:
Expand Down Expand Up @@ -1178,6 +1184,13 @@ func formatVal(
// Some unknown/new time-like format.
tfmt = timeutil.FullTimeFormat
}
if tfmt == timeutil.TimestampWithTZFormat || tfmt == timeutil.TimeWithTZFormat {
if _, offsetSeconds := t.Zone(); offsetSeconds%60 != 0 {
tfmt += ":00:00"
} else if offsetSeconds%3600 != 0 {
tfmt += ":00"
}
}
return t.Format(tfmt)
}

Expand All @@ -1186,7 +1199,7 @@ func formatVal(

var timeOutputFormats = map[string]string{
"TIMESTAMP": timeutil.TimestampWithoutTZFormat,
"TIMESTAMPTZ": timeutil.FullTimeFormat,
"TIMESTAMPTZ": timeutil.TimestampWithTZFormat,
"TIME": timeutil.TimeWithoutTZFormat,
"TIMETZ": timeutil.TimeWithTZFormat,
"DATE": timeutil.DateFormat,
Expand Down
18 changes: 16 additions & 2 deletions pkg/cmd/generate-binary/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,6 @@ var inputs = map[string][]string{
"9004-10-19 10:23:54",
},

/* TODO(mjibson): fix these; there's a slight timezone display difference
"'%s'::timestamptz": {
"1999-01-08 04:05:06+00",
"1999-01-08 04:05:06+00:00",
Expand All @@ -317,7 +316,22 @@ var inputs = map[string][]string{
"4004-10-19 10:23:54",
"9004-10-19 10:23:54",
},
*/

"'%s'::timetz": {
"04:05:06+00",
"04:05:06+00:00",
"04:05:06+10",
"04:05:06+10:00",
"04:05:06+10:30",
"04:05:06",
"10:23:54",
"00:00:00",
"10:23:54",
"10:23:54 BC",
"10:23:54",
"10:23:54+1:2:3",
"10:23:54+1:2",
},

"'%s'::date": {
"1999-01-08",
Expand Down
2 changes: 1 addition & 1 deletion pkg/sql/pgwire/pgwirebase/encoding.go
Original file line number Diff line number Diff line change
Expand Up @@ -683,7 +683,7 @@ func DecodeDatum(
return nil, pgerror.Newf(pgcode.Syntax, "timetz requires 12 bytes for binary format")
}
timeOfDayMicros := int64(binary.BigEndian.Uint64(b))
offsetSecs := int32(binary.BigEndian.Uint32(b))
offsetSecs := int32(binary.BigEndian.Uint32(b[8:]))
return tree.NewDTimeTZFromOffset(timeofday.TimeOfDay(timeOfDayMicros), offsetSecs), nil
case oid.T_interval:
if len(b) < 16 {
Expand Down
175 changes: 175 additions & 0 deletions pkg/sql/pgwire/testdata/encodings.json
Original file line number Diff line number Diff line change
Expand Up @@ -1700,6 +1700,181 @@
"TextAsBinary": [57, 48, 48, 52, 45, 49, 48, 45, 49, 57, 32, 49, 48, 58, 50, 51, 58, 53, 52],
"Binary": [3, 17, 83, 233, 31, 54, 66, 128]
},
{
"SQL": "'1999-01-08 04:05:06+00'::timestamptz",
"Oid": 1184,
"Text": "1999-01-08 04:05:06+00",
"TextAsBinary": [49, 57, 57, 57, 45, 48, 49, 45, 48, 56, 32, 48, 52, 58, 48, 53, 58, 48, 54, 43, 48, 48],
"Binary": [255, 255, 227, 225, 177, 91, 128, 128]
},
{
"SQL": "'1999-01-08 04:05:06+00:00'::timestamptz",
"Oid": 1184,
"Text": "1999-01-08 04:05:06+00",
"TextAsBinary": [49, 57, 57, 57, 45, 48, 49, 45, 48, 56, 32, 48, 52, 58, 48, 53, 58, 48, 54, 43, 48, 48],
"Binary": [255, 255, 227, 225, 177, 91, 128, 128]
},
{
"SQL": "'1999-01-08 04:05:06+10'::timestamptz",
"Oid": 1184,
"Text": "1999-01-07 18:05:06+00",
"TextAsBinary": [49, 57, 57, 57, 45, 48, 49, 45, 48, 55, 32, 49, 56, 58, 48, 53, 58, 48, 54, 43, 48, 48],
"Binary": [255, 255, 227, 217, 79, 151, 24, 128]
},
{
"SQL": "'1999-01-08 04:05:06+10:00'::timestamptz",
"Oid": 1184,
"Text": "1999-01-07 18:05:06+00",
"TextAsBinary": [49, 57, 57, 57, 45, 48, 49, 45, 48, 55, 32, 49, 56, 58, 48, 53, 58, 48, 54, 43, 48, 48],
"Binary": [255, 255, 227, 217, 79, 151, 24, 128]
},
{
"SQL": "'1999-01-08 04:05:06+10:30'::timestamptz",
"Oid": 1184,
"Text": "1999-01-07 17:35:06+00",
"TextAsBinary": [49, 57, 57, 57, 45, 48, 49, 45, 48, 55, 32, 49, 55, 58, 51, 53, 58, 48, 54, 43, 48, 48],
"Binary": [255, 255, 227, 216, 228, 77, 70, 128]
},
{
"SQL": "'1999-01-08 04:05:06'::timestamptz",
"Oid": 1184,
"Text": "1999-01-08 04:05:06+00",
"TextAsBinary": [49, 57, 57, 57, 45, 48, 49, 45, 48, 56, 32, 48, 52, 58, 48, 53, 58, 48, 54, 43, 48, 48],
"Binary": [255, 255, 227, 225, 177, 91, 128, 128]
},
{
"SQL": "'2004-10-19 10:23:54'::timestamptz",
"Oid": 1184,
"Text": "2004-10-19 10:23:54+00",
"TextAsBinary": [50, 48, 48, 52, 45, 49, 48, 45, 49, 57, 32, 49, 48, 58, 50, 51, 58, 53, 52, 43, 48, 48],
"Binary": [0, 0, 137, 201, 15, 13, 226, 128]
},
{
"SQL": "'0001-01-01 00:00:00'::timestamptz",
"Oid": 1184,
"Text": "0001-01-01 00:00:00+00",
"TextAsBinary": [48, 48, 48, 49, 45, 48, 49, 45, 48, 49, 32, 48, 48, 58, 48, 48, 58, 48, 48, 43, 48, 48],
"Binary": [255, 31, 226, 255, 197, 156, 96, 0]
},
{
"SQL": "'0004-10-19 10:23:54'::timestamptz",
"Oid": 1184,
"Text": "0004-10-19 10:23:54+00",
"TextAsBinary": [48, 48, 48, 52, 45, 49, 48, 45, 49, 57, 32, 49, 48, 58, 50, 51, 58, 53, 52, 43, 48, 48],
"Binary": [255, 32, 80, 6, 42, 191, 2, 128]
},
{
"SQL": "'0004-10-19 10:23:54 BC'::timestamptz",
"Oid": 1184,
"Text": "0004-10-19 10:23:54+00 BC",
"TextAsBinary": [48, 48, 48, 52, 45, 49, 48, 45, 49, 57, 32, 49, 48, 58, 50, 51, 58, 53, 52, 43, 48, 48, 32, 66, 67],
"Binary": [255, 31, 135, 24, 26, 133, 34, 128]
},
{
"SQL": "'4004-10-19 10:23:54'::timestamptz",
"Oid": 1184,
"Text": "4004-10-19 10:23:54+00",
"TextAsBinary": [52, 48, 48, 52, 45, 49, 48, 45, 49, 57, 32, 49, 48, 58, 50, 51, 58, 53, 52, 43, 48, 48],
"Binary": [0, 224, 195, 139, 243, 92, 194, 128]
},
{
"SQL": "'9004-10-19 10:23:54'::timestamptz",
"Oid": 1184,
"Text": "9004-10-19 10:23:54+00",
"TextAsBinary": [57, 48, 48, 52, 45, 49, 48, 45, 49, 57, 32, 49, 48, 58, 50, 51, 58, 53, 52, 43, 48, 48],
"Binary": [3, 17, 83, 233, 31, 54, 66, 128]
},
{
"SQL": "'04:05:06+00'::timetz",
"Oid": 1266,
"Text": "04:05:06+00",
"TextAsBinary": [48, 52, 58, 48, 53, 58, 48, 54, 43, 48, 48],
"Binary": [0, 0, 0, 3, 108, 139, 192, 128, 0, 0, 0, 0]
},
{
"SQL": "'04:05:06+00:00'::timetz",
"Oid": 1266,
"Text": "04:05:06+00",
"TextAsBinary": [48, 52, 58, 48, 53, 58, 48, 54, 43, 48, 48],
"Binary": [0, 0, 0, 3, 108, 139, 192, 128, 0, 0, 0, 0]
},
{
"SQL": "'04:05:06+10'::timetz",
"Oid": 1266,
"Text": "04:05:06+10",
"TextAsBinary": [48, 52, 58, 48, 53, 58, 48, 54, 43, 49, 48],
"Binary": [0, 0, 0, 3, 108, 139, 192, 128, 255, 255, 115, 96]
},
{
"SQL": "'04:05:06+10:00'::timetz",
"Oid": 1266,
"Text": "04:05:06+10",
"TextAsBinary": [48, 52, 58, 48, 53, 58, 48, 54, 43, 49, 48],
"Binary": [0, 0, 0, 3, 108, 139, 192, 128, 255, 255, 115, 96]
},
{
"SQL": "'04:05:06+10:30'::timetz",
"Oid": 1266,
"Text": "04:05:06+10:30",
"TextAsBinary": [48, 52, 58, 48, 53, 58, 48, 54, 43, 49, 48, 58, 51, 48],
"Binary": [0, 0, 0, 3, 108, 139, 192, 128, 255, 255, 108, 88]
},
{
"SQL": "'04:05:06'::timetz",
"Oid": 1266,
"Text": "04:05:06+00",
"TextAsBinary": [48, 52, 58, 48, 53, 58, 48, 54, 43, 48, 48],
"Binary": [0, 0, 0, 3, 108, 139, 192, 128, 0, 0, 0, 0]
},
{
"SQL": "'10:23:54'::timetz",
"Oid": 1266,
"Text": "10:23:54+00",
"TextAsBinary": [49, 48, 58, 50, 51, 58, 53, 52, 43, 48, 48],
"Binary": [0, 0, 0, 8, 183, 61, 130, 128, 0, 0, 0, 0]
},
{
"SQL": "'00:00:00'::timetz",
"Oid": 1266,
"Text": "00:00:00+00",
"TextAsBinary": [48, 48, 58, 48, 48, 58, 48, 48, 43, 48, 48],
"Binary": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
},
{
"SQL": "'10:23:54'::timetz",
"Oid": 1266,
"Text": "10:23:54+00",
"TextAsBinary": [49, 48, 58, 50, 51, 58, 53, 52, 43, 48, 48],
"Binary": [0, 0, 0, 8, 183, 61, 130, 128, 0, 0, 0, 0]
},
{
"SQL": "'10:23:54 BC'::timetz",
"Oid": 1266,
"Text": "10:23:54+00",
"TextAsBinary": [49, 48, 58, 50, 51, 58, 53, 52, 43, 48, 48],
"Binary": [0, 0, 0, 8, 183, 61, 130, 128, 0, 0, 0, 0]
},
{
"SQL": "'10:23:54'::timetz",
"Oid": 1266,
"Text": "10:23:54+00",
"TextAsBinary": [49, 48, 58, 50, 51, 58, 53, 52, 43, 48, 48],
"Binary": [0, 0, 0, 8, 183, 61, 130, 128, 0, 0, 0, 0]
},
{
"SQL": "'10:23:54+1:2:3'::timetz",
"Oid": 1266,
"Text": "10:23:54+01:02:03",
"TextAsBinary": [49, 48, 58, 50, 51, 58, 53, 52, 43, 48, 49, 58, 48, 50, 58, 48, 51],
"Binary": [0, 0, 0, 8, 183, 61, 130, 128, 255, 255, 241, 117]
},
{
"SQL": "'10:23:54+1:2'::timetz",
"Oid": 1266,
"Text": "10:23:54+01:02",
"TextAsBinary": [49, 48, 58, 50, 51, 58, 53, 52, 43, 48, 49, 58, 48, 50],
"Binary": [0, 0, 0, 8, 183, 61, 130, 128, 255, 255, 241, 120]
},
{
"SQL": "'{00000000-0000-0000-0000-000000000000}'::uuid[]",
"Oid": 2951,
Expand Down
7 changes: 3 additions & 4 deletions pkg/sql/pgwire/testdata/pgtest/timezone
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,15 @@ ReadyForQuery
{"Type":"CommandComplete","CommandTag":"SELECT 1"}
{"Type":"ReadyForQuery","TxStatus":"I"}

# PostgreSQL does not display seconds offset here, but CockroachDB does.
send crdb_only
send
Query {"String": "SELECT '1882-05-23T00:00:00'::\"timestamptz\""}
----

until crdb_only ignore_data_type_sizes
until ignore_data_type_sizes
ReadyForQuery
----
{"Type":"RowDescription","Fields":[{"Name":"timestamptz","TableOID":0,"TableAttributeNumber":0,"DataTypeOID":1184,"DataTypeSize":0,"TypeModifier":-1,"Format":0}]}
{"Type":"DataRow","Values":[{"text":"1882-05-23 00:00:00+00:00"}]}
{"Type":"DataRow","Values":[{"text":"1882-05-23 00:00:00+00"}]}
{"Type":"CommandComplete","CommandTag":"SELECT 1"}
{"Type":"ReadyForQuery","TxStatus":"I"}

Expand Down
12 changes: 7 additions & 5 deletions pkg/sql/pgwire/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -533,10 +533,10 @@ func (b *writeBuffer) writeBinaryDatum(

const (
pgTimeFormat = "15:04:05.999999"
pgTimeTZFormat = pgTimeFormat + "-07:00"
pgTimeTZFormat = pgTimeFormat + "-07"
pgDateFormat = "2006-01-02"
pgTimeStampFormatNoOffset = pgDateFormat + " " + pgTimeFormat
pgTimeStampFormat = pgTimeStampFormatNoOffset + "-07:00"
pgTimeStampFormat = pgTimeStampFormatNoOffset + "-07"
pgTime2400Format = "24:00:00"
)

Expand All @@ -554,11 +554,11 @@ func formatTime(t timeofday.TimeOfDay, tmp []byte) []byte {
// formatTimeTZ formats t into a format lib/pq understands, appending to the
// provided tmp buffer and reallocating if needed. The function will then return
// the resulting buffer.
// Note it does not understand the "second" component of the offset as lib/pq
// cannot parse it.
func formatTimeTZ(t timetz.TimeTZ, tmp []byte) []byte {
format := pgTimeTZFormat
if t.OffsetSecs%60 != 0 {
format += ":00:00"
} else if t.OffsetSecs%3600 != 0 {
format += ":00"
}
ret := t.ToTime().AppendFormat(tmp, format)
Expand All @@ -577,7 +577,9 @@ func formatTs(t time.Time, offset *time.Location, tmp []byte) (b []byte) {
var format string
if offset != nil {
format = pgTimeStampFormat
if _, offset := t.In(offset).Zone(); offset%60 != 0 {
if _, offsetSeconds := t.In(offset).Zone(); offsetSeconds%60 != 0 {
format += ":00:00"
} else if offsetSeconds%3600 != 0 {
format += ":00"
}
} else {
Expand Down
14 changes: 10 additions & 4 deletions pkg/util/timeutil/timeutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,23 @@

package timeutil

// FullTimeFormat is the time format used to display any timestamp
// with date, time and time zone data.
// FullTimeFormat is the time format used to display any unknown timestamp
// type, and always shows the full time zone offset.
const FullTimeFormat = "2006-01-02 15:04:05.999999-07:00:00"

// TimestampWithTZFormat is the time format used to display
// timestamps with a time zone offset. The minutes and seconds
// offsets are only added if they are non-zero.
const TimestampWithTZFormat = "2006-01-02 15:04:05.999999-07"

// TimestampWithoutTZFormat is the time format used to display
// timestamps without a time zone offset.
// timestamps without a time zone offset. The minutes and seconds
// offsets are only added if they are non-zero.
const TimestampWithoutTZFormat = "2006-01-02 15:04:05.999999"

// TimeWithTZFormat is the time format used to display a time
// with a time zone offset.
const TimeWithTZFormat = "15:04:05.999999-07:00:00"
const TimeWithTZFormat = "15:04:05.999999-07"

// TimeWithoutTZFormat is the time format used to display a time
// without a time zone offset.
Expand Down