-
Notifications
You must be signed in to change notification settings - Fork 5.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
*: fix union result when mix signed/unsigned columns #7112
Conversation
plan/logical_plan_builder.go
Outdated
// This logic need be combined with buildProjection4Union. | ||
if a.Tp == mysql.TypeNewDecimal { | ||
// For Decimal result be unsigned when all union children be unsigned. | ||
resultTp.Flag &= b.Flag & mysql.UnsignedFlag |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This "undocumented behaver" comes from https://github.com/mysql/mysql-server/blob/e02b6e29913786c10c766f997f57915903f6d275/sql/item.cc#L10532
types/field_type.go
Outdated
@@ -39,7 +39,8 @@ type FieldType struct { | |||
Charset string | |||
Collate string | |||
// Elems is the element list for enum and set type. | |||
Elems []string | |||
Elems []string | |||
BelowZeroBeZero bool |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we need this "black flag"... because we can not cast < 0 to zero directly in chunk_executor, orz
because select cast(-1 as unsigned)
need be MAX_UINT64..
but for union or insert need be 0
and this flag is the most convenient way to get there, IMHO
types/field_type.go
Outdated
@@ -39,7 +39,8 @@ type FieldType struct { | |||
Charset string | |||
Collate string | |||
// Elems is the element list for enum and set type. | |||
Elems []string | |||
Elems []string | |||
NegativeUnsignedBeZero bool |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- add a comment for this
- Do we really need this, this is set to true only when building union
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes~ because in union context cast(negative unsigned be zero) is differ to cast in normal(negative unsigned be int.Max)...so we need a flag.
we can add it in field_type or cast expression/executor, and it seems previous way's modification is less.
yes, it is not very comfortable for mee too, or have any other good way ~ ^ ^ ?
tk.MustQuery("select id, i, b, d, dd from t2 union select id, i, b, d, dd from t1 order by id"). | ||
Check(testkit.Rows("1 0 0 0 -1", "2 1 1 1.1 1")) | ||
tk.MustQuery("select id, i from t2 union select id, cast(i as unsigned int) from t1 order by id"). | ||
Check(testkit.Rows("1 18446744073709551615", "2 1")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it the same as MySQL's?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes~ mysql, cast will got int.Max
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what about unsigned double union int
? we need a case to cover union different types.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
emma, added and find a bug....o.o~
PTAL~thx
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
types/field_type.go
Outdated
@@ -40,6 +40,9 @@ type FieldType struct { | |||
Collate string | |||
// Elems is the element list for enum and set type. | |||
Elems []string | |||
// NegativeUnsignedBeZero indicates that unsigned type with negative number will be cast 0 instead of Big number. | |||
// it's only used in union implicitly cast. | |||
NegativeUnsignedBeZero bool |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please take more time to find a way to avoid adding this field.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should move this field to the cast function.
/run-all-tests |
expression/builtin_cast.go
Outdated
@@ -1593,6 +1659,24 @@ func (b *builtinCastJSONAsDurationSig) evalDuration(row types.Row) (res types.Du | |||
return | |||
} | |||
|
|||
type castContext int |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what is this used for?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it used in BuildCastFunction4Insert, build a special tmp ctx to void modify BuildCastFunction
, castXXFunctionClass
or getFunction
plan/logical_plan_builder.go
Outdated
@@ -647,7 +655,7 @@ func (b *planBuilder) buildProjection4Union(u *LogicalUnionAll) { | |||
dstType := unionSchema.Columns[i].RetType | |||
srcType := srcCol.RetType | |||
if !srcType.Equal(dstType) { | |||
exprs[i] = expression.BuildCastFunction(b.ctx, srcCol, dstType) | |||
exprs[i] = expression.BuildCastFunction4Insert(b.ctx, srcCol, dstType) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like BuildCastFunction4Insert
is only used for Union
, maybe name the function to BuildCastFunction4Union
is better?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Emma, but maybe it will use for insert, insert cast logic is the same as this one, but now insert values is datum based cast, or we change to insert in future?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can change it in the future. I believe it will take us a long time to reach that future 😂
plan/logical_plan_builder.go
Outdated
@@ -612,6 +612,14 @@ func (b *planBuilder) buildDistinct(child LogicalPlan, length int) LogicalPlan { | |||
// joinFieldType finds the type which can carry the given types. | |||
func joinFieldType(a, b *types.FieldType) *types.FieldType { | |||
resultTp := types.NewFieldType(types.MergeFieldType(a.Tp, b.Tp)) | |||
// This logic need be combined with buildProjection4Union. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we'd better not embed this function to buildProjection4Union
. this function is focused on deciding the data type for each column of the union
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This logic will be intelligible when it is associated with the buildProjection4Union logic.
plan/logical_plan_builder.go
Outdated
@@ -612,6 +612,14 @@ func (b *planBuilder) buildDistinct(child LogicalPlan, length int) LogicalPlan { | |||
// joinFieldType finds the type which can carry the given types. | |||
func joinFieldType(a, b *types.FieldType) *types.FieldType { | |||
resultTp := types.NewFieldType(types.MergeFieldType(a.Tp, b.Tp)) | |||
// This logic need be combined with buildProjection4Union. | |||
if a.Tp == mysql.TypeNewDecimal { | |||
// For Decimal result be unsigned when all union children be unsigned. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how about: "the decimal type could be unsigned only when all the decimals to be united are unsigned" @CaitinChen We need your help to refine this comment 😂
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@CaitinChen need help~~
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The decimal type will be unsigned only when all the decimals to be united are unsigned.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
expression/builtin_cast.go
Outdated
@@ -426,25 +426,34 @@ func (c *castAsJSONFunctionClass) getFunction(ctx sessionctx.Context, args []Exp | |||
|
|||
type builtinCastIntAsIntSig struct { | |||
baseBuiltinFunc |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how about defining another struct to wrap baseBuiltinFunc
and insertContext
, and use this struct for every builtinCastXAsYSig
:
type baseBuiltinCastFunc struct {
baseBuiltinFunc
inUnionAll bool
}
If so, we only need to change the base
or bf
when constructing builtinCastXAsYSig
s, and the code can be cleaner.
expression/builtin_cast.go
Outdated
@@ -456,20 +465,26 @@ func (b *builtinCastIntAsRealSig) evalReal(row types.Row) (res float64, isNull b | |||
if !mysql.HasUnsignedFlag(b.args[0].GetType().Flag) { | |||
res = float64(val) | |||
} else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how about:
if !mysql.HasUnsignedFlag(b.args[0].GetType().Flag) {
...
} else if b.insertContext && val < 0 {
...
} else {
...
}
expression/builtin_cast.go
Outdated
@@ -873,9 +914,13 @@ func (b *builtinCastDecimalAsIntSig) evalInt(row types.Row) (res int64, isNull b | |||
} | |||
|
|||
if mysql.HasUnsignedFlag(b.tp.Flag) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how about:
if !mysql.HasUnsignedFlag(b.tp.Flag) {
...
} else if b.insertContext && val < 0 {
...
} else {
...
}
expression/builtin_cast.go
Outdated
res = 0 | ||
} else { | ||
res, err = types.StrToInt(sc, val) | ||
if err == nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if err != nil
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.....it seems be a old bug.
select cast(-1 as signed)
should not return a warning but master branch does.
I think it should be 05bf3bfd4d19705639a1350eacd5587bcb36fb47 ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
plan/logical_plan_builder.go
Outdated
// the decimal type could be unsigned only when all the decimals to be united are unsigned | ||
resultTp.Flag &= b.Flag & mysql.UnsignedFlag | ||
} else { | ||
// Other types result will be unsigned only if first child be unsigned. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Non-decimal results will be unsigned when the first SQL statement result in the union is unsigned.
plan/logical_plan_builder.go
Outdated
resultTp := types.NewFieldType(types.MergeFieldType(a.Tp, b.Tp)) | ||
// This logic need be understood with buildProjection4Union logic. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This logic will be intelligible when it is associated with the buildProjection4Union logic.
plan/logical_plan_builder.go
Outdated
resultTp := types.NewFieldType(types.MergeFieldType(a.Tp, b.Tp)) | ||
// This logic need be understood with buildProjection4Union logic. | ||
if a.Tp == mysql.TypeNewDecimal { | ||
// the decimal type could be unsigned only when all the decimals to be united are unsigned |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The decimal type will be unsigned only when all the decimals to be united are unsigned.
expression/builtin.go
Outdated
// baseBuiltinCastFunc will be contained in every struct that implement cast builtinFunc | ||
type baseBuiltinCastFunc struct { | ||
baseBuiltinFunc | ||
inUnionAll bool |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add a comment for this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
expression/builtin_cast.go
Outdated
@@ -1593,6 +1626,26 @@ func (b *builtinCastJSONAsDurationSig) evalDuration(row types.Row) (res types.Du | |||
return | |||
} | |||
|
|||
// inCastContext is session key type that indicates whether executing in special cast context. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we make a more detail explaination for special
?
expression/builtin.go
Outdated
b.inUnion = from.inUnion | ||
} | ||
|
||
func newBaseBuiltinCastFunc(builtinFunc baseBuiltinFunc, inUnionAll bool) baseBuiltinCastFunc { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/ inUnionAll/ inUnion
expression/builtin.go
Outdated
func newBaseBuiltinCastFunc(builtinFunc baseBuiltinFunc, inUnionAll bool) baseBuiltinCastFunc { | ||
return baseBuiltinCastFunc{ | ||
baseBuiltinFunc: builtinFunc, | ||
inUnion: inUnionAll, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto
tk.MustQuery("select id, i, b, d, dd from t2 union select id, i, b, d, dd from t1 order by id"). | ||
Check(testkit.Rows("1 0 0 0 -1", "2 1 1 1.1 1")) | ||
tk.MustQuery("select id, i from t2 union select id, cast(i as unsigned int) from t1 order by id"). | ||
Check(testkit.Rows("1 18446744073709551615", "2 1")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what about unsigned double union int
? we need a case to cover union different types.
fix unsigned flag infer.
/run-all-tests tidb-test=pr/594 |
/run-common-test tidb-test=pr/594 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
What have you changed? (mandatory)
fix #7075
unsigned_flag
rightWhat is the type of the changes? (mandatory)
How has this PR been tested? (mandatory)
Does this PR affect documentation (docs/docs-cn) update? (mandatory)
No
Does this PR affect tidb-ansible update? (mandatory)
No
Does this PR need to be added to the release notes? (mandatory)
Refer to a related PR or issue link (optional)
#7075
Add a few positive/negative examples (optional)
see #7075 and unit test
This change is