Skip to content

Commit f6c04d7

Browse files
authored
refactor and implement TimestampAdd() (#2943)
1 parent 814abcc commit f6c04d7

File tree

7 files changed

+1093
-1079
lines changed

7 files changed

+1093
-1079
lines changed

enginetest/queries/queries.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6439,6 +6439,10 @@ SELECT * FROM cte WHERE d = 2;`,
64396439
Query: "SELECT FROM_BASE64('YmFy')",
64406440
Expected: []sql.Row{{[]byte("bar")}},
64416441
},
6442+
{
6443+
Query: "SELECT TIMESTAMPADD(DAY, 1, '2018-05-02')",
6444+
Expected: []sql.Row{{"2018-05-03"}},
6445+
},
64426446
{
64436447
Query: "SELECT DATE_ADD('2018-05-02', INTERVAL 1 day)",
64446448
Expected: []sql.Row{{"2018-05-03"}},

sql/expression/function/date.go

Lines changed: 0 additions & 294 deletions
Original file line numberDiff line numberDiff line change
@@ -29,300 +29,6 @@ import (
2929
"github.com/dolthub/go-mysql-server/sql/types"
3030
)
3131

32-
// NewAddDate returns a new function expression, or an error if one couldn't be created. The ADDDATE
33-
// function is a synonym for DATE_ADD, with the one exception that if the second argument is NOT an
34-
// explicitly declared interval, then the value is used and the interval period is assumed to be DAY.
35-
// In either case, this function will actually return a *DateAdd struct.
36-
func NewAddDate(args ...sql.Expression) (sql.Expression, error) {
37-
if len(args) != 2 {
38-
return nil, sql.ErrInvalidArgumentNumber.New("ADDDATE", 2, len(args))
39-
}
40-
41-
// If the interval is explicitly specified, then we simply pass it all to DateSub
42-
i, ok := args[1].(*expression.Interval)
43-
if ok {
44-
return &DateAdd{args[0], i}, nil
45-
}
46-
47-
// Otherwise, the interval period is assumed to be DAY
48-
i = expression.NewInterval(args[1], "DAY")
49-
return &DateAdd{args[0], i}, nil
50-
}
51-
52-
// DateAdd adds an interval to a date.
53-
type DateAdd struct {
54-
Date sql.Expression
55-
Interval *expression.Interval
56-
}
57-
58-
var _ sql.FunctionExpression = (*DateAdd)(nil)
59-
var _ sql.CollationCoercible = (*DateAdd)(nil)
60-
61-
// NewDateAdd creates a new date add function.
62-
func NewDateAdd(args ...sql.Expression) (sql.Expression, error) {
63-
if len(args) != 2 {
64-
return nil, sql.ErrInvalidArgumentNumber.New("DATE_ADD", 2, len(args))
65-
}
66-
67-
i, ok := args[1].(*expression.Interval)
68-
if !ok {
69-
return nil, fmt.Errorf("DATE_ADD expects an interval as second parameter")
70-
}
71-
72-
return &DateAdd{args[0], i}, nil
73-
}
74-
75-
// FunctionName implements sql.FunctionExpression
76-
func (d *DateAdd) FunctionName() string {
77-
return "date_add"
78-
}
79-
80-
// Description implements sql.FunctionExpression
81-
func (d *DateAdd) Description() string {
82-
return "adds the interval to the given date."
83-
}
84-
85-
// Children implements the sql.Expression interface.
86-
func (d *DateAdd) Children() []sql.Expression {
87-
return []sql.Expression{d.Date, d.Interval}
88-
}
89-
90-
// Resolved implements the sql.Expression interface.
91-
func (d *DateAdd) Resolved() bool {
92-
return d.Date.Resolved() && d.Interval.Resolved()
93-
}
94-
95-
// IsNullable implements the sql.Expression interface.
96-
func (d *DateAdd) IsNullable() bool {
97-
return true
98-
}
99-
100-
// Type implements the sql.Expression interface.
101-
func (d *DateAdd) Type() sql.Type {
102-
sqlType := dateOffsetType(d.Date, d.Interval)
103-
return sqlType
104-
}
105-
106-
// CollationCoercibility implements the interface sql.CollationCoercible.
107-
func (*DateAdd) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) {
108-
return ctx.GetCollation(), 4
109-
}
110-
111-
// WithChildren implements the Expression interface.
112-
func (d *DateAdd) WithChildren(children ...sql.Expression) (sql.Expression, error) {
113-
return NewDateAdd(children...)
114-
}
115-
116-
// Eval implements the sql.Expression interface.
117-
func (d *DateAdd) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) {
118-
date, err := d.Date.Eval(ctx, row)
119-
if err != nil {
120-
return nil, err
121-
}
122-
123-
if date == nil {
124-
return nil, nil
125-
}
126-
127-
delta, err := d.Interval.EvalDelta(ctx, row)
128-
if err != nil {
129-
return nil, err
130-
}
131-
132-
if delta == nil {
133-
return nil, nil
134-
}
135-
136-
var dateVal interface{}
137-
dateVal, _, err = types.DatetimeMaxPrecision.Convert(ctx, date)
138-
if err != nil {
139-
ctx.Warn(1292, err.Error())
140-
return nil, nil
141-
}
142-
143-
// return appropriate type
144-
res := types.ValidateTime(delta.Add(dateVal.(time.Time)))
145-
if res == nil {
146-
return nil, nil
147-
}
148-
149-
resType := d.Type()
150-
if types.IsText(resType) {
151-
// If the input is a properly formatted date/datetime string, the output should also be a string
152-
if dateStr, isStr := date.(string); isStr {
153-
if res.(time.Time).Nanosecond() > 0 {
154-
return res.(time.Time).Format(sql.DatetimeLayoutNoTrim), nil
155-
}
156-
if isHmsInterval(d.Interval) {
157-
return res.(time.Time).Format(sql.TimestampDatetimeLayout), nil
158-
}
159-
for _, layout := range types.DateOnlyLayouts {
160-
if _, pErr := time.Parse(layout, dateStr); pErr != nil {
161-
continue
162-
}
163-
return res.(time.Time).Format(sql.DateLayout), nil
164-
}
165-
}
166-
}
167-
168-
ret, _, err := resType.Convert(ctx, res)
169-
if err != nil {
170-
return nil, err
171-
}
172-
return ret, nil
173-
}
174-
175-
func (d *DateAdd) String() string {
176-
return fmt.Sprintf("%s(%s,%s)", d.FunctionName(), d.Date, d.Interval)
177-
}
178-
179-
// NewSubDate returns a new function expression, or an error if one couldn't be created. The SUBDATE
180-
// function is a synonym for DATE_SUB, with the one exception that if the second argument is NOT an
181-
// explicitly declared interval, then the value is used and the interval period is assumed to be DAY.
182-
// In either case, this function will actually return a *DateSub struct.
183-
func NewSubDate(args ...sql.Expression) (sql.Expression, error) {
184-
if len(args) != 2 {
185-
return nil, sql.ErrInvalidArgumentNumber.New("SUBDATE", 2, len(args))
186-
}
187-
188-
// If the interval is explicitly specified, then we simply pass it all to DateSub
189-
i, ok := args[1].(*expression.Interval)
190-
if ok {
191-
return &DateSub{args[0], i}, nil
192-
}
193-
194-
// Otherwise, the interval period is assumed to be DAY
195-
i = expression.NewInterval(args[1], "DAY")
196-
return &DateSub{args[0], i}, nil
197-
}
198-
199-
// DateSub subtracts an interval from a date.
200-
type DateSub struct {
201-
Date sql.Expression
202-
Interval *expression.Interval
203-
}
204-
205-
var _ sql.FunctionExpression = (*DateSub)(nil)
206-
var _ sql.CollationCoercible = (*DateSub)(nil)
207-
208-
// NewDateSub creates a new date add function.
209-
func NewDateSub(args ...sql.Expression) (sql.Expression, error) {
210-
if len(args) != 2 {
211-
return nil, sql.ErrInvalidArgumentNumber.New("DATE_SUB", 2, len(args))
212-
}
213-
214-
i, ok := args[1].(*expression.Interval)
215-
if !ok {
216-
return nil, fmt.Errorf("DATE_SUB expects an interval as second parameter")
217-
}
218-
219-
return &DateSub{args[0], i}, nil
220-
}
221-
222-
// FunctionName implements sql.FunctionExpression
223-
func (d *DateSub) FunctionName() string {
224-
return "date_sub"
225-
}
226-
227-
// Description implements sql.FunctionExpression
228-
func (d *DateSub) Description() string {
229-
return "subtracts the interval from the given date."
230-
}
231-
232-
// Children implements the sql.Expression interface.
233-
func (d *DateSub) Children() []sql.Expression {
234-
return []sql.Expression{d.Date, d.Interval}
235-
}
236-
237-
// Resolved implements the sql.Expression interface.
238-
func (d *DateSub) Resolved() bool {
239-
return d.Date.Resolved() && d.Interval.Resolved()
240-
}
241-
242-
// IsNullable implements the sql.Expression interface.
243-
func (d *DateSub) IsNullable() bool {
244-
return true
245-
}
246-
247-
// Type implements the sql.Expression interface.
248-
func (d *DateSub) Type() sql.Type {
249-
sqlType := dateOffsetType(d.Date, d.Interval)
250-
return sqlType
251-
}
252-
253-
// CollationCoercibility implements the interface sql.CollationCoercible.
254-
func (*DateSub) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) {
255-
return ctx.GetCollation(), 4
256-
}
257-
258-
// WithChildren implements the Expression interface.
259-
func (d *DateSub) WithChildren(children ...sql.Expression) (sql.Expression, error) {
260-
return NewDateSub(children...)
261-
}
262-
263-
// Eval implements the sql.Expression interface.
264-
func (d *DateSub) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) {
265-
date, err := d.Date.Eval(ctx, row)
266-
if err != nil {
267-
return nil, err
268-
}
269-
270-
if date == nil {
271-
return nil, nil
272-
}
273-
274-
delta, err := d.Interval.EvalDelta(ctx, row)
275-
if err != nil {
276-
return nil, err
277-
}
278-
279-
if delta == nil {
280-
return nil, nil
281-
}
282-
283-
var dateVal interface{}
284-
dateVal, _, err = types.DatetimeMaxPrecision.Convert(ctx, date)
285-
if err != nil {
286-
ctx.Warn(1292, err.Error())
287-
return nil, nil
288-
}
289-
290-
// return appropriate type
291-
res := types.ValidateTime(delta.Sub(dateVal.(time.Time)))
292-
if res == nil {
293-
return nil, nil
294-
}
295-
296-
resType := d.Type()
297-
if types.IsText(resType) {
298-
// If the input is a properly formatted date/datetime string, the output should also be a string
299-
if dateStr, isStr := date.(string); isStr {
300-
if res.(time.Time).Nanosecond() > 0 {
301-
return res.(time.Time).Format(sql.DatetimeLayoutNoTrim), nil
302-
}
303-
if isHmsInterval(d.Interval) {
304-
return res.(time.Time).Format(sql.TimestampDatetimeLayout), nil
305-
}
306-
for _, layout := range types.DateOnlyLayouts {
307-
if _, pErr := time.Parse(layout, dateStr); pErr != nil {
308-
continue
309-
}
310-
return res.(time.Time).Format(sql.DateLayout), nil
311-
}
312-
}
313-
}
314-
315-
ret, _, err := resType.Convert(ctx, res)
316-
if err != nil {
317-
return nil, err
318-
}
319-
return ret, nil
320-
}
321-
322-
func (d *DateSub) String() string {
323-
return fmt.Sprintf("%s(%s,%s)", d.FunctionName(), d.Date, d.Interval)
324-
}
325-
32632
// DatetimeConversion is a shorthand function for CONVERT(expr, DATETIME)
32733
type DatetimeConversion struct {
32834
Date sql.Expression

0 commit comments

Comments
 (0)