Skip to content

Commit

Permalink
WIP: Implement temporal comparisons
Browse files Browse the repository at this point in the history
Signed-off-by: Dirkjan Bussink <d.bussink@gmail.com>
  • Loading branch information
dbussink committed Feb 12, 2025
1 parent c47f1bd commit 1742096
Show file tree
Hide file tree
Showing 5 changed files with 255 additions and 32 deletions.
24 changes: 24 additions & 0 deletions go/vt/vtgate/evalengine/compiler_asm.go
Original file line number Diff line number Diff line change
Expand Up @@ -2670,6 +2670,30 @@ func (asm *assembler) Fn_MULTICMP_u(args int, lessThan bool) {
}, "FN MULTICMP UINT64(SP-%d)...UINT64(SP-1)", args)
}

func (asm *assembler) Fn_MULTICMP_temporal(args int, lessThan bool) {
asm.adjustStack(-(args - 1))

asm.emit(func(env *ExpressionEnv) int {
var x *evalTemporal
for sp := env.vm.sp - args; sp < env.vm.sp; sp++ {
if env.vm.stack[sp] == nil {
continue
}
if x == nil {
x = env.vm.stack[sp].(*evalTemporal)
continue
}
y := env.vm.stack[sp].(*evalTemporal)
if lessThan == (y.compare(x) < 0) {
x = y
}
}
env.vm.stack[env.vm.sp-args] = x
env.vm.sp -= args - 1
return 1
}, "FN MULTICMP TEMPORAL(SP-%d)...TEMPORAL(SP-1)", args)
}

func (asm *assembler) Fn_REPEAT(base sqltypes.Type, fallback sqltypes.Type) {
asm.adjustStack(-1)

Expand Down
94 changes: 86 additions & 8 deletions go/vt/vtgate/evalengine/eval_temporal.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func (e *evalTemporal) ToRawBytes() []byte {
switch e.t {
case sqltypes.Date:
return e.dt.Date.Format()
case sqltypes.Datetime:
case sqltypes.Datetime, sqltypes.Timestamp:
return e.dt.Format(e.prec)
case sqltypes.Time:
return e.dt.Time.Format(e.prec)
Expand All @@ -54,7 +54,7 @@ func (e *evalTemporal) toInt64() int64 {
switch e.SQLType() {
case sqltypes.Date:
return e.dt.Date.FormatInt64()
case sqltypes.Datetime:
case sqltypes.Datetime, sqltypes.Timestamp:
return e.dt.FormatInt64()
case sqltypes.Time:
return e.dt.Time.FormatInt64()
Expand All @@ -67,7 +67,7 @@ func (e *evalTemporal) toFloat() float64 {
switch e.SQLType() {
case sqltypes.Date:
return float64(e.dt.Date.FormatInt64())
case sqltypes.Datetime:
case sqltypes.Datetime, sqltypes.Timestamp:
return e.dt.FormatFloat64()
case sqltypes.Time:
return e.dt.Time.FormatFloat64()
Expand All @@ -80,7 +80,7 @@ func (e *evalTemporal) toDecimal() decimal.Decimal {
switch e.SQLType() {
case sqltypes.Date:
return decimal.NewFromInt(e.dt.Date.FormatInt64())
case sqltypes.Datetime:
case sqltypes.Datetime, sqltypes.Timestamp:
return e.dt.FormatDecimal()
case sqltypes.Time:
return e.dt.Time.FormatDecimal()
Expand All @@ -93,7 +93,7 @@ func (e *evalTemporal) toJSON() *evalJSON {
switch e.SQLType() {
case sqltypes.Date:
return json.NewDate(hack.String(e.dt.Date.Format()))
case sqltypes.Datetime:
case sqltypes.Datetime, sqltypes.Timestamp:
return json.NewDateTime(hack.String(e.dt.Format(datetime.DefaultPrecision)))
case sqltypes.Time:
return json.NewTime(hack.String(e.dt.Time.Format(datetime.DefaultPrecision)))
Expand All @@ -103,8 +103,11 @@ func (e *evalTemporal) toJSON() *evalJSON {
}

func (e *evalTemporal) toDateTime(l int, now time.Time) *evalTemporal {
if l == -1 {
l = int(e.prec)
}
switch e.SQLType() {
case sqltypes.Datetime, sqltypes.Date:
case sqltypes.Datetime, sqltypes.Date, sqltypes.Timestamp:
return &evalTemporal{t: sqltypes.Datetime, dt: e.dt.Round(l), prec: uint8(l)}
case sqltypes.Time:
return &evalTemporal{t: sqltypes.Datetime, dt: e.dt.Time.Round(l).ToDateTime(now), prec: uint8(l)}
Expand All @@ -113,9 +116,26 @@ func (e *evalTemporal) toDateTime(l int, now time.Time) *evalTemporal {
}
}

func (e *evalTemporal) toTimestamp(l int, now time.Time) *evalTemporal {
if l == -1 {
l = int(e.prec)
}
switch e.SQLType() {
case sqltypes.Datetime, sqltypes.Date, sqltypes.Timestamp:
return &evalTemporal{t: sqltypes.Timestamp, dt: e.dt.Round(l), prec: uint8(l)}
case sqltypes.Time:
return &evalTemporal{t: sqltypes.Timestamp, dt: e.dt.Time.Round(l).ToDateTime(now), prec: uint8(l)}
default:
panic("unreachable")
}
}

func (e *evalTemporal) toTime(l int) *evalTemporal {
if l == -1 {
l = int(e.prec)
}
switch e.SQLType() {
case sqltypes.Datetime:
case sqltypes.Datetime, sqltypes.Timestamp:
dt := datetime.DateTime{Time: e.dt.Time.Round(l)}
return &evalTemporal{t: sqltypes.Time, dt: dt, prec: uint8(l)}
case sqltypes.Date:
Expand All @@ -130,7 +150,7 @@ func (e *evalTemporal) toTime(l int) *evalTemporal {

func (e *evalTemporal) toDate(now time.Time) *evalTemporal {
switch e.SQLType() {
case sqltypes.Datetime:
case sqltypes.Datetime, sqltypes.Timestamp:
dt := datetime.DateTime{Date: e.dt.Date}
return &evalTemporal{t: sqltypes.Date, dt: dt}
case sqltypes.Date:
Expand All @@ -148,6 +168,10 @@ func (e *evalTemporal) isZero() bool {
return e.dt.IsZero()
}

func (e *evalTemporal) compare(other *evalTemporal) int {
return e.dt.Compare(other.dt)
}

func (e *evalTemporal) addInterval(interval *datetime.Interval, coll collations.ID, now time.Time) eval {
var tmp *evalTemporal
var ok bool
Expand Down Expand Up @@ -179,6 +203,13 @@ func newEvalDateTime(dt datetime.DateTime, l int, allowZero bool) *evalTemporal
return &evalTemporal{t: sqltypes.Datetime, dt: dt.Round(l), prec: uint8(l)}
}

func newEvalTimestamp(dt datetime.DateTime, l int, allowZero bool) *evalTemporal {
if !allowZero && dt.IsZero() {
return nil
}
return &evalTemporal{t: sqltypes.Timestamp, dt: dt.Round(l), prec: uint8(l)}
}

func newEvalDate(d datetime.Date, allowZero bool) *evalTemporal {
if !allowZero && d.IsZero() {
return nil
Expand Down Expand Up @@ -387,6 +418,53 @@ func evalToDateTime(e eval, l int, now time.Time, allowZero bool) *evalTemporal
return nil
}

func evalToTimestamp(e eval, l int, now time.Time, allowZero bool) *evalTemporal {
switch e := e.(type) {
case *evalTemporal:
return e.toTimestamp(precision(l, int(e.prec)), now)
case *evalBytes:
if t, l, _ := datetime.ParseDateTime(e.string(), l); !t.IsZero() {
return newEvalTimestamp(t, l, allowZero)
}
if d, _ := datetime.ParseDate(e.string()); !d.IsZero() {
return newEvalTimestamp(datetime.DateTime{Date: d}, precision(l, 0), allowZero)
}
case *evalInt64:
if t, ok := datetime.ParseDateTimeInt64(e.i); ok {
return newEvalTimestamp(t, precision(l, 0), allowZero)
}
if d, ok := datetime.ParseDateInt64(e.i); ok {
return newEvalTimestamp(datetime.DateTime{Date: d}, precision(l, 0), allowZero)
}
case *evalUint64:
if t, ok := datetime.ParseDateTimeInt64(int64(e.u)); ok {
return newEvalTimestamp(t, precision(l, 0), allowZero)
}
if d, ok := datetime.ParseDateInt64(int64(e.u)); ok {
return newEvalTimestamp(datetime.DateTime{Date: d}, precision(l, 0), allowZero)
}
case *evalFloat:
if t, l, ok := datetime.ParseDateTimeFloat(e.f, l); ok {
return newEvalTimestamp(t, l, allowZero)
}
if d, ok := datetime.ParseDateFloat(e.f); ok {
return newEvalTimestamp(datetime.DateTime{Date: d}, precision(l, 0), allowZero)
}
case *evalDecimal:
if t, l, ok := datetime.ParseDateTimeDecimal(e.dec, e.length, l); ok {
return newEvalTimestamp(t, l, allowZero)
}
if d, ok := datetime.ParseDateDecimal(e.dec); ok {
return newEvalTimestamp(datetime.DateTime{Date: d}, precision(l, 0), allowZero)
}
case *evalJSON:
if dt, ok := e.DateTime(); ok {
return newEvalTimestamp(dt, precision(l, datetime.DefaultPrecision), allowZero)
}
}
return nil
}

func evalToDate(e eval, now time.Time, allowZero bool) *evalTemporal {
switch e := e.(type) {
case *evalTemporal:
Expand Down
Loading

0 comments on commit 1742096

Please sign in to comment.