diff --git a/expression/builtin_compare.go b/expression/builtin_compare.go index 5c9367004ec89..f09adf9b4c0b9 100644 --- a/expression/builtin_compare.go +++ b/expression/builtin_compare.go @@ -22,6 +22,7 @@ import ( "github.com/pingcap/parser/opcode" "github.com/pingcap/parser/terror" "github.com/pingcap/tidb/sessionctx" + "github.com/pingcap/tidb/sessionctx/stmtctx" "github.com/pingcap/tidb/types" "github.com/pingcap/tidb/types/json" "github.com/pingcap/tidb/util/chunk" @@ -403,10 +404,37 @@ func ResolveType4Between(args [3]Expression) types.EvalType { return cmpTp } +// GLCmpStringMode represents Greatest/Least interal string comparison mode +type GLCmpStringMode uint8 + +const ( + // GLCmpStringDirectly Greatest and Least function compares string directly + GLCmpStringDirectly GLCmpStringMode = iota + // GLCmpStringAsDate Greatest/Least function compares string as 'yyyy-mm-dd' format + GLCmpStringAsDate + // GLCmpStringAsDatetime Greatest/Least function compares string as 'yyyy-mm-dd hh:mm:ss' format + GLCmpStringAsDatetime +) + +// GLRetTimeType represents Greatest/Least return time type +type GLRetTimeType uint8 + +const ( + // GLRetNoneTemporal Greatest/Least function returns non temporal time + GLRetNoneTemporal GLRetTimeType = iota + // GLRetDate Greatest/Least function returns date type, 'yyyy-mm-dd' + GLRetDate + // GLRetDatetime Greatest/Least function returns datetime type, 'yyyy-mm-dd hh:mm:ss' + GLRetDatetime +) + // resolveType4Extremum gets compare type for GREATEST and LEAST and BETWEEN (mainly for datetime). +<<<<<<< HEAD func resolveType4Extremum(args []Expression) types.EvalType { +======= +func resolveType4Extremum(args []Expression) (_ types.EvalType, fieldTimeType GLRetTimeType, cmpStringMode GLCmpStringMode) { +>>>>>>> a8ce4af9f... expression: Fix incorrect behavior of greatest/least when mixed temporal type with others (#31037) aggType := aggregateType(args) - var temporalItem *types.FieldType if aggType.EvalType().IsStringKind() { for i := range args { @@ -419,12 +447,31 @@ func resolveType4Extremum(args []Expression) types.EvalType { } } +<<<<<<< HEAD if !types.IsTypeTemporal(aggType.Tp) && temporalItem != nil { aggType.Tp = temporalItem.Tp } // TODO: String charset, collation checking are needed. } return aggType.EvalType() +======= + if !types.IsTypeTemporal(aggType.Tp) && temporalItem != nil && types.IsTemporalWithDate(temporalItem.Tp) { + if temporalItem.Tp == mysql.TypeDate { + cmpStringMode = GLCmpStringAsDate + } else { + cmpStringMode = GLCmpStringAsDatetime + } + } + // TODO: String charset, collation checking are needed. + } + var timeType = GLRetNoneTemporal + if aggType.Tp == mysql.TypeDate { + timeType = GLRetDate + } else if aggType.Tp == mysql.TypeDatetime || aggType.Tp == mysql.TypeTimestamp { + timeType = GLRetDatetime + } + return aggType.EvalType(), timeType, cmpStringMode +>>>>>>> a8ce4af9f... expression: Fix incorrect behavior of greatest/least when mixed temporal type with others (#31037) } // unsupportedJSONComparison reports warnings while there is a JSON type in least/greatest function's arguments @@ -446,6 +493,7 @@ func (c *greatestFunctionClass) getFunction(ctx sessionctx.Context, args []Expre if err = c.verifyArgs(args); err != nil { return nil, err } +<<<<<<< HEAD tp := resolveType4Extremum(args) cmpAsDatetime := false if tp == types.ETDatetime || tp == types.ETTimestamp { @@ -453,22 +501,34 @@ func (c *greatestFunctionClass) getFunction(ctx sessionctx.Context, args []Expre tp = types.ETString } else if tp == types.ETDuration { tp = types.ETString +======= + tp, fieldTimeType, cmpStringMode := resolveType4Extremum(args) + argTp := tp + if cmpStringMode != GLCmpStringDirectly { + // Args are temporal and string mixed, we cast all args as string and parse it to temporal mannualy to compare. + argTp = types.ETString +>>>>>>> a8ce4af9f... expression: Fix incorrect behavior of greatest/least when mixed temporal type with others (#31037) } else if tp == types.ETJson { unsupportedJSONComparison(ctx, args) + argTp = types.ETString tp = types.ETString } argTps := make([]types.EvalType, len(args)) for i := range args { - argTps[i] = tp + argTps[i] = argTp } bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, tp, argTps...) if err != nil { return nil, err } +<<<<<<< HEAD if cmpAsDatetime { tp = types.ETDatetime } switch tp { +======= + switch argTp { +>>>>>>> a8ce4af9f... expression: Fix incorrect behavior of greatest/least when mixed temporal type with others (#31037) case types.ETInt: sig = &builtinGreatestIntSig{bf} sig.setPbCode(tipb.ScalarFuncSig_GreatestInt) @@ -479,11 +539,32 @@ func (c *greatestFunctionClass) getFunction(ctx sessionctx.Context, args []Expre sig = &builtinGreatestDecimalSig{bf} sig.setPbCode(tipb.ScalarFuncSig_GreatestDecimal) case types.ETString: +<<<<<<< HEAD sig = &builtinGreatestStringSig{bf} sig.setPbCode(tipb.ScalarFuncSig_GreatestString) +======= + if cmpStringMode == GLCmpStringAsDate { + sig = &builtinGreatestCmpStringAsTimeSig{bf, true} + sig.setPbCode(tipb.ScalarFuncSig_GreatestCmpStringAsDate) + } else if cmpStringMode == GLCmpStringAsDatetime { + sig = &builtinGreatestCmpStringAsTimeSig{bf, false} + sig.setPbCode(tipb.ScalarFuncSig_GreatestCmpStringAsTime) + } else { + sig = &builtinGreatestStringSig{bf} + sig.setPbCode(tipb.ScalarFuncSig_GreatestString) + } + case types.ETDuration: + sig = &builtinGreatestDurationSig{bf} + sig.setPbCode(tipb.ScalarFuncSig_GreatestDuration) +>>>>>>> a8ce4af9f... expression: Fix incorrect behavior of greatest/least when mixed temporal type with others (#31037) case types.ETDatetime, types.ETTimestamp: - sig = &builtinGreatestTimeSig{bf} - sig.setPbCode(tipb.ScalarFuncSig_GreatestTime) + if fieldTimeType == GLRetDate { + sig = &builtinGreatestTimeSig{bf, true} + sig.setPbCode(tipb.ScalarFuncSig_GreatestDate) + } else { + sig = &builtinGreatestTimeSig{bf, false} + sig.setPbCode(tipb.ScalarFuncSig_GreatestTime) + } } sig.getRetTp().Flen, sig.getRetTp().Decimal = fixFlenAndDecimalForGreatestAndLeast(args) return sig, nil @@ -624,11 +705,13 @@ func (b *builtinGreatestStringSig) evalString(row chunk.Row) (max string, isNull type builtinGreatestTimeSig struct { baseBuiltinFunc + cmpAsDate bool } func (b *builtinGreatestTimeSig) Clone() builtinFunc { newSig := &builtinGreatestTimeSig{} newSig.cloneFrom(&b.baseBuiltinFunc) + newSig.cmpAsDate = b.cmpAsDate return newSig } @@ -645,18 +728,15 @@ func (b *builtinGreatestTimeSig) evalString(row chunk.Row) (res string, isNull b if isNull || err != nil { return "", true, err } - t, err := types.ParseDatetime(sc, v) + v, err = doTimeConversionForGL(b.cmpAsDate, b.ctx, sc, v) if err != nil { - if err = handleInvalidTimeError(b.ctx, err); err != nil { - return v, true, err - } - } else { - v = t.String() + return v, true, err } // In MySQL, if the compare result is zero, than we will try to use the string comparison result if i == 0 || strings.Compare(v, strRes) > 0 { strRes = v } +<<<<<<< HEAD if i == 0 || t.Compare(timeRes) > 0 { timeRes = t } @@ -665,6 +745,86 @@ func (b *builtinGreatestTimeSig) evalString(row chunk.Row) (res string, isNull b res = strRes } else { res = timeRes.String() +======= + } + return strRes, false, nil +} + +func doTimeConversionForGL(cmpAsDate bool, ctx sessionctx.Context, sc *stmtctx.StatementContext, strVal string) (string, error) { + var t types.Time + var err error + if cmpAsDate { + t, err = types.ParseDate(sc, strVal) + if err == nil { + t, err = t.Convert(sc, mysql.TypeDate) + } + } else { + t, err = types.ParseDatetime(sc, strVal) + if err == nil { + t, err = t.Convert(sc, mysql.TypeDatetime) + } + } + if err != nil { + if err = handleInvalidTimeError(ctx, err); err != nil { + return "", err + } + } else { + strVal = t.String() + } + return strVal, nil +} + +type builtinGreatestTimeSig struct { + baseBuiltinFunc + cmpAsDate bool +} + +func (b *builtinGreatestTimeSig) Clone() builtinFunc { + newSig := &builtinGreatestTimeSig{} + newSig.cloneFrom(&b.baseBuiltinFunc) + newSig.cmpAsDate = b.cmpAsDate + return newSig +} + +func (b *builtinGreatestTimeSig) evalTime(row chunk.Row) (res types.Time, isNull bool, err error) { + for i := 0; i < len(b.args); i++ { + v, isNull, err := b.args[i].EvalTime(b.ctx, row) + if isNull || err != nil { + return types.ZeroTime, true, err + } + if i == 0 || v.Compare(res) > 0 { + res = v + } + } + // Convert ETType Time value to MySQL actual type, distinguish date and datetime + sc := b.ctx.GetSessionVars().StmtCtx + resTimeTp := getAccurateTimeTypeForGLRet(b.cmpAsDate) + if res, err = res.Convert(sc, resTimeTp); err != nil { + return types.ZeroTime, true, handleInvalidTimeError(b.ctx, err) + } + return res, false, nil +} + +type builtinGreatestDurationSig struct { + baseBuiltinFunc +} + +func (b *builtinGreatestDurationSig) Clone() builtinFunc { + newSig := &builtinGreatestDurationSig{} + newSig.cloneFrom(&b.baseBuiltinFunc) + return newSig +} + +func (b *builtinGreatestDurationSig) evalDuration(row chunk.Row) (res types.Duration, isNull bool, err error) { + for i := 0; i < len(b.args); i++ { + v, isNull, err := b.args[i].EvalDuration(b.ctx, row) + if isNull || err != nil { + return types.Duration{}, true, err + } + if i == 0 || v.Compare(res) > 0 { + res = v + } +>>>>>>> a8ce4af9f... expression: Fix incorrect behavior of greatest/least when mixed temporal type with others (#31037) } return res, false, nil } @@ -677,6 +837,7 @@ func (c *leastFunctionClass) getFunction(ctx sessionctx.Context, args []Expressi if err = c.verifyArgs(args); err != nil { return nil, err } +<<<<<<< HEAD tp := resolveType4Extremum(args) cmpAsDatetime := false if tp == types.ETDatetime || tp == types.ETTimestamp { @@ -684,13 +845,21 @@ func (c *leastFunctionClass) getFunction(ctx sessionctx.Context, args []Expressi tp = types.ETString } else if tp == types.ETDuration { tp = types.ETString +======= + tp, fieldTimeType, cmpStringMode := resolveType4Extremum(args) + argTp := tp + if cmpStringMode != GLCmpStringDirectly { + // Args are temporal and string mixed, we cast all args as string and parse it to temporal mannualy to compare. + argTp = types.ETString +>>>>>>> a8ce4af9f... expression: Fix incorrect behavior of greatest/least when mixed temporal type with others (#31037) } else if tp == types.ETJson { unsupportedJSONComparison(ctx, args) + argTp = types.ETString tp = types.ETString } argTps := make([]types.EvalType, len(args)) for i := range args { - argTps[i] = tp + argTps[i] = argTp } bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, tp, argTps...) if err != nil { @@ -710,11 +879,32 @@ func (c *leastFunctionClass) getFunction(ctx sessionctx.Context, args []Expressi sig = &builtinLeastDecimalSig{bf} sig.setPbCode(tipb.ScalarFuncSig_LeastDecimal) case types.ETString: +<<<<<<< HEAD sig = &builtinLeastStringSig{bf} sig.setPbCode(tipb.ScalarFuncSig_LeastString) +======= + if cmpStringMode == GLCmpStringAsDate { + sig = &builtinLeastCmpStringAsTimeSig{bf, true} + sig.setPbCode(tipb.ScalarFuncSig_LeastCmpStringAsDate) + } else if cmpStringMode == GLCmpStringAsDatetime { + sig = &builtinLeastCmpStringAsTimeSig{bf, false} + sig.setPbCode(tipb.ScalarFuncSig_LeastCmpStringAsTime) + } else { + sig = &builtinLeastStringSig{bf} + sig.setPbCode(tipb.ScalarFuncSig_LeastString) + } + case types.ETDuration: + sig = &builtinLeastDurationSig{bf} + sig.setPbCode(tipb.ScalarFuncSig_LeastDuration) +>>>>>>> a8ce4af9f... expression: Fix incorrect behavior of greatest/least when mixed temporal type with others (#31037) case types.ETDatetime, types.ETTimestamp: - sig = &builtinLeastTimeSig{bf} - sig.setPbCode(tipb.ScalarFuncSig_LeastTime) + if fieldTimeType == GLRetDate { + sig = &builtinLeastTimeSig{bf, true} + sig.setPbCode(tipb.ScalarFuncSig_LeastDate) + } else { + sig = &builtinLeastTimeSig{bf, false} + sig.setPbCode(tipb.ScalarFuncSig_LeastTime) + } } sig.getRetTp().Flen, sig.getRetTp().Decimal = fixFlenAndDecimalForGreatestAndLeast(args) return sig, nil @@ -842,11 +1032,13 @@ func (b *builtinLeastStringSig) evalString(row chunk.Row) (min string, isNull bo type builtinLeastTimeSig struct { baseBuiltinFunc + cmpAsDate bool } func (b *builtinLeastTimeSig) Clone() builtinFunc { newSig := &builtinLeastTimeSig{} newSig.cloneFrom(&b.baseBuiltinFunc) + newSig.cmpAsDate = b.cmpAsDate return newSig } @@ -864,21 +1056,69 @@ func (b *builtinLeastTimeSig) evalString(row chunk.Row) (res string, isNull bool if isNull || err != nil { return "", true, err } - t, err := types.ParseDatetime(sc, v) + v, err = doTimeConversionForGL(b.cmpAsDate, b.ctx, sc, v) if err != nil { - if err = handleInvalidTimeError(b.ctx, err); err != nil { - return v, true, err - } - } else { - v = t.String() + return v, true, err } if i == 0 || strings.Compare(v, strRes) < 0 { strRes = v } +<<<<<<< HEAD if i == 0 || t.Compare(timeRes) < 0 { timeRes = t } } +======= + } + + return strRes, false, nil +} + +type builtinLeastTimeSig struct { + baseBuiltinFunc + cmpAsDate bool +} + +func (b *builtinLeastTimeSig) Clone() builtinFunc { + newSig := &builtinLeastTimeSig{} + newSig.cloneFrom(&b.baseBuiltinFunc) + newSig.cmpAsDate = b.cmpAsDate + return newSig +} + +func (b *builtinLeastTimeSig) evalTime(row chunk.Row) (res types.Time, isNull bool, err error) { + for i := 0; i < len(b.args); i++ { + v, isNull, err := b.args[i].EvalTime(b.ctx, row) + if isNull || err != nil { + return types.ZeroTime, true, err + } + if i == 0 || v.Compare(res) < 0 { + res = v + } + } + // Convert ETType Time value to MySQL actual type, distinguish date and datetime + sc := b.ctx.GetSessionVars().StmtCtx + resTimeTp := getAccurateTimeTypeForGLRet(b.cmpAsDate) + if res, err = res.Convert(sc, resTimeTp); err != nil { + return types.ZeroTime, true, handleInvalidTimeError(b.ctx, err) + } + return res, false, nil +} + +func getAccurateTimeTypeForGLRet(cmpAsDate bool) byte { + var resTimeTp byte + if cmpAsDate { + resTimeTp = mysql.TypeDate + } else { + resTimeTp = mysql.TypeDatetime + } + return resTimeTp +} + +type builtinLeastDurationSig struct { + baseBuiltinFunc +} +>>>>>>> a8ce4af9f... expression: Fix incorrect behavior of greatest/least when mixed temporal type with others (#31037) if timeRes.IsZero() { res = strRes diff --git a/expression/builtin_compare_test.go b/expression/builtin_compare_test.go index 9d11d8b5ad18a..9fb6ffd7e48e6 100644 --- a/expression/builtin_compare_test.go +++ b/expression/builtin_compare_test.go @@ -330,6 +330,10 @@ func (s *testEvaluatorSuite) TestGreatestLeastFunc(c *C) { []interface{}{905969664.0, 4556, "1990-06-16 17:22:56.005534"}, "905969664", "1990-06-16 17:22:56.005534", false, false, }, + { + []interface{}{105969664.0, 120000, types.Duration{Duration: 20*time.Hour + 0*time.Minute + 0*time.Second}}, + "20:00:00", "105969664", false, false, + }, } { f0, err := newFunctionForTest(s.ctx, ast.Greatest, s.primitiveValsToConstants(t.args)...) c.Assert(err, IsNil) diff --git a/expression/builtin_compare_vec.go b/expression/builtin_compare_vec.go index 043f77cfa193a..b299f5c9d9d29 100644 --- a/expression/builtin_compare_vec.go +++ b/expression/builtin_compare_vec.go @@ -651,14 +651,10 @@ func (b *builtinGreatestTimeSig) vecEvalString(input *chunk.Chunk, result *chunk // NOTE: can't use Column.GetString because it returns an unsafe string, copy the row instead. argTimeStr := string(result.GetBytes(i)) - - argTime, err := types.ParseDatetime(sc, argTimeStr) + var err error + argTimeStr, err = doTimeConversionForGL(b.cmpAsDate, b.ctx, sc, argTimeStr) if err != nil { - if err = handleInvalidTimeError(b.ctx, err); err != nil { - return err - } - } else { - argTimeStr = argTime.String() + return err } if j == 0 || strings.Compare(argTimeStr, dstStrings[i]) > 0 { dstStrings[i] = argTimeStr @@ -736,14 +732,10 @@ func (b *builtinLeastTimeSig) vecEvalString(input *chunk.Chunk, result *chunk.Co // NOTE: can't use Column.GetString because it returns an unsafe string, copy the row instead. argTimeStr := string(result.GetBytes(i)) - - argTime, err := types.ParseDatetime(sc, argTimeStr) + var err error + argTimeStr, err = doTimeConversionForGL(b.cmpAsDate, b.ctx, sc, argTimeStr) if err != nil { - if err = handleInvalidTimeError(b.ctx, err); err != nil { - return err - } - } else { - argTimeStr = argTime.String() + return err } if j == 0 || strings.Compare(argTimeStr, dstStrings[i]) < 0 { dstStrings[i] = argTimeStr @@ -814,3 +806,152 @@ func (b *builtinGreatestStringSig) vecEvalString(input *chunk.Chunk, result *chu } return nil } +<<<<<<< HEAD +======= + +func (b *builtinGreatestTimeSig) vectorized() bool { + return true +} + +func (b *builtinGreatestTimeSig) vecEvalTime(input *chunk.Chunk, result *chunk.Column) error { + n := input.NumRows() + buf, err := b.bufAllocator.get() + if err != nil { + return err + } + defer b.bufAllocator.put(buf) + + result.ResizeTime(n, false) + for argIdx := 0; argIdx < len(b.args); argIdx++ { + if err := b.args[argIdx].VecEvalTime(b.ctx, input, buf); err != nil { + return err + } + result.MergeNulls(buf) + resTimes := result.Times() + argTimes := buf.Times() + for rowIdx := 0; rowIdx < n; rowIdx++ { + if result.IsNull(rowIdx) { + continue + } + if argIdx == 0 || argTimes[rowIdx].Compare(resTimes[rowIdx]) > 0 { + resTimes[rowIdx] = argTimes[rowIdx] + } + } + } + sc := b.ctx.GetSessionVars().StmtCtx + resTimeTp := getAccurateTimeTypeForGLRet(b.cmpAsDate) + for rowIdx := 0; rowIdx < n; rowIdx++ { + resTimes := result.Times() + resTimes[rowIdx], err = resTimes[rowIdx].Convert(sc, resTimeTp) + if err != nil { + return err + } + } + return nil +} + +func (b *builtinLeastTimeSig) vectorized() bool { + return true +} + +func (b *builtinLeastTimeSig) vecEvalTime(input *chunk.Chunk, result *chunk.Column) error { + n := input.NumRows() + buf, err := b.bufAllocator.get() + if err != nil { + return err + } + defer b.bufAllocator.put(buf) + + result.ResizeTime(n, false) + for argIdx := 0; argIdx < len(b.args); argIdx++ { + if err := b.args[argIdx].VecEvalTime(b.ctx, input, buf); err != nil { + return err + } + result.MergeNulls(buf) + resTimes := result.Times() + argTimes := buf.Times() + for rowIdx := 0; rowIdx < n; rowIdx++ { + if result.IsNull(rowIdx) { + continue + } + if argIdx == 0 || argTimes[rowIdx].Compare(resTimes[rowIdx]) < 0 { + resTimes[rowIdx] = argTimes[rowIdx] + } + } + } + sc := b.ctx.GetSessionVars().StmtCtx + resTimeTp := getAccurateTimeTypeForGLRet(b.cmpAsDate) + for rowIdx := 0; rowIdx < n; rowIdx++ { + resTimes := result.Times() + resTimes[rowIdx], err = resTimes[rowIdx].Convert(sc, resTimeTp) + if err != nil { + return err + } + } + return nil +} + +func (b *builtinGreatestDurationSig) vectorized() bool { + return true +} + +func (b *builtinGreatestDurationSig) vecEvalDuration(input *chunk.Chunk, result *chunk.Column) error { + n := input.NumRows() + buf, err := b.bufAllocator.get() + if err != nil { + return err + } + defer b.bufAllocator.put(buf) + + result.ResizeGoDuration(n, false) + for argIdx := 0; argIdx < len(b.args); argIdx++ { + if err := b.args[argIdx].VecEvalDuration(b.ctx, input, buf); err != nil { + return err + } + result.MergeNulls(buf) + resDurations := result.GoDurations() + argDurations := buf.GoDurations() + for rowIdx := 0; rowIdx < n; rowIdx++ { + if result.IsNull(rowIdx) { + continue + } + if argIdx == 0 || argDurations[rowIdx] > resDurations[rowIdx] { + resDurations[rowIdx] = argDurations[rowIdx] + } + } + } + return nil +} + +func (b *builtinLeastDurationSig) vectorized() bool { + return true +} + +func (b *builtinLeastDurationSig) vecEvalDuration(input *chunk.Chunk, result *chunk.Column) error { + n := input.NumRows() + buf, err := b.bufAllocator.get() + if err != nil { + return err + } + defer b.bufAllocator.put(buf) + + result.ResizeGoDuration(n, false) + for argIdx := 0; argIdx < len(b.args); argIdx++ { + if err := b.args[argIdx].VecEvalDuration(b.ctx, input, buf); err != nil { + return err + } + result.MergeNulls(buf) + resDurations := result.GoDurations() + argDurations := buf.GoDurations() + for rowIdx := 0; rowIdx < n; rowIdx++ { + if result.IsNull(rowIdx) { + continue + } + if argIdx == 0 || argDurations[rowIdx] < resDurations[rowIdx] { + resDurations[rowIdx] = argDurations[rowIdx] + } + } + } + return nil +} +>>>>>>> a8ce4af9f... expression: Fix incorrect behavior of greatest/least when mixed temporal type with others (#31037) diff --git a/expression/distsql_builtin.go b/expression/distsql_builtin.go index 7fd61b30fa3fa..4c40e311e36ea 100644 --- a/expression/distsql_builtin.go +++ b/expression/distsql_builtin.go @@ -220,7 +220,13 @@ func getSignatureByPB(ctx sessionctx.Context, sigCode tipb.ScalarFuncSig, tp *ti case tipb.ScalarFuncSig_GreatestString: f = &builtinGreatestStringSig{base} case tipb.ScalarFuncSig_GreatestTime: - f = &builtinGreatestTimeSig{base} + f = &builtinGreatestTimeSig{base, false} + case tipb.ScalarFuncSig_GreatestDate: + f = &builtinGreatestTimeSig{base, true} + case tipb.ScalarFuncSig_GreatestCmpStringAsTime: + f = &builtinGreatestCmpStringAsTimeSig{base, false} + case tipb.ScalarFuncSig_GreatestCmpStringAsDate: + f = &builtinGreatestCmpStringAsTimeSig{base, true} case tipb.ScalarFuncSig_LeastInt: f = &builtinLeastIntSig{base} case tipb.ScalarFuncSig_LeastReal: @@ -230,7 +236,13 @@ func getSignatureByPB(ctx sessionctx.Context, sigCode tipb.ScalarFuncSig, tp *ti case tipb.ScalarFuncSig_LeastString: f = &builtinLeastStringSig{base} case tipb.ScalarFuncSig_LeastTime: - f = &builtinLeastTimeSig{base} + f = &builtinLeastTimeSig{base, false} + case tipb.ScalarFuncSig_LeastDate: + f = &builtinLeastTimeSig{base, true} + case tipb.ScalarFuncSig_LeastCmpStringAsTime: + f = &builtinLeastCmpStringAsTimeSig{base, false} + case tipb.ScalarFuncSig_LeastCmpStringAsDate: + f = &builtinLeastCmpStringAsTimeSig{base, true} case tipb.ScalarFuncSig_IntervalInt: f = &builtinIntervalIntSig{base, false} // Since interval function won't be pushed down to TiKV, therefore it doesn't matter what value we give to hasNullable case tipb.ScalarFuncSig_IntervalReal: diff --git a/expression/integration_test.go b/expression/integration_test.go index aa8c8eb2fce4d..7a033e11c359b 100644 --- a/expression/integration_test.go +++ b/expression/integration_test.go @@ -9878,3 +9878,404 @@ func (s *testIntegrationSuite) TestConstPropNullFunctions(c *C) { tk.MustExec("insert into t2 values (0, 'c', null), (1, null, 0.1), (3, 'b', 0.01), (2, 'q', 0.12), (null, 'a', -0.1), (null, null, null)") tk.MustQuery("select * from t2 where t2.i2=((select count(1) from t1 where t1.i1=t2.i2))").Check(testkit.Rows("1 0.1")) } +<<<<<<< HEAD +======= + +func TestIssue27233(t *testing.T) { + store, clean := testkit.CreateMockStore(t) + defer clean() + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test;") + tk.MustExec("drop table if exists t;") + tk.MustExec("CREATE TABLE `t` (\n `COL1` tinyint(45) NOT NULL,\n `COL2` tinyint(45) NOT NULL,\n PRIMARY KEY (`COL1`,`COL2`) /*T![clustered_index] NONCLUSTERED */\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;") + tk.MustExec("insert into t values(122,100),(124,-22),(124,34),(127,103);") + tk.MustQuery("SELECT col2 FROM t AS T1 WHERE ( SELECT count(DISTINCT COL1, COL2) FROM t AS T2 WHERE T2.COL1 > T1.COL1 ) > 2 ;"). + Check(testkit.Rows("100")) +} + +func TestIssue27236(t *testing.T) { + store, clean := testkit.CreateMockStore(t) + defer clean() + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test;") + row := tk.MustQuery(`select extract(hour_second from "-838:59:59.00");`) + row.Check(testkit.Rows("-8385959")) + + tk.MustExec(`drop table if exists t`) + tk.MustExec(`create table t(c1 varchar(100));`) + tk.MustExec(`insert into t values('-838:59:59.00'), ('700:59:59.00');`) + row = tk.MustQuery(`select extract(hour_second from c1) from t order by c1;`) + row.Check(testkit.Rows("-8385959", "7005959")) +} + +func TestIssue26977(t *testing.T) { + store, clean := testkit.CreateMockStore(t) + defer clean() + + tk := testkit.NewTestKit(t, store) + result := tk.MustQuery("select a + 1 as f from (select cast(0xfffffffffffffff0 as unsigned) as a union select cast(1 as unsigned)) t having f != 2;") + result.Check(testkit.Rows("18446744073709551601")) +} + +func TestIssue27610(t *testing.T) { + store, clean := testkit.CreateMockStore(t) + defer clean() + + tk := testkit.NewTestKit(t, store) + tk.MustExec(`use test;`) + tk.MustExec(`drop table if exists PK_TCOLLATION3966STROBJSTROBJ;`) + tk.MustExec("CREATE TABLE `PK_TCOLLATION3966STROBJSTROBJ` (\n `COL1` enum('ll','aa','bb','cc','dd','ee') COLLATE utf8_general_ci NOT NULL,\n `COL2` varchar(20) COLLATE utf8_general_ci DEFAULT NULL,\n PRIMARY KEY (`COL1`) /*T![clustered_index] CLUSTERED */\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;") + tk.MustExec(`insert into PK_TCOLLATION3966STROBJSTROBJ values("ee", "tttt");`) + tk.MustQuery("SELECT col1, COL2 FROM PK_TCOLLATION3966STROBJSTROBJ WHERE COL1 IN ('notexist','6') and col2 not in (\"abcd\");"). + Check(testkit.Rows()) +} + +func TestLastInsertId(t *testing.T) { + store, clean := testkit.CreateMockStore(t) + defer clean() + + tk := testkit.NewTestKit(t, store) + tk.MustExec(`use test;`) + tk.MustExec(`drop table if exists lastinsertid;`) + tk.MustExec(`create table lastinsertid (id int not null primary key auto_increment);`) + tk.MustQuery("SELECT @@last_insert_id;").Check(testkit.Rows("0")) + tk.MustExec(`INSERT INTO lastinsertid VALUES (NULL);`) + tk.MustQuery("SELECT @@last_insert_id, LAST_INSERT_ID()").Check(testkit.Rows("1 1")) + tk.MustExec(`INSERT INTO lastinsertid VALUES (NULL);`) + tk.MustQuery("SELECT @@last_insert_id, LAST_INSERT_ID()").Check(testkit.Rows("2 2")) + tk.MustExec(`INSERT INTO lastinsertid VALUES (NULL);`) + tk.MustQuery("SELECT @@last_insert_id, LAST_INSERT_ID()").Check(testkit.Rows("3 3")) +} + +func TestTimestamp(t *testing.T) { + store, clean := testkit.CreateMockStore(t) + defer clean() + + tk := testkit.NewTestKit(t, store) + tk.MustExec(`use test;`) + tk.MustExec("SET time_zone = '+00:00';") + defer tk.MustExec("SET time_zone = DEFAULT;") + timestampStr1 := fmt.Sprintf("%s", tk.MustQuery("SELECT @@timestamp;").Rows()[0]) + timestampStr1 = timestampStr1[1:] + timestampStr1 = timestampStr1[:len(timestampStr1)-1] + timestamp1, err := strconv.ParseFloat(timestampStr1, 64) + require.NoError(t, err) + nowStr1 := fmt.Sprintf("%s", tk.MustQuery("SELECT NOW(6);").Rows()[0]) + now1, err := time.Parse("[2006-01-02 15:04:05.000000]", nowStr1) + require.NoError(t, err) + tk.MustExec("set @@timestamp = 12345;") + tk.MustQuery("SELECT @@timestamp;").Check(testkit.Rows("12345")) + tk.MustQuery("SELECT NOW();").Check(testkit.Rows("1970-01-01 03:25:45")) + tk.MustQuery("SELECT NOW();").Check(testkit.Rows("1970-01-01 03:25:45")) + tk.MustExec("set @@timestamp = default;") + time.Sleep(2 * time.Microsecond) + timestampStr2 := fmt.Sprintf("%s", tk.MustQuery("SELECT @@timestamp;").Rows()[0]) + timestampStr2 = timestampStr2[1:] + timestampStr2 = timestampStr2[:len(timestampStr2)-1] + timestamp2, err := strconv.ParseFloat(timestampStr2, 64) + require.NoError(t, err) + nowStr2 := fmt.Sprintf("%s", tk.MustQuery("SELECT NOW(6);").Rows()[0]) + now2, err := time.Parse("[2006-01-02 15:04:05.000000]", nowStr2) + require.NoError(t, err) + require.Less(t, timestamp1, timestamp2) + require.Less(t, now1.UnixNano(), now2.UnixNano()) + tk.MustExec("set @@timestamp = 12345;") + tk.MustQuery("SELECT @@timestamp;").Check(testkit.Rows("12345")) + tk.MustQuery("SELECT NOW();").Check(testkit.Rows("1970-01-01 03:25:45")) + tk.MustQuery("SELECT NOW();").Check(testkit.Rows("1970-01-01 03:25:45")) + tk.MustExec("set @@timestamp = 0;") + time.Sleep(2 * time.Microsecond) + timestampStr3 := fmt.Sprintf("%s", tk.MustQuery("SELECT @@timestamp;").Rows()[0]) + timestampStr3 = timestampStr3[1:] + timestampStr3 = timestampStr3[:len(timestampStr3)-1] + timestamp3, err := strconv.ParseFloat(timestampStr3, 64) + require.NoError(t, err) + nowStr3 := fmt.Sprintf("%s", tk.MustQuery("SELECT NOW(6);").Rows()[0]) + now3, err := time.Parse("[2006-01-02 15:04:05.000000]", nowStr3) + require.NoError(t, err) + require.Less(t, timestamp2, timestamp3) + require.Less(t, now2.UnixNano(), now3.UnixNano()) +} + +func TestIdentity(t *testing.T) { + store, clean := testkit.CreateMockStore(t) + defer clean() + + tk := testkit.NewTestKit(t, store) + tk.MustExec(`use test;`) + tk.MustExec(`drop table if exists identity;`) + tk.MustExec(`create table identity (id int not null primary key auto_increment);`) + tk.MustQuery("SELECT @@identity;").Check(testkit.Rows("0")) + tk.MustExec(`INSERT INTO identity VALUES (NULL);`) + tk.MustQuery("SELECT @@identity, LAST_INSERT_ID()").Check(testkit.Rows("1 1")) + tk.MustExec(`INSERT INTO identity VALUES (NULL);`) + tk.MustQuery("SELECT @@identity, LAST_INSERT_ID()").Check(testkit.Rows("2 2")) + tk.MustExec(`INSERT INTO identity VALUES (NULL);`) + tk.MustQuery("SELECT @@identity, LAST_INSERT_ID()").Check(testkit.Rows("3 3")) +} + +func TestIssue28804(t *testing.T) { + store, clean := testkit.CreateMockStore(t) + defer clean() + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists perf_offline_day;") + tk.MustExec(`CREATE TABLE perf_offline_day ( +uuid varchar(50), +ts timestamp NOT NULL, +user_id varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL, +platform varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL, +host_id bigint(20) DEFAULT NULL, +PRIMARY KEY (uuid,ts) /*T![clustered_index] NONCLUSTERED */ +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci +PARTITION BY RANGE ( UNIX_TIMESTAMP(ts) ) ( +PARTITION p20210906 VALUES LESS THAN (1630944000), +PARTITION p20210907 VALUES LESS THAN (1631030400), +PARTITION p20210908 VALUES LESS THAN (1631116800), +PARTITION p20210909 VALUES LESS THAN (1631203200) +);`) + tk.MustExec("set @@tidb_partition_prune_mode = 'static'") + tk.MustExec("INSERT INTO `perf_offline_day` VALUES ('dd082c8a-3bab-4431-943a-348fe0592abd','2021-09-08 13:00:07','Xg9C8zq81jGNbugM', 'pc', 12345);") + tk.MustQuery("SELECT cast(floor(hour(ts) / 4) as char) as win_start FROM perf_offline_day partition (p20210907, p20210908) GROUP BY win_start;").Check(testkit.Rows("3")) +} + +func TestIssue28643(t *testing.T) { + store, clean := testkit.CreateMockStore(t) + defer clean() + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a time(4));") + tk.MustExec("insert into t values(\"-838:59:59.000000\");") + tk.MustExec("insert into t values(\"838:59:59.000000\");") + tk.MustExec("set tidb_enable_vectorized_expression = on;") + tk.MustQuery("select hour(a) from t;").Check(testkit.Rows("838", "838")) + tk.MustExec("set tidb_enable_vectorized_expression = off;") + tk.MustQuery("select hour(a) from t;").Check(testkit.Rows("838", "838")) +} + +func TestIssue27831(t *testing.T) { + store, clean := testkit.CreateMockStore(t) + defer clean() + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a enum(\"a\", \"b\"), b enum(\"a\", \"b\"), c bool)") + tk.MustExec("insert into t values(\"a\", \"a\", 1);") + tk.MustQuery("select * from t t1 right join t t2 on t1.a=t2.b and t1.a= t2.c;").Check(testkit.Rows("a a 1 a a 1")) + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a enum(\"a\", \"b\"), b enum(\"a\", \"b\"), c bool, d int, index idx(d))") + tk.MustExec("insert into t values(\"a\", \"a\", 1, 1);") + tk.MustQuery("select /*+ inl_hash_join(t1) */ * from t t1 right join t t2 on t1.a=t2.b and t1.a= t2.c and t1.d=t2.d;").Check(testkit.Rows("a a 1 1 a a 1 1")) +} + +func TestIssue29434(t *testing.T) { + store, clean := testkit.CreateMockStore(t) + defer clean() + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + tk.MustExec("drop table if exists t1;") + tk.MustExec("create table t1(c1 datetime);") + tk.MustExec("insert into t1 values('2021-12-12 10:10:10.000');") + tk.MustExec("set tidb_enable_vectorized_expression = on;") + tk.MustQuery("select greatest(c1, '99999999999999') from t1;").Check(testkit.Rows("99999999999999")) + tk.MustExec("set tidb_enable_vectorized_expression = off;") + tk.MustQuery("select greatest(c1, '99999999999999') from t1;").Check(testkit.Rows("99999999999999")) + + tk.MustExec("set tidb_enable_vectorized_expression = on;") + tk.MustQuery("select least(c1, '99999999999999') from t1;").Check(testkit.Rows("2021-12-12 10:10:10")) + tk.MustExec("set tidb_enable_vectorized_expression = off;") + tk.MustQuery("select least(c1, '99999999999999') from t1;").Check(testkit.Rows("2021-12-12 10:10:10")) +} + +func TestIssue29417(t *testing.T) { + store, clean := testkit.CreateMockStore(t) + defer clean() + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t1;") + tk.MustExec("create table t1 (f1 decimal(5,5));") + tk.MustExec("insert into t1 values (-0.12345);") + tk.MustQuery("select concat(f1) from t1;").Check(testkit.Rows("-0.12345")) +} + +func TestIssue29244(t *testing.T) { + store, clean := testkit.CreateMockStore(t) + defer clean() + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a time(4));") + tk.MustExec("insert into t values(\"-700:10:10.123456111\");") + tk.MustExec("insert into t values(\"700:10:10.123456111\");") + tk.MustExec("set tidb_enable_vectorized_expression = on;") + tk.MustQuery("select microsecond(a) from t;").Check(testkit.Rows("123500", "123500")) + tk.MustExec("set tidb_enable_vectorized_expression = off;") + tk.MustQuery("select microsecond(a) from t;").Check(testkit.Rows("123500", "123500")) +} + +func TestIssue29513(t *testing.T) { + store, clean := testkit.CreateMockStore(t) + defer clean() + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustQuery("select '123' union select cast(45678 as char);").Sort().Check(testkit.Rows("123", "45678")) + tk.MustQuery("select '123' union select cast(45678 as char(2));").Sort().Check(testkit.Rows("123", "45")) + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int);") + tk.MustExec("insert into t values(45678);") + tk.MustQuery("select '123' union select cast(a as char) from t;").Sort().Check(testkit.Rows("123", "45678")) + tk.MustQuery("select '123' union select cast(a as char(2)) from t;").Sort().Check(testkit.Rows("123", "45")) +} + +func TestIssue29755(t *testing.T) { + store, clean := testkit.CreateMockStore(t) + defer clean() + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + tk.MustExec("set tidb_enable_vectorized_expression = on;") + tk.MustQuery("select char(123, NULL, 123)").Check(testkit.Rows("{{")) + tk.MustQuery("select char(NULL, 123, 123)").Check(testkit.Rows("{{")) + tk.MustExec("set tidb_enable_vectorized_expression = off;") + tk.MustQuery("select char(123, NULL, 123)").Check(testkit.Rows("{{")) + tk.MustQuery("select char(NULL, 123, 123)").Check(testkit.Rows("{{")) +} + +func TestIssue30101(t *testing.T) { + store, clean := testkit.CreateMockStore(t) + defer clean() + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t1;") + tk.MustExec("create table t1(c1 bigint unsigned, c2 bigint unsigned);") + tk.MustExec("insert into t1 values(9223372036854775808, 9223372036854775809);") + tk.MustQuery("select greatest(c1, c2) from t1;").Sort().Check(testkit.Rows("9223372036854775809")) +} + +func TestIssue28739(t *testing.T) { + store, clean := testkit.CreateMockStore(t) + defer clean() + + tk := testkit.NewTestKit(t, store) + tk.MustExec(`USE test`) + tk.MustExec("SET time_zone = 'Europe/Vilnius'") + tk.MustQuery("SELECT UNIX_TIMESTAMP('2020-03-29 03:45:00')").Check(testkit.Rows("1585443600")) + tk.MustQuery("SELECT FROM_UNIXTIME(UNIX_TIMESTAMP('2020-03-29 03:45:00'))").Check(testkit.Rows("2020-03-29 04:00:00")) + tk.MustExec(`DROP TABLE IF EXISTS t`) + tk.MustExec(`CREATE TABLE t (dt DATETIME NULL)`) + defer tk.MustExec(`DROP TABLE t`) + // Test the vector implememtation + tk.MustExec(`INSERT INTO t VALUES ('2021-10-31 02:30:00'), ('2021-03-28 02:30:00'), ('2020-10-04 02:15:00'), ('2020-03-29 03:45:00'), (NULL)`) + tk.MustQuery(`SELECT dt, UNIX_TIMESTAMP(dt) FROM t`).Sort().Check(testkit.Rows( + "2020-03-29 03:45:00 1585443600", + "2020-10-04 02:15:00 1601766900", + "2021-03-28 02:30:00 1616891400", + "2021-10-31 02:30:00 1635636600", + " ")) +} + +func TestIssue30326(t *testing.T) { + store, clean := testkit.CreateMockStore(t) + defer clean() + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t(a int);") + tk.MustExec("insert into t values(1),(1),(2),(2);") + tk.MustExec("set tidb_window_concurrency = 1;") + err := tk.QueryToErr("select (FIRST_VALUE(1) over (partition by v.a)) as c3 from (select a from t where t.a = (select a from t t2 where t.a = t2.a)) as v;") + require.Error(t, err, "[executor:1242]Subquery returns more than 1 row") +} + +func TestIssue30174(t *testing.T) { + store, clean := testkit.CreateMockStore(t) + defer clean() + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t1,t2;") + tk.MustExec("CREATE TABLE `t1` (\n `c1` enum('Alice','Bob','Charlie','David') NOT NULL,\n `c2` blob NOT NULL,\n PRIMARY KEY (`c2`(5)),\n UNIQUE KEY `idx_89` (`c1`)\n);") + tk.MustExec("CREATE TABLE `t2` (\n `c1` enum('Alice','Bob','Charlie','David') NOT NULL DEFAULT 'Alice',\n `c2` set('Alice','Bob','Charlie','David') NOT NULL DEFAULT 'David',\n `c3` enum('Alice','Bob','Charlie','David') NOT NULL,\n PRIMARY KEY (`c3`,`c2`)\n);") + tk.MustExec("insert into t1 values('Charlie','');") + tk.MustExec("insert into t2 values('Charlie','Charlie','Alice');") + tk.MustQuery("select * from t2 where c3 in (select c2 from t1);").Check(testkit.Rows()) + tk.MustQuery("select * from t2 where c2 in (select c2 from t1);").Check(testkit.Rows()) +} + +func TestIssue30264(t *testing.T) { + store, clean := testkit.CreateMockStore(t) + defer clean() + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + // compare Time/Int/Int as string type, return string type + tk.MustQuery("select greatest(time '21:00', year(date'20220101'), 23);").Check(testkit.Rows("23")) + // compare Time/Date/Int as string type, return string type + tk.MustQuery("select greatest(time '21:00', date'891001', 120000);").Check(testkit.Rows("21:00:00")) + // compare Time/Date/Int as string type, return string type + tk.MustQuery("select greatest(time '20:00', date'101001', 120101);").Check(testkit.Rows("20:00:00")) + // compare Date/String/Int as Date type, return string type + tk.MustQuery("select greatest(date'101001', '19990329', 120101);").Check(testkit.Rows("2012-01-01")) + // compare Time/Date as DateTime type, return DateTime type + tk.MustQuery("select greatest(time '20:00', date'691231');").Check(testkit.Rows("2069-12-31 00:00:00")) + // compare Date/Date as Date type, return Date type + tk.MustQuery("select greatest(date '120301', date'691231');").Check(testkit.Rows("2069-12-31")) + // compare Time/Time as Time type, return Time type + tk.MustQuery("select greatest(time '203001', time '2230');").Check(testkit.Rows("20:30:01")) + // compare DateTime/DateTime as DateTime type, return DateTime type + tk.MustQuery("select greatest(timestamp '2021-01-31 00:00:01', timestamp '2021-12-31 12:00:00');").Check(testkit.Rows("2021-12-31 12:00:00")) + // compare Time/DateTime as DateTime type, return DateTime type + tk.MustQuery("select greatest(time '00:00:01', timestamp '2069-12-31 12:00:00');").Check(testkit.Rows("2069-12-31 12:00:00")) + // compare Date/DateTime as DateTime type, return DateTime type + tk.MustQuery("select greatest(date '21000101', timestamp '2069-12-31 12:00:00');").Check(testkit.Rows("2100-01-01 00:00:00")) + // compare JSON/JSON, return JSON type + tk.MustQuery("select greatest(cast('1' as JSON), cast('2' as JSON));").Check(testkit.Rows("2")) + //Original 30264 Issue: + tk.MustQuery("select greatest(time '20:00:00', 120000);").Check(testkit.Rows("20:00:00")) + tk.MustQuery("select greatest(date '2005-05-05', 20010101, 20040404, 20030303);").Check(testkit.Rows("2005-05-05")) + tk.MustQuery("select greatest(date '1995-05-05', 19910101, 20050505, 19930303);").Check(testkit.Rows("2005-05-05")) + + tk.MustExec("use test") + tk.MustExec("drop table if exists t1,t2;") + tk.MustExec("CREATE TABLE `t1` (a datetime, b date, c time)") + tk.MustExec("insert into t1 values(timestamp'2021-01-31 00:00:01', '2069-12-31', '20:00:01');") + tk.MustExec("set tidb_enable_vectorized_expression = on;") + // compare Time/Int/Int as string type, return string type + tk.MustQuery("select greatest(c, year(date'20220101'), 23) from t1;").Check(testkit.Rows("23")) + // compare Time/Date/Int as string type, return string type + tk.MustQuery("select greatest(c, date'891001', 120000) from t1;").Check(testkit.Rows("20:00:01")) + // compare Time/Date/Int as string type, return string type + tk.MustQuery("select greatest(c, date'101001', 120101) from t1;").Check(testkit.Rows("20:00:01")) + // compare Date/String/Int as Date type, return string type + tk.MustQuery("select greatest(b, '19990329', 120101) from t1;").Check(testkit.Rows("2069-12-31")) + // compare Time/Date as DateTime type, return DateTime type + tk.MustQuery("select greatest(time '20:00', b) from t1;").Check(testkit.Rows("2069-12-31 00:00:00")) + // compare Date/Date as Date type, return Date type + tk.MustQuery("select greatest(date '120301', b) from t1;").Check(testkit.Rows("2069-12-31")) + // compare Time/Time as Time type, return Time type + tk.MustQuery("select greatest(c, time '2230') from t1;").Check(testkit.Rows("20:00:01")) + // compare DateTime/DateTime as DateTime type, return DateTime type + tk.MustQuery("select greatest(a, timestamp '2021-12-31 12:00:00') from t1;").Check(testkit.Rows("2021-12-31 12:00:00")) + // compare Time/DateTime as DateTime type, return DateTime type + tk.MustQuery("select greatest(c, timestamp '2069-12-31 12:00:00') from t1;").Check(testkit.Rows("2069-12-31 12:00:00")) + // compare Date/DateTime as DateTime type, return DateTime type + tk.MustQuery("select greatest(date '21000101', a) from t1;").Check(testkit.Rows("2100-01-01 00:00:00")) + // compare JSON/JSON, return JSON type + tk.MustQuery("select greatest(cast(a as JSON), cast('3' as JSON)) from t1;").Check(testkit.Rows("3")) +} +>>>>>>> a8ce4af9f... expression: Fix incorrect behavior of greatest/least when mixed temporal type with others (#31037)