Skip to content

Commit

Permalink
bugfix: decode the string before parsing timezone
Browse files Browse the repository at this point in the history
Signed-off-by: Andres Taylor <[email protected]>
  • Loading branch information
systay committed Sep 24, 2024
1 parent 95f2e3e commit c4d53fd
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 3 deletions.
49 changes: 49 additions & 0 deletions go/test/endtoend/vtgate/queries/misc/misc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,16 @@ limitations under the License.
package misc

import (
"context"
"database/sql"
"fmt"
"strconv"
"strings"
"testing"
"time"

"vitess.io/vitess/go/mysql"

_ "github.com/go-sql-driver/mysql"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -473,3 +476,49 @@ func TestEnumSetVals(t *testing.T) {
mcmp.AssertMatches("select id, enum_col, cast(enum_col as signed) from tbl_enum_set order by enum_col, id", `[[INT64(4) ENUM("xsmall") INT64(1)] [INT64(2) ENUM("small") INT64(2)] [INT64(1) ENUM("medium") INT64(3)] [INT64(5) ENUM("medium") INT64(3)] [INT64(3) ENUM("large") INT64(4)]]`)
mcmp.AssertMatches("select id, set_col, cast(set_col as unsigned) from tbl_enum_set order by set_col, id", `[[INT64(4) SET("a,b") UINT64(3)] [INT64(3) SET("c") UINT64(4)] [INT64(5) SET("a,d") UINT64(9)] [INT64(1) SET("a,b,e") UINT64(19)] [INT64(2) SET("e,f,g") UINT64(112)]]`)
}

func TestTimeZones(t *testing.T) {
testCases := []struct {
name string
targetTZ string
expectedDiff time.Duration
}{
{"UTC to +08:00", "+08:00", 8 * time.Hour},
{"UTC to -08:00", "-08:00", -8 * time.Hour},
{"UTC to +05:30", "+05:30", 5*time.Hour + 30*time.Minute},
{"UTC to -05:45", "-05:45", -(5*time.Hour + 45*time.Minute)},
{"UTC to +09:00", "+09:00", 9 * time.Hour},
{"UTC to -12:00", "-12:00", -12 * time.Hour},
}

// Connect to Vitess
conn, err := mysql.Connect(context.Background(), &vtParams)
require.NoError(t, err)

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
// Set the initial time zone and get the time
utils.Exec(t, conn, "set time_zone = '+00:00'")
rs1 := utils.Exec(t, conn, "select now()")

// Set the target time zone and get the time
utils.Exec(t, conn, fmt.Sprintf("set time_zone = '%s'", tc.targetTZ))
rs2 := utils.Exec(t, conn, "select now()")

// Parse the times from the query result
layout := "2006-01-02 15:04:05" // MySQL default datetime format
time1, err := time.Parse(layout, rs1.Rows[0][0].ToString())
require.NoError(t, err)
time2, err := time.Parse(layout, rs2.Rows[0][0].ToString())
require.NoError(t, err)

// Calculate the actual difference between time2 and time1
actualDiff := time2.Sub(time1)
allowableDeviation := time.Second // allow up to 1-second difference

// Use a range to allow for slight variations
require.InDeltaf(t, tc.expectedDiff.Seconds(), actualDiff.Seconds(), allowableDeviation.Seconds(),
"time2 should be approximately %v after time1, within 1 second tolerance\n%v vs %v", tc.expectedDiff, time1, time2)
})
}
}
19 changes: 19 additions & 0 deletions go/vt/vtgate/executor_set_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -625,3 +625,22 @@ func TestExecutorSetAndSelect(t *testing.T) {
})
}
}

// TestTimeZone verifies that setting different time zones in the session
// results in different outputs for the `now()` function.
func TestExecutorTimeZone(t *testing.T) {
e, _, _, _, ctx := createExecutorEnv(t)

session := NewAutocommitSession(&vtgatepb.Session{TargetString: KsTestUnsharded, EnableSystemSettings: true})
session.SetSystemVariable("time_zone", "'+08:00'")

qr, err := e.Execute(ctx, nil, "TestExecutorSetAndSelect", session, "select now()", nil)

require.NoError(t, err)
session.SetSystemVariable("time_zone", "'+02:00'")

qrWith, err := e.Execute(ctx, nil, "TestExecutorSetAndSelect", session, "select now()", nil)
require.NoError(t, err)

assert.False(t, qr.Rows[0][0].Equal(qrWith.Rows[0][0]), "%v vs %v", qr.Rows[0][0].ToString(), qrWith.Rows[0][0].ToString())
}
12 changes: 11 additions & 1 deletion go/vt/vtgate/safe_session.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import (
"sync"
"time"

"vitess.io/vitess/go/sqltypes"

"google.golang.org/protobuf/proto"

"vitess.io/vitess/go/mysql/datetime"
Expand Down Expand Up @@ -563,13 +565,21 @@ func (session *SafeSession) HasSystemVariables() (found bool) {

func (session *SafeSession) TimeZone() *time.Location {
session.mu.Lock()
tz, ok := session.SystemVariables["time_zone"]
zoneSQL, ok := session.SystemVariables["time_zone"]
session.mu.Unlock()

if !ok {
return nil
}

tz, err := sqltypes.DecodeStringSQL(zoneSQL)
if err != nil {
return nil
}

loc, _ := datetime.ParseTimeZone(tz)
// it's safe to ignore the error - if we get an error, loc will be nil,
// and this is exactly the behaviour we want anyway
return loc
}

Expand Down
4 changes: 2 additions & 2 deletions go/vt/vtgate/safe_session_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,11 @@ func TestTimeZone(t *testing.T) {
want string
}{
{
tz: "Europe/Amsterdam",
tz: "'Europe/Amsterdam'",
want: "Europe/Amsterdam",
},
{
tz: "+02:00",
tz: "'+02:00'",
want: "UTC+02:00",
},
{
Expand Down

0 comments on commit c4d53fd

Please sign in to comment.