-
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
plan: refactor the code of building Insert
.
#7068
Conversation
Please fix CI @winoros |
2e963da
to
8d99c66
Compare
/run-all-tests |
/run-integration-common-test tidb-test=pr/589 |
/run-integration-ddl-test tidb-test=pr/589 |
Integration-common-test is failed because the bug in tikv. |
@@ -1332,7 +1332,7 @@ func (s *testPlanSuite) TestVisitInfo(c *C) { | |||
ans []visitInfo | |||
}{ | |||
{ | |||
sql: "insert into t values (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.
Does the old test result in an error after this change?
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, this the length of value_list is not same with the number of table's columns.
plan/planbuilder.go
Outdated
return onDupColSet, dupCols, nil | ||
} | ||
|
||
func (p *Insert) getAffectCols(insert *ast.InsertStmt) (affectedValuesCols []*table.Column, err error) { |
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 it's better to make the receiver of this function be the planBuilder
.
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 s/getAffectCols/getInsertColumns/?
plan/planbuilder.go
Outdated
} | ||
|
||
} else if len(insert.Setlist) == 0 { | ||
// Branch for `INSERT INTO tbl_name {VALUES | VALUE} (value_list) [, (value_list)] ...`, |
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:
// This is for the following scenario:
// 1. `INSERT INTO tbl_name {VALUES | VALUE} ...`
// 2. `INSERT INTO tbl_name SELECT ...`
plan/planbuilder.go
Outdated
|
||
func (p *Insert) getAffectCols(insert *ast.InsertStmt) (affectedValuesCols []*table.Column, err error) { | ||
if len(insert.Columns) > 0 { | ||
// Branch for `INSERT INTO tbl_name (col_name [, col_name] ...) {VALUES | VALUE} (value_list) [, (value_list)] ...`, |
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:
// This is for the following scenario:
// 1. `INSERT INTO tbl_name (col_name [, col_name] ...) {VALUES | VALUE} ...`
// 2. `INSERT INTO tbl_name (col_name [, col_name] ...) SELECT ...`
plan/planbuilder.go
Outdated
} | ||
} | ||
// If the value_list and col_list is empty and we have generated column, we can still write to this table. |
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 comment should reside to if len(insert.Columns) == 0 && len(insert.Lists[0]) == 0
branch? Maybe we can remove it?
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, it's for the else
branch, explaining that else branch doesn't need to do anything.
I think it's useful. But maybe we can move it other place.
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 can put this comment to the if len(insert.Columns) > 0 || len(insert.Lists[0]) > 0 {
to explain why we don't check whether there is generated column in the table when the value_list
and col_list
is empty.
plan/planbuilder.go
Outdated
// "insert into t values (), (1)" is not valid. | ||
// "insert into t values (1), ()" is not valid. | ||
// "insert into t values (1,2), (1)" is not valid. | ||
if i > 0 && len(insert.Lists[i-1]) != len(valuesItem) { |
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.
Maybe this form can help reading the code:
if i > 0 && len(insert.Lists[i]) != len(insert.Lists[i-1])
plan/planbuilder.go
Outdated
for j, valueItem := range valuesItem { | ||
var expr expression.Expression | ||
var err error | ||
if dft, ok := valueItem.(*ast.DefaultExpr); ok { |
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.
Usually the abbreviation for default
is def
, see https://www.abbreviations.com/abbreviation/Default
plan/planbuilder.go
Outdated
for j, valueItem := range valuesItem { | ||
var expr expression.Expression | ||
var err error | ||
if dft, ok := valueItem.(*ast.DefaultExpr); ok { |
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 the "type switch" is more suitable.
plan/planbuilder.go
Outdated
return nil | ||
totalTableCols := insertPlan.Table.Cols() | ||
for i, valuesItem := range insert.Lists { | ||
// The length of the value_list should keep unchanged. |
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 lengths of all the value lists should be the same, e.g:
plan/planbuilder.go
Outdated
if err != nil { | ||
b.err = errors.Trace(err) | ||
return nil | ||
// Check that there's no generated column. |
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 @lilin90 PTAL this comment, thanks
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.
Check to guarantee that no generated column exists.
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
…into insert-plan-refactor
PTAL @zz-jason |
plan/planbuilder.go
Outdated
// i.e. table t have two column a and b where b is generated column. | ||
// `insert into t (b) select * from t` will raise a error which tells you that the column count is not matched. | ||
// `insert into t select * from t` will raise a error which tells you that there's generated column in column list. | ||
// If we do this check before the above one, the first example will raise the error same with the second example. |
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 Could help us to refine this comment block 😂
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 we do this check before the above one, "insert into t (b) select * from t" will raise an error that there's a generated column in the column list.
Note: I don't use "the first example" and "the second example" because there is only one example. "insert into t (b) select * from t" and "insert into t select * from t" are not named as two examples. Both of them are in this example.
plan/planbuilder.go
Outdated
return nil | ||
// Check to guarantee that there's no generated column. | ||
// This check is done after the above one is to make compatible with mysql. | ||
// i.e. table t have two column a and b where b is generated column. |
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.
For example, table t has two columns, namely a and b, and b is a generated column.
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.
Note: "i.e." means "that is to say" or "namely".
plan/planbuilder.go
Outdated
// This check is done after the above one is to make compatible with mysql. | ||
// i.e. table t have two column a and b where b is generated column. | ||
// `insert into t (b) select * from t` will raise a error which tells you that the column count is not matched. | ||
// `insert into t select * from t` will raise a error which tells you that there's generated column in column list. |
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.
"insert into t select * from t" will raise an error that there's a generated column in the column list.
plan/planbuilder.go
Outdated
// Check to guarantee that there's no generated column. | ||
// This check is done after the above one is to make compatible with mysql. | ||
// i.e. table t have two column a and b where b is generated column. | ||
// `insert into t (b) select * from t` will raise a error which tells you that the column count is not matched. |
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.
"insert into t (b) select * from t" will raise an error that the column count is not matched.
Note: I use "insert into t (b) select * from t" instead of insert into t (b) select * from t
because the inline code format will not be displayed in the code comments as expected.
plan/planbuilder.go
Outdated
// i.e. table t have two column a and b where b is generated column. | ||
// `insert into t (b) select * from t` will raise a error which tells you that the column count is not matched. | ||
// `insert into t select * from t` will raise a error which tells you that there's generated column in column list. | ||
// If we do this check before the above one, the first example will raise the error same with the second example. |
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 we do this check before the above one, "insert into t (b) select * from t" will raise an error that there's a generated column in the column list.
Note: I don't use "the first example" and "the second example" because there is only one example. "insert into t (b) select * from t" and "insert into t select * from t" are not named as two examples. Both of them are in this example.
plan/planbuilder.go
Outdated
b.err = errors.Trace(err) | ||
return nil | ||
// Check to guarantee that there's no generated column. | ||
// This check is done after the above one is to make compatible with mysql. |
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 check should be done after the above one to make its behavior compatible with MySQL.
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
@jackysp PTAL |
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
/run-all-tests |
/run-integration-ddl-test tidb-test=pr/589 |
What have you changed? (mandatory)
Refactor the code when building
Insert
plan.To fix some bugs make code more maintainable.
Fix #7064
Fix #7061
And another bug:
the table have 3 columns.
insert into t values(default, default, default, default)
will make tidb panicked.Main change:
We used to check the length of value_list and column_list when executing. Now we move this change to planner.
What is the type of the changes? (mandatory)
How has this PR been tested? (mandatory)
existing tests and newly added tests.
Does this PR need to be added to the release notes? (mandatory)
Fix the behavior of
INSERT
statement in some scenarios.