Repository: pingcap/tidb (Go)

Issue:
Generated columns can be wrongly moved before its referred columns
## Bug Report

Please answer these questions before submitting your issue. Thanks!

### 1. Minimal reproduce step (Required)

```sql
create table t(a int, b int as (a+1), c int as (b+1));
insert into t (a) values (1);
alter table t modify column c int as (b+1) first;
select * from t;
```

### 2. What did you expect to see? (Required)

The `alter table modify column` statement should fail with

```
ERROR 3107 (HY000): Generated column can refer only to generated columns defined prior to it.
```

### 3. What did you see instead (Required)

The `alter table modify column` statement succeeds, and the final `select` returns 

```
mysql> select * from t;
+------+------+------+
| c    | a    | b    |
+------+------+------+
| NULL |    1 |    2 |
+------+------+------+
1 row in set (0.00 sec)
```

should be related to #11365.

### 4. What is your TiDB version? (Required)

<!-- Paste the output of SELECT tidb_version() -->

549a34f1e8d6bac23e2488420574d744f36aa1e5

```
Release Version: v4.0.0-beta.2-1690-g549a34f1e
Edition: Community
Git Commit Hash: 549a34f1e8d6bac23e2488420574d744f36aa1e5
Git Branch: master
UTC Build Time: 2020-12-02 03:06:28
GoVersion: go1.13
Race Enabled: false
TiKV Min Version: v3.0.0-60965b006877ca7234adaced7890d7b029ed1306
Check Table Before Drop: false
```


Discussion:
/label severity/critical
## Please edit this comment or add a new comment to complete the following information

### Not a bug

1. Remove the 'type/bug' label
2. Add notes to indicate why it is not a bug

### Duplicate bug

1. Add the 'type/duplicate' label
2. Add the link to the original bug

### Bug

Note: Make Sure that 'component', and 'severity' labels are added
Example for how to fill out the template: https://github.com/pingcap/tidb/issues/20100

#### 1. Root Cause Analysis (RCA) (optional) 
<!-- Write down the reason why this bug occurs -->

#### 2. Symptom (optional)

<!-- What will the user see when this bug occurs. The error message may be in the terminal, log or monitoring -->

#### 3. All Trigger Conditions (optional)

<!-- All the user scenarios that may trigger this bug -->

#### 4. Workaround (optional)

#### 5. Affected versions

<!--
In the format of [start_version:end_version], multiple version ranges are
accepted. If the bug only affects the unreleased version, please input:
"unreleased". For example:

Notes:
  1. Do not use any white spaces in '[]'.
  2. The range in '[]' is a closed interval
  3. The version format is `v$Major.$Minor.$Patch`, the $Majoy and $Minor
     number in a version range should be the same. [v3.0.1:v3.1.2] is
     invalid because the $Minor number of the version range is different.

Example 1: [v3.0.1:v3.0.5], [v4.0.1:v4.0.5]
Example 2: unreleased
-->

#### 6. Fixed versions

<!--
The first released version that contains this fix in each minor version. If the bug's affected version has been released, the fixed version should be a detailed version number; If the bug doesn't affect any released version, the fixed version can be "master";  

Example 1: v3.0.13, v4.0.5
Example 2: master
-->



Patch:
diff --git a/ddl/ddl_api.go b/ddl/ddl_api.go
index ae6833a655212..5d78be49fb59e 100644
--- a/ddl/ddl_api.go
+++ b/ddl/ddl_api.go
@@ -3795,7 +3795,7 @@ func (d *ddl) getModifiableColumnJob(ctx sessionctx.Context, ident ast.Ident, or
 	}
 
 	// As same with MySQL, we don't support modifying the stored status for generated columns.
-	if err = checkModifyGeneratedColumn(t, col, newCol, specNewColumn); err != nil {
+	if err = checkModifyGeneratedColumn(t, col, newCol, specNewColumn, spec.Position); err != nil {
 		return nil, errors.Trace(err)
 	}
 
diff --git a/ddl/generated_column.go b/ddl/generated_column.go
index 62a7c6dff9588..f07790d11ab1b 100644
--- a/ddl/generated_column.go
+++ b/ddl/generated_column.go
@@ -160,7 +160,7 @@ func (c *generatedColumnChecker) Leave(inNode ast.Node) (node ast.Node, ok bool)
 //  3. check if the modified expr contains non-deterministic functions
 //  4. check whether new column refers to any auto-increment columns.
 //  5. check if the new column is indexed or stored
-func checkModifyGeneratedColumn(tbl table.Table, oldCol, newCol *table.Column, newColDef *ast.ColumnDef) error {
+func checkModifyGeneratedColumn(tbl table.Table, oldCol, newCol *table.Column, newColDef *ast.ColumnDef, pos *ast.ColumnPosition) error {
 	// rule 1.
 	oldColIsStored := !oldCol.IsGenerated() || oldCol.GeneratedStored
 	newColIsStored := !newCol.IsGenerated() || newCol.GeneratedStored
@@ -170,10 +170,17 @@ func checkModifyGeneratedColumn(tbl table.Table, oldCol, newCol *table.Column, n
 
 	// rule 2.
 	originCols := tbl.Cols()
+	var err error
 	var colName2Generation = make(map[string]columnGenerationInDDL, len(originCols))
 	for i, column := range originCols {
 		// We can compare the pointers simply.
 		if column == oldCol {
+			if pos != nil && pos.Tp != ast.ColumnPositionNone {
+				i, err = findPositionRelativeColumn(originCols, pos)
+				if err != nil {
+					return errors.Trace(err)
+				}
+			}
 			colName2Generation[newCol.Name.L] = columnGenerationInDDL{
 				position:    i,
 				generated:   newCol.IsGenerated(),
@@ -213,7 +220,8 @@ func checkModifyGeneratedColumn(tbl table.Table, oldCol, newCol *table.Column, n
 		}
 
 		// rule 4.
-		if err := checkGeneratedWithAutoInc(tbl.Meta(), newColDef); err != nil {
+		_, dependColNames := findDependedColumnNames(newColDef)
+		if err := checkAutoIncrementRef(newColDef.Name.Name.L, dependColNames, tbl.Meta()); err != nil {
 			return errors.Trace(err)
 		}
 
@@ -299,15 +307,6 @@ func checkIllegalFn4Generated(name string, genType int, expr ast.ExprNode) error
 	return nil
 }
 
-// Check whether newColumnDef refers to any auto-increment columns.
-func checkGeneratedWithAutoInc(tableInfo *model.TableInfo, newColumnDef *ast.ColumnDef) error {
-	_, dependColNames := findDependedColumnNames(newColumnDef)
-	if err := checkAutoIncrementRef(newColumnDef.Name.Name.L, dependColNames, tableInfo); err != nil {
-		return errors.Trace(err)
-	}
-	return nil
-}
-
 func checkIndexOrStored(tbl table.Table, oldCol, newCol *table.Column) error {
 	if oldCol.GeneratedExprString == newCol.GeneratedExprString {
 		return nil


Test patch:
diff --git a/executor/ddl_test.go b/executor/ddl_test.go
index 4979081c01607..e3421abb130fb 100644
--- a/executor/ddl_test.go
+++ b/executor/ddl_test.go
@@ -1262,6 +1262,12 @@ func (s *testSuite6) TestGeneratedColumnRelatedDDL(c *C) {
 	c.Assert(err.Error(), Equals, ddl.ErrBadField.GenWithStackByArgs("z", "generated column function").Error())
 
 	tk.MustExec("drop table t1;")
+
+	tk.MustExec("create table t1(a int, b int as (a+1), c int as (b+1));")
+	tk.MustExec("insert into t1 (a) values (1);")
+	tk.MustGetErrCode("alter table t1 modify column c int as (b+1) first;", mysql.ErrGeneratedColumnNonPrior)
+	tk.MustGetErrCode("alter table t1 modify column b int as (a+1) after c;", mysql.ErrGeneratedColumnNonPrior)
+	tk.MustQuery("select * from t1").Check(testkit.Rows("1 2 3"))
 }
 
 func (s *testSuite6) TestSetDDLErrorCountLimit(c *C) {