Skip to content

Commit

Permalink
support get_format() (#2657)
Browse files Browse the repository at this point in the history
  • Loading branch information
jycor authored Sep 13, 2024
1 parent 87cdecf commit a4ca0b0
Show file tree
Hide file tree
Showing 6 changed files with 325 additions and 3 deletions.
13 changes: 13 additions & 0 deletions enginetest/queries/queries.go
Original file line number Diff line number Diff line change
Expand Up @@ -9957,6 +9957,19 @@ from typestable`,
{"73.1"},
},
},

{
Query: "select get_format(date, 'usa')",
Expected: []sql.Row{
{"%m.%d.%Y"},
},
},
{
Query: "select date_format('2003-10-03', get_format(date, 'eur'))",
Expected: []sql.Row{
{"03.10.2003"},
},
},
}

var KeylessQueries = []QueryTest{
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ require (
github.com/dolthub/go-icu-regex v0.0.0-20230524105445-af7e7991c97e
github.com/dolthub/jsonpath v0.0.2-0.20240227200619-19675ab05c71
github.com/dolthub/sqllogictest/go v0.0.0-20201107003712-816f3ae12d81
github.com/dolthub/vitess v0.0.0-20240910182452-9291457d0a98
github.com/dolthub/vitess v0.0.0-20240912222847-5ea527306043
github.com/go-kit/kit v0.10.0
github.com/go-sql-driver/mysql v1.7.2-0.20231213112541-0004702b931d
github.com/gocraft/dbr/v2 v2.7.2
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ github.com/dolthub/jsonpath v0.0.2-0.20240227200619-19675ab05c71 h1:bMGS25NWAGTE
github.com/dolthub/jsonpath v0.0.2-0.20240227200619-19675ab05c71/go.mod h1:2/2zjLQ/JOOSbbSboojeg+cAwcRV0fDLzIiWch/lhqI=
github.com/dolthub/sqllogictest/go v0.0.0-20201107003712-816f3ae12d81 h1:7/v8q9XGFa6q5Ap4Z/OhNkAMBaK5YeuEzwJt+NZdhiE=
github.com/dolthub/sqllogictest/go v0.0.0-20201107003712-816f3ae12d81/go.mod h1:siLfyv2c92W1eN/R4QqG/+RjjX5W2+gCTRjZxBjI3TY=
github.com/dolthub/vitess v0.0.0-20240910182452-9291457d0a98 h1:f4Y0ZUO3SPsgS88w5X377tYSHVLlA8JzvKbNu2C5uks=
github.com/dolthub/vitess v0.0.0-20240910182452-9291457d0a98/go.mod h1:uBvlRluuL+SbEWTCZ68o0xvsdYZER3CEG/35INdzfJM=
github.com/dolthub/vitess v0.0.0-20240912222847-5ea527306043 h1:0gGPPIdXc4ThH/UCl/qBwuXZIaoN9vayXSHDk6HmtLg=
github.com/dolthub/vitess v0.0.0-20240912222847-5ea527306043/go.mod h1:uBvlRluuL+SbEWTCZ68o0xvsdYZER3CEG/35INdzfJM=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
Expand Down
138 changes: 138 additions & 0 deletions sql/expression/function/get_format.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
// Copyright 2024 Dolthub, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package function

import (
"fmt"
"strings"

"github.com/dolthub/go-mysql-server/sql"
"github.com/dolthub/go-mysql-server/sql/expression"
"github.com/dolthub/go-mysql-server/sql/types"
)

// GetFormat returns the time format string for the specified region
type GetFormat struct {
expression.BinaryExpressionStub
}

var _ sql.FunctionExpression = (*GetFormat)(nil)
var _ sql.CollationCoercible = (*GetFormat)(nil)

// NewGetFormat creates a new GetFormat expression.
func NewGetFormat(e1, e2 sql.Expression) sql.Expression {
return &GetFormat{
expression.BinaryExpressionStub{
LeftChild: e1,
RightChild: e2,
},
}
}

// FunctionName implements sql.FunctionExpression
func (g *GetFormat) FunctionName() string {
return "get_format"
}

// Description implements sql.FunctionExpression
func (g *GetFormat) Description() string {
return "returns the time format string for the specified region"
}

// Type implements the Expression interface.
func (g *GetFormat) Type() sql.Type {
return types.Text
}

// CollationCoercibility implements the interface sql.CollationCoercible.
func (*GetFormat) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) {
return ctx.GetCollation(), 5
}

func (g *GetFormat) String() string {
return fmt.Sprintf("%s(%s, %s)", g.FunctionName(), g.LeftChild, g.RightChild)
}

// WithChildren implements the Expression interface.
func (g *GetFormat) WithChildren(children ...sql.Expression) (sql.Expression, error) {
if len(children) != 2 {
return nil, sql.ErrInvalidChildrenNumber.New(g, len(children), 2)
}
return NewGetFormat(children[0], children[1]), nil
}

var formats = map[string]map[string]string{
"date": {
"usa": "%m.%d.%Y",
"jis": "%Y-%m-%d",
"iso": "%Y-%m-%d",
"eur": "%d.%m.%Y",
"internal": "%Y%m%d",
},
"datetime": {
"usa": "%Y-%m-%d %H.%i.%s",
"jis": "%Y-%m-%d %H:%i:%s",
"iso": "%Y-%m-%d %H:%i:%s",
"eur": "%Y-%m-%d %H.%i.%s",
"internal": "%Y%m%d%H%i%s",
},
"time": {
"usa": "%h:%i:%s %p",
"jis": "%H:%i:%s",
"iso": "%H:%i:%s",
"eur": "%H.%i.%s",
"internal": "%H%i%s",
},
"timestamp": {
"usa": "%Y-%m-%d %H.%i.%s",
"jis": "%Y-%m-%d %H:%i:%s",
"iso": "%Y-%m-%d %H:%i:%s",
"eur": "%Y-%m-%d %H.%i.%s",
"internal": "%Y%m%d%H%i%s",
},
}

// Eval implements the Expression interface.
func (g *GetFormat) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) {
if g.LeftChild == nil || g.RightChild == nil {
return nil, nil
}

left, err := g.LeftChild.Eval(ctx, row)
if err != nil {
return nil, err
}
right, err := g.RightChild.Eval(ctx, row)
if err != nil {
return nil, err
}
if left == nil || right == nil {
return nil, nil
}

timeType, ok := left.(string)
if !ok {
return nil, nil
}
region, ok := right.(string)
if !ok {
return nil, nil
}
format, ok := formats[strings.ToLower(timeType)][strings.ToLower(region)]
if !ok {
return nil, nil
}
return format, nil
}
170 changes: 170 additions & 0 deletions sql/expression/function/get_format_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
// Copyright 2020-2021 Dolthub, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package function

import (
"fmt"
"testing"

"github.com/stretchr/testify/require"

"github.com/dolthub/go-mysql-server/sql"
"github.com/dolthub/go-mysql-server/sql/expression"
"github.com/dolthub/go-mysql-server/sql/types"
)

func TestGetFormat(t *testing.T) {
testCases := []struct {
left sql.Expression
right sql.Expression
exp interface{}
}{
{
left: expression.NewLiteral(nil, types.Null),
right: expression.NewLiteral(nil, types.Null),
exp: nil,
},
{
left: expression.NewLiteral("date", types.Text),
right: expression.NewLiteral(nil, types.Null),
exp: nil,
},
{
left: expression.NewLiteral(nil, types.Null),
right: expression.NewLiteral("us", types.Text),
exp: nil,
},
{
left: expression.NewLiteral("dATeTiMe", types.Text),
right: expression.NewLiteral("InTeRnAl", types.Text),
exp: "%Y%m%d%H%i%s",
},

{
left: expression.NewLiteral("date", types.Text),
right: expression.NewLiteral("usa", types.Text),
exp: "%m.%d.%Y",
},
{
left: expression.NewLiteral("date", types.Text),
right: expression.NewLiteral("jis", types.Text),
exp: "%Y-%m-%d",
},
{
left: expression.NewLiteral("date", types.Text),
right: expression.NewLiteral("iso", types.Text),
exp: "%Y-%m-%d",
},
{
left: expression.NewLiteral("date", types.Text),
right: expression.NewLiteral("eur", types.Text),
exp: "%d.%m.%Y",
},
{
left: expression.NewLiteral("date", types.Text),
right: expression.NewLiteral("internal", types.Text),
exp: "%Y%m%d",
},

{
left: expression.NewLiteral("datetime", types.Text),
right: expression.NewLiteral("usa", types.Text),
exp: "%Y-%m-%d %H.%i.%s",
},
{
left: expression.NewLiteral("datetime", types.Text),
right: expression.NewLiteral("jis", types.Text),
exp: "%Y-%m-%d %H:%i:%s",
},
{
left: expression.NewLiteral("datetime", types.Text),
right: expression.NewLiteral("iso", types.Text),
exp: "%Y-%m-%d %H:%i:%s",
},
{
left: expression.NewLiteral("datetime", types.Text),
right: expression.NewLiteral("eur", types.Text),
exp: "%Y-%m-%d %H.%i.%s",
},
{
left: expression.NewLiteral("datetime", types.Text),
right: expression.NewLiteral("internal", types.Text),
exp: "%Y%m%d%H%i%s",
},

{
left: expression.NewLiteral("time", types.Text),
right: expression.NewLiteral("usa", types.Text),
exp: "%h:%i:%s %p",
},
{
left: expression.NewLiteral("time", types.Text),
right: expression.NewLiteral("jis", types.Text),
exp: "%H:%i:%s",
},
{
left: expression.NewLiteral("time", types.Text),
right: expression.NewLiteral("iso", types.Text),
exp: "%H:%i:%s",
},
{
left: expression.NewLiteral("time", types.Text),
right: expression.NewLiteral("eur", types.Text),
exp: "%H.%i.%s",
},
{
left: expression.NewLiteral("time", types.Text),
right: expression.NewLiteral("internal", types.Text),
exp: "%H%i%s",
},

{
left: expression.NewLiteral("timestamp", types.Text),
right: expression.NewLiteral("usa", types.Text),
exp: "%Y-%m-%d %H.%i.%s",
},
{
left: expression.NewLiteral("timestamp", types.Text),
right: expression.NewLiteral("jis", types.Text),
exp: "%Y-%m-%d %H:%i:%s",
},
{
left: expression.NewLiteral("timestamp", types.Text),
right: expression.NewLiteral("iso", types.Text),
exp: "%Y-%m-%d %H:%i:%s",
},
{
left: expression.NewLiteral("timestamp", types.Text),
right: expression.NewLiteral("eur", types.Text),
exp: "%Y-%m-%d %H.%i.%s",
},
{
left: expression.NewLiteral("timestamp", types.Text),
right: expression.NewLiteral("internal", types.Text),
exp: "%Y%m%d%H%i%s",
},
}

for _, tt := range testCases {
t.Run(fmt.Sprintf("%s %s", tt.left.String(), tt.right.String()), func(t *testing.T) {
require := require.New(t)
ctx := sql.NewEmptyContext()
f := NewGetFormat(tt.left, tt.right)
out, err := f.Eval(ctx, nil)
require.NoError(err)
require.Equal(tt.exp, out)
})
}
}
1 change: 1 addition & 0 deletions sql/expression/function/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ var BuiltIns = []sql.Function{
sql.Function1{Name: "from_base64", Fn: NewFromBase64},
sql.Function1{Name: "from_days", Fn: NewFromDays},
sql.Function1{Name: "from_unixtime", Fn: NewFromUnixtime},
sql.Function2{Name: "get_format", Fn: NewGetFormat},
sql.FunctionN{Name: "greatest", Fn: NewGreatest},
sql.Function0{Name: "group_concat", Fn: aggregation.NewEmptyGroupConcat},
sql.Function2{Name: "gtid_subtract", Fn: NewGtidSubtract},
Expand Down

0 comments on commit a4ca0b0

Please sign in to comment.