From f778a0c9bdea0e1633a4134e3b05b7db4fd15df3 Mon Sep 17 00:00:00 2001 From: Ping Yu Date: Wed, 10 Apr 2019 19:24:29 +0800 Subject: [PATCH] type/compatibility: check `KEY` option for generated column. (#9529) --- planner/core/errors.go | 3 +++ planner/core/preprocess.go | 26 +++++++++++++++++++++----- planner/core/preprocess_test.go | 5 +++++ 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/planner/core/errors.go b/planner/core/errors.go index 6612bcc0dcd7..9facfedc6d24 100644 --- a/planner/core/errors.go +++ b/planner/core/errors.go @@ -75,6 +75,7 @@ const ( codeWindowRangeBoundNotConstant = mysql.ErrWindowRangeBoundNotConstant codeWindowRowsIntervalUse = mysql.ErrWindowRowsIntervalUse codeWindowFunctionIgnoresFrame = mysql.ErrWindowFunctionIgnoresFrame + codeUnsupportedOnGeneratedColumn = mysql.ErrUnsupportedOnGeneratedColumn ) // error definitions. @@ -136,6 +137,7 @@ var ( ErrWindowRangeBoundNotConstant = terror.ClassOptimizer.New(codeWindowRangeBoundNotConstant, mysql.MySQLErrName[mysql.ErrWindowRangeBoundNotConstant]) ErrWindowRowsIntervalUse = terror.ClassOptimizer.New(codeWindowRowsIntervalUse, mysql.MySQLErrName[mysql.ErrWindowRowsIntervalUse]) ErrWindowFunctionIgnoresFrame = terror.ClassOptimizer.New(codeWindowFunctionIgnoresFrame, mysql.MySQLErrName[mysql.ErrWindowFunctionIgnoresFrame]) + ErrUnsupportedOnGeneratedColumn = terror.ClassOptimizer.New(codeUnsupportedOnGeneratedColumn, mysql.MySQLErrName[mysql.ErrUnsupportedOnGeneratedColumn]) ErrNoSuchThread = terror.ClassOptimizer.New(mysql.ErrNoSuchThread, mysql.MySQLErrName[mysql.ErrNoSuchThread]) // Since we cannot know if user loggined with a password, use message of ErrAccessDeniedNoPassword instead ErrAccessDenied = terror.ClassOptimizer.New(mysql.ErrAccessDenied, mysql.MySQLErrName[mysql.ErrAccessDeniedNoPassword]) @@ -190,6 +192,7 @@ func init() { codeWindowRangeBoundNotConstant: mysql.ErrWindowRangeBoundNotConstant, codeWindowRowsIntervalUse: mysql.ErrWindowRowsIntervalUse, codeWindowFunctionIgnoresFrame: mysql.ErrWindowFunctionIgnoresFrame, + codeUnsupportedOnGeneratedColumn: mysql.ErrUnsupportedOnGeneratedColumn, mysql.ErrNoSuchThread: mysql.ErrNoSuchThread, mysql.ErrAccessDenied: mysql.ErrAccessDenied, diff --git a/planner/core/preprocess.go b/planner/core/preprocess.go index 3ebd9f3eb9de..862d0ae44dab 100644 --- a/planner/core/preprocess.go +++ b/planner/core/preprocess.go @@ -331,7 +331,12 @@ func (p *preprocessor) checkCreateTableGrammar(stmt *ast.CreateTableStmt) { p.err = err return } - countPrimaryKey += isPrimary(colDef.Options) + isPrimary, err := checkColumnOptions(colDef.Options) + if err != nil { + p.err = err + return + } + countPrimaryKey += isPrimary if countPrimaryKey > 1 { p.err = infoschema.ErrMultiplePriKey return @@ -428,13 +433,24 @@ func isTableAliasDuplicate(node ast.ResultSetNode, tableAliases map[string]inter return nil } -func isPrimary(ops []*ast.ColumnOption) int { +func checkColumnOptions(ops []*ast.ColumnOption) (int, error) { + isPrimary, isGenerated, isStored := 0, 0, false + for _, op := range ops { - if op.Tp == ast.ColumnOptionPrimaryKey { - return 1 + switch op.Tp { + case ast.ColumnOptionPrimaryKey: + isPrimary = 1 + case ast.ColumnOptionGenerated: + isGenerated = 1 + isStored = op.Stored } } - return 0 + + if isPrimary > 0 && isGenerated > 0 && !isStored { + return isPrimary, ErrUnsupportedOnGeneratedColumn.GenWithStackByArgs("Defining a virtual generated column as primary key") + } + + return isPrimary, nil } func (p *preprocessor) checkCreateIndexGrammar(stmt *ast.CreateIndexStmt) { diff --git a/planner/core/preprocess_test.go b/planner/core/preprocess_test.go index 6cf90b99524d..045812eeb546 100644 --- a/planner/core/preprocess_test.go +++ b/planner/core/preprocess_test.go @@ -198,6 +198,11 @@ func (s *testValidatorSuite) TestValidator(c *C) { {"select * from (select 1 ) a , (select 2) b, (select * from (select 3) a join (select 4) b) c;", false, nil}, {"CREATE VIEW V (a,b,c) AS SELECT 1,1,3;", false, nil}, + + // issue 9464 + {"CREATE TABLE t1 (id INT NOT NULL, c1 VARCHAR(20) AS ('foo') VIRTUAL KEY NULL, PRIMARY KEY (id));", false, core.ErrUnsupportedOnGeneratedColumn}, + {"CREATE TABLE t1 (id INT NOT NULL, c1 VARCHAR(20) AS ('foo') VIRTUAL KEY NOT NULL, PRIMARY KEY (id));", false, core.ErrUnsupportedOnGeneratedColumn}, + {"create table t (a DOUBLE NULL, b_sto DOUBLE GENERATED ALWAYS AS (a + 2) STORED UNIQUE KEY NOT NULL PRIMARY KEY);", false, nil}, } store, dom, err := newStoreWithBootstrap()